1 ;;; gnuserv.el --- Lisp interface code between Emacs and gnuserv
2 ;; Copyright (C) 1989-1997 Free Software Foundation, Inc.
5 ;; Author: Andy Norman (ange@hplb.hpl.hp.com), originally based on server.el
6 ;; Hrvoje Niksic <hniksic@xemacs.org>
7 ;; Maintainer: Jan Vroonhof <vroonhof@math.ethz.ch>,
8 ;; Hrvoje Niksic <hniksic@xemacs.org>
9 ;; Keywords: environment, processes, terminals
11 ;; This file is part of SXEmacs.
13 ;; SXEmacs is free software: you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation, either version 3 of the License, or
16 ;; (at your option) any later version.
18 ;; SXEmacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
26 ;;; Synched up with: Not in FSF.
30 ;; Gnuserv is run when Emacs needs to operate as a server for other
31 ;; processes. Specifically, any number of files can be attached for
32 ;; editing to a running XEmacs process using the `gnuclient' program.
34 ;; Use `M-x gnuserv-start' to start the server and `gnuclient files'
35 ;; to load them to XEmacs. When you are done with a buffer, press
36 ;; `C-x #' (`M-x gnuserv-edit'). You can put (gnuserv-start) to your
37 ;; .emacs, and enable `gnuclient' as your Unix "editor". When all the
38 ;; buffers for a client have been edited and exited with
39 ;; `gnuserv-edit', the client "editor" will return to the program that
42 ;; Your editing commands and Emacs' display output go to and from the
43 ;; terminal or X display in the usual way. If you are running under
44 ;; X, a new X frame will be open for each gnuclient. If you are on a
45 ;; TTY, this TTY will be attached as a new device to the running
46 ;; XEmacs, and will be removed once you are done with the buffer.
48 ;; To evaluate a Lisp form in a running Emacs, use the `-eval'
49 ;; argument of gnuclient. To simplify this, we provide the `gnudoit'
50 ;; shell script. For example `gnudoit "(+ 2 3)"' will print `5',
51 ;; whereas `gnudoit "(gnus)"' will fire up your favorite newsreader.
52 ;; Like gnuclient, `gnudoit' requires the server to be started prior
55 ;; For more information you can refer to man pages of gnuclient,
56 ;; gnudoit and gnuserv, distributed with XEmacs.
58 ;; gnuserv.el was originally written by Andy Norman as an improvement
59 ;; over William Sommerfeld's server.el. Since then, a number of
60 ;; people have worked on it, including Bob Weiner, Darell Kindred,
61 ;; Arup Mukherjee, Ben Wing and Jan Vroonhof. It was completely
62 ;; rewritten (labeled as version 3) by Hrvoje Niksic in May 1997. The
63 ;; new code will not run on GNU Emacs.
65 ;; Jan Vroonhof <vroonhof@math.ethz.ch> July/1996
66 ;; ported the server-temp-file-regexp feature from server.el
67 ;; ported server hooks from server.el
68 ;; ported kill-*-query functions from server.el (and made it optional)
69 ;; synced other behavior with server.el
74 ;; Hrvoje Niksic <hniksic@xemacs.org> May/1997
75 ;; Completely rewritten. Now uses `defstruct' and other CL stuff
76 ;; to define clients cleanly. Many thanks to Dave Gillespie!
78 ;; Mike Scheidler <c23mts@eng.delcoelect.com> July, 1997
79 ;; Added 'Done' button to the menubar.
85 "The gnuserv suite of programs to talk to Emacs from outside."
91 (defcustom gnuserv-mode-line-string " Server"
92 "*String to display in the modeline when Gnuserv is active.
93 Set this to nil if you don't want a modeline indicator."
95 (const :tag "none" nil))
99 ;; Provide the old variables as aliases, to avoid breaking .emacs
100 ;; files. However, they are obsolete and should be converted to the
101 ;; new forms. This ugly crock must be before the variable
102 ;; declaration, or the scheme fails.
104 (define-obsolete-variable-alias 'server-frame 'gnuserv-frame)
105 (define-obsolete-variable-alias 'server-done-function
106 'gnuserv-done-function)
107 (define-obsolete-variable-alias 'server-done-temp-file-function
108 'gnuserv-done-temp-file-function)
109 (define-obsolete-variable-alias 'server-find-file-function
110 'gnuserv-find-file-function)
111 (define-obsolete-variable-alias 'server-program
113 (define-obsolete-variable-alias 'server-visit-hook
115 (define-obsolete-variable-alias 'server-done-hook
117 (define-obsolete-variable-alias 'server-kill-quietly
118 'gnuserv-kill-quietly)
119 (define-obsolete-variable-alias 'server-temp-file-regexp
120 'gnuserv-temp-file-regexp)
121 (define-obsolete-variable-alias 'server-make-temp-file-backup
122 'gnuserv-make-temp-file-backup)
125 (defcustom gnuserv-frame nil
126 "*The frame to be used to display all edited files.
127 If nil, then a new frame is created for each file edited.
128 If t, then the currently selected frame will be used.
129 If a function, then this will be called with a symbol `x' or `tty' as the
130 only argument, and its return value will be interpreted as above."
132 :type '(radio (const :tag "Create new frame each time" nil)
133 (const :tag "Use selected frame" t)
134 (function-item :tag "Use main Emacs frame"
135 gnuserv-main-frame-function)
136 (function-item :tag "Use visible frame, otherwise create new"
137 gnuserv-visible-frame-function)
138 (function-item :tag "Create special Gnuserv frame and use it"
139 gnuserv-special-frame-function)
140 (function :tag "Other"))
144 (defcustom gnuserv-frame-plist nil
145 "*Plist of frame properties for creating a gnuserv frame."
150 (defcustom gnuserv-done-function 'kill-buffer
151 "*Function used to remove a buffer after editing.
152 It is called with one BUFFER argument. Functions such as `kill-buffer' and
153 `bury-buffer' are good values. See also `gnuserv-done-temp-file-function'."
154 :type '(radio (function-item kill-buffer)
155 (function-item bury-buffer)
156 (function :tag "Other"))
159 (defcustom gnuserv-done-temp-file-function 'kill-buffer
160 "*Function used to remove a temporary buffer after editing.
161 It is called with one BUFFER argument. Functions such as `kill-buffer' and
162 `bury-buffer' are good values. See also `gnuserv-done-temp-file-function'."
163 :type '(radio (function-item kill-buffer)
164 (function-item bury-buffer)
165 (function :tag "Other"))
168 (defcustom gnuserv-find-file-function 'find-file
169 "*Function to visit a file with.
170 It takes one argument, a file name to visit."
174 (defcustom gnuserv-view-file-function 'view-file
175 "*Function to view a file with.
176 It takes one argument, a file name to view."
177 :type '(radio (function-item view-file)
178 (function-item find-file-read-only)
179 (function :tag "Other"))
182 (defcustom gnuserv-program "gnuserv"
183 "*Program to use as the editing server."
187 (defcustom gnuserv-visit-hook nil
188 "*Hook run after visiting a file."
192 (defcustom gnuserv-done-hook nil
193 "*Hook run when done editing a buffer for the Emacs server.
194 The hook functions are called after the file has been visited, with the
195 current buffer set to the visiting buffer."
199 (defcustom gnuserv-init-hook nil
200 "*Hook run after the server is started."
204 (defcustom gnuserv-shutdown-hook nil
205 "*Hook run before the server exits."
209 (defcustom gnuserv-kill-quietly nil
210 "*Non-nil means to kill buffers with clients attached without requiring confirmation."
214 (defcustom gnuserv-temp-file-regexp
215 (concat "^" (temp-directory) "/Re\\|/draft$")
216 "*Regexp which should match filenames of temporary files deleted
217 and reused by the programs that invoke the Emacs server."
221 (defcustom gnuserv-make-temp-file-backup nil
222 "*Non-nil makes the server backup temporary files also."
227 ;;; Internal variables:
230 "An object that encompasses several buffers in one.
231 Normally, a client connecting to Emacs will be assigned an id, and
232 will request editing of several files.
234 ID - Client id (integer).
235 BUFFERS - List of buffers that \"belong\" to the client.
236 NOTE: one buffer can belong to several clients.
237 DEVICE - The device this client is on. If the device was also created.
238 by a client, it will be placed to `gnuserv-devices' list.
239 FRAME - Frame created by the client, or nil if the client didn't
242 All the slots default to nil."
248 (defvar gnuserv-process nil
249 "The current gnuserv process.")
251 (defvar gnuserv-string ""
252 "The last input string from the server.")
254 (defvar gnuserv-current-client nil
255 "The client we are currently talking to.")
257 (defvar gnuserv-clients nil
258 "List of current gnuserv clients.
259 Each element is a gnuclient structure that identifies a client.")
261 (defvar gnuserv-devices nil
262 "List of devices created by clients.")
264 (defvar gnuserv-special-frame nil
265 "Frame created specially for Server.")
267 ;; We want the client-infested buffers to have some modeline
268 ;; identification, so we'll make a "minor mode".
269 (defvar gnuserv-minor-mode nil)
270 (make-variable-buffer-local 'gnuserv-minor-mode)
271 ;;(pushnew '(gnuserv-minor-mode "Server") minor-mode-alist
273 (add-minor-mode 'gnuserv-minor-mode 'gnuserv-mode-line-string)
276 ;; Sample gnuserv-frame functions
278 (defun gnuserv-main-frame-function (type)
279 "Return a sensible value for the main Emacs frame."
285 (defun gnuserv-visible-frame-function (type)
286 "Return a frame if there is a frame that is truly visible, nil otherwise.
287 This is meant in the X sense, so it will not return frames that are on another
288 visual screen. Totally visible frames are preferred. If none found, return nil."
291 (cond ((car (filtered-frame-list 'frame-totally-visible-p
293 ((car (filtered-frame-list (lambda (frame)
294 ;; eq t as in not 'hidden
295 (eq t (frame-visible-p frame)))
296 (selected-device)))))
299 (defun gnuserv-special-frame-function (type)
300 "Create a special frame for Gnuserv and return it on later invocations."
301 (unless (frame-live-p gnuserv-special-frame)
302 (setq gnuserv-special-frame (make-frame gnuserv-frame-plist)))
303 gnuserv-special-frame)
306 ;;; Communication functions
308 ;; We used to restart the server here, but it's too risky -- if
309 ;; something goes awry, it's too easy to wind up in a loop.
310 (defun gnuserv-sentinel (proc msg)
311 (let ((msgstring (concat "Gnuserv process %s; restart with `%s'"))
312 (keystring (substitute-command-keys "\\[gnuserv-start]")))
313 (case (process-status proc)
315 (message msgstring "exited" keystring)
316 (gnuserv-prepare-shutdown))
318 (message msgstring "killed" keystring)
319 (gnuserv-prepare-shutdown))
321 (message msgstring "closed" keystring))
322 (gnuserv-prepare-shutdown))))
324 ;; This function reads client requests from our current server. Every
325 ;; client is identified by a unique ID within the server
326 ;; (incidentally, the same ID is the file descriptor the server uses
327 ;; to communicate to client).
329 ;; The request string can arrive in several chunks. As the request
330 ;; ends with \C-d, we check for that character at the end of string.
331 ;; If not found, keep reading, and concatenating to former strings.
332 ;; So, if at first read we receive "5 (gn", that text will be stored
333 ;; to gnuserv-string. If we then receive "us)\C-d", the two will be
334 ;; concatenated, `current-client' will be set to 5, and `(gnus)' form
335 ;; will be evaluated.
337 ;; Server will send the following:
339 ;; "ID <text>\C-d" (no quotes)
341 ;; ID - file descriptor of the given client;
342 ;; <text> - the actual contents of the request.
343 (defun gnuserv-process-filter (proc string)
344 "Process gnuserv client requests to execute Emacs commands."
345 (setq gnuserv-string (concat gnuserv-string string))
346 ;; C-d means end of request.
347 (when (string-match "\C-d\n?\\'" gnuserv-string)
348 (cond ((string-match "\\`[0-9]+" gnuserv-string) ; client request id
349 (let ((header (read-from-string gnuserv-string)))
350 ;; Set the client we are talking to.
351 (setq gnuserv-current-client (car header))
352 ;; Evaluate the expression
354 (eval (car (read-from-string gnuserv-string (cdr header))))
355 ;; In case of an error, write the description to the
356 ;; client, and then signal it.
357 (error (setq gnuserv-string "")
358 (when gnuserv-current-client
359 (gnuserv-write-to-client gnuserv-current-client oops))
360 (setq gnuserv-current-client nil)
361 (signal (car oops) (cdr oops)))
362 (quit (setq gnuserv-string "")
363 (when gnuserv-current-client
364 (gnuserv-write-to-client gnuserv-current-client oops))
365 (setq gnuserv-current-client nil)
367 (setq gnuserv-string "")))
369 (let ((response (car (split-string gnuserv-string "\C-d"))))
370 (setq gnuserv-string "")
371 (error "%s: invalid response from gnuserv" response))))))
373 ;; This function is somewhat of a misnomer. Actually, we write to the
374 ;; server (using `process-send-string' to gnuserv-process), which
375 ;; interprets what we say and forwards it to the client. The
376 ;; incantation server understands is (from gnuserv.c):
378 ;; "FD/LEN:<text>\n" (no quotes)
379 ;; FD - file descriptor of the given client (which we obtained from
380 ;; the server earlier);
381 ;; LEN - length of the stuff we are about to send;
382 ;; <text> - the actual contents of the request.
383 (defun gnuserv-write-to-client (client-id form)
384 "Write the given form to the given client via the gnuserv process."
385 (when (eq (process-status gnuserv-process) 'run)
386 (let* ((result (format "%s" form))
387 (s (format "%s/%d:%s\n" client-id
388 (length result) result)))
389 (process-send-string gnuserv-process s))))
391 ;; The following two functions are helper functions, used by
394 (defun gnuserv-eval (form)
395 "Evaluate form and return result to client."
396 (gnuserv-write-to-client gnuserv-current-client (eval form))
397 (setq gnuserv-current-client nil))
399 (defun gnuserv-eval-quickly (form)
400 "Let client know that we've received the request, and then eval the form.
401 This order is important as not to keep the client waiting."
402 (gnuserv-write-to-client gnuserv-current-client nil)
403 (setq gnuserv-current-client nil)
408 (defun make-x-device-with-gtk-fallback (device)
409 (or (condition-case ()
410 (make-x-device device)
414 ;; "Execute" a client connection, called by gnuclient. This is the
415 ;; backbone of gnuserv.el.
416 (defun gnuserv-edit-files (type list &rest flags)
417 "For each (line-number . file) pair in LIST, edit the file at line-number.
418 The visited buffers are memorized, so that when \\[gnuserv-edit] is invoked
419 in such a buffer, or when it is killed, or the client's device deleted, the
420 client will be invoked that the edit is finished.
422 TYPE should either be a (tty TTY TERM PID) list, or (x DISPLAY) list.
423 If a flag is `quick', just edit the files in Emacs.
424 If a flag is `view', view the files read-only."
428 (quick (setq quick t))
430 (t (error "Invalid flag %s" flag))))
432 (let* ((old-device-num (length (device-list)))
434 (dest-frame (if (functionp gnuserv-frame)
435 (funcall gnuserv-frame (car type))
437 ;; The gnuserv-frame dependencies are ugly, but it's
438 ;; extremely hard to make that stuff cleaner without
439 ;; breaking everything in sight.
440 (device (cond ((frame-live-p dest-frame)
441 (frame-device dest-frame))
444 (tty (apply 'make-tty-device (cdr type)))
445 (gtk (make-gtk-device))
446 (x (make-x-device-with-gtk-fallback (cadr type)))
447 (t (error "Invalid device type"))))
450 (frame (cond ((frame-live-p dest-frame)
453 (setq new-frame (make-frame gnuserv-frame-plist
456 (t (selected-frame))))
457 (client (make-gnuclient :id gnuserv-current-client
461 (setq gnuserv-current-client nil)
462 ;; If the device was created by this client, push it to the list.
463 (and (/= old-device-num (length (device-list)))
464 (push device gnuserv-devices))
465 (and (frame-iconified-p frame)
466 (deiconify-frame frame))
467 ;; Visit all the listed files.
469 (let ((line (caar list)) (path (cdar list)))
473 gnuserv-view-file-function
474 gnuserv-find-file-function)
477 ;; Don't memorize the quick and view buffers.
478 (unless (or quick view)
479 (pushnew (current-buffer) (gnuclient-buffers client))
480 (setq gnuserv-minor-mode t)
481 ;; Add the "Done" button to the menubar, only in this buffer.
482 (if (and (featurep 'menubar) current-menubar)
483 (progn (set-buffer-menubar current-menubar)
484 (add-menu-button nil ["Done" gnuserv-edit]))
486 (run-hooks 'gnuserv-visit-hook)
489 ((and (or quick view)
490 (device-on-window-system-p device))
491 ;; Exit if on X device, and quick or view. NOTE: if the
492 ;; client is to finish now, it must absolutely /not/ be
493 ;; included to the list of clients. This way the client-ids
495 (gnuserv-write-to-client (gnuclient-id client) nil))
497 ;; Else, the client gets a vote.
498 (push client gnuserv-clients)
499 ;; Explain buffer exit options. If dest-frame is nil, the
500 ;; user can exit via `delete-frame'. OTOH, if FLAGS are nil
501 ;; and there are some buffers, the user can exit via
503 (if (and (not (or quick view))
504 (gnuclient-buffers client))
506 (substitute-command-keys
507 "Type `\\[gnuserv-edit]' to finish editing"))
510 (substitute-command-keys
511 "Type `\\[delete-frame]' to finish editing")))))))))
514 ;;; Functions that hook into Emacs in various way to enable operation
517 (add-hook 'kill-emacs-hook 'gnuserv-kill-all-clients t)
519 ;; A helper function; used by others. Try avoiding it whenever
520 ;; possible, because it is slow, and conses a list. Use
521 ;; `gnuserv-buffer-p' when appropriate, for instance.
522 (defun gnuserv-buffer-clients (buffer)
523 "Return a list of clients to which BUFFER belongs."
525 (dolist (client gnuserv-clients)
526 (when (memq buffer (gnuclient-buffers client))
530 ;; Like `gnuserv-buffer-clients', but returns a boolean; doesn't
532 (defun gnuserv-buffer-p (buffer)
533 (member* buffer gnuserv-clients
535 :key 'gnuclient-buffers))
537 ;; This function makes sure that a killed buffer is deleted off the
538 ;; list for the particular client.
540 ;; This hooks into `kill-buffer-hook'. It is *not* a replacement for
541 ;; `kill-buffer' (thanks God).
542 (defun gnuserv-kill-buffer-function ()
543 "Remove the buffer from the buffer lists of all the clients it belongs to.
544 Any client that remains \"empty\" after the removal is informed that the
546 (let* ((buf (current-buffer)))
547 (dolist (client (gnuserv-buffer-clients buf))
548 (callf2 delq buf (gnuclient-buffers client))
549 ;; If no more buffers, kill the client.
550 (when (null (gnuclient-buffers client))
551 (gnuserv-kill-client client)))))
553 (add-hook 'kill-buffer-hook 'gnuserv-kill-buffer-function)
555 ;; Ask for confirmation before killing a buffer that belongs to a
557 (defun gnuserv-kill-buffer-query-function ()
558 (or gnuserv-kill-quietly
559 (not (gnuserv-buffer-p (current-buffer)))
561 (format "Buffer %s belongs to gnuserv client(s); kill anyway? "
564 (add-hook 'kill-buffer-query-functions
565 'gnuserv-kill-buffer-query-function)
567 (defun gnuserv-kill-emacs-query-function ()
568 (or gnuserv-kill-quietly
569 (not (some 'gnuclient-buffers gnuserv-clients))
570 (yes-or-no-p "Gnuserv buffers still have clients; exit anyway? ")))
572 (add-hook 'kill-emacs-query-functions
573 'gnuserv-kill-emacs-query-function)
575 ;; If the device of a client is to be deleted, the client should die
576 ;; as well. This is why we hook into `delete-device-hook'.
577 (defun gnuserv-check-device (device)
578 (when (memq device gnuserv-devices)
579 (dolist (client gnuserv-clients)
580 (when (eq device (gnuclient-device client))
581 ;; we must make sure that the server kill doesn't result in
582 ;; killing the device, because it would cause a device-dead
583 ;; error when `delete-device' tries to do the job later.
584 (gnuserv-kill-client client t))))
585 (callf2 delq device gnuserv-devices))
587 (add-hook 'delete-device-hook 'gnuserv-check-device)
589 (defun gnuserv-temp-file-p (buffer)
590 "Return non-nil if BUFFER contains a file considered temporary.
591 These are files whose names suggest they are repeatedly
592 reused to pass information to another program.
594 The variable `gnuserv-temp-file-regexp' controls which filenames
595 are considered temporary."
596 (and (buffer-file-name buffer)
597 (string-match gnuserv-temp-file-regexp (buffer-file-name buffer))))
599 (defun gnuserv-kill-client (client &optional leave-frame)
600 "Kill the gnuclient CLIENT.
601 This will do away with all the associated buffers. If LEAVE-FRAME,
602 the function will not remove the frames associated with the client."
603 ;; Order is important: first delete client from gnuserv-clients, to
604 ;; prevent gnuserv-buffer-done-1 calling us recursively.
605 (callf2 delq client gnuserv-clients)
606 ;; Process the buffers.
607 (mapc 'gnuserv-buffer-done-1 (gnuclient-buffers client))
609 (let ((device (gnuclient-device client)))
610 ;; kill frame created by this client (if any), unless
611 ;; specifically requested otherwise.
613 ;; note: last frame on a device will not be deleted here.
614 (when (and (gnuclient-frame client)
615 (frame-live-p (gnuclient-frame client))
616 (second (device-frame-list device)))
617 (delete-frame (gnuclient-frame client)))
618 ;; If the device is live, created by a client, and no longer used
619 ;; by any client, delete it.
620 (when (and (device-live-p device)
621 (memq device gnuserv-devices)
622 (second (device-list))
623 (not (member* device gnuserv-clients
624 :key 'gnuclient-device)))
625 ;; `gnuserv-check-device' will remove it from `gnuserv-devices'.
626 (delete-device device))))
627 ;; Notify the client.
628 (gnuserv-write-to-client (gnuclient-id client) nil))
630 ;; Do away with the buffer.
631 (defun gnuserv-buffer-done-1 (buffer)
632 (dolist (client (gnuserv-buffer-clients buffer))
633 (callf2 delq buffer (gnuclient-buffers client))
634 (when (null (gnuclient-buffers client))
635 (gnuserv-kill-client client)))
636 ;; Get rid of the buffer.
639 (run-hooks 'gnuserv-done-hook)
640 (setq gnuserv-minor-mode nil)
641 ;; Delete the menu button.
642 (if (and (featurep 'menubar) current-menubar)
643 (delete-menu-item '("Done")))
644 (funcall (if (gnuserv-temp-file-p buffer)
645 gnuserv-done-temp-file-function
646 gnuserv-done-function)
650 ;;; Higher-level functions
652 ;; Choose a `next' server buffer, according to several criteria, and
653 ;; return it. If none are found, return nil.
654 (defun gnuserv-next-buffer ()
655 (let* ((frame (selected-frame))
656 (device (selected-device))
659 ;; If we have a client belonging to this frame, return
660 ;; the first buffer from it.
662 (car (member* frame gnuserv-clients :key 'gnuclient-frame)))
663 (car (gnuclient-buffers client)))
664 ;; Else, look for a device.
666 (memq (selected-device) gnuserv-devices)
668 (car (member* device gnuserv-clients :key 'gnuclient-device))))
669 (car (gnuclient-buffers client)))
670 ;; Else, try to find any client with at least one buffer, and
671 ;; return its first buffer.
673 (car (member-if-not #'null gnuserv-clients
674 :key 'gnuclient-buffers)))
675 (car (gnuclient-buffers client)))
679 (defun gnuserv-buffer-done (buffer)
680 "Mark BUFFER as \"done\" for its client(s).
681 Does the save/backup queries first, and calls `gnuserv-done-function'."
682 ;; Check whether this is the real thing.
683 (unless (gnuserv-buffer-p buffer)
684 (error "%s does not belong to a gnuserv client" buffer))
686 (if (gnuserv-temp-file-p buffer)
687 ;; For a temp file, save, and do NOT make a non-numeric backup
688 ;; Why does server.el explicitly back up temporary files?
689 (let ((version-control nil)
690 (buffer-backed-up (not gnuserv-make-temp-file-backup)))
692 (if (and (buffer-modified-p)
693 (y-or-n-p (concat "Save file " buffer-file-name "? ")))
694 (save-buffer buffer)))
695 (gnuserv-buffer-done-1 buffer))
697 ;; Called by `gnuserv-start-1' to clean everything. Hooked into
698 ;; `kill-emacs-hook', too.
699 (defun gnuserv-kill-all-clients ()
700 "Kill all the gnuserv clients. Ruthlessly."
701 (mapc 'gnuserv-kill-client gnuserv-clients))
703 ;; This serves to run the hook and reset
704 ;; `allow-deletion-of-last-visible-frame'.
705 (defun gnuserv-prepare-shutdown ()
706 (setq allow-deletion-of-last-visible-frame nil)
707 (run-hooks 'gnuserv-shutdown-hook))
709 ;; This is a user-callable function, too.
710 (defun gnuserv-shutdown ()
711 "Shutdown the gnuserv server, if one is currently running.
712 All the clients will be disposed of via the normal methods."
714 (gnuserv-kill-all-clients)
715 (when gnuserv-process
716 (set-process-sentinel gnuserv-process nil)
717 (gnuserv-prepare-shutdown)
719 (delete-process gnuserv-process)
721 (setq gnuserv-process nil)))
723 ;; Actually start the process. Kills all the clients before-hand.
724 (defun gnuserv-start-1 (&optional leave-dead)
725 ;; Shutdown the existing server, if any.
727 ;; If we already had a server, clear out associated status.
729 (setq gnuserv-string ""
730 gnuserv-current-client nil)
731 (let ((process-connection-type t))
732 (setq gnuserv-process
733 (start-process "gnuserv" nil gnuserv-program)))
734 (set-process-sentinel gnuserv-process 'gnuserv-sentinel)
735 (set-process-filter gnuserv-process 'gnuserv-process-filter)
736 (process-kill-without-query gnuserv-process)
737 (setq allow-deletion-of-last-visible-frame t)
738 (run-hooks 'gnuserv-init-hook)))
741 ;;; User-callable functions:
744 (defun gnuserv-running-p ()
745 "Return non-nil if a gnuserv process is running from this XEmacs session."
746 (not (not gnuserv-process)))
749 (defun gnuserv-start (&optional leave-dead)
750 "Allow this Emacs process to be a server for client processes.
751 This starts a gnuserv communications subprocess through which
752 client \"editors\" (gnuclient and gnudoit) can send editing commands to
753 this Emacs job. See the gnuserv(1) manual page for more details.
755 Prefix arg means just kill any existing server communications subprocess."
759 (message "Restarting gnuserv"))
760 (gnuserv-start-1 leave-dead))
762 (defun gnuserv-edit (&optional count)
763 "Mark the current gnuserv editing buffer as \"done\", and switch to next one.
765 Run with a numeric prefix argument, repeat the operation that number
766 of times. If given a universal prefix argument, close all the buffers
767 of this buffer's clients.
769 The `gnuserv-done-function' (bound to `kill-buffer' by default) is
770 called to dispose of the buffer after marking it as done.
772 Files that match `gnuserv-temp-file-regexp' are considered temporary and
773 are saved unconditionally and backed up if `gnuserv-make-temp-file-backup'
774 is non-nil. They are disposed of using `gnuserv-done-temp-file-function'
775 \(also bound to `kill-buffer' by default).
777 When all of a client's buffers are marked as \"done\", the client is notified."
781 (cond ((numberp count)
782 (while (natnump (decf count))
783 (let ((frame (selected-frame)))
784 (gnuserv-buffer-done (current-buffer))
785 (when (eq frame (selected-frame))
786 ;; Switch to the next gnuserv buffer. However, do this
787 ;; only if we remain in the same frame.
788 (let ((next (gnuserv-next-buffer)))
790 (switch-to-buffer next)))))))
792 (let* ((buf (current-buffer))
793 (clients (gnuserv-buffer-clients buf)))
795 (error "%s does not belong to a gnuserv client" buf))
796 (mapc 'gnuserv-kill-client (gnuserv-buffer-clients buf))))))
798 (global-set-key "\C-x#" 'gnuserv-edit)
802 ;;; gnuserv.el ends here