Mercurial > hg > xemacs-beta
comparison lisp/packages/gnuserv.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 ; Lisp Interface code between GNU Emacs and gnuserv. | |
2 ; | |
3 ; This file is part of GNU Emacs. | |
4 ; | |
5 ; Copying is permitted under those conditions described by the GNU | |
6 ; General Public License. | |
7 ; | |
8 ; Copyright (C) 1989-1994 Free Software Foundation, Inc. | |
9 ; | |
10 ; Author: Andy Norman (ange@hplb.hpl.hp.com) based on | |
11 ; 'lisp/server.el' from the 18.52 GNU Emacs distribution. | |
12 ; | |
13 ; Please mail bugs and suggestions to the author at the above address. | |
14 ; | |
15 ;;; Synched up with: Not in FSF. | |
16 | |
17 ; Updated for XEmacs, GNU Emacs 19 and Epoch V4 to use multiple frames | |
18 ; by Bob Weiner, <weiner@mot.com>, 1/20/94. (Still works with Emacs V18, too.) | |
19 ; Modified 'server-process-filter' to use \^D as end of request terminator | |
20 ; as gnuclient and gnudoit have been modified to send. This permits | |
21 ; multi-line requests. | |
22 ; Modified 'server-make-window-visible' to work with multiple frames. | |
23 ; Modified 'server-find-file' to display in a separate frame when possible. | |
24 ; Modified 'server-edit' to delete newly created frame when used to | |
25 ; terminate an edit and to signal an error if called within a | |
26 ; non-server-edit buffer. | |
27 ; Bob Weiner, <weiner@mot.com>, 5/9/94. | |
28 ; Added 'server-done-function' variable. Made default value 'kill-buffer' | |
29 ; instead of 'bury-buffer' as in original gnuserv.el. | |
30 ; | |
31 ; Darrell Kindred <dkindred+@cmu.edu> May/1994 | |
32 ; Updated to allow multi-line return values: | |
33 ; - output to gnuserv is "m/n:xxx" where m is the client number, | |
34 ; n is the length of the data, and xxx is the data itself, followed | |
35 ; by newline | |
36 ; | |
37 ; Arup Mukherjee <arup+@cmu.edu> May/1994 | |
38 ; Updated for XEmacs 19.10, and others: | |
39 ; - use find-file-other-screen if present | |
40 ; - new variable gnuserv-frame can be set to a frame or screen which is | |
41 ; is used for all edited files. | |
42 ; - check to see if server.el is already loaded and complain if it is, since | |
43 ; gnuserv.el can't coexist with server.el | |
44 ; - rename server-start to gnuserv-start, although server-start remains as | |
45 ; an alias. This allows gnuserv-start to be autoloaded from gnuserv.el | |
46 ; - changed server-get-buffer to take into account that in newer emacsen, | |
47 ; get buffer returns nil on deleted buffers. | |
48 ; - only try to create/delete frames or screens if window-system is non-nil | |
49 ; (otherwise things don't work w/ emacs19 on a dumb terminal) | |
50 ; | |
51 ; Ben Wing <wing@666.com> sometime in 1995 | |
52 ; Updated to allow `gnuattach'-type connections to the existing TTY | |
53 ; | |
54 ; Ben Wing <wing@666.com> May/1996 | |
55 ; patch to get TTY terminal type correct. | |
56 | |
57 | |
58 | |
59 (defconst gnuserv-rcs-header-id "!Header: gnuserv.el,v 2.1 95/02/16 12:00:16 arup alpha !") | |
60 | |
61 | |
62 ;; server.el and gnuserv.el can't coexist because of conflicting defvar's and | |
63 ;; function names. | |
64 | |
65 (if (and (boundp 'server-buffer-clients) | |
66 (not (featurep 'gnuserv))) | |
67 (error "Can't run gnuserv because server.el appears to be loaded already")) | |
68 | |
69 (defvar gnuserv-frame nil | |
70 "*If non-nil, the frame to be used to display all edited files. | |
71 If nil, then a new frame is created for each file edited. | |
72 This variable has no effect in XEmacs versions older than 19.9.") | |
73 | |
74 (defvar server-done-function 'kill-buffer | |
75 "*A function of one argument, a buffer, which removes the buffer after editing. | |
76 Functions such as 'kill-buffer' and 'bury-buffer' are good values.") | |
77 | |
78 (defvar server-program "gnuserv" | |
79 "*The program to use as the edit server") | |
80 | |
81 (defvar server-process nil | |
82 "The current server process") | |
83 | |
84 (defvar server-string "" | |
85 "The last input string from the server") | |
86 | |
87 (defvar current-client nil | |
88 "The client we are currently talking to") | |
89 | |
90 (defvar server-clients nil | |
91 "List of current server clients. | |
92 Each element is (CLIENTID BUFFER...) where CLIENTID is an integer | |
93 that can be given to the server process to identify a client. | |
94 When a buffer is killed, it is removed from this list.") | |
95 | |
96 (defvar server-buffer-clients nil | |
97 "List of client ids for clients requesting editing of the current buffer.") | |
98 | |
99 (make-variable-buffer-local 'server-buffer-clients) | |
100 (setq-default server-buffer-clients nil) | |
101 (or (assq 'server-buffer-clients minor-mode-alist) | |
102 (setq minor-mode-alist (cons '(server-buffer-clients " Server") | |
103 minor-mode-alist))) | |
104 | |
105 (defun server-log (string) | |
106 "If a *server* buffer exists, write STRING to it for logging purposes." | |
107 (if (get-buffer "*server*") | |
108 (save-excursion | |
109 (set-buffer "*server*") | |
110 (goto-char (point-max)) | |
111 (insert string) | |
112 (or (bolp) (newline))))) | |
113 | |
114 | |
115 (defun server-sentinel (proc msg) | |
116 (cond ((eq (process-status proc) 'exit) | |
117 (server-log (message "Server subprocess exited"))) | |
118 ((eq (process-status proc) 'signal) | |
119 (server-log (message "Server subprocess killed"))))) | |
120 | |
121 | |
122 (defun server-process-display-error (string) | |
123 "Whenever a gnuserv error is reported, display it in a pop-up window." | |
124 (let ((cur (selected-window)) | |
125 (pop-up-windows t)) | |
126 (pop-to-buffer (get-buffer-create "*server*")) | |
127 (set-window-start (selected-window) (point)) | |
128 (server-log string) | |
129 (select-window cur))) | |
130 | |
131 | |
132 (defun server-process-filter (proc string) | |
133 "Process client gnuserv requests to execute Emacs commands." | |
134 (setq server-string (concat server-string string)) | |
135 (if (string-match "\^D$" server-string) ; requests end with ctrl-D | |
136 (if (string-match "^[0-9]+" server-string) ; client request id | |
137 (progn | |
138 (server-log server-string) | |
139 (let ((header (read-from-string server-string))) | |
140 (setq current-client (car header)) | |
141 (condition-case oops | |
142 (eval (car (read-from-string server-string | |
143 (cdr header)))) | |
144 (error (setq server-string "") | |
145 (server-write-to-client current-client oops) | |
146 (setq current-client nil) | |
147 (signal (car oops) (cdr oops))) | |
148 (quit (setq server-string "") | |
149 (server-write-to-client current-client oops) | |
150 (setq current-client nil) | |
151 (signal 'quit nil))) | |
152 (setq server-string ""))) | |
153 (progn ;error string from server | |
154 (server-process-display-error server-string) | |
155 (setq server-string ""))))) | |
156 | |
157 | |
158 (defun server-release-outstanding-buffers () | |
159 "Release all buffers that have clients waiting for them to be finished." | |
160 (interactive) | |
161 (while server-clients | |
162 (let ((buffer (nth 1 (car server-clients)))) ; for all buffers... | |
163 (server-buffer-done buffer)))) ; destructively modifies server-clients | |
164 | |
165 ;;;###autoload | |
166 (defun gnuserv-start (&optional leave-dead) | |
167 "Allow this Emacs process to be a server for client processes. | |
168 This starts a server communications subprocess through which | |
169 client \"editors\" (gnuclient and gnudoit) can send editing commands to | |
170 this Emacs job. See the gnuserv(1) manual page for more details. | |
171 | |
172 Prefix arg means just kill any existing server communications subprocess." | |
173 (interactive "P") | |
174 ;; kill it dead! | |
175 (if server-process | |
176 (progn | |
177 (server-release-outstanding-buffers) | |
178 (set-process-sentinel server-process nil) | |
179 (condition-case () | |
180 (delete-process server-process) | |
181 (error nil)))) | |
182 ;; If we already had a server, clear out associated status. | |
183 (if leave-dead | |
184 nil | |
185 (if server-process | |
186 (server-log (message "Restarting server"))) | |
187 (setq server-string "") | |
188 (setq current-client nil) | |
189 (let ((process-connection-type t)) | |
190 (setq server-process | |
191 (start-process "server" nil server-program))) | |
192 (set-process-sentinel server-process 'server-sentinel) | |
193 (set-process-filter server-process 'server-process-filter) | |
194 (process-kill-without-query server-process))) | |
195 | |
196 ;; make gnuserv-start an alias to server-start, for backward compatibility | |
197 (fset 'server-start (function gnuserv-start)) | |
198 | |
199 | |
200 (defun server-write-to-client (client form) | |
201 "Write the given form to the given client via the server process." | |
202 (if (and client | |
203 (eq (process-status server-process) 'run)) | |
204 (let* ((result (format "%s" form)) | |
205 (s (format "%s/%d:%s\n" client (length result) result))) | |
206 (process-send-string server-process s) | |
207 (server-log s)))) | |
208 | |
209 (defun server-eval (form) | |
210 "Evaluate form and return result to client." | |
211 (server-write-to-client current-client (eval form)) | |
212 (setq current-client nil)) | |
213 | |
214 | |
215 (defun server-eval-quickly (form) | |
216 "Let client know that we've received the request, but eval the form | |
217 afterwards in order to not keep the client waiting." | |
218 (server-write-to-client current-client nil) | |
219 (setq current-client nil) | |
220 (eval form)) | |
221 | |
222 | |
223 (defun server-make-window-visible () | |
224 "Try to make this window even more visible." | |
225 (and (boundp 'window-system) | |
226 (boundp 'window-system-version) | |
227 (eq window-system 'x) | |
228 (eq window-system-version 11) | |
229 (cond ((fboundp 'raise-frame) | |
230 (raise-frame (selected-frame))) | |
231 ((fboundp 'deiconify-screen) | |
232 (deiconify-screen (selected-screen)) | |
233 (raise-screen (selected-screen))) | |
234 ((fboundp 'mapraised-screen) | |
235 (mapraised-screen)) | |
236 ((fboundp 'x-remap-window) | |
237 (x-remap-window) | |
238 ;; give window chance to re-display text | |
239 (accept-process-output))))) | |
240 | |
241 (defun server-tty-find-file (tty termtype file) | |
242 (let ((device (make-tty-device tty termtype))) | |
243 (select-frame (make-frame nil device)) | |
244 (if (not file) | |
245 (switch-to-buffer (get-buffer-create "*scratch*")) | |
246 (find-file file)))) | |
247 | |
248 (defun server-find-file (file) | |
249 "Edit file FILENAME. | |
250 Switch to a buffer visiting file FILENAME, | |
251 creating one if none already exists." | |
252 (let ((obuf (get-file-buffer file)) | |
253 ;; XEmacs addition. | |
254 (force-dialog-box-use t)) | |
255 (if (and obuf (set-buffer obuf)) | |
256 (if (file-exists-p file) | |
257 (if (or (not (verify-visited-file-modtime obuf)) | |
258 (buffer-modified-p obuf)) | |
259 (revert-buffer t nil)) | |
260 (if (y-or-n-p | |
261 (concat "File no longer exists: " | |
262 file | |
263 ", write buffer to file? ")) | |
264 (write-file file)))) | |
265 (cond ((and window-system | |
266 gnuserv-frame (fboundp 'frame-live-p) ;; v19 & XEmacs 19.12+ | |
267 (frame-live-p gnuserv-frame)) | |
268 (select-frame gnuserv-frame) | |
269 (find-file file)) | |
270 | |
271 ((and window-system | |
272 gnuserv-frame (fboundp 'live-screen-p) ;; XEmacs 19.9+ | |
273 (live-screen-p gnuserv-frame)) | |
274 (select-screen gnuserv-frame) | |
275 (find-file file)) | |
276 | |
277 ((and window-system | |
278 (fboundp 'select-frame)) ;; v19 & XEmacs 19.12+ | |
279 (select-frame (make-frame)) | |
280 (find-file file)) | |
281 | |
282 ((and window-system | |
283 (fboundp 'select-screen) ;; XEmacs 19.10+ | |
284 (fboundp 'make-screen)) | |
285 (select-screen (make-screen)) | |
286 (find-file file)) | |
287 | |
288 ((and (eq window-system 'x) ;; XEmacs 19.9- | |
289 (fboundp 'select-screen) | |
290 (fboundp 'x-create-screen)) | |
291 (select-screen (x-create-screen nil)) | |
292 (find-file file)) | |
293 | |
294 ((and window-system | |
295 (fboundp 'create-screen)) ;; epoch | |
296 (if (screenp gnuserv-frame) | |
297 (progn (select-screen gnuserv-frame) | |
298 (find-file file)) | |
299 (select-screen (create-screen (find-file-noselect file))))) | |
300 | |
301 (t (find-file file))))) ;; emacs18+ | |
302 | |
303 | |
304 (defun server-edit-files-quickly (list) | |
305 "For each (line-number . file) pair in LIST, edit the file at line-number. | |
306 Unlike (server-edit-files), no information is saved about clients waiting on | |
307 edits to this buffer." | |
308 (server-write-to-client current-client nil) | |
309 (setq current-client nil) | |
310 (while list | |
311 (let ((line (car (car list))) | |
312 (path (cdr (car list)))) | |
313 (server-find-file path) | |
314 (server-make-window-visible) | |
315 (goto-line line)) | |
316 (setq list (cdr list)))) | |
317 | |
318 | |
319 (defun server-edit-files (list) | |
320 "For each (line-number . file) pair in LIST, edit the file at line-number. | |
321 Save enough information for (server-kill-buffer) to inform the client when | |
322 the edit is finished." | |
323 (while list | |
324 (let ((line (car (car list))) | |
325 (path (cdr (car list)))) | |
326 (server-find-file path) | |
327 (server-make-window-visible) | |
328 (let ((old-clients (assq current-client server-clients)) | |
329 (buffer (current-buffer))) | |
330 (goto-line line) | |
331 (setq server-buffer-clients | |
332 (cons current-client server-buffer-clients)) | |
333 (if old-clients ;client already waiting for buffers? | |
334 (nconc old-clients (list buffer)) ;yes -- append this one as well | |
335 (setq server-clients ;nope -- make a new record | |
336 (cons (list current-client buffer) | |
337 server-clients))))) | |
338 (setq list (cdr list))) | |
339 (message (substitute-command-keys | |
340 (if (and (boundp 'infodock-version) window-system) | |
341 "Type {\\[server-edit]} or select Frame/Delete to finish edit." | |
342 "When done with a buffer, type \\[server-edit].")))) | |
343 | |
344 (defun server-tty-edit-files (tty termtype list) | |
345 "For each (line-number . file) pair in LIST, edit the file at line-number. | |
346 Save enough information for (server-kill-buffer) to inform the client when | |
347 the edit is finished." | |
348 (or list (setq list '((1 . nil)))) | |
349 (while list | |
350 (let ((line (car (car list))) | |
351 (path (cdr (car list)))) | |
352 (server-tty-find-file tty termtype path) | |
353 (server-make-window-visible) | |
354 (let ((old-clients (assq current-client server-clients)) | |
355 (buffer (current-buffer))) | |
356 (goto-line line) | |
357 (setq server-buffer-clients | |
358 (cons current-client server-buffer-clients)) | |
359 (if old-clients ;client already waiting for buffers? | |
360 (nconc old-clients (list buffer)) ;yes -- append this one as well | |
361 (setq server-clients ;nope -- make a new record | |
362 (cons (list current-client buffer) | |
363 server-clients))))) | |
364 (setq list (cdr list))) | |
365 (message (substitute-command-keys | |
366 (if (and (boundp 'infodock-version) window-system) | |
367 "Type {\\[server-edit]} or select Frame/Delete to finish edit." | |
368 "When done with a buffer, type \\[server-edit].")))) | |
369 | |
370 (defun server-get-buffer (buffer) | |
371 "One arg, a BUFFER or a buffer name. Return the buffer object even if killed. | |
372 Signal an error if there is no record of BUFFER." | |
373 (if (null buffer) | |
374 (current-buffer) | |
375 (let ((buf (get-buffer buffer))) | |
376 (if (null buf) | |
377 (if (bufferp buffer) | |
378 buffer | |
379 (if (stringp buffer) | |
380 (error "No buffer named %s" buffer) | |
381 (error "Invalid buffer argument"))) | |
382 buf)))) | |
383 | |
384 (defun server-kill-buffer (buffer) | |
385 "Kill the BUFFER. The argument may be a buffer object or buffer name. | |
386 NOTE: This function has been enhanced to allow for remote editing | |
387 in the following way: | |
388 | |
389 If the buffer is waited upon by one or more clients, and a client is | |
390 not waiting for other buffers to be killed, then the client is told | |
391 that the buffer has been killed." | |
392 (interactive "bKill buffer ") | |
393 (setq buffer (server-get-buffer buffer)) | |
394 (if (buffer-name buffer) | |
395 (save-excursion | |
396 (set-buffer buffer) | |
397 (let ((old-clients server-clients)) | |
398 (server-real-kill-buffer buffer) ;try to kill it | |
399 (if (buffer-name buffer) ;succeeded in killing? | |
400 nil ;nope | |
401 (while old-clients | |
402 (let ((client (car old-clients))) | |
403 (delq buffer client) | |
404 (if (cdr client) ;pending buffers? | |
405 nil ;yep | |
406 (server-write-to-client (car client) nil) ;nope, tell client | |
407 (setq server-clients (delq client server-clients)))) | |
408 (setq old-clients (cdr old-clients)))))))) | |
409 | |
410 | |
411 (defun server-kill-all-local-variables () | |
412 "Eliminate all the buffer-local variable values of the current buffer. | |
413 This buffer will then see the default values of all variables. | |
414 NOTE: This function has been modified to ignore the variable | |
415 server-buffer-clients." | |
416 (let ((clients server-buffer-clients)) | |
417 (server-real-kill-all-local-variables) | |
418 (if clients | |
419 (setq server-buffer-clients clients)))) | |
420 | |
421 | |
422 (or (fboundp 'server-real-kill-buffer) | |
423 (fset 'server-real-kill-buffer (symbol-function 'kill-buffer))) | |
424 | |
425 (fset 'kill-buffer 'server-kill-buffer) | |
426 | |
427 (or (fboundp 'server-real-kill-all-local-variables) | |
428 (fset 'server-real-kill-all-local-variables | |
429 (symbol-function 'kill-all-local-variables))) | |
430 | |
431 (fset 'kill-all-local-variables 'server-kill-all-local-variables) | |
432 | |
433 | |
434 (defun server-buffer-done (buffer) | |
435 "Mark BUFFER as \"done\" for its client(s). | |
436 Buries the buffer, and returns another server buffer as a suggestion for the | |
437 new current buffer." | |
438 (let ((next-buffer nil) | |
439 (old-clients server-clients)) | |
440 (while old-clients | |
441 (let ((client (car old-clients))) | |
442 (or next-buffer | |
443 (setq next-buffer (nth 1 (memq buffer client)))) | |
444 (delq buffer client) | |
445 ;; If client now has no pending buffers, | |
446 ;; tell it that it is done, and forget it entirely. | |
447 (if (cdr client) | |
448 nil | |
449 (server-write-to-client (car client) nil) | |
450 (setq server-clients (delq client server-clients)))) | |
451 (setq old-clients (cdr old-clients))) | |
452 (if (buffer-name buffer) | |
453 (save-excursion | |
454 (set-buffer buffer) | |
455 (setq server-buffer-clients nil))) | |
456 (funcall server-done-function buffer) | |
457 next-buffer)) | |
458 | |
459 | |
460 (defun mh-draft-p (buffer) | |
461 "Return non-nil if this BUFFER is an mh <draft> file. Since MH deletes | |
462 draft *BEFORE* it is edited, the server treats them specially." | |
463 ;; This may not be appropriately robust for all cases. | |
464 (string= (buffer-name buffer) "draft")) | |
465 | |
466 | |
467 (defun server-done () | |
468 "Offer to save current buffer and mark it as \"done\" for clients. | |
469 Also bury it, and return a suggested new current buffer." | |
470 (let ((buffer (current-buffer))) | |
471 (if server-buffer-clients | |
472 (progn | |
473 (if (mh-draft-p buffer) | |
474 (progn (save-buffer) | |
475 (write-region (point-min) (point-max) | |
476 (concat buffer-file-name "~")) | |
477 (kill-buffer buffer)) | |
478 (if (and (buffer-modified-p) | |
479 (y-or-n-p (concat "Save file " buffer-file-name "? "))) | |
480 (save-buffer buffer))) | |
481 (server-buffer-done buffer))))) | |
482 | |
483 | |
484 (defun server-edit (&optional arg) | |
485 "Switch to next server editing buffer and mark current one as \"done\". | |
486 If a server buffer is current, it is marked \"done\" and optionally saved. | |
487 MH <draft> files are always saved and backed up, no questions asked. | |
488 When all of a client's buffers are marked as \"done\", the client is notified. | |
489 | |
490 If invoked with a prefix argument, or if there is no server process running, | |
491 starts server process and that is all. Invoked by \\[server-edit]." | |
492 (interactive "P") | |
493 (if (or arg | |
494 (not server-process) | |
495 (memq (process-status server-process) '(signal exit))) | |
496 (server-start nil) | |
497 (if server-buffer-clients | |
498 (progn (server-switch-buffer (server-done)) | |
499 (cond ((or ;(not window-system) #### someone examine! | |
500 (and gnuserv-frame | |
501 (or (and (fboundp 'frame-live-p) | |
502 (frame-live-p gnuserv-frame)) | |
503 (and (fboundp 'live-screen-p) | |
504 (live-screen-p gnuserv-frame)) | |
505 (and (fboundp 'create-screen) | |
506 (screenp gnuserv-frame))))) | |
507 ()) ;; do nothing | |
508 ((fboundp 'delete-frame) | |
509 (delete-frame (selected-frame) t)) | |
510 ((fboundp 'delete-screen) | |
511 (delete-screen)))) | |
512 (error | |
513 "(server-edit): Use only on buffers created by external programs.") | |
514 ))) | |
515 | |
516 (defun server-switch-buffer (next-buffer) | |
517 "Switch to NEXT-BUFFER if a live buffer, otherwise switch to another buffer | |
518 with gnuserv clients. If no such buffer is available, simply choose another | |
519 one." | |
520 (if next-buffer | |
521 (if (and (bufferp next-buffer) | |
522 (buffer-name next-buffer)) | |
523 (switch-to-buffer next-buffer) | |
524 ;; If NEXT-BUFFER is a dead buffer, | |
525 ;; remove the server records for it | |
526 ;; and try the next surviving server buffer. | |
527 (server-switch-buffer | |
528 (server-buffer-done next-buffer))) | |
529 (if server-clients | |
530 (server-switch-buffer (nth 1 (car server-clients))) | |
531 (switch-to-buffer (other-buffer))))) | |
532 | |
533 (global-set-key "\C-x#" 'server-edit) | |
534 | |
535 (provide 'gnuserv) | |
536 |