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