Import XE riece pkg Makefile/package-info.in
[packages] / xemacs-packages / edit-utils / saveconf.el
1 ;;; Save Emacs buffer and window configuration between editing sessions.
2 ;;; Copyright (C) 1987, 1988, 1989 Kyle E. Jones
3 ;;;
4 ;;; This program is free software; you can redistribute it and/or modify
5 ;;; it under the terms of the GNU General Public License as published by
6 ;;; the Free Software Foundation; either version 1, or (at your option)
7 ;;; any later version.
8 ;;;
9 ;;; This program is distributed in the hope that it will be useful,
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 ;;; GNU General Public License for more details.
13 ;;;
14 ;;; A copy of the GNU General Public License can be obtained from the
15 ;;; program's author (send electronic mail to kyle@cs.odu.edu) or from
16 ;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
17 ;;; 02139, USA.
18 ;;;
19 ;;; Send bug reports to kyle@cs.odu.edu.
20
21 ;;; Synched up with: Not in FSF.
22
23 ;; This package of functions gives Emacs the ability to remember which
24 ;; files were being visited, the windows that were on them, and the
25 ;; value of point in their buffers the last Emacs session in the same
26 ;; directory.  This is an emulation of an old Gosling Emacs feature.
27 ;;
28 ;; The relevant commands are save-context and recover-context.
29 ;;
30 ;; Most of the time you'll want an Emacs session's context saved even if
31 ;; you choose not to recover it later.  To avoid having to manually
32 ;; M-x save-context at each emacs exit, put the line:
33 ;;    (setq auto-save-and-recover-context t)
34 ;; in your .emacs or in default.el in the lisp directory of the Emacs
35 ;; distribution.  The context will then automatically be saved when
36 ;; Emacs exits.
37 ;;
38 ;; By default only the contexts of visible buffers (buffers with windows
39 ;; on them) are saved.  Setting the variable save-buffer-context to t
40 ;; causes the contexts of all buffers to be saved.
41 ;;
42 ;; Put this file in the "lisp" directory of the emacs distribution in a
43 ;; file called saveconf.el.  Byte-compile it.
44 ;;
45 ;; There are two ways to use this package.
46 ;;   1) Put the line
47 ;;       (require 'saveconf)
48 ;;      in the file site-init.el in the lisp directory of the Emacs
49 ;;      directory and rebuild Emacs.  If you get the "Pure Lisp storage
50 ;;      exhausted" error message when rebuilding Emacs, increase PURESIZE
51 ;;      in src/config.h by about 30000 bytes and try again.  It's almost
52 ;;      certain that this will happen to you so you might as well increase
53 ;;      PURESIZE beforehand.
54 ;;
55 ;;      This is the preferred mode of operation because it allows the
56 ;;      package to become part of Emacs' startup sequence and automatically
57 ;;      restore context in a directory if Emacs is invoked without any
58 ;;      command line arguments.
59 ;;
60 ;;   2) Put these lines
61 ;;       (require 'saveconf)
62 ;;       (if (null (cdr command-line-args))
63 ;;           (setq inihibit-startup-message (recover-context)))
64 ;;      at the end of your .emacs file or the default.el file in the
65 ;;      lisp directory of the Emacs distribution.  This causes the
66 ;;      context saved in the current directory to be recovered whenever
67 ;;      Emacs is invoked without any arguments.
68
69 (provide 'saveconf)
70
71 (defconst save-context-version "Norma Jean"
72   "A unique string which is placed at the beginning of every saved context
73 file.  If the string at the beginning of the context file doesn't match the
74 value of this variable the `recover-context' command will ignore the file's
75 contents.")
76
77 (defvar auto-save-and-recover-context nil
78   "*If non-nil the `save-context' command will always be run before Emacs is
79 exited.  Also upon Emacs startup, if this variable is non-nil and Emacs is
80 passed no command line arguments, `recover-context' will be run.")
81
82 (defvar save-buffer-context nil
83   "*If non-nil the `save-context' command will save the context
84 of buffers that are visiting files, as well as the contexts of buffers
85 that have windows.")
86
87 (defvar save-context-predicate
88   (function (lambda (w)
89               (and (buffer-file-name (window-buffer w))
90                    (not (string-match "^\\(/usr\\)?/tmp/"
91                                       (buffer-file-name (window-buffer w)))))))
92   "*Value is a predicate function which determines which windows' contexts
93 are saved.  When the `save-context' command is invoked, this function will
94 be called once for each existing Emacs window.  The function should accept
95 one argument which will be a window object, and should return non-nil if
96 the window's context should be saved.")
97
98
99 ;; kill-emacs' function definition must be saved
100 (if (not (fboundp 'just-kill-emacs))
101     (fset 'just-kill-emacs (symbol-function 'kill-emacs)))
102
103 ;; Make Emacs call recover-context at startup if appropriate.
104 (setq top-level
105       (list 'let '((starting-up (not command-line-processed)))
106             (list 'prog1
107                   top-level
108                   '(and starting-up auto-save-and-recover-context
109                         (null (cdr command-line-args)) (recover-context)))))
110
111 (defun kill-emacs (&optional query)
112   "End this Emacs session.
113 Prefix ARG or optional first ARG non-nil means exit with no questions asked,
114 even if there are unsaved buffers.  If Emacs is running non-interactively
115 and ARG is an integer, then Emacs exits with ARG as its exit code.
116
117 If the variable `auto-save-and-restore-context' is non-nil,
118 the function save-context will be called first."
119   (interactive "P")
120   ;; check the purify flag.  try to save only if this is a dumped Emacs.
121   ;; saving context from a undumped Emacs caused a NULL pointer to be
122   ;; referenced through.  I'm not sure why.
123   (if (and auto-save-and-recover-context (null purify-flag))
124       (save-context))
125   (just-kill-emacs query))
126
127 (defun save-context ()
128   "Save context of all Emacs windows (files visited and position of point).
129 The information goes into a file called .emacs_<username> in the directory
130 where the Emacs session was started.  The context can be recovered with the
131 `recover-context' command, provided you are in the same directory where
132 the context was saved.
133
134 If the variable `save-buffer-context' is non-nil, the context of all buffers
135 visiting files will be saved as well.
136
137 Window sizes and shapes are not saved, since these may not be recoverable
138 on terminals with a different number of rows and columns."
139   (interactive)
140   (condition-case error-data
141       (let (context-buffer mark save-file-name)
142         (setq save-file-name (concat (original-working-directory)
143                                      ".emacs_" (user-login-name)))
144         (if (not (file-writable-p save-file-name))
145             (if (file-writable-p (original-working-directory))
146                 (error "context is write-protected, %s" save-file-name)
147               (error "can't access directory, %s"
148                      (original-working-directory))))
149         ;;
150         ;; set up a buffer for the saved context information
151         ;; Note that we can't set the visited file yet, because by
152         ;; giving the buffer a file to visit we are making it
153         ;; eligible to have it's context saved.
154         ;;
155         (setq context-buffer (get-buffer-create " *Context Info*"))
156         (set-buffer context-buffer)
157         (erase-buffer)
158         (set-buffer-modified-p nil)
159         ;;
160         ;; record the context information
161         ;;
162         (mapcar
163          (function
164           (lambda (w)
165             (cond ((funcall save-context-predicate w)
166                    (prin1 (buffer-file-name (window-buffer w)) context-buffer)
167                    (princ " " context-buffer)
168                    (prin1 (window-point w) context-buffer)
169                    (princ "\n" context-buffer)))))
170          (window-list))
171         
172         ;;
173         ;; nil is the data sentinel.  We will insert it later if we
174         ;; need it but for now just remember where the last line of
175         ;; window context ended.
176         ;;
177         (setq mark (point))
178
179         ;;
180         ;; If `save-buffer-context' is non-nil we save buffer contexts.
181         ;;
182         (if save-buffer-context
183             (mapcar
184              (function
185               (lambda (b)
186                 (set-buffer b)
187                 (cond (buffer-file-name
188                        (prin1 buffer-file-name context-buffer)
189                        (princ " " context-buffer)
190                        (prin1 (point) context-buffer)
191                        (princ "\n" context-buffer)))))
192              (buffer-list)))
193
194         ;;
195         ;; If the context-buffer contains information, we add the version
196         ;;   string and sentinels, and write out the saved context.
197         ;; If the context-buffer is empty, we don't create a file at all.
198         ;; If there's an old saved context in this directory we attempt
199         ;;   to delete it.
200         ;;
201         (cond ((buffer-modified-p context-buffer)
202                (set-buffer context-buffer)
203                (setq buffer-offer-save nil)
204                ;; sentinel for EOF
205                (insert "nil\n")
206                ;; sentinel for end of window contexts
207                (goto-char mark)
208                (insert "nil\n")
209                ;; version string
210                (goto-char (point-min))
211                (prin1 save-context-version context-buffer)
212                (insert "\n\n")
213                ;; so kill-buffer won't need confirmation later
214                (set-buffer-modified-p nil)
215                ;; save it
216                (write-region (point-min) (point-max) save-file-name
217                              nil 'quiet))
218               (t (condition-case data
219                      (delete-file save-file-name) (error nil))))
220
221         (kill-buffer context-buffer))
222     (error nil)))
223
224 (defun recover-context ()
225   "Recover an Emacs context saved by `save-context' command.
226 Files that were visible in windows when the context was saved are visited and
227 point is set in each window to what is was when the context was saved."
228   (interactive)
229   (condition-case error-data
230       ;;
231       ;; Set up some local variables.
232       ;;
233       (let (sexpr context-buffer recover-file-name)
234         (setq recover-file-name (concat (original-working-directory)
235                                         ".emacs_" (user-login-name)))
236         (if (not (file-readable-p recover-file-name))
237             (error "can't access context, %s" recover-file-name))
238         ;;
239         ;; create a temp buffer and copy the saved context into it.
240         ;;
241         (setq context-buffer (get-buffer-create " *Recovered Context*"))
242         (set-buffer context-buffer)
243         (erase-buffer)
244         (insert-file-contents recover-file-name nil)
245         ;; so kill-buffer won't need confirmation later
246         (set-buffer-modified-p nil)
247         ;;
248         ;; If it's empty forget it.
249         ;;
250         (if (zerop (buffer-size))
251             (error "context file is empty, %s" recover-file-name))
252         ;;
253         ;; check the version and make sure it matches ours
254         ;;
255         (setq sexpr (read context-buffer))
256         (if (not (equal sexpr save-context-version))
257             (error "version string incorrect, %s" sexpr))
258         ;;
259         ;; Recover the window contexts
260         ;;
261         (while (setq sexpr (read context-buffer))
262           (select-window (get-largest-window))
263           (if (buffer-file-name)
264               (split-window))
265           (other-window 1)
266           (find-file sexpr)
267           (goto-char (read context-buffer)))
268         ;;
269         ;; Recover buffer contexts, if any.
270         ;;
271         (while (setq sexpr (read context-buffer)
272                      point (read context-buffer))
273           (set-buffer (find-file-noselect sexpr t))
274           (if (zerop (buffer-size))
275               (kill-buffer (current-buffer))
276             (goto-char point)))
277         (bury-buffer "*scratch*")
278         (kill-buffer context-buffer)
279         t )
280     (error nil)))
281          
282 (defun original-working-directory ()
283   (save-excursion
284     (set-buffer (get-buffer-create "*scratch*"))
285     default-directory))