Mercurial > hg > xemacs-beta
comparison lisp/packages/gnuserv.el @ 149:538048ae2ab8 r20-3b1
Import from CVS: tag r20-3b1
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:36:16 +0200 |
parents | b980b6286996 |
children | 59463afc5666 |
comparison
equal
deleted
inserted
replaced
148:f659db2a1f73 | 149:538048ae2ab8 |
---|---|
1 ; Lisp Interface code between GNU Emacs and gnuserv. | 1 ;;; gnuserv.el --- Lisp interface code between Emacs and gnuserv |
2 ; | 2 ;; Copyright (C) 1989-1997 Free Software Foundation, Inc. |
3 ; This file is part of GNU Emacs. | 3 |
4 ; | 4 ;; Version: 3.1 |
5 ; Copying is permitted under those conditions described by the GNU | 5 ;; Author: Andy Norman (ange@hplb.hpl.hp.com), originally based on server.el |
6 ; General Public License. | 6 ;; Hrvoje Niksic <hniksic@srce.hr> |
7 ; | 7 ;; Keywords: environment, processes, terminals |
8 ; Copyright (C) 1989-1996 Free Software Foundation, Inc. | 8 |
9 ; | 9 ;; This file is part of XEmacs. |
10 ; Author: Andy Norman (ange@hplb.hpl.hp.com) based on | 10 |
11 ; 'lisp/server.el' from the 18.52 GNU Emacs distribution. | 11 ;; XEmacs is free software; you can redistribute it and/or modify it |
12 ; | 12 ;; under the terms of the GNU General Public License as published by |
13 ; Please mail bugs and suggestions to the author at the above address. | 13 ;; the Free Software Foundation; either version 2, or (at your option) |
14 ; | 14 ;; any later version. |
15 | |
16 ;; XEmacs is distributed in the hope that it will be useful, but | |
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 ;; General Public License for more details. | |
20 | |
21 ;; You should have received a copy of the GNU General Public License | |
22 ;; along with XEmacs; see the file COPYING. If not, write to the | |
23 ;; Free Software Foundation, 59 Temple Place - Suite 330, | |
24 ;; Boston, MA 02111-1307, USA. | |
25 | |
15 ;;; Synched up with: Not in FSF. | 26 ;;; Synched up with: Not in FSF. |
16 | 27 |
17 ; Updated for XEmacs, GNU Emacs 19 and Epoch V4 to use multiple frames | 28 ;;; Commentary: |
18 ; by Bob Weiner, <weiner@mot.com>, 1/20/94. (Still works with Emacs V18, too.) | 29 |
19 ; Modified 'server-process-filter' to use \^D as end of request terminator | 30 ;; Gnuserv is run when Emacs needs to operate as a server for other |
20 ; as gnuclient and gnudoit have been modified to send. This permits | 31 ;; processes. Specifically, any number of files can be attached for |
21 ; multi-line requests. | 32 ;; editing to a running XEmacs process using the `gnuclient' program. |
22 ; Modified 'server-make-window-visible' to work with multiple frames. | 33 |
23 ; Modified 'server-find-file' to display in a separate frame when possible. | 34 ;; Use `M-x gnuserv-start' to start the server and `gnuclient files' |
24 ; Modified 'server-edit' to delete newly created frame when used to | 35 ;; to load them to XEmacs. When you are done with a buffer, press |
25 ; terminate an edit and to signal an error if called within a | 36 ;; `C-x #' (`M-x gnuserv-edit'). You can put (gnuserv-start) to your |
26 ; non-server-edit buffer. | 37 ;; .emacs, and enable `gnuclient' as your Unix "editor". When all the |
27 ; Bob Weiner, <weiner@mot.com>, 5/9/94. | 38 ;; buffers for a client have been edited and exited with |
28 ; Added 'server-done-function' variable. Made default value 'kill-buffer' | 39 ;; `gnuserv-edit', the client "editor" will return to the program that |
29 ; instead of 'bury-buffer' as in original gnuserv.el. | 40 ;; invoked it. |
30 ; | 41 |
31 ; Darrell Kindred <dkindred+@cmu.edu> May/1994 | 42 ;; Your editing commands and Emacs' display output go to and from the |
32 ; Updated to allow multi-line return values: | 43 ;; terminal or X display in the usual way. If you are running under |
33 ; - output to gnuserv is "m/n:xxx" where m is the client number, | 44 ;; X, a new X frame will be open for each gnuclient. If you are on a |
34 ; n is the length of the data, and xxx is the data itself, followed | 45 ;; TTY, this TTY will be attached as a new device to the running |
35 ; by newline | 46 ;; XEmacs, and will be removed once you are done with the buffer. |
36 ; | 47 |
37 ; Arup Mukherjee <arup+@cmu.edu> May/1994 | 48 ;; To evaluate a Lisp form in a running Emacs, use the `gnudoit' |
38 ; Updated for XEmacs 19.10, and others: | 49 ;; utility. For example `gnudoit "(+ 2 3)"' will print `5', whereas |
39 ; - use find-file-other-screen if present | 50 ;; `gnudoit "(gnus)"' will fire up your favorite newsreader. Like |
40 ; - new variable gnuserv-frame can be set to a frame or screen which | 51 ;; gnuclient, `gnudoit' requires the server to be started prior to |
41 ; is used for all edited files. | 52 ;; using it. |
42 ; - check to see if server.el is already loaded and complain if it is, since | 53 |
43 ; gnuserv.el can't coexist with server.el | 54 ;; For more information you can refer to man pages of gnuclient, |
44 ; - rename server-start to gnuserv-start, although server-start remains as | 55 ;; gnudoit and gnuserv, distributed with XEmacs. |
45 ; an alias. This allows gnuserv-start to be autoloaded from gnuserv.el | 56 |
46 ; - changed server-get-buffer to take into account that in newer emacsen, | 57 ;; gnuserv.el was originally written by Andy Norman as an improvement |
47 ; get buffer returns nil on deleted buffers. | 58 ;; ver William Sommerfeld's server.el. Since then, a number of people |
48 ; - only try to create/delete frames or screens if window-system is non-nil | 59 ;; have worked on it, including Bob Weiner, Darell Kindred, Arup |
49 ; (otherwise things don't work w/ emacs19 on a dumb terminal) | 60 ;; Mukherjee, Ben Wing and Jan Vroonhof. It was completely rewritten |
50 ; | 61 ;; (labeled as version 3) by Hrvoje Niksic in May 1997. |
51 ; Ben Wing <wing@666.com> sometime in 1995 | 62 |
52 ; Updated to allow `gnuattach'-type connections to the existing TTY | 63 ;; Jan Vroonhof <vroonhof@math.ethz.ch> July/1996 |
53 ; | 64 ;; ported the server-temp-file-regexp feature from server.el |
54 ; Ben Wing <wing@666.com> May/1996 | 65 ;; ported server hooks from server.el |
55 ; patch to get TTY terminal type correct. | 66 ;; ported kill-*-query functions from server.el (and made it optional) |
56 ; | 67 ;; synced other behaviour with server.el |
57 ; Jan Vroonhof <vroonhof@math.ethz.ch> July/1996 | 68 ;; |
58 ; ported the server-temp-file-regexp feature from server.el | 69 ;; Jan Vroonhof |
59 ; ported server hooks from server.el | 70 ;; Customized. |
60 ; ported kill-*-query functions from server.el (and made it optional) | 71 ;; |
61 ; synced other behaviour with server.el | 72 ;; Hrvoje Niksic <hniksic@srce.hr> May/1997 |
62 | 73 ;; Completely rewritten. Now uses `defstruct' and other CL stuff |
63 | 74 ;; to define clients cleanly. Dave, thanks! |
75 | |
76 | |
77 ;;; Code: | |
78 | |
79 (defconst gnuserv-rcs-version | |
80 "$Id: gnuserv.el,v 1.8 1997/05/18 03:40:06 steve Exp $") | |
64 | 81 |
65 (defgroup gnuserv nil | 82 (defgroup gnuserv nil |
66 "Lisp interface between Emacs and gnuserv" | 83 "The gnuserv suite of programs to talk to Emacs from outside." |
67 :prefix "server-" | 84 :group 'environment |
68 :group 'processes | 85 :group 'processes |
69 :group 'terminals) | 86 :group 'terminals) |
70 | 87 |
71 | 88 |
72 (defconst gnuserv-rcs-header-id "!Header: gnuserv.el,v 2.1 95/02/16 12:00:16 arup alpha !") | |
73 | |
74 | |
75 ;; server.el and gnuserv.el can't coexist because of conflicting defvar's and | |
76 ;; function names. | |
77 | |
78 (if (and (boundp 'server-buffer-clients) | |
79 (not (featurep 'gnuserv))) | |
80 (error "Can't run gnuserv because server.el appears to be loaded already")) | |
81 | |
82 ;;;###autoload | 89 ;;;###autoload |
83 (defvar gnuserv-frame nil | 90 (defcustom gnuserv-frame nil |
84 "*If non-nil, the frame to be used to display all edited files. | 91 "*The frame to be used to display all edited files. |
85 If nil, then a new frame is created for each file edited. | 92 If nil, then a new frame is created for each file edited. |
86 This variable has no effect in XEmacs versions older than 19.9.") | 93 If t, then the currently selected frame will be used. |
87 | 94 If a function, then this will be called with a symbol `x' or `tty' as the |
88 (defcustom server-done-function 'kill-buffer | 95 only argument, and its return value will be interpreted as above." |
89 "*A function of one argument, a buffer, which removes the buffer after editing. | 96 :tag "Gnuserv Frame" |
90 Functions such as 'kill-buffer' and 'bury-buffer' are good values. See also | 97 :type '(radio (const :tag "Create new frame each time" nil) |
91 `server-done-temp-file-function'" | 98 (const :tag "Use selected frame" t) |
99 (function-item :tag "Use main Emacs frame" | |
100 gnuserv-main-frame-function) | |
101 (function-item :tag "Use visible frame, otherwise create new" | |
102 gnuserv-visible-frame-function) | |
103 (function-item :tag "Create special Gnuserv frame and use it" | |
104 gnuserv-special-frame-function) | |
105 (function :tag "Other")) | |
106 :group 'gnuserv) | |
107 | |
108 (defcustom gnuserv-done-function 'kill-buffer | |
109 "*Function used to remove a buffer after editing. | |
110 It is called with one BUFFER argument. Functions such as 'kill-buffer' and | |
111 'bury-buffer' are good values. See also `gnuserv-done-temp-file-function'." | |
112 :type '(radio (function-item kill-buffer) | |
113 (function-item bury-buffer) | |
114 (function :tag "Other")) | |
115 :group 'gnuserv) | |
116 | |
117 (defcustom gnuserv-done-temp-file-function 'kill-buffer | |
118 "*Function used to remove a temporary buffer after editing. | |
119 It is called with one BUFFER argument. Functions such as 'kill-buffer' and | |
120 'bury-buffer' are good values. See also `gnuserv-done-temp-file-function'." | |
121 :type '(radio (function-item kill-buffer) | |
122 (function-item bury-buffer) | |
123 (function :tag "Other")) | |
124 :group 'gnuserv) | |
125 | |
126 (defcustom gnuserv-find-file-function 'find-file | |
127 "*Function to visit a file with. | |
128 It takes one argument, a file name to visit." | |
92 :type 'function | 129 :type 'function |
93 :group 'gnuserv) | 130 :group 'gnuserv) |
94 | 131 |
95 (defcustom server-done-temp-file-function 'kill-buffer | 132 (defcustom gnuserv-view-file-function 'view-file |
96 "*A function of one argument, a buffer, which removes the buffer after editing a | 133 "*Function to view a file with. |
97 temporary file. Functions such as 'kill-buffer' and 'bury-buffer' are | 134 It takes one argument, a file name to view." |
98 good values. See also `server-done-function'" | 135 :type '(radio (function-item view-file) |
99 :type 'function | 136 (function-item find-file-read-only) |
100 :group 'gnuserv) | 137 (function :tag "Other")) |
101 | 138 :group 'gnuserv) |
102 (defcustom server-program "gnuserv" | 139 |
103 "*The program to use as the edit server" | 140 (defcustom gnuserv-program "gnuserv" |
141 "*Program to use as the editing server." | |
104 :type 'string | 142 :type 'string |
105 :group 'gnuserv) | 143 :group 'gnuserv) |
106 | 144 |
107 | 145 (defcustom gnuserv-visit-hook nil |
108 (defcustom server-visit-hook nil | 146 "*Hook run after visiting a file." |
109 "*List of hooks to call when visiting a file for the Emacs server." | |
110 :type 'hook | 147 :type 'hook |
111 :group 'gnuserv) | 148 :group 'gnuserv) |
112 | 149 |
113 ;; defined by server.el but obsolete? | 150 (defcustom gnuserv-done-hook nil |
114 ;; (defvar server-switch-hook nil | 151 "*Hook run when done editing a buffer for the Emacs server. |
115 ;; "*List of hooks to call when switching to a buffer for the Emacs server.") | 152 The hook functions are called after the file has been visited, with the |
116 | 153 current buffer set to the visiting buffer." |
117 (defcustom server-done-hook nil | |
118 "*List of hooks to call when done editing a buffer for the Emacs server." | |
119 :type 'hook | 154 :type 'hook |
120 :group 'gnuserv) | 155 :group 'gnuserv) |
121 | 156 |
122 | 157 (defcustom gnuserv-kill-quietly nil |
123 (defvar server-process nil | 158 "*Non-nil means to kill buffers with clients attached without requiring confirmation." |
124 "The current server process") | |
125 | |
126 (defvar server-string "" | |
127 "The last input string from the server") | |
128 | |
129 (defvar server-kill-last-frame nil | |
130 "set to t to kill last frame") | |
131 | |
132 (defvar current-client nil | |
133 "The client we are currently talking to") | |
134 | |
135 (defvar server-clients nil | |
136 "List of current server clients. | |
137 Each element is (CLIENTID BUFFER...) where CLIENTID is an integer | |
138 that can be given to the server process to identify a client. | |
139 When a buffer is killed, it is removed from this list.") | |
140 | |
141 (defcustom server-kill-quietly nil | |
142 "If this variable is set then don't confirm kills of buffers with | |
143 clients attached" | |
144 :type 'boolean | 159 :type 'boolean |
145 :group 'gnuserv) | 160 :group 'gnuserv) |
146 | 161 |
147 | 162 (defcustom gnuserv-temp-file-regexp "^/tmp/Re\\|/draft$" |
148 (defvar server-buffer-clients nil | 163 "*Regexp which should match filenames of temporary files deleted |
149 "List of client ids for clients requesting editing of the current buffer.") | 164 and reused by the programs that invoke the Emacs server." |
150 | |
151 (defcustom server-temp-file-regexp "^/tmp/Re\\|/draft$" | |
152 "*Regexp which should match filenames of temporary files | |
153 which are deleted and reused after each edit | |
154 by the programs that invoke the emacs server." | |
155 :type 'regexp | 165 :type 'regexp |
156 :group 'gnuserv) | 166 :group 'gnuserv) |
157 | 167 |
158 (defcustom server-make-temp-file-backup nil | 168 (defcustom gnuserv-make-temp-file-backup nil |
159 "Non-nil makes the server backup temporary files also" | 169 "*Non-nil makes the server backup temporary files also." |
160 :type 'regexp | 170 :type 'boolean |
161 :group 'gnuserv) | 171 :group 'gnuserv) |
162 | 172 |
163 (make-variable-buffer-local 'server-buffer-clients) | 173 |
164 (setq-default server-buffer-clients nil) | 174 ;; The old functions are provided as aliases, to avoid breaking .emacs |
165 (or (assq 'server-buffer-clients minor-mode-alist) | 175 ;; files. However, they are obsolete and should be avoided. |
166 (setq minor-mode-alist (cons '(server-buffer-clients " Server") | 176 |
167 minor-mode-alist))) | 177 (defvaralias 'server-frame 'gnuserv-frame) |
168 | 178 (defvaralias 'server-done-function 'gnuserv-done-function) |
169 (defun server-temp-file-p (buffer) | 179 (defvaralias 'server-done-temp-file-function 'gnuserv-done-temp-file-function) |
180 (defvaralias 'server-find-file-function 'gnuserv-find-file-function) | |
181 (defvaralias 'server-program 'gnuserv-program) | |
182 (defvaralias 'server-visit-hook 'gnuserv-visit-hook) | |
183 (defvaralias 'server-done-hook 'gnuserv-done-hook) | |
184 (defvaralias 'server-kill-quietly 'gnuserv-kill-quietly) | |
185 (defvaralias 'server-temp-file-regexp 'gnuserv-temp-file-regexp) | |
186 (defvaralias 'server-make-temp-file-backup 'gnuserv-make-temp-file-backup) | |
187 | |
188 | |
189 ;;; Internal variables: | |
190 | |
191 (defstruct gnuclient | |
192 "An object that encompasses several buffers in one. | |
193 Normally, a client connecting to Emacs will be assigned an id, and | |
194 will request editing of several files. | |
195 | |
196 ID - Client id (integer). | |
197 BUFFERS - List of buffers that \"belong\" to the client. | |
198 NOTE: one buffer can belong to several clients. | |
199 DEVICE - The device this client is on. If the device was also created. | |
200 by a client, it will be placed to `gnuserv-devices' list. | |
201 FRAME - Frame created by the client, or nil if the client didn't | |
202 create a frame. | |
203 | |
204 All the slots default to nil." | |
205 (id nil) | |
206 (buffers nil) | |
207 (device nil) | |
208 (frame nil)) | |
209 | |
210 (defvar gnuserv-process nil | |
211 "The current gnuserv process.") | |
212 | |
213 (defvar gnuserv-string "" | |
214 "The last input string from the server.") | |
215 | |
216 (defvar gnuserv-current-client nil | |
217 "The client we are currently talking to.") | |
218 | |
219 (defvar gnuserv-clients nil | |
220 "List of current gnuserv clients. | |
221 Each element is a gnuclient structure that identifies a client.") | |
222 | |
223 (defvar gnuserv-devices nil | |
224 "List of devices created by clients.") | |
225 | |
226 (defvar gnuserv-special-frame nil | |
227 "Frame created specially for Server.") | |
228 | |
229 ;; We want the client-infested buffers to have some modeline | |
230 ;; identification, so we'll make a "minor mode". | |
231 (defvar gnuserv-minor-mode nil) | |
232 (make-variable-buffer-local 'gnuserv-mode) | |
233 (pushnew '(gnuserv-minor-mode " Server") minor-mode-alist) | |
234 | |
235 | |
236 ;; Sample gnuserv-frame functions | |
237 | |
238 (defun gnuserv-main-frame-function (type) | |
239 "Returns a sensible value for the main Emacs frame." | |
240 (if (eq type 'x) | |
241 (car (frame-list)) | |
242 nil)) | |
243 | |
244 (defun gnuserv-visible-frame-function (type) | |
245 "Returns a frame if there is a frame that is truly visible, nil otherwise. | |
246 This is meant in the X sense, so it will not return frames that are on another | |
247 visual screen. Totally visible frames are preferred. If none found, return nil." | |
248 (if (eq type 'x) | |
249 (cond ((car (filtered-frame-list 'frame-totally-visible-p | |
250 (selected-device)))) | |
251 ((car (filtered-frame-list (lambda (frame) | |
252 ;; eq t as in not 'hidden | |
253 (eq t (frame-visible-p frame))) | |
254 (selected-device))))) | |
255 nil)) | |
256 | |
257 (defun gnuserv-special-frame-function (type) | |
258 "Creates a special frame for Gnuserv and returns it on later invocations." | |
259 (unless (frame-live-p gnuserv-special-frame) | |
260 (setq gnuserv-special-frame (make-frame))) | |
261 gnuserv-special-frame) | |
262 | |
263 | |
264 ;;; Communication functions | |
265 | |
266 (defun gnuserv-sentinel (proc msg) | |
267 (case (process-status proc) | |
268 (exit (message "Gnuserv subprocess exited; restarting") | |
269 ;; This will also kill all the existing clients. | |
270 (gnuserv-start-1)) | |
271 (closed (message "Gnuserv subprocess closed")) | |
272 (signal (message "Gnuserv subprocess killed")))) | |
273 | |
274 (defun gnuserv-process-filter (proc string) | |
275 "Process gnuserv client requests to execute Emacs commands." | |
276 (setq gnuserv-string (concat gnuserv-string string)) | |
277 ;; C-d means end of request. | |
278 (when (string-match "\C-d$" gnuserv-string) | |
279 (cond ((string-match "^[0-9]+" gnuserv-string) ; client request id | |
280 (let ((header (read-from-string gnuserv-string))) | |
281 ;; Set the client we are talking to. | |
282 (setq gnuserv-current-client (car header)) | |
283 ;; Evaluate the expression | |
284 (condition-case oops | |
285 (eval (car (read-from-string gnuserv-string (cdr header)))) | |
286 ;; In case of an error, write the description to the | |
287 ;; client, and then signal it. | |
288 (error (setq gnuserv-string "") | |
289 (gnuserv-write-to-client gnuserv-current-client oops) | |
290 (setq gnuserv-current-client nil) | |
291 (signal (car oops) (cdr oops))) | |
292 (quit (setq gnuserv-string "") | |
293 (gnuserv-write-to-client gnuserv-current-client oops) | |
294 (setq gnuserv-current-client nil) | |
295 (signal 'quit nil))) | |
296 (setq gnuserv-string ""))) | |
297 (t | |
298 (error "%s: invalid response from gnuserv" gnuserv-string) | |
299 (setq gnuserv-string ""))))) | |
300 | |
301 (defun gnuserv-write-to-client (client-id form) | |
302 "Write the given form to the given client via the gnuserv process." | |
303 (when (eq (process-status gnuserv-process) 'run) | |
304 (let* ((result (format "%s" form)) | |
305 (s (format "%s/%d:%s\n" client-id | |
306 (length result) result))) | |
307 (process-send-string gnuserv-process s)))) | |
308 | |
309 | |
310 ;; The following two functions are helper functions, used by | |
311 ;; gnuclient. | |
312 | |
313 (defun gnuserv-eval (form) | |
314 "Evaluate form and return result to client." | |
315 (gnuserv-write-to-client gnuserv-current-client (eval form)) | |
316 (setq gnuserv-current-client nil)) | |
317 | |
318 (defun gnuserv-eval-quickly (form) | |
319 "Let client know that we've received the request, and then eval the form. | |
320 This order is important as not to keep the client waiting." | |
321 (gnuserv-write-to-client gnuserv-current-client nil) | |
322 (setq gnuserv-current-client nil) | |
323 (eval form)) | |
324 | |
325 | |
326 ;; "Execute" a client connection, called by gnuclient. This is the | |
327 ;; backbone of gnuserv.el. | |
328 (defun gnuserv-edit-files (type list &optional flags) | |
329 "For each (line-number . file) pair in LIST, edit the file at line-number. | |
330 The visited buffers are memorized, so that when \\[gnuserv-edit] is invoked | |
331 in such a buffer, or when it is killed, or the client's device deleted, the | |
332 client will be invoked that the edit is finished. | |
333 | |
334 TYPE should either be a (tty TTY TERM PID) list, or (x DISPLAY) list. | |
335 If FLAGS is `quick', just edit the files in Emacs. | |
336 If FLAGS is `view', view the files read-only." | |
337 (or (not flags) | |
338 (memq flags '(quick view)) | |
339 (error "Invalid flag %s" flags)) | |
340 (let* ((old-device-num (length (device-list))) | |
341 (new-frame nil) | |
342 (dest-frame (if (functionp gnuserv-frame) | |
343 (funcall gnuserv-frame (car type)) | |
344 gnuserv-frame)) | |
345 ;; The gnuserv-frame dependencies are ugly. | |
346 (device (cond ((frame-live-p dest-frame) | |
347 (frame-device dest-frame)) | |
348 ((null dest-frame) | |
349 (case (car type) | |
350 (tty (apply 'make-tty-device (cdr type))) | |
351 (x (make-x-device (cadr type))) | |
352 (t (error "Invalid device type")))) | |
353 (t | |
354 (selected-device)))) | |
355 (frame (cond ((frame-live-p dest-frame) | |
356 dest-frame) | |
357 ((null dest-frame) | |
358 (setq new-frame (make-frame nil device)) | |
359 new-frame) | |
360 (t (selected-frame)))) | |
361 (client (make-gnuclient :id gnuserv-current-client | |
362 :device device | |
363 :frame new-frame))) | |
364 (setq gnuserv-current-client nil) | |
365 ;; If the device was created by this client, push it to the list. | |
366 (and (/= old-device-num (length (device-list))) | |
367 (push device gnuserv-devices)) | |
368 ;; Visit all the listed files. | |
369 (while list | |
370 (let ((line (caar list)) (path (cdar list))) | |
371 (select-frame frame) | |
372 ;; Visit the file. | |
373 (funcall (if (eq flags 'view) | |
374 gnuserv-view-file-function | |
375 gnuserv-find-file-function) | |
376 path) | |
377 (goto-line line) | |
378 (run-hooks 'gnuserv-visit-hook) | |
379 ;; Don't memorize the quick and view buffers. | |
380 (when (null flags) | |
381 (pushnew (current-buffer) (gnuclient-buffers client)) | |
382 (setq gnuserv-minor-mode t)) | |
383 (pop list))) | |
384 (cond ((and flags (device-on-window-system-p device)) | |
385 ;; Exit if on X device, and quick or view. | |
386 ;; NOTE: if the client is to finish now, it must absolutely | |
387 ;; /not/ be included to the list of clients. This way the | |
388 ;; client-ids should be unique. | |
389 (gnuserv-write-to-client (gnuclient-id client) nil)) | |
390 (t | |
391 ;; Else, the client gets a vote. | |
392 (push client gnuserv-clients) | |
393 ;; Explain buffer exit options. If dest-frame is nil, the | |
394 ;; user can exit via `delete-frame'. OTOH, if FLAGS are | |
395 ;; nil and there are some buffers, the user can exit via | |
396 ;; `gnuserv-edit'. | |
397 (if (and (null flags) | |
398 (gnuclient-buffers client)) | |
399 (message (substitute-command-keys | |
400 "Type `\\[gnuserv-edit]' to finish editing")) | |
401 (or dest-frame | |
402 (message (substitute-command-keys | |
403 "Type `\\[delete-frame]' to finish editing")))))))) | |
404 | |
405 | |
406 ;;; Functions that hook into Emacs in various way to enable operation | |
407 | |
408 ;; Defined later. | |
409 (add-hook 'kill-emacs-hook 'gnuserv-kill-all-clients t) | |
410 | |
411 ;; A helper function; used by others. | |
412 (defun gnuserv-buffer-clients (buffer) | |
413 "Returns a list of clients to which BUFFER belongs." | |
414 (let ((client gnuserv-clients) | |
415 res) | |
416 (while client | |
417 (if (memq buffer (gnuclient-buffers (car client))) | |
418 (push (car client) res)) | |
419 (pop client)) | |
420 res)) | |
421 | |
422 ;; This function makes sure that a killed buffer is deleted off the | |
423 ;; list for the particular client. | |
424 ;; | |
425 ;; This hooks into `kill-buffer-hook'. It is *not* a replacement for | |
426 ;; `kill-buffer' (thanks God). | |
427 (defun gnuserv-kill-buffer-function () | |
428 "Remove the buffer from the buffer lists of all the clients it belongs to. | |
429 Any client that remains \"empty\" after the removal is informed that the | |
430 editing has ended." | |
431 (let* ((buf (current-buffer)) | |
432 (clients (gnuserv-buffer-clients buf))) | |
433 (while clients | |
434 (callf2 delq buf (gnuclient-buffers (car clients))) | |
435 ;; If no more buffers, kill the client. | |
436 (when (null (gnuclient-buffers (car clients))) | |
437 (gnuserv-kill-client (car clients))) | |
438 (pop clients)))) | |
439 | |
440 (add-hook 'kill-buffer-hook 'gnuserv-kill-buffer-function) | |
441 | |
442 ;; Ask for confirmation before killing a buffer that belongs to a | |
443 ;; living client. | |
444 (defun gnuserv-kill-buffer-query-function () | |
445 (or gnuserv-kill-quietly | |
446 (not (gnuserv-buffer-clients (current-buffer))) | |
447 (yes-or-no-p | |
448 (format "Buffer %s belongs to gnuserv client(s); kill anyway? " | |
449 (current-buffer))))) | |
450 | |
451 (add-hook 'kill-buffer-query-functions | |
452 'gnuserv-kill-buffer-query-function) | |
453 | |
454 (defun gnuserv-kill-emacs-query-function () | |
455 (or gnuserv-kill-quietly | |
456 (not (some 'gnuclient-buffers gnuserv-clients)) | |
457 (yes-or-no-p "Gnuserv buffers still have clients; exit anyway? "))) | |
458 | |
459 (add-hook 'kill-emacs-query-functions | |
460 'gnuserv-kill-emacs-query-function) | |
461 | |
462 ;; If the device of a client is to be deleted, the client should die | |
463 ;; as well. This is why we hook into `delete-device-hook'. | |
464 (defun gnuserv-check-device (device) | |
465 (when (memq device gnuserv-devices) | |
466 (let ((client gnuserv-clients)) | |
467 (while client | |
468 (when (eq device (gnuclient-device (car client))) | |
469 ;; we must make sure that the server kill doesn't result in | |
470 ;; killing the device, because it would cause a device-dead | |
471 ;; error when `delete-device' tries to do the job later. | |
472 (gnuserv-kill-client (car client) t)) | |
473 (pop client))) | |
474 (callf2 delq device gnuserv-devices))) | |
475 | |
476 (add-hook 'delete-device-hook 'gnuserv-check-device) | |
477 | |
478 (defun gnuserv-temp-file-p (buffer) | |
170 "Return non-nil if BUFFER contains a file considered temporary. | 479 "Return non-nil if BUFFER contains a file considered temporary. |
171 These are files whose names suggest they are repeatedly | 480 These are files whose names suggest they are repeatedly |
172 reused to pass information to another program. | 481 reused to pass information to another program. |
173 | 482 |
174 The variable `server-temp-file-regexp' controls which filenames | 483 The variable `gnuserv-temp-file-regexp' controls which filenames |
175 are considered temporary." | 484 are considered temporary." |
176 (and (buffer-file-name buffer) | 485 (and (buffer-file-name buffer) |
177 (string-match server-temp-file-regexp (buffer-file-name buffer)))) | 486 (string-match gnuserv-temp-file-regexp (buffer-file-name buffer)))) |
178 | 487 |
179 | 488 (defun gnuserv-kill-client (client &optional leave-frame) |
180 (defun server-log (string) | 489 "Kill the gnuclient CLIENT. |
181 "If a *server* buffer exists, write STRING to it for logging purposes." | 490 This will do away with all the associated buffers. If LEAVE-FRAME, |
182 (if (get-buffer "*server*") | 491 the function will not remove the frames associated with the client." |
183 (save-excursion | 492 ;; Order is important: first delete client from gnuserv-clients, to |
184 (set-buffer "*server*") | 493 ;; prevent gnuserv-buffer-done-1 calling us recursively. |
185 (goto-char (point-max)) | 494 (callf2 delq client gnuserv-clients) |
186 (insert string) | 495 ;; Process the buffers. |
187 (or (bolp) (newline))))) | 496 (mapc 'gnuserv-buffer-done-1 (gnuclient-buffers client)) |
188 | 497 (unless leave-frame |
189 | 498 (let ((device (gnuclient-device client))) |
190 (defun server-sentinel (proc msg) | 499 ;; kill frame created by this client (if any), unless |
191 (cond ((eq (process-status proc) 'exit) | 500 ;; specifically requested otherwise. |
192 (server-log (message "Server subprocess exited"))) | 501 ;; |
193 ((eq (process-status proc) 'signal) | 502 ;; note: last frame on a device will not be deleted here. |
194 (server-log (message "Server subprocess killed"))))) | 503 (when (and (gnuclient-frame client) |
195 | 504 (frame-live-p (gnuclient-frame client)) |
196 | 505 (second (device-frame-list device))) |
197 (defun server-process-display-error (string) | 506 (delete-frame (gnuclient-frame client))) |
198 "Whenever a gnuserv error is reported, display it in a pop-up window." | 507 ;; If the device is live, created by a client, and no longer used |
199 (let ((cur (selected-window)) | 508 ;; by any client, delete it. |
200 (pop-up-windows t)) | 509 (when (and (device-live-p device) |
201 (pop-to-buffer (get-buffer-create "*server*")) | 510 (memq device gnuserv-devices) |
202 (set-window-start (selected-window) (point)) | 511 (second (device-list)) |
203 (server-log string) | 512 (not (member* device gnuserv-clients |
204 (select-window cur))) | 513 :key 'gnuclient-device))) |
205 | 514 ;; `gnuserv-check-device' will remove it from `gnuserv-devices'. |
206 | 515 (delete-device device)))) |
207 (defun server-process-filter (proc string) | 516 ;; Notify the client. |
208 "Process client gnuserv requests to execute Emacs commands." | 517 (gnuserv-write-to-client (gnuclient-id client) nil)) |
209 (setq server-string (concat server-string string)) | 518 |
210 (if (string-match "\^D$" server-string) ; requests end with ctrl-D | 519 ;; Do away with the buffer. |
211 (if (string-match "^[0-9]+" server-string) ; client request id | 520 (defun gnuserv-buffer-done-1 (buffer) |
212 (progn | 521 (let ((clients (gnuserv-buffer-clients buffer))) |
213 (server-log server-string) | 522 (while clients |
214 (let ((header (read-from-string server-string))) | 523 (callf2 delq buffer (gnuclient-buffers (car clients))) |
215 (setq current-client (car header)) | 524 (when (null (gnuclient-buffers (car clients))) |
216 (condition-case oops | 525 (gnuserv-kill-client (car clients))) |
217 (eval (car (read-from-string server-string | 526 (pop clients)) |
218 (cdr header)))) | 527 ;; Get rid of the buffer |
219 (error (setq server-string "") | 528 (save-excursion |
220 (server-write-to-client current-client oops) | 529 (set-buffer buffer) |
221 (setq current-client nil) | 530 (run-hooks 'gnuserv-done-hook) |
222 (signal (car oops) (cdr oops))) | 531 (setq gnuserv-minor-mode nil) |
223 (quit (setq server-string "") | 532 (funcall (if (gnuserv-temp-file-p buffer) |
224 (server-write-to-client current-client oops) | 533 gnuserv-done-temp-file-function |
225 (setq current-client nil) | 534 gnuserv-done-function) |
226 (signal 'quit nil))) | 535 buffer)))) |
227 (setq server-string ""))) | 536 |
228 (progn ;error string from server | 537 |
229 (server-process-display-error server-string) | 538 ;;; Higher-level functions |
230 (setq server-string ""))))) | 539 |
231 | 540 ;; Choose a `next' server buffer, according to several criteria, and |
232 | 541 ;; return it. If none appropriate are found, return nil. |
233 (defun server-release-outstanding-buffers () | 542 (defun gnuserv-next-buffer () |
234 "Release all buffers that have clients waiting for them to be finished." | 543 (let* ((frame (selected-frame)) |
235 (interactive) | 544 (device (selected-device)) |
236 (while server-clients | 545 client) |
237 (let ((buffer (nth 1 (car server-clients)))) ; for all buffers... | 546 (cond |
238 (server-buffer-done buffer)))) ; destructively modifies server-clients | 547 ;; If we have a client belonging to this frame, return |
548 ;; the first buffer from it. | |
549 ((setq client | |
550 (car (member* frame gnuserv-clients :key 'gnuclient-frame))) | |
551 (car (gnuclient-buffers client))) | |
552 ;; Else, look for a device. | |
553 ((and | |
554 (memq (selected-device) gnuserv-devices) | |
555 (setq client | |
556 (car (member* device gnuserv-clients :key 'gnuclient-device)))) | |
557 (car (gnuclient-buffers client))) | |
558 ;; Else, try to find just any client, and return its first buffer. | |
559 (gnuserv-clients | |
560 (car (gnuclient-buffers (car gnuserv-clients)))) | |
561 ;; Oh, give up. | |
562 (t nil)))) | |
563 | |
564 (defun gnuserv-buffer-done (buffer) | |
565 "Mark BUFFER as \"done\" for its client(s). | |
566 Calls `gnuserv-done-function' and returns another gnuserv buffer as a | |
567 suggestion for the new current buffer." | |
568 ;; Check whether this is the real thing. | |
569 (unless (gnuserv-buffer-clients buffer) | |
570 (error "%s does not belong to a gnuserv client" buffer)) | |
571 ;; Backup/ask query. | |
572 (if (gnuserv-temp-file-p buffer) | |
573 ;; For a temp file, save, and do NOT make a non-numeric backup | |
574 ;; Why does server.el explicitly back up temporary files? | |
575 (let ((version-control nil) | |
576 (buffer-backed-up (not gnuserv-make-temp-file-backup))) | |
577 (save-buffer)) | |
578 (if (and (buffer-modified-p) | |
579 (y-or-n-p (concat "Save file " buffer-file-name "? "))) | |
580 (save-buffer buffer))) | |
581 (gnuserv-buffer-done-1 buffer) | |
582 (gnuserv-next-buffer)) | |
583 | |
584 ;; Called by `gnuserv-start-1' to clean everything. Hooked into | |
585 ;; `kill-emacs-hook', too. | |
586 (defun gnuserv-kill-all-clients () | |
587 "Kill all the gnuserv clients. Ruthlessly." | |
588 (mapc 'gnuserv-kill-client gnuserv-clients)) | |
589 | |
590 ;; Actually start the process. Kills all the clients before-hand. | |
591 (defun gnuserv-start-1 (&optional leave-dead) | |
592 (gnuserv-kill-all-clients) | |
593 (when gnuserv-process | |
594 (set-process-sentinel gnuserv-process nil) | |
595 (condition-case () | |
596 (delete-process gnuserv-process) | |
597 (error nil))) | |
598 ;; If we already had a server, clear out associated status. | |
599 (unless leave-dead | |
600 (setq gnuserv-string "") | |
601 (setq gnuserv-current-client nil) | |
602 (let ((process-connection-type t)) | |
603 (setq gnuserv-process | |
604 (start-process "gnuserv" nil gnuserv-program))) | |
605 (set-process-sentinel gnuserv-process 'gnuserv-sentinel) | |
606 (set-process-filter gnuserv-process 'gnuserv-process-filter) | |
607 (process-kill-without-query gnuserv-process))) | |
608 | |
609 | |
610 ;;; User-callable functions: | |
239 | 611 |
240 ;;;###autoload | 612 ;;;###autoload |
241 (defun gnuserv-start (&optional leave-dead) | 613 (defun gnuserv-start (&optional leave-dead) |
242 "Allow this Emacs process to be a server for client processes. | 614 "Allow this Emacs process to be a server for client processes. |
243 This starts a server communications subprocess through which | 615 This starts a gnuserv communications subprocess through which |
244 client \"editors\" (gnuclient and gnudoit) can send editing commands to | 616 client \"editors\" (gnuclient and gnudoit) can send editing commands to |
245 this Emacs job. See the gnuserv(1) manual page for more details. | 617 this Emacs job. See the gnuserv(1) manual page for more details. |
246 | 618 |
247 Prefix arg means just kill any existing server communications subprocess." | 619 Prefix arg means just kill any existing server communications subprocess." |
248 (interactive "P") | 620 (interactive "P") |
249 ;; kill it dead! | 621 ;; kill it dead! |
250 (if server-process | 622 (and gnuserv-process |
251 (progn | 623 (not leave-dead) |
252 (server-release-outstanding-buffers) | 624 (message "Restarting gnuserv")) |
253 (set-process-sentinel server-process nil) | 625 (gnuserv-start-1 leave-dead)) |
254 (condition-case () | 626 |
255 (delete-process server-process) | 627 ;;;###autoload |
256 (error nil)))) | 628 (defun gnuserv-edit (&optional arg) |
257 ;; If we already had a server, clear out associated status. | 629 "Mark the current gnuserv editing buffer as \"done\", and switch to next one. |
258 (if leave-dead | 630 |
259 nil | 631 The `gnuserv-done-function' is used to dispose of the buffer after marking it |
260 (if server-process | 632 as done; it is `kill-buffer' by default. |
261 (server-log (message "Restarting server"))) | 633 |
262 (setq server-string "") | 634 Files that match `gnuserv-temp-file-regexp' are considered temporary and |
263 (setq current-client nil) | 635 are saved unconditionally and backed up if `gnuserv-make-temp-file-backup' |
264 (let ((process-connection-type t)) | 636 is non-nil. They are disposed of using `gnuserv-done-temp-file-function'. |
265 (setq server-process | 637 |
266 (start-process "server" nil server-program))) | 638 When all of a client's buffers are marked as \"done\", the client is notified. |
267 (set-process-sentinel server-process 'server-sentinel) | 639 |
268 (set-process-filter server-process 'server-process-filter) | 640 If invoked with a prefix argument, or if there is no gnuserv process |
269 (process-kill-without-query server-process))) | 641 running, only starts server process. Invoked with \\[gnuserv-edit]." |
270 | |
271 ;; make gnuserv-start an alias to server-start, for backward compatibility | |
272 (fset 'server-start (function gnuserv-start)) | |
273 | |
274 ; Can gnuserv handle commands in close succesion? (See server.el line 283) JV | |
275 (defun server-write-to-client (client form) | |
276 "Write the given form to the given client via the server process." | |
277 (if (and client | |
278 (eq (process-status server-process) 'run)) | |
279 (let* ((result (format "%s" form)) | |
280 (s (format "%s/%d:%s\n" client (length result) result))) | |
281 (process-send-string server-process s) | |
282 (server-log s)))) | |
283 | |
284 (defun server-eval (form) | |
285 "Evaluate form and return result to client." | |
286 (server-write-to-client current-client (eval form)) | |
287 (setq current-client nil)) | |
288 | |
289 | |
290 (defun server-eval-quickly (form) | |
291 "Let client know that we've received the request, but eval the form | |
292 afterwards in order to not keep the client waiting." | |
293 (server-write-to-client current-client nil) | |
294 (setq current-client nil) | |
295 (eval form)) | |
296 | |
297 (defun server-make-window-visible () | |
298 "Try to make this window even more visible." | |
299 (cond | |
300 ;; XEmacs can (in theory) raise any kind of frame | |
301 ((fboundp 'raise-frame) | |
302 (raise-frame (selected-frame))) | |
303 ((not (and (boundp 'window-system) window-system)) | |
304 nil) | |
305 ((fboundp 'deiconify-screen) | |
306 (deiconify-screen (selected-screen)) | |
307 (raise-screen (selected-screen))) | |
308 ((fboundp 'mapraised-screen) | |
309 (mapraised-screen)) | |
310 ((fboundp 'x-remap-window) | |
311 (x-remap-window) | |
312 ;; give window chance to re-display text | |
313 (accept-process-output)))) | |
314 | |
315 (defun server-tty-find-file (tty termtype pid file) | |
316 (let ((device (make-tty-device tty termtype pid ))) | |
317 (select-frame (make-frame nil device)) | |
318 (if (not file) | |
319 (switch-to-buffer (get-buffer-create "*scratch*")) | |
320 (find-file file))) | |
321 (run-hooks 'server-visit-hook)) | |
322 | |
323 (defun server-find-file (file) | |
324 "Edit file FILENAME. | |
325 Switch to a buffer visiting file FILENAME, | |
326 creating one if none already exists." | |
327 (let ((obuf (get-file-buffer file)) | |
328 ;; XEmacs addition. | |
329 (force-dialog-box-use t)) | |
330 (if (and obuf (set-buffer obuf)) | |
331 (if (file-exists-p file) | |
332 (if (or (not (verify-visited-file-modtime obuf)) | |
333 (buffer-modified-p obuf)) | |
334 (revert-buffer t nil)) | |
335 (if (y-or-n-p | |
336 (concat "File no longer exists: " | |
337 file | |
338 ", write buffer to file? ")) | |
339 (write-file file)))) | |
340 (cond ((and window-system | |
341 gnuserv-frame (fboundp 'frame-live-p) ;; v19 & XEmacs 19.12+ | |
342 (frame-live-p gnuserv-frame)) | |
343 (select-frame gnuserv-frame) | |
344 (find-file file)) | |
345 | |
346 ((and window-system | |
347 gnuserv-frame (fboundp 'live-screen-p) ;; XEmacs 19.9+ | |
348 (live-screen-p gnuserv-frame)) | |
349 (select-screen gnuserv-frame) | |
350 (find-file file)) | |
351 | |
352 ((and window-system | |
353 (fboundp 'select-frame)) ;; v19 & XEmacs 19.12+ | |
354 (select-frame (make-frame)) | |
355 (find-file file)) | |
356 | |
357 ((and window-system | |
358 (fboundp 'select-screen) ;; XEmacs 19.10+ | |
359 (fboundp 'make-screen)) | |
360 (select-screen (make-screen)) | |
361 (find-file file)) | |
362 | |
363 ((and (eq window-system 'x) ;; XEmacs 19.9- | |
364 (fboundp 'select-screen) | |
365 (fboundp 'x-create-screen)) | |
366 (select-screen (x-create-screen nil)) | |
367 (find-file file)) | |
368 | |
369 ((and window-system | |
370 (fboundp 'create-screen)) ;; epoch | |
371 (if (screenp gnuserv-frame) | |
372 (progn (select-screen gnuserv-frame) | |
373 (find-file file)) | |
374 (select-screen (create-screen (find-file-noselect file))))) | |
375 | |
376 (t (find-file file)))) ;; emacs18+ | |
377 (run-hooks 'server-visit-hook)) | |
378 | |
379 | |
380 (defun server-edit-files-quickly (list) | |
381 "For each (line-number . file) pair in LIST, edit the file at line-number. | |
382 Unlike (server-edit-files), no information is saved about clients waiting on | |
383 edits to this buffer." | |
384 (server-write-to-client current-client nil) | |
385 (setq current-client nil) | |
386 (while list | |
387 (let ((line (car (car list))) | |
388 (path (cdr (car list)))) | |
389 (server-find-file path) | |
390 (server-make-window-visible) | |
391 (goto-line line)) | |
392 (setq list (cdr list)))) | |
393 | |
394 | |
395 (defun server-edit-files (list) | |
396 "For each (line-number . file) pair in LIST, edit the file at line-number. | |
397 Save enough information for (server-kill-buffer) to inform the client when | |
398 the edit is finished." | |
399 (while list | |
400 (let ((line (car (car list))) | |
401 (path (cdr (car list)))) | |
402 (server-find-file path) | |
403 (server-make-window-visible) | |
404 (let ((old-clients (assq current-client server-clients)) | |
405 (buffer (current-buffer))) | |
406 (goto-line line) | |
407 (setq server-buffer-clients | |
408 (cons current-client server-buffer-clients)) | |
409 (if old-clients ;client already waiting for buffers? | |
410 (nconc old-clients (list buffer)) ;yes -- append this one as well | |
411 (setq server-clients ;nope -- make a new record | |
412 (cons (list current-client buffer) | |
413 server-clients))))) | |
414 (setq list (cdr list))) | |
415 (message (substitute-command-keys | |
416 (if (and (boundp 'infodock-version) window-system) | |
417 "Type {\\[server-edit]} or select Frame/Delete to finish edit." | |
418 "When done with a buffer, type \\[server-edit].")))) | |
419 | |
420 (defun server-tty-edit-files (tty termtype pid list) | |
421 "For each (line-number . file) pair in LIST, edit the file at line-number. | |
422 Save enough information for (server-kill-buffer) to inform the client when | |
423 the edit is finished." | |
424 (or list (setq list '((1 . nil)))) | |
425 (while list | |
426 (let ((line (car (car list))) | |
427 (path (cdr (car list)))) | |
428 (server-tty-find-file tty termtype pid path) | |
429 (server-make-window-visible) | |
430 (let ((old-clients (assq current-client server-clients)) | |
431 (buffer (current-buffer))) | |
432 (goto-line line) | |
433 (setq server-buffer-clients | |
434 (cons current-client server-buffer-clients)) | |
435 (if old-clients ;client already waiting for buffers? | |
436 (nconc old-clients (list buffer)) ;yes -- append this one as well | |
437 (setq server-clients ;nope -- make a new record | |
438 (cons (list current-client buffer) | |
439 server-clients))))) | |
440 (setq list (cdr list))) | |
441 (message (substitute-command-keys | |
442 (if (and (boundp 'infodock-version) window-system) | |
443 "Type {\\[server-edit]} or select Frame/Delete to finish edit." | |
444 "When done with a buffer, type \\[server-edit].")))) | |
445 | |
446 (defun server-get-buffer (buffer) | |
447 "One arg, a BUFFER or a buffer name. Return the buffer object even if killed. | |
448 Signal an error if there is no record of BUFFER." | |
449 (if (null buffer) | |
450 (current-buffer) | |
451 (let ((buf (get-buffer buffer))) | |
452 (if (null buf) | |
453 (if (bufferp buffer) | |
454 buffer | |
455 (if (stringp buffer) | |
456 (error "No buffer named %s" buffer) | |
457 (error "Invalid buffer argument"))) | |
458 buf)))) | |
459 | |
460 (defun server-kill-buffer (buffer) | |
461 "Kill the BUFFER. The argument may be a buffer object or buffer name. | |
462 NOTE: This function has been enhanced to allow for remote editing | |
463 in the following way: | |
464 | |
465 If the buffer is waited upon by one or more clients, and a client is | |
466 not waiting for other buffers to be killed, then the client is told | |
467 that the buffer has been killed." | |
468 (interactive "bKill buffer ") | |
469 (setq buffer (server-get-buffer buffer)) | |
470 (if (buffer-name buffer) | |
471 (save-excursion | |
472 (set-buffer buffer) | |
473 (let ((old-clients server-clients)) | |
474 (server-real-kill-buffer buffer) ;try to kill it | |
475 (if (buffer-name buffer) ;succeeded in killing? | |
476 nil ;nope | |
477 (while old-clients | |
478 (let ((client (car old-clients))) | |
479 (delq buffer client) | |
480 (if (cdr client) ;pending buffers? | |
481 nil ;yep | |
482 (server-write-to-client (car client) nil) ;nope, tell client | |
483 (setq server-clients (delq client server-clients)))) | |
484 (setq old-clients (cdr old-clients))) | |
485 t))))) | |
486 | |
487 | |
488 ;; Ask before killing a server buffer. | |
489 ;; It was suggested to release its client instead, | |
490 ;; but I think that is dangerous--the client would proceed | |
491 ;; using whatever is on disk in that file. -- rms. | |
492 (defun server-kill-buffer-query-function () | |
493 (or server-kill-quietly | |
494 (not server-buffer-clients) | |
495 (yes-or-no-p (format "Buffer `%s' still has clients; kill it? " | |
496 (buffer-name (current-buffer)))))) | |
497 | |
498 (add-hook 'kill-buffer-query-functions | |
499 'server-kill-buffer-query-function) | |
500 | |
501 (defun server-kill-emacs-query-function () | |
502 (let (live-client | |
503 (tail server-clients)) | |
504 ;; See if any clients have any buffers that are still alive. | |
505 (while tail | |
506 (if (memq t (mapcar 'stringp (mapcar 'buffer-name (cdr (car tail))))) | |
507 (setq live-client t)) | |
508 (setq tail (cdr tail))) | |
509 (or server-kill-quietly | |
510 (not live-client) | |
511 (yes-or-no-p "Server buffers still have clients; exit anyway? ")))) | |
512 | |
513 (add-hook 'kill-emacs-query-functions 'server-kill-emacs-query-function) | |
514 | |
515 | |
516 (defun server-kill-all-local-variables () | |
517 "Eliminate all the buffer-local variable values of the current buffer. | |
518 This buffer will then see the default values of all variables. | |
519 NOTE: This function has been modified to ignore the variable | |
520 server-buffer-clients." | |
521 (let ((clients server-buffer-clients)) | |
522 (server-real-kill-all-local-variables) | |
523 (if clients | |
524 (setq server-buffer-clients clients)))) | |
525 | |
526 | |
527 (or (fboundp 'server-real-kill-buffer) | |
528 (fset 'server-real-kill-buffer (symbol-function 'kill-buffer))) | |
529 | |
530 (fset 'kill-buffer 'server-kill-buffer) | |
531 | |
532 (or (fboundp 'server-real-kill-all-local-variables) | |
533 (fset 'server-real-kill-all-local-variables | |
534 (symbol-function 'kill-all-local-variables))) | |
535 | |
536 (fset 'kill-all-local-variables 'server-kill-all-local-variables) | |
537 | |
538 | |
539 (defun server-buffer-done (buffer) | |
540 "Mark BUFFER as \"done\" for its client(s). | |
541 Buries the buffer, and returns another server buffer as a suggestion for the | |
542 new current buffer." | |
543 ; Note we do NOT return a list with a killed flag, doesn't seem usefull to me. JV | |
544 (let ((next-buffer nil) | |
545 (old-clients server-clients)) | |
546 (while old-clients | |
547 (let ((client (car old-clients))) | |
548 (or next-buffer | |
549 (setq next-buffer (nth 1 (memq buffer client)))) | |
550 (delq buffer client) | |
551 ;; Delete all dead buffers from CLIENT. (Why? JV , copyed from server.el) | |
552 (let ((tail client)) | |
553 (while tail | |
554 (and (bufferp (car tail)) | |
555 (null (buffer-name (car tail))) | |
556 (delq (car tail) client)) | |
557 (setq tail (cdr tail)))) | |
558 ;; If client now has no pending buffers, | |
559 ;; tell it that it is done, and forget it entirely. | |
560 (if (cdr client) | |
561 nil | |
562 (if (buffer-name buffer) | |
563 (save-excursion | |
564 (set-buffer buffer) | |
565 (setq server-buffer-clients nil) | |
566 (run-hooks 'server-done-hook))) | |
567 ; Order is important here -- | |
568 ; server-kill-buffer tries to notify clients that | |
569 ; they are done, too, but if we try and notify twice, | |
570 ; we are h0zed -- Hunter Kelly 3/3/97 | |
571 (setq server-clients (delq client server-clients)) | |
572 (if (server-temp-file-p buffer) | |
573 (funcall server-done-temp-file-function buffer) | |
574 (funcall server-done-function buffer)) | |
575 (server-write-to-client (car client) nil))) | |
576 (setq old-clients (cdr old-clients))) | |
577 next-buffer)) | |
578 | |
579 | |
580 (defun mh-draft-p (buffer) | |
581 "Return non-nil if this BUFFER is an mh <draft> file. Since MH deletes | |
582 draft *BEFORE* it is edited, the server treats them specially." | |
583 ;; This may not be appropriately robust for all cases. | |
584 (string= (buffer-name buffer) "draft")) | |
585 | |
586 | |
587 (defun server-done () | |
588 "Offer to save current buffer and mark it as \"done\" for clients. | |
589 Also bury it, and return a suggested new current buffer." | |
590 (let ((buffer (current-buffer))) | |
591 (if server-buffer-clients | |
592 (progn | |
593 (if (mh-draft-p buffer);; Does this comflict with temp-file ? JV | |
594 (progn (save-buffer) | |
595 (write-region (point-min) (point-max) | |
596 (concat buffer-file-name "~")) | |
597 (kill-buffer buffer)) | |
598 (if (server-temp-file-p buffer) | |
599 ;; For a temp file, save, and do NOT make a non-numeric backup | |
600 ;; Why does server.el explicitly back up temporary files? | |
601 (let ((version-control nil) | |
602 (buffer-backed-up (not server-make-temp-file-backup))) | |
603 (save-buffer)) | |
604 (if (and (buffer-modified-p) | |
605 (y-or-n-p (concat "Save file " buffer-file-name "? "))) | |
606 (save-buffer buffer)))) | |
607 (server-buffer-done buffer))))) | |
608 | |
609 | |
610 (defun server-edit (&optional arg) | |
611 "Switch to next server editing | |
612 buffer and mark current one as \"done\". If a server buffer is | |
613 current, it is marked \"done\" and optionally saved. MH <draft> files | |
614 are always saved and backed up, no questions asked. Files that match | |
615 server-temp-file-regexp are considered temporary and are saved | |
616 unconditionally and | |
617 backed up if server-make-temp-file-backup is non-nil. When all of a | |
618 client's buffers are marked as \"done\", the client is notified. | |
619 | |
620 If invoked with a prefix argument, or if there is no server process running, | |
621 starts server process and that is all. Invoked by \\[server-edit]. | |
622 | |
623 If `server-kill-last-frame' is t, then the final frame will be killed." | |
624 (interactive "P") | 642 (interactive "P") |
625 (if (or arg | 643 (if (or arg (not gnuserv-process) |
626 (not server-process) | 644 (memq (process-status gnuserv-process) '(signal exit))) |
627 (memq (process-status server-process) '(signal exit))) | 645 (gnuserv-start) |
628 (server-start nil) | 646 (switch-to-buffer (or (gnuserv-buffer-done (current-buffer)) |
629 (if server-buffer-clients | 647 (current-buffer))))) |
630 (progn (server-done-and-switch) | 648 |
631 (cond ((fboundp 'console-type) ;; XEmacs 19.14+ | 649 ;;;###autoload |
632 (or (and (equal (console-type) 'x) | 650 (global-set-key "\C-x#" 'gnuserv-edit) |
633 gnuserv-frame | |
634 (frame-live-p gnuserv-frame)) | |
635 (condition-case () | |
636 (delete-frame (selected-frame) | |
637 server-kill-last-frame) | |
638 (error | |
639 (message "Not deleting last visible frame..."))))) | |
640 ((or (not window-system) | |
641 (and gnuserv-frame | |
642 (or (and (fboundp 'frame-live-p) | |
643 (frame-live-p gnuserv-frame)) | |
644 (and (fboundp 'live-screen-p) | |
645 (live-screen-p gnuserv-frame)) | |
646 (and (fboundp 'create-screen) | |
647 (screenp gnuserv-frame))))) | |
648 ()) ;; do nothing | |
649 ((fboundp 'delete-frame) | |
650 (delete-frame (selected-frame) t)) | |
651 ((fboundp 'delete-screen) | |
652 (delete-screen)))) | |
653 (error | |
654 "(server-edit): Use only on buffers created by external programs.") | |
655 ))) | |
656 | |
657 (defun server-switch-buffer-internal (next-buffer always) | |
658 "Switch to NEXT-BUFFER if a live buffer, otherwise switch to another buffer | |
659 with gnuserv clients. If no such buffer is available, we switch to | |
660 another normal buffer if `always' is non-nil!" | |
661 ;; switching | |
662 (if next-buffer | |
663 (if (and (bufferp next-buffer) | |
664 (buffer-name next-buffer)) | |
665 (switch-to-buffer next-buffer) | |
666 ;; If NEXT-BUFFER is a dead buffer, | |
667 ;; remove the server records for it | |
668 ;; and try the next surviving server buffer. | |
669 (server-switch-buffer-internal | |
670 (server-buffer-done next-buffer) always)) | |
671 (if server-clients | |
672 (server-switch-buffer-internal (nth 1 (car server-clients)) always) | |
673 (if always | |
674 (switch-to-buffer (other-buffer)))))) | |
675 | |
676 ;; For compatability | |
677 (defun server-switch-buffer (next-buffer) | |
678 (server-switch-buffer-internal next-buffer t)) | |
679 | |
680 ;; The below function calles server-done and switches to the next | |
681 ;; sensible buffer. This implementation works regardless of the values | |
682 ;; of server-*-function and doens't need the tail recursion | |
683 ;; variable passing of server.el. It is more transparant too. JV | |
684 (defun server-done-and-switch () | |
685 "Be done with the current buffer and switch to another server buffer | |
686 if there is one, otherwise just switch buffer" | |
687 (let ((old-current (current-buffer))) | |
688 (server-switch-buffer-internal (server-done) nil) | |
689 (if (eq old-current (current-buffer)) | |
690 (switch-to-buffer (other-buffer))))) | |
691 | |
692 (global-set-key "\C-x#" 'server-edit) | |
693 | 651 |
694 (provide 'gnuserv) | 652 (provide 'gnuserv) |
695 | 653 |
696 ;;; gnuserv.el ends here | 654 ;;; gnuserv.el ends here |