(current-fill-column): If fill-column is nil, return nil.
[bpt/emacs.git] / lisp / time-stamp.el
CommitLineData
9565745a 1;;; time-stamp.el --- Maintain last change time stamps in files edited by Emacs
b1defad2 2;;; Copyright 1989, 1993, 1994, 1995 Free Software Foundation, Inc.
1f92d7ef 3;;; Maintainer's Time-stamp: <95/05/31 10:47:14 gildea>
9565745a
RS
4
5;; Maintainer: Stephen Gildea <gildea@lcs.mit.edu>
9565745a
RS
6;; Keywords: tools
7
8;; This file is free software; you can redistribute it and/or modify
9;; it under the terms of the GNU General Public License as published by
10;; the Free Software Foundation; either version 2, or (at your option)
11;; any later version.
12
13;; This file is distributed in the hope that it will be useful,
14;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;; GNU General Public License for more details.
17
18;; You should have received a copy of the GNU General Public License
19;; along with GNU Emacs; see the file COPYING. If not, write to
20;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22;;; Commentary:
23
24;;; If you put a time stamp template anywhere in the first 8 lines of a file,
25;;; it can be updated every time you save the file. See the top of
26;;; time-stamp.el for a sample. The template looks like one of the following:
27;;; Time-stamp: <>
08cb6a9f 28;;; Time-stamp: " "
9565745a 29;;; The time stamp is written between the brackets or quotes, resulting in
b1defad2 30;;; Time-stamp: <95/01/18 10:20:51 gildea>
9565745a
RS
31;;; Here is an example which puts the file name and time stamp in the binary:
32;;; static char *time_stamp = "sdmain.c Time-stamp: <>";
33
b1defad2
RS
34;;; To activate automatic time stamping in GNU Emacs 19, add this code
35;;; to your .emacs file:
36;;; (add-hook 'write-file-hooks 'time-stamp)
9565745a 37;;;
b1defad2 38;;; In Emacs 18 you will need to do this instead:
9565745a
RS
39;;; (if (not (memq 'time-stamp write-file-hooks))
40;;; (setq write-file-hooks
41;;; (cons 'time-stamp write-file-hooks)))
75ee4bfe 42;;; (autoload 'time-stamp "time-stamp" "Update the time stamp in a buffer." t)
9565745a 43
b1defad2
RS
44;;; See the documentation for the function `time-stamp' for more details.
45
9565745a
RS
46;;; Change Log:
47
48;;; Originally based on the 19 Dec 88 version of
49;;; date.el by John Sturdy <mcvax!harlqn.co.uk!jcgs@uunet.uu.net>
b1defad2 50;;; version 2, January 1995: replaced functions with %-escapes
1f92d7ef 51;;; $Id: time-stamp.el,v 1.13 1995/05/30 21:20:09 kwzh Exp kwzh $
9565745a
RS
52
53;;; Code:
54
55(defvar time-stamp-active t
b1defad2
RS
56 "*Non-nil to enable time-stamping of files.
57Can be toggled by \\[time-stamp-toggle-active].
58See also the variable time-stamp-warn-inactive.")
59
60(defvar time-stamp-warn-inactive t
61 "*Non-nil to have time-stamp warn if time-stamp-active is nil.")
62
63(defvar time-stamp-format "%02y/%02m/%02d %02H:%02M:%02S %u"
64 "*Template for the string inserted by the time-stamp function.
65Value may be a string or a list. (Lists are supported only for
66backward compatibility.) A string is used verbatim except for character
67sequences beginning with %. See the documentation for the function
68time-stamp-strftime for a list of %-escapes.
69 Each element of a list is called as a function and the results are
70concatenated together separated by spaces. List elements may also be
71strings, which are included verbatim. Spaces are not inserted around
72literal strings.")
9565745a 73
9565745a
RS
74
75;;; Do not change time-stamp-line-limit, time-stamp-start, or
76;;; time-stamp-end in your .emacs or you will be incompatible
77;;; with other people's files! If you must change them,
78;;; do so only in the local variables section of the file itself.
79
e1f40b28 80(defvar time-stamp-line-limit 8 ;Do not change!
9565745a 81 "Number of lines at the beginning of a file that are searched.
e1f40b28
RS
82The patterns `time-stamp-start' and `time-stamp-end' must be found on one
83of the first `time-stamp-line-limit' lines of the file for the file to
84be time-stamped by \\[time-stamp].
85
86Do not change `time-stamp-line-limit', `time-stamp-start', or
87`time-stamp-end' for yourself or you will be incompatible
88with other people's files! If you must change them for some application,
89do so in the local variables section of the time-stamped file itself.")
90
9565745a 91
36081614 92(defvar time-stamp-start "Time-stamp:[ \t]+\\\\?[\"<]+" ;Do not change!
9565745a 93 "Regexp after which the time stamp is written by \\[time-stamp].
e1f40b28 94See also the variables `time-stamp-end' and `time-stamp-line-limit'.
9565745a 95
e1f40b28
RS
96Do not change `time-stamp-line-limit', `time-stamp-start', or
97`time-stamp-end' for yourself or you will be incompatible
9565745a
RS
98with other people's files! If you must change them for some application,
99do so in the local variables section of the time-stamped file itself.")
100
101
e1f40b28 102(defvar time-stamp-end "\\\\?[\">]" ;Do not change!
9565745a 103 "Regexp marking the text after the time stamp.
e1f40b28
RS
104\\[time-stamp] deletes the text between the first match of `time-stamp-start'
105and the following match of `time-stamp-end' on the same line,
106then writes the time stamp specified by `time-stamp-format' between them.
107
108Do not change `time-stamp-line-limit', `time-stamp-start', or
109`time-stamp-end' for yourself or you will be incompatible
110with other people's files! If you must change them for some application,
111do so in the local variables section of the time-stamped file itself.")
112
9565745a 113
59b644e8 114;;;###autoload
9565745a
RS
115(defun time-stamp ()
116 "Update the time stamp string in the buffer.
e1f40b28
RS
117If you put a time stamp template anywhere in the first 8 lines of a file,
118it can be updated every time you save the file. See the top of
119`time-stamp.el' for a sample. The template looks like one of the following:
120 Time-stamp: <>
121 Time-stamp: \" \"
122The time stamp is written between the brackets or quotes, resulting in
b1defad2 123 Time-stamp: <95/01/18 10:20:51 gildea>
9565745a
RS
124Only does its thing if the variable time-stamp-active is non-nil.
125Typically used on write-file-hooks for automatic time-stamping.
e1f40b28
RS
126The format of the time stamp is determined by the variable time-stamp-format.
127The variables time-stamp-line-limit, time-stamp-start, and time-stamp-end
128control finding the template."
9565745a 129 (interactive)
fe8287c6
KH
130 (let ((case-fold-search nil)
131 (need-to-warn nil))
132 (if (and (stringp time-stamp-start)
133 (stringp time-stamp-end))
134 (save-excursion
135 (save-restriction
136 (widen)
137 (goto-char (point-min))
138 (if (re-search-forward time-stamp-start
139 (save-excursion
140 (forward-line time-stamp-line-limit)
141 (point))
142 t)
143 (let ((start (point)))
144 (if (re-search-forward time-stamp-end
145 (save-excursion
146 (end-of-line)
147 (point))
148 t)
149 (if time-stamp-active
b1defad2
RS
150 (let ((end (match-beginning 0)))
151 (delete-region start end)
152 (goto-char start)
153 (insert (time-stamp-string))
154 (setq end (point))
155 ;; remove any tabs used to format the time stamp
156 (goto-char start)
157 (if (search-forward "\t" end t)
fe8287c6
KH
158 (untabify start end)))
159 (if time-stamp-warn-inactive
160 ;; do the actual warning outside save-excursion
161 (setq need-to-warn t))))))))
162 ;; don't signal an error in a write-file-hook
163 (message "time-stamp-start or time-stamp-end is not a string")
164 (sit-for 1))
165 (if need-to-warn
166 (progn
167 (message "Warning: did not time-stamp buffer.")
168 (sit-for 1))))
9565745a
RS
169 ;; be sure to return nil so can be used on write-file-hooks
170 nil)
171
b1defad2
RS
172;;;###autoload
173(defun time-stamp-toggle-active (&optional arg)
174 "Toggle time-stamp-active, which enables time stamping of files.
175With arg, turn time stamping on if and only if arg is positive."
176 (interactive "P")
177 (setq time-stamp-active
178 (if (null arg)
179 (not time-stamp-active)
180 (> (prefix-numeric-value arg) 0)))
181 (message "time-stamp is now %s." (if time-stamp-active "active" "off")))
182
183
9565745a
RS
184(defun time-stamp-string ()
185 "Generate the new string to be inserted by \\[time-stamp]."
b1defad2
RS
186 (if (stringp time-stamp-format)
187 (time-stamp-strftime time-stamp-format)
188 (time-stamp-fconcat time-stamp-format " "))) ;version 1 compatibility
189
1f92d7ef
KH
190(defconst time-stamp-month-numbers
191 '(("Jan" . 1) ("Feb" . 2) ("Mar" . 3) ("Apr" . 4) ("May" . 5) ("Jun" . 6)
192 ("Jul" . 7) ("Aug" . 8) ("Sep" . 9) ("Oct" . 10) ("Nov" . 11) ("Dec" . 12))
193 "Alist of months and their number.")
194
195(defconst time-stamp-month-full-names
196 ["(zero)" "January" "February" "March" "April" "May" "June"
197 "July" "August" "September" "October" "November" "December"])
198
199(defconst time-stamp-weekday-numbers
200 '(("Sun" . 0) ("Mon" . 1) ("Tue" . 2) ("Wed" . 3)
201 ("Thu" . 4) ("Fri" . 5) ("Sat" . 6))
202 "Alist of weekdays and their number.")
203
204(defconst time-stamp-weekday-full-names
205 ["Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"])
206
b1defad2
RS
207(defun time-stamp-strftime (format &optional time)
208 "Uses a FORMAT to format date, time, file, and user information.
209Optional second argument TIME will be used instead of the current time.
210Characters in the format are copied literally except for %-directives:
211
212%a weekday name: `Monday'. %A gives uppercase: `MONDAY'
213%b month name: `January'. %B gives uppercase: `JANUARY'
214%d day of month
215%H 24-hour clock hour
216%I 12-hour clock hour
217%m month number
218%M minute
219%p `am' or `pm'. %P gives uppercase: `AM' or `PM'
220%S seconds
221%w day number of week, Sunday is 0
222%y year: `1995'
223%z time zone name: `est'. %Z gives uppercase: `EST'
224
225Non-date items:
226%% a literal percent character: `%'
227%f file name without directory %F gives absolute pathname
228%s system name
229%u user's login name
230%h mail host name
231
232Decimal digits between the % and the type character specify the
233field width. Strings are truncated on the right; numbers on the left.
234A leading zero causes numbers to be zero-filled.
235
236For example, to get the format used by the `date' command,
237use \"%3a %3b %2d %02H:%02M:%02S %Z %y\""
238 (let ((time-string (cond ((stringp time)
239 time)
240 (time
241 (current-time-string time))
242 (t
243 (current-time-string))))
244 (fmt-len (length format))
245 (ind 0)
246 cur-char
247 (result "")
248 field-index
249 field-width
250 field-result)
251 (while (< ind fmt-len)
252 (setq cur-char (aref format ind))
253 (setq
254 result
255 (concat result
256 (cond
257 ((and (eq cur-char ?%)
258 (< (1+ ind) fmt-len))
259 (setq field-index (1+ ind))
260 (while (progn
261 (setq ind (1+ ind))
262 (setq cur-char (aref format ind))
263 (and (<= ?0 cur-char) (>= ?9 cur-char))))
264 (setq field-width (substring format field-index ind))
265 (setq field-result
266 (cond
267 ((eq cur-char ?%)
268 "%")
269 ((or (eq cur-char ?a) ;weekday name
270 (eq cur-char ?A))
271 (let ((name
272 (aref time-stamp-weekday-full-names
273 (cdr (assoc (substring time-string 0 3)
274 time-stamp-weekday-numbers)))))
275 (if (eq cur-char ?a)
276 name
277 (upcase name))))
278 ((or (eq cur-char ?b) ;month name
279 (eq cur-char ?B))
280 (let ((name
281 (aref time-stamp-month-full-names
282 (cdr (assoc (substring time-string 4 7)
283 time-stamp-month-numbers)))))
284 (if (eq cur-char ?b)
285 name
286 (upcase name))))
287 ((eq cur-char ?d) ;day of month, 1-31
288 (string-to-int (substring time-string 8 10)))
289 ((eq cur-char ?H) ;hour, 0-23
290 (string-to-int (substring time-string 11 13)))
291 ((eq cur-char ?I) ;hour, 1-12
292 (let ((hour (string-to-int (substring time-string 11 13))))
293 (cond ((< hour 1)
294 (+ hour 12))
295 ((> hour 12)
296 (- hour 12))
297 (t
298 hour))))
299 ((eq cur-char ?m) ;month number, 1-12
300 (cdr (assoc (substring time-string 4 7)
301 time-stamp-month-numbers)))
302 ((eq cur-char ?M) ;minute, 0-59
303 (string-to-int (substring time-string 14 16)))
304 ((or (eq cur-char ?p) ;am or pm
305 (eq cur-char ?P))
306 (let ((name
307 (if (> 12 (string-to-int (substring time-string 11 13)))
308 "am"
309 "pm")))
310 (if (eq cur-char ?p)
311 name
312 (upcase name))))
313 ((eq cur-char ?S) ;seconds, 00-60
314 (string-to-int (substring time-string 17 19)))
315 ((eq cur-char ?w) ;weekday number, Sunday is 0
316 (cdr (assoc (substring time-string 0 3) time-stamp-weekday-numbers)))
317 ((eq cur-char ?y) ;year
318 (string-to-int (substring time-string -4)))
319 ((or (eq cur-char ?z) ;time zone
320 (eq cur-char ?Z))
321 (let ((name
322 (if (fboundp 'current-time-zone)
323 (car (cdr (current-time-zone time))))))
324 (or name (setq name ""))
325 (if (eq cur-char ?z)
326 (downcase name)
327 (upcase name))))
328 ((eq cur-char ?f) ;buffer-file-name, base name only
329 (if buffer-file-name
330 (file-name-nondirectory buffer-file-name)
331 "(no file)"))
332 ((eq cur-char ?F) ;buffer-file-name, full path
333 (or buffer-file-name
334 "(no file)"))
335 ((eq cur-char ?s) ;system name
336 (system-name))
337 ((eq cur-char ?u) ;user name
338 (user-login-name))
339 ((eq cur-char ?h) ;mail host name
340 (time-stamp-mail-host-name))
341 ))
342 (if (string-equal field-width "")
343 field-result
344 (let ((padded-result
345 (format (format "%%%s%c"
346 field-width
347 (if (numberp field-result) ?d ?s))
348 (or field-result ""))))
349 (let ((initial-length (length padded-result))
350 (desired-length (string-to-int field-width)))
351 (if (> initial-length desired-length)
352 ;; truncate strings on right, numbers on left
353 (if (stringp field-result)
354 (substring padded-result 0 desired-length)
355 (substring padded-result (- desired-length)))
356 padded-result)))))
357 (t
358 (char-to-string cur-char)))))
359 (setq ind (1+ ind)))
360 result))
361
b1defad2
RS
362(defun time-stamp-mail-host-name ()
363 "Return the name of the host where the user receives mail.
364This is the value of `mail-host-address' if bound and a string,
365otherwise the value of `time-stamp-mail-host' (for versions of Emacs
366before 19.29) otherwise the value of the function system-name.
367This function may be usefully referenced by `time-stamp-format'."
368 (or (and (boundp 'mail-host-address)
369 (stringp mail-host-address)
370 mail-host-address)
371 (and (boundp 'time-stamp-mail-host) ;for backward compatibility
372 (stringp time-stamp-mail-host)
373 time-stamp-mail-host)
374 (system-name)))
375
376;;; the rest of this file is for version 1 compatibility
9565745a
RS
377
378(defun time-stamp-fconcat (list sep)
e1f40b28 379 "Similar to (mapconcat 'funcall LIST SEP) but LIST allows literals.
9565745a
RS
380If an element of LIST is a symbol, it is funcalled to get the string to use;
381the separator SEP is used between two strings obtained by funcalling a
382symbol. Otherwise the element itself is inserted; no separator is used
383around literals."
384 (let ((return-string "")
385 (insert-sep-p nil))
386 (while list
387 (cond ((symbolp (car list))
388 (if insert-sep-p
389 (setq return-string (concat return-string sep)))
390 (setq return-string (concat return-string (funcall (car list))))
391 (setq insert-sep-p t))
392 (t
393 (setq return-string (concat return-string (car list)))
394 (setq insert-sep-p nil)))
395 (setq list (cdr list)))
396 return-string))
397
398
9565745a
RS
399;;; Some useful functions to use in time-stamp-format
400
401;;; Could generate most of a message-id with
e1f40b28 402;;; '(time-stamp-yymmdd "" time-stamp-hhmm "@" time-stamp-mail-host-name)
9565745a 403
9565745a
RS
404;;; pretty form, suitable for a title page
405
406(defun time-stamp-month-dd-yyyy ()
e1f40b28 407 "Return the current date as a string in \"Month DD, YYYY\" form."
9565745a 408 (let ((date (current-time-string)))
579cc01c 409 (format "%s %d, %s"
9565745a
RS
410 (aref time-stamp-month-full-names
411 (cdr (assoc (substring date 4 7) time-stamp-month-numbers)))
412 (string-to-int (substring date 8 10))
413 (substring date -4))))
414
415;;; same as __DATE__ in ANSI C
416
417(defun time-stamp-mon-dd-yyyy ()
e1f40b28
RS
418 "Return the current date as a string in \"Mon DD YYYY\" form.
419The first character of DD is space if the value is less than 10."
9565745a
RS
420 (let ((date (current-time-string)))
421 (format "%s %2d %s"
422 (substring date 4 7)
423 (string-to-int (substring date 8 10))
424 (substring date -4))))
425
426;;; RFC 822 date
427
428(defun time-stamp-dd-mon-yy ()
e1f40b28 429 "Return the current date as a string in \"DD Mon YY\" form."
9565745a
RS
430 (let ((date (current-time-string)))
431 (format "%02d %s %s"
432 (string-to-int (substring date 8 10))
433 (substring date 4 7)
434 (substring date -2))))
435
436;;; RCS 3 date
437
438(defun time-stamp-yy/mm/dd ()
e1f40b28 439 "Return the current date as a string in \"YY/MM/DD\" form."
9565745a
RS
440 (let ((date (current-time-string)))
441 (format "%s/%02d/%02d"
442 (substring date -2)
443 (cdr (assoc (substring date 4 7) time-stamp-month-numbers))
444 (string-to-int (substring date 8 10)))))
445
446;;; RCS 5 date
447
448(defun time-stamp-yyyy/mm/dd ()
e1f40b28 449 "Return the current date as a string in \"YYYY/MM/DD\" form."
9565745a
RS
450 (let ((date (current-time-string)))
451 (format "%s/%02d/%02d"
452 (substring date -4)
453 (cdr (assoc (substring date 4 7) time-stamp-month-numbers))
454 (string-to-int (substring date 8 10)))))
455
e1f40b28 456;;; ISO 8601 date
9565745a 457
e1f40b28
RS
458(defun time-stamp-yyyy-mm-dd ()
459 "Return the current date as a string in \"YYYY-MM-DD\" form."
9565745a 460 (let ((date (current-time-string)))
e1f40b28
RS
461 (format "%s-%02d-%02d"
462 (substring date -4)
9565745a 463 (cdr (assoc (substring date 4 7) time-stamp-month-numbers))
e1f40b28 464 (string-to-int (substring date 8 10)))))
9565745a 465
e1f40b28
RS
466(defun time-stamp-yymmdd ()
467 "Return the current date as a string in \"YYMMDD\" form."
9565745a 468 (let ((date (current-time-string)))
e1f40b28
RS
469 (format "%s%02d%02d"
470 (substring date -2)
9565745a 471 (cdr (assoc (substring date 4 7) time-stamp-month-numbers))
e1f40b28 472 (string-to-int (substring date 8 10)))))
9565745a
RS
473
474(defun time-stamp-hh:mm:ss ()
e1f40b28 475 "Return the current time as a string in \"HH:MM:SS\" form."
9565745a
RS
476 (substring (current-time-string) 11 19))
477
9565745a 478(defun time-stamp-hhmm ()
e1f40b28 479 "Return the current time as a string in \"HHMM\" form."
9565745a
RS
480 (let ((date (current-time-string)))
481 (concat (substring date 11 13)
482 (substring date 14 16))))
483
484(provide 'time-stamp)
485
486;;; time-stamp.el ends here