Add defgroup; use defcustom for user vars.
[emacs.git] / lisp / time-stamp.el
blob77b1a64646a7b1d7a37ead373507ffbb670a8a2c
1 ;;; time-stamp.el --- Maintain last change time stamps in files edited by Emacs
3 ;; Copyright 1989, 1993, 1994, 1995 Free Software Foundation, Inc.
5 ;; Maintainer's Time-stamp: <1996-08-13 14:03:17 gildea>
6 ;; Maintainer: Stephen Gildea <gildea@lcs.mit.edu>
7 ;; Keywords: tools
9 ;; This file is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
14 ;; This file is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
24 ;;; Commentary:
26 ;; A template in a file can be updated with a new time stamp when
27 ;; you save the file. For example:
28 ;; static char *ts = "sdmain.c Time-stamp: <1996-08-13 10:20:51 gildea>";
29 ;; See the top of `time-stamp.el' for another example.
31 ;; To use time-stamping, add this line to your .emacs file:
32 ;; (add-hook 'write-file-hooks 'time-stamp)
33 ;; Now any time-stamp templates in your files will be updated automatically.
35 ;; See the documentation for the functions `time-stamp'
36 ;; and `time-stamp-toggle-active' for details.
38 ;;; Change Log:
40 ;; Originally based on the 19 Dec 88 version of
41 ;; date.el by John Sturdy <mcvax!harlqn.co.uk!jcgs@uunet.uu.net>
42 ;; Version 2, January 1995: replaced functions with %-escapes
43 ;; $Id: time-stamp.el,v 1.24 1996/12/18 02:45:09 rms Exp rms $
45 ;;; Code:
47 (defgroup time-stamp nil
48 "Maintain last change time stamps in files edited by Emacs."
49 :group 'data
50 :group 'extensions)
52 (defcustom time-stamp-active t
53 "*Non-nil to enable time-stamping of buffers by \\[time-stamp].
54 Can be toggled by \\[time-stamp-toggle-active].
55 See also the variable `time-stamp-warn-inactive'."
56 :type 'boolean
57 :group 'time-stamp)
59 (defcustom time-stamp-warn-inactive t
60 "Non-nil to have \\[time-stamp] warn if a buffer did not get time-stamped.
61 A warning is printed if `time-stamp-active' is nil and the buffer contains
62 a time stamp template that would otherwise have been updated."
63 :type 'boolean
64 :group 'time-stamp)
66 (defcustom time-stamp-old-format-warn 'ask
67 "Action to take if `time-stamp-format' is an old-style list.
68 If `error', the format is not used. If `ask', the user is queried about
69 using the time-stamp-format. If `warn', a warning is displayed.
70 If nil, no notification is given."
71 :type '(choice (const :tag "No modification" nil)
72 (const :tag "Don't use the format" error)
73 (const ask) (const warn))
74 :group 'time-stamp)
76 (defcustom time-stamp-format "%02y/%02m/%02d %02H:%02M:%02S %u"
77 "*Format of the string inserted by \\[time-stamp].
78 The value may be a string or a list. Lists are supported only for
79 backward compatibility; see variable `time-stamp-old-format-warn'.
81 A string is used with `format-time-string'.
82 For example, to get the format used by the `date' command,
83 use \"%3a %3b %2d %H:%M:%S %Z %y\".
85 In addition to the features of `format-time-string',
86 you can use the following %-constructs:
88 %f file name without directory
89 %F full file name
90 %h mail host name
91 %s system name
92 %u user's login name"
93 :type 'string
94 :group 'time-stamp)
96 ;;; Do not change time-stamp-line-limit, time-stamp-start, or
97 ;;; time-stamp-end in your .emacs or you will be incompatible
98 ;;; with other people's files! If you must change them,
99 ;;; do so only in the local variables section of the file itself.
102 (defvar time-stamp-line-limit 8 ;Do not change!
103 "Lines of a file searched; positive counts from start, negative from end.
104 The patterns `time-stamp-start' and `time-stamp-end' must be found on one
105 of the first (last) `time-stamp-line-limit' lines of the file for the
106 file to be time-stamped by \\[time-stamp].
108 Do not change `time-stamp-line-limit', `time-stamp-start', or
109 `time-stamp-end' for yourself or you will be incompatible
110 with other people's files! If you must change them for some application,
111 do so in the local variables section of the time-stamped file itself.")
114 (defvar time-stamp-start "Time-stamp:[ \t]+\\\\?[\"<]+" ;Do not change!
115 "Regexp after which the time stamp is written by \\[time-stamp].
116 See also the variables `time-stamp-end' and `time-stamp-line-limit'.
118 Do not change `time-stamp-line-limit', `time-stamp-start', or
119 `time-stamp-end' for yourself or you will be incompatible
120 with other people's files! If you must change them for some application,
121 do so in the local variables section of the time-stamped file itself.")
124 (defvar time-stamp-end "\\\\?[\">]" ;Do not change!
125 "Regexp marking the text after the time stamp.
126 \\[time-stamp] deletes the text between the first match of `time-stamp-start'
127 and the following match of `time-stamp-end' on the same line,
128 then writes the time stamp specified by `time-stamp-format' between them.
130 Do not change `time-stamp-line-limit', `time-stamp-start', or
131 `time-stamp-end' for yourself or you will be incompatible
132 with other people's files! If you must change them for some application,
133 do so in the local variables section of the time-stamped file itself.")
137 ;;;###autoload
138 (defun time-stamp ()
139 "Update the time stamp string in the buffer.
140 A template in a file can be automatically updated with a new time stamp
141 every time you save the file. Add this line to your .emacs file:
142 (add-hook 'write-file-hooks 'time-stamp)
143 Normally the template must appear in the first 8 lines of a file and
144 look like one of the following:
145 Time-stamp: <>
146 Time-stamp: \" \"
147 The time stamp is written between the brackets or quotes:
148 Time-stamp: <1996-07-18 10:20:51 gildea>
149 Only updates the time stamp if the variable `time-stamp-active' is non-nil.
150 The format of the time stamp is set by the variable `time-stamp-format'.
151 The variables `time-stamp-line-limit', `time-stamp-start',
152 and `time-stamp-end' control finding the template."
153 (interactive)
154 (let ((case-fold-search nil)
155 (start nil)
156 (end nil)
157 search-limit)
158 (save-excursion
159 (save-restriction
160 (widen)
161 (cond ((> time-stamp-line-limit 0)
162 (goto-char (setq start (point-min)))
163 (forward-line time-stamp-line-limit)
164 (setq search-limit (point)))
166 (goto-char (setq search-limit (point-max)))
167 (forward-line time-stamp-line-limit)
168 (setq start (point))))
169 (goto-char start)
170 (while (and (< (point) search-limit)
171 (not end)
172 (re-search-forward time-stamp-start search-limit 'move))
173 (setq start (point))
174 (end-of-line)
175 (let ((line-end (point)))
176 (goto-char start)
177 (if (re-search-forward time-stamp-end line-end 'move)
178 (setq end (match-beginning 0)))))))
179 (if end
180 (progn
181 ;; do all warnings outside save-excursion
182 (cond
183 ((not time-stamp-active)
184 (if time-stamp-warn-inactive
185 ;; don't signal an error in a write-file-hook
186 (progn
187 (message "Warning: time-stamp-active is off; did not time-stamp buffer.")
188 (sit-for 1))))
189 ((not (and (stringp time-stamp-start)
190 (stringp time-stamp-end)))
191 (message "time-stamp-start or time-stamp-end is not a string")
192 (sit-for 1))
194 (let ((new-time-stamp (time-stamp-string)))
195 (if (stringp new-time-stamp)
196 (save-excursion
197 (save-restriction
198 (widen)
199 (delete-region start end)
200 (goto-char start)
201 (insert new-time-stamp)
202 (setq end (point))
203 ;; remove any tabs used to format time stamp
204 (goto-char start)
205 (if (search-forward "\t" end t)
206 (untabify start end)))))))))))
207 ;; be sure to return nil so can be used on write-file-hooks
208 nil)
210 ;;;###autoload
211 (defun time-stamp-toggle-active (&optional arg)
212 "Toggle `time-stamp-active', setting whether \\[time-stamp] updates a buffer.
213 With arg, turn time stamping on if and only if arg is positive."
214 (interactive "P")
215 (setq time-stamp-active
216 (if (null arg)
217 (not time-stamp-active)
218 (> (prefix-numeric-value arg) 0)))
219 (message "time-stamp is now %s." (if time-stamp-active "active" "off")))
221 (defconst time-stamp-no-file "(no file)"
222 "String to use when the buffer is not associated with a file.")
224 (defun time-stamp-string-preprocess (format)
225 "Process occurrences in FORMAT of %f, %F, %h, %s and %u.
226 These are replaced with the file name (nondirectory part),
227 full file name, host name for mail, system name, and user name.
228 Do not alter other %-combinations, and do detect %%."
229 (let ((result "") (pos 0) (case-fold-search nil)
230 (file (or buffer-file-name "(no file)")))
231 (while (string-match "%[%uhfFs]" format pos)
232 (setq result (concat result (substring format pos (match-beginning 0))))
233 (let ((char (aref format (1+ (match-beginning 0)))))
234 (cond ((= char ?%)
235 (setq result (concat result "%%")))
236 ((= char ?u)
237 (setq result (concat result (user-login-name))))
238 ((= char ?f)
239 (setq result (concat result (file-name-nondirectory file))))
240 ((= char ?f)
241 (setq result (concat result file)))
242 ((= char ?s)
243 (setq result (concat result (system-name))))
244 ((= char ?h)
245 (setq result (concat result (time-stamp-mail-host-name))))))
246 (setq pos (match-end 0)))
247 (concat result (substring format pos))))
249 (defun time-stamp-string ()
250 "Generate the new string to be inserted by \\[time-stamp]."
251 (if (stringp time-stamp-format)
252 (format-time-string (time-stamp-string-preprocess time-stamp-format)
253 (current-time))
254 ;; handle version 1 compatibility
255 (cond ((or (eq time-stamp-old-format-warn 'error)
256 (and (eq time-stamp-old-format-warn 'ask)
257 (not (y-or-n-p "Use non-string time-stamp-format? "))))
258 (message "Warning: no time-stamp: time-stamp-format not a string")
259 (sit-for 1)
260 nil)
262 (cond ((eq time-stamp-old-format-warn 'warn)
263 (message "Obsolescent time-stamp-format type; should be string")
264 (sit-for 1)))
265 (time-stamp-fconcat time-stamp-format " ")))))
267 (defconst time-stamp-no-file "(no file)"
268 "String to use when the buffer is not associated with a file.")
270 (defun time-stamp-mail-host-name ()
271 "Return the name of the host where the user receives mail.
272 This is the value of `mail-host-address' if bound and a string,
273 otherwise the value of the function system-name."
274 (or (and (boundp 'mail-host-address)
275 (stringp mail-host-address)
276 mail-host-address)
277 (system-name)))
279 ;;; the rest of this file is for version 1 compatibility
281 (defun time-stamp-fconcat (list sep)
282 "Similar to (mapconcat 'funcall LIST SEP) but LIST allows literals.
283 If an element of LIST is a symbol, it is funcalled to get the string to use;
284 the separator SEP is used between two strings obtained by funcalling a
285 symbol. Otherwise the element itself is inserted; no separator is used
286 around literals."
287 (let ((return-string "")
288 (insert-sep-p nil))
289 (while list
290 (cond ((symbolp (car list))
291 (if insert-sep-p
292 (setq return-string (concat return-string sep)))
293 (setq return-string (concat return-string (funcall (car list))))
294 (setq insert-sep-p t))
296 (setq return-string (concat return-string (car list)))
297 (setq insert-sep-p nil)))
298 (setq list (cdr list)))
299 return-string))
301 ;;; Some functions used in time-stamp-format
303 ;;; Could generate most of a message-id with
304 ;;; '(time-stamp-yymmdd "" time-stamp-hhmm "@" time-stamp-mail-host-name)
306 ;;; pretty form, suitable for a title page
308 (defun time-stamp-month-dd-yyyy ()
309 "Return the current date as a string in \"Month DD, YYYY\" form."
310 (format-time-string "%B %e, %Y"))
312 (defun time-stamp-dd/mm/yyyy ()
313 "Return the current date as a string in \"DD/MM/YYYY\" form."
314 (format-time-string "%d/%m/%Y"))
316 ;;; same as __DATE__ in ANSI C
318 (defun time-stamp-mon-dd-yyyy ()
319 "Return the current date as a string in \"Mon DD YYYY\" form.
320 The first character of DD is space if the value is less than 10."
321 (format-time-string "%b %d %Y"))
323 ;;; RFC 822 date
325 (defun time-stamp-dd-mon-yy ()
326 "Return the current date as a string in \"DD Mon YY\" form."
327 (format-time-string "%d %b %y"))
329 ;;; RCS 3 date
331 (defun time-stamp-yy/mm/dd ()
332 "Return the current date as a string in \"YY/MM/DD\" form."
333 (format-time-string "%y/%m/%d"))
335 ;;; RCS 5 date
337 (defun time-stamp-yyyy/mm/dd ()
338 "Return the current date as a string in \"YYYY/MM/DD\" form."
339 (format-time-string "%Y/%m/%d"))
341 ;;; ISO 8601 date
343 (defun time-stamp-yyyy-mm-dd ()
344 "Return the current date as a string in \"YYYY-MM-DD\" form."
345 (format-time-string "%Y-%m-%d"))
347 (defun time-stamp-yymmdd ()
348 "Return the current date as a string in \"YYMMDD\" form."
349 (format-time-string "%y%m%d"))
351 (defun time-stamp-hh:mm:ss ()
352 "Return the current time as a string in \"HH:MM:SS\" form."
353 (format-time-string "%T"))
355 (defun time-stamp-hhmm ()
356 "Return the current time as a string in \"HHMM\" form."
357 (format-time-string "%H%M"))
359 (provide 'time-stamp)
361 ;;; time-stamp.el ends here