Convert consecutive FSF copyright years to ranges.
[bpt/emacs.git] / lisp / time-stamp.el
CommitLineData
9565745a 1;;; time-stamp.el --- Maintain last change time stamps in files edited by Emacs
9565745a 2
73b0cd50 3;; Copyright (C) 1989, 1993-1995, 1997, 2000-2011 Free Software Foundation, Inc.
b578f267 4
e8af40ee
PJ
5;; This file is part of GNU Emacs.
6
97ee303a 7;; Maintainer's Time-stamp: <2006-04-12 20:30:56 rms>
e602e779 8;; Maintainer: Stephen Gildea <gildea@stop.mail-abuse.org>
9565745a
RS
9;; Keywords: tools
10
eb3fa2cf 11;; GNU Emacs is free software: you can redistribute it and/or modify
9565745a 12;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
9565745a 15
7490a1e4 16;; GNU Emacs is distributed in the hope that it will be useful,
9565745a
RS
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
eb3fa2cf 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
9565745a
RS
23
24;;; Commentary:
25
03d7856a
KH
26;; A template in a file can be updated with a new time stamp when
27;; you save the file. For example:
33128c29 28;; static char *ts = "sdmain.c Time-stamp: <2001-08-13 10:20:51 gildea>";
03d7856a
KH
29;; See the top of `time-stamp.el' for another example.
30
31;; To use time-stamping, add this line to your .emacs file:
66c6d25e 32;; (add-hook 'before-save-hook 'time-stamp)
03d7856a
KH
33;; Now any time-stamp templates in your files will be updated automatically.
34
35;; See the documentation for the functions `time-stamp'
36;; and `time-stamp-toggle-active' for details.
b578f267 37
9565745a
RS
38;;; Code:
39
cb35a83c
RS
40(defgroup time-stamp nil
41 "Maintain last change time stamps in files edited by Emacs."
42 :group 'data
43 :group 'extensions)
44
fd72ddf6 45(defcustom time-stamp-format "%:y-%02m-%02d %02H:%02M:%02S %u"
9201cc28 46 "Format of the string inserted by \\[time-stamp].
c9c0e4bb
RS
47The value may be a string or a list. Lists are supported only for
48backward compatibility; see variable `time-stamp-old-format-warn'.
f8d35bf3 49
22453b51
DL
50A string is used verbatim except for character sequences beginning
51with %, as follows. The values of non-numeric formatted items depend
ef080b16
EZ
52on the locale setting recorded in `system-time-locale' and
53`locale-coding-system'. The examples here are for the default
4b70b30b 54\(`C') locale.
fd72ddf6
RS
55
56%:a weekday name: `Monday'. %#A gives uppercase: `MONDAY'
57%3a abbreviated weekday: `Mon'. %3A gives uppercase: `MON'
58%:b month name: `January'. %#B gives uppercase: `JANUARY'
59%3b abbreviated month: `Jan'. %3B gives uppercase: `JAN'
60%02d day of month
61%02H 24-hour clock hour
62%02I 12-hour clock hour
63%02m month number
64%02M minute
65%#p `am' or `pm'. %P gives uppercase: `AM' or `PM'
66%02S seconds
67%w day number of week, Sunday is 0
33128c29 68%02y 2-digit year: `03' %:y 4-digit year: `2003'
fd72ddf6
RS
69%z time zone name: `est'. %Z gives uppercase: `EST'
70
71Non-date items:
72%% a literal percent character: `%'
73%f file name without directory %F gives absolute pathname
74%s system name
f7769aa5 75%u user's login name %U user's full name
fd72ddf6
RS
76%h mail host name
77
78Decimal digits between the % and the type character specify the
79field width. Strings are truncated on the right; years on the left.
55ac4d11 80A leading zero in the field width zero-fills a number.
622b7ede
RS
81
82For example, to get the format used by the `date' command,
fd72ddf6
RS
83use \"%3a %3b %2d %02H:%02M:%02S %Z %:y\".
84
55ac4d11 85In the future these formats will be aligned more with `format-time-string'.
fd72ddf6
RS
86Because of this transition, the default padding for numeric formats will
87change in a future version. Therefore either a padding width should be
88specified, or the : modifier should be used to explicitly request the
89historical default."
cb35a83c 90 :type 'string
33128c29
SG
91 :group 'time-stamp
92 :version "20.1")
97ee303a 93;;;###autoload(put 'time-stamp-format 'safe-local-variable 'stringp)
03d7856a 94
95772e85 95(defcustom time-stamp-active t
9201cc28 96 "Non-nil to enable time-stamping of buffers by \\[time-stamp].
95772e85
RS
97Can be toggled by \\[time-stamp-toggle-active].
98See also the variable `time-stamp-warn-inactive'."
99 :type 'boolean
100 :group 'time-stamp)
101
102(defcustom time-stamp-warn-inactive t
3b5e21df 103 "Have \\[time-stamp] warn if a buffer did not get time-stamped.
33128c29
SG
104If non-nil, a warning is displayed if `time-stamp-active' has
105deactivated time stamping and the buffer contains a template that
106otherwise would have been updated."
95772e85 107 :type 'boolean
33128c29
SG
108 :group 'time-stamp
109 :version "19.29")
95772e85
RS
110
111(defcustom time-stamp-old-format-warn 'ask
3b5e21df 112 "Action if `time-stamp-format' is an old-style list.
95772e85
RS
113If `error', the format is not used. If `ask', the user is queried about
114using the time-stamp-format. If `warn', a warning is displayed.
115If nil, no notification is given."
0a8052bd
GM
116 :type '(choice (const :tag "Don't use the format" error)
117 (const ask)
118 (const warn)
119 (const :tag "No notification" nil))
95772e85
RS
120 :group 'time-stamp)
121
122(defcustom time-stamp-time-zone nil
123 "If non-nil, a string naming the timezone to be used by \\[time-stamp].
124Format is the same as that used by the environment variable TZ on your system."
7967f8ab 125 :type '(choice (const nil) string)
33128c29
SG
126 :group 'time-stamp
127 :version "20.1")
65b332b1 128;;;###autoload(put 'time-stamp-time-zone 'safe-local-variable 'string-or-null-p)
622b7ede 129
b4b33e01 130;;; Do not change time-stamp-line-limit, time-stamp-start,
55ac4d11
KH
131;;; time-stamp-end, time-stamp-pattern, time-stamp-inserts-lines,
132;;; or time-stamp-count in your .emacs or you will be incompatible
133;;; with other people's files! If you must change them, do so only
134;;; in the local variables section of the file itself.
9565745a 135
03d7856a 136
e1f40b28 137(defvar time-stamp-line-limit 8 ;Do not change!
c7c4ddd4 138 "Lines of a file searched; positive counts from start, negative from end.
55ac4d11
KH
139The patterns `time-stamp-start' and `time-stamp-end' must be found in
140the first (last) `time-stamp-line-limit' lines of the file for the
f7769aa5
RS
141file to be time-stamped by \\[time-stamp]. A value of 0 searches the
142entire buffer (use with care).
e1f40b28 143
0ec63318
SG
144This value can also be set with the variable `time-stamp-pattern'.
145
146Do not change `time-stamp-line-limit', `time-stamp-start',
147`time-stamp-end', or `time-stamp-pattern' for yourself or you will be
148incompatible with other people's files! If you must change them for some
149application, do so in the local variables section of the time-stamped file
150itself.")
97ee303a 151;;;###autoload(put 'time-stamp-line-limit 'safe-local-variable 'integerp)
9565745a 152
36081614 153(defvar time-stamp-start "Time-stamp:[ \t]+\\\\?[\"<]+" ;Do not change!
9565745a 154 "Regexp after which the time stamp is written by \\[time-stamp].
e1f40b28 155See also the variables `time-stamp-end' and `time-stamp-line-limit'.
9565745a 156
0ec63318
SG
157This value can also be set with the variable `time-stamp-pattern'.
158
159Do not change `time-stamp-line-limit', `time-stamp-start',
160`time-stamp-end', or `time-stamp-pattern' for yourself or you will be
161incompatible with other people's files! If you must change them for some
162application, do so in the local variables section of the time-stamped file
163itself.")
72c0b9b8 164;;;###autoload(put 'time-stamp-start 'safe-local-variable 'stringp)
9565745a 165
e1f40b28 166(defvar time-stamp-end "\\\\?[\">]" ;Do not change!
9565745a 167 "Regexp marking the text after the time stamp.
e1f40b28 168\\[time-stamp] deletes the text between the first match of `time-stamp-start'
55ac4d11
KH
169and the following match of `time-stamp-end', then writes the
170time stamp specified by `time-stamp-format' between them.
e1f40b28 171
0ec63318
SG
172This value can also be set with the variable `time-stamp-pattern'.
173
55ac4d11
KH
174The end text normally starts on the same line as the start text ends,
175but if there are any newlines in `time-stamp-format', the same number
176of newlines must separate the start and end. \\[time-stamp] tries
177to not change the number of lines in the buffer. `time-stamp-inserts-lines'
178controls this behavior.
179
0ec63318 180Do not change `time-stamp-start', `time-stamp-end', `time-stamp-pattern',
55ac4d11 181or `time-stamp-inserts-lines' for yourself or you will be incompatible
e1f40b28
RS
182with other people's files! If you must change them for some application,
183do so in the local variables section of the time-stamped file itself.")
72c0b9b8 184;;;###autoload(put 'time-stamp-end 'safe-local-variable 'stringp)
e1f40b28 185
9565745a 186
55ac4d11 187(defvar time-stamp-inserts-lines nil ;Do not change!
33128c29 188 "Whether \\[time-stamp] can change the number of lines in a file.
55ac4d11
KH
189If nil, \\[time-stamp] skips as many lines as there are newlines in
190`time-stamp-format' before looking for the `time-stamp-end' pattern,
191thus it tries not to change the number of lines in the buffer.
192If non-nil, \\[time-stamp] starts looking for the end pattern
193immediately after the start pattern. This behavior can cause
194unexpected changes in the buffer if used carelessly, but it is useful
195for generating repeated time stamps.
196
197Do not change `time-stamp-end' or `time-stamp-inserts-lines' for
198yourself or you will be incompatible with other people's files!
199If you must change them for some application, do so in the local
200variables section of the time-stamped file itself.")
72c0b9b8 201;;;###autoload(put 'time-stamp-inserts-lines 'safe-local-variable 'symbolp)
55ac4d11
KH
202
203
204(defvar time-stamp-count 1 ;Do not change!
3b5e21df 205 "How many templates \\[time-stamp] will look for in a buffer.
33128c29 206The same time stamp will be written in each case.
55ac4d11
KH
207
208Do not change `time-stamp-count' for yourself or you will be
209incompatible with other people's files! If you must change it for
210some application, do so in the local variables section of the
211time-stamped file itself.")
97ee303a 212;;;###autoload(put 'time-stamp-count 'safe-local-variable 'integerp)
55ac4d11
KH
213
214
33128c29 215(defvar time-stamp-pattern nil ;Do not change!
55ac4d11 216 "Convenience variable setting all `time-stamp' location and format values.
b4b33e01 217This string has four parts, each of which is optional.
55ac4d11
KH
218These four parts set `time-stamp-line-limit', `time-stamp-start',
219`time-stamp-format', and `time-stamp-end'. See the documentation
b4b33e01
RS
220for each of these variables for details.
221
222The first part is a number followed by a slash; the number sets the number
223of lines at the beginning (negative counts from end) of the file searched
33128c29 224for the time stamp. The number and the slash may be omitted to use the
b4b33e01
RS
225normal value.
226
227The second part is a regexp identifying the pattern preceding the time stamp.
228This part may be omitted to use the normal pattern.
229
33128c29 230The third part specifies the format of the time stamp inserted. See
55ac4d11 231the documentation for `time-stamp-format' for details. Specify this
b4b33e01
RS
232part as \"%%\" to use the normal format.
233
234The fourth part is a regexp identifying the pattern following the time stamp.
235This part may be omitted to use the normal pattern.
236
33128c29
SG
237Examples:
238\"-10/\"
239\"-9/^Last modified: %%$\"
240\"@set Time-stamp: %:b %:d, %:y$\"
241\"newcommand{\\\\\\\\timestamp}{%%}\"
b4b33e01 242
0ec63318
SG
243Do not change `time-stamp-pattern' `time-stamp-line-limit',
244`time-stamp-start', or `time-stamp-end' for yourself or you will be
245incompatible with other people's files! If you must change them for
246some application, do so only in the local variables section of the
247time-stamped file itself.")
97ee303a 248;;;###autoload(put 'time-stamp-pattern 'safe-local-variable 'stringp)
b4b33e01
RS
249
250
03d7856a 251
59b644e8 252;;;###autoload
9565745a 253(defun time-stamp ()
55ac4d11 254 "Update the time stamp string(s) in the buffer.
03d7856a
KH
255A template in a file can be automatically updated with a new time stamp
256every time you save the file. Add this line to your .emacs file:
66c6d25e 257 (add-hook 'before-save-hook 'time-stamp)
26f643c2 258or customize `before-save-hook' through Custom.
03d7856a
KH
259Normally the template must appear in the first 8 lines of a file and
260look like one of the following:
261 Time-stamp: <>
262 Time-stamp: \" \"
263The time stamp is written between the brackets or quotes:
33128c29 264 Time-stamp: <2001-02-18 10:20:51 gildea>
622b7ede 265The time stamp is updated only if the variable `time-stamp-active' is non-nil.
0ec63318
SG
266The format of the time stamp is set by the variable `time-stamp-pattern' or
267`time-stamp-format'. The variables `time-stamp-pattern',
268`time-stamp-line-limit', `time-stamp-start', `time-stamp-end',
9201cc28 269`time-stamp-count', and `time-stamp-inserts-lines' control finding
0ec63318 270the template."
9565745a 271 (interactive)
55ac4d11 272 (let ((line-limit time-stamp-line-limit)
b4b33e01
RS
273 (ts-start time-stamp-start)
274 (ts-format time-stamp-format)
55ac4d11
KH
275 (ts-end time-stamp-end)
276 (ts-count time-stamp-count)
277 (format-lines 0)
278 (end-lines 1)
279 (start nil)
280 search-limit)
b4b33e01
RS
281 (if (stringp time-stamp-pattern)
282 (progn
33128c29 283 (string-match "\\`\\(\\(-?[0-9]+\\)/\\)?\\([^%]+\\)?\\(\\(%[-.,:@+_ #^()0-9]*[A-Za-z%][^%]*\\)*%[-.,:@+_ #^()0-9]*[A-Za-z%]\\)?\\([^%]+\\)?\\'" time-stamp-pattern)
b4b33e01
RS
284 (and (match-beginning 2)
285 (setq line-limit
027a4b6b 286 (string-to-number (match-string 2 time-stamp-pattern))))
b4b33e01
RS
287 (and (match-beginning 3)
288 (setq ts-start (match-string 3 time-stamp-pattern)))
289 (and (match-beginning 4)
290 (not (string-equal (match-string 4 time-stamp-pattern) "%%"))
291 (setq ts-format (match-string 4 time-stamp-pattern)))
15ceaba4
KH
292 (and (match-beginning 6)
293 (setq ts-end (match-string 6 time-stamp-pattern)))))
622b7ede
RS
294 (cond ((not (integerp line-limit))
295 (setq line-limit 8)
b4b33e01 296 (message "time-stamp-line-limit is not an integer")
622b7ede 297 (sit-for 1)))
55ac4d11
KH
298 (cond ((not (integerp ts-count))
299 (setq ts-count 1)
300 (message "time-stamp-count is not an integer")
301 (sit-for 1))
302 ((< ts-count 1)
303 ;; We need to call time-stamp-once at least once
304 ;; to output any warnings about time-stamp not being active.
305 (setq ts-count 1)))
306 ;; Figure out what lines the end should be on.
3b5e21df
GM
307 (if (stringp ts-format)
308 (let ((nl-start 0))
309 (while (string-match "\n" ts-format nl-start)
310 (setq format-lines (1+ format-lines) nl-start (match-end 0)))))
55ac4d11
KH
311 (let ((nl-start 0))
312 (while (string-match "\n" ts-end nl-start)
313 (setq end-lines (1+ end-lines) nl-start (match-end 0))))
314 ;; Find overall what lines to look at
622b7ede
RS
315 (save-excursion
316 (save-restriction
317 (widen)
318 (cond ((> line-limit 0)
319 (goto-char (setq start (point-min)))
320 (forward-line line-limit)
321 (setq search-limit (point)))
f7769aa5 322 ((< line-limit 0)
622b7ede
RS
323 (goto-char (setq search-limit (point-max)))
324 (forward-line line-limit)
f7769aa5
RS
325 (setq start (point)))
326 (t ;0 => no limit (use with care!)
327 (setq start (point-min))
55ac4d11
KH
328 (setq search-limit (point-max))))))
329 (while (and start
330 (< start search-limit)
331 (> ts-count 0))
332 (setq start (time-stamp-once start search-limit ts-start ts-end
333 ts-format format-lines end-lines))
334 (setq ts-count (1- ts-count))))
55ac4d11
KH
335 nil)
336
337(defun time-stamp-once (start search-limit ts-start ts-end
338 ts-format format-lines end-lines)
33128c29 339 "Update one time stamp. Internal routine called by \\[time-stamp].
55ac4d11
KH
340Returns the end point, which is where `time-stamp' begins the next search."
341 (let ((case-fold-search nil)
342 (end nil)
343 end-search-start
344 (end-length nil))
345 (save-excursion
346 (save-restriction
347 (widen)
348 ;; Find the location of the time stamp.
349 (while (and (< (goto-char start) search-limit)
622b7ede 350 (not end)
b4b33e01 351 (re-search-forward ts-start search-limit 'move))
622b7ede 352 (setq start (point))
55ac4d11
KH
353 (if (not time-stamp-inserts-lines)
354 (forward-line format-lines))
355 (setq end-search-start (max start (point)))
356 (if (= (forward-line end-lines) 0)
357 (progn
358 (and (bolp) (backward-char))
359 (let ((line-end (min (point) search-limit)))
360 (if (>= line-end end-search-start)
361 (progn
362 (goto-char end-search-start)
363 (if (re-search-forward ts-end line-end t)
364 (progn
365 (setq end (match-beginning 0))
366 (setq end-length (- (match-end 0) end))))))))))))
b4b33e01
RS
367 (if end
368 (progn
369 ;; do all warnings outside save-excursion
370 (cond
371 ((not time-stamp-active)
372 (if time-stamp-warn-inactive
373 ;; don't signal an error in a write-file-hook
374 (progn
375 (message "Warning: time-stamp-active is off; did not time-stamp buffer.")
376 (sit-for 1))))
377 ((not (and (stringp ts-start)
378 (stringp ts-end)))
379 (message "time-stamp-start or time-stamp-end is not a string")
380 (sit-for 1))
381 (t
382 (let ((new-time-stamp (time-stamp-string ts-format)))
f7769aa5
RS
383 (if (and (stringp new-time-stamp)
384 (not (string-equal (buffer-substring start end)
385 new-time-stamp)))
b4b33e01
RS
386 (save-excursion
387 (save-restriction
388 (widen)
389 (delete-region start end)
390 (goto-char start)
391 (insert-and-inherit new-time-stamp)
392 (setq end (point))
393 ;; remove any tabs used to format time stamp
55ac4d11
KH
394 (if (search-backward "\t" start t)
395 (progn
396 (untabify start end)
397 (setq end (point))))))))))))
398 ;; return the location after this time stamp, if there was one
399 (and end end-length
400 (+ end end-length))))
401
9565745a 402
b1defad2
RS
403;;;###autoload
404(defun time-stamp-toggle-active (&optional arg)
03d7856a 405 "Toggle `time-stamp-active', setting whether \\[time-stamp] updates a buffer.
33128c29 406With ARG, turn time stamping on if and only if arg is positive."
b1defad2
RS
407 (interactive "P")
408 (setq time-stamp-active
409 (if (null arg)
410 (not time-stamp-active)
411 (> (prefix-numeric-value arg) 0)))
55ac4d11
KH
412 (message "time-stamp is now %s." (if time-stamp-active "active" "off")))
413
414
415(defun time-stamp-string (&optional ts-format)
416 "Generate the new string to be inserted by \\[time-stamp].
e7b0deaf
EZ
417Optionally use format TS-FORMAT instead of `time-stamp-format' to
418format the string."
55ac4d11
KH
419 (or ts-format
420 (setq ts-format time-stamp-format))
421 (if (stringp ts-format)
422 (if (stringp time-stamp-time-zone)
423 (let ((ts-real-time-zone (getenv "TZ")))
424 (unwind-protect
425 (progn
33128c29 426 (set-time-zone-rule time-stamp-time-zone)
55ac4d11
KH
427 (format-time-string
428 (time-stamp-string-preprocess ts-format)))
33128c29 429 (set-time-zone-rule ts-real-time-zone)))
55ac4d11
KH
430 (format-time-string
431 (time-stamp-string-preprocess ts-format)))
432 ;; handle version 1 compatibility
433 (cond ((or (eq time-stamp-old-format-warn 'error)
434 (and (eq time-stamp-old-format-warn 'ask)
435 (not (y-or-n-p "Use non-string time-stamp-format? "))))
436 (message "Warning: no time-stamp: time-stamp-format not a string")
437 (sit-for 1)
438 nil)
439 (t
440 (cond ((eq time-stamp-old-format-warn 'warn)
441 (message "Obsolescent time-stamp-format type; should be string")
442 (sit-for 1)))
443 (time-stamp-fconcat ts-format " ")))))
03d7856a 444
6946129c
RS
445(defconst time-stamp-no-file "(no file)"
446 "String to use when the buffer is not associated with a file.")
447
fd72ddf6
RS
448;;; time-stamp is transitioning to using the new, expanded capabilities
449;;; of format-time-string. During the process, this function implements
450;;; intermediate, compatible formats and complains about old, soon to
451;;; be unsupported, formats. This function will get a lot (a LOT) shorter
452;;; when the transition is complete and we can just pass most things
453;;; straight through to format-time-string.
454;;; At all times, all the formats recommended in the doc string
455;;; of time-stamp-format will work not only in the current version of
456;;; Emacs, but in all versions that have been released within the past
457;;; two years.
458;;; The : modifier is a temporary conversion feature used to resolve
459;;; ambiguous formats--formats that are changing (over time) incompatibly.
460(defun time-stamp-string-preprocess (format &optional time)
22453b51
DL
461 "Use a FORMAT to format date, time, file, and user information.
462Optional second argument TIME is only for testing.
463Implements non-time extensions to `format-time-string'
33128c29 464and all `time-stamp-format' compatibility."
fd72ddf6
RS
465 (let ((fmt-len (length format))
466 (ind 0)
467 cur-char
468 (prev-char nil)
469 (result "")
fd72ddf6
RS
470 field-width
471 field-result
472 alt-form change-case require-padding
473 (paren-level 0))
474 (while (< ind fmt-len)
475 (setq cur-char (aref format ind))
476 (setq
477 result
478 (concat result
479 (cond
480 ((eq cur-char ?%)
481 ;; eat any additional args to allow for future expansion
15ceaba4 482 (setq alt-form nil change-case nil require-padding nil field-width "")
fd72ddf6
RS
483 (while (progn
484 (setq ind (1+ ind))
485 (setq cur-char (if (< ind fmt-len)
486 (aref format ind)
487 ?\0))
488 (or (eq ?. cur-char)
489 (eq ?, cur-char) (eq ?: cur-char) (eq ?@ cur-char)
22453b51 490 (eq ?- cur-char) (eq ?+ cur-char) (eq ?_ cur-char)
fe15c2c6 491 (eq ?\s cur-char) (eq ?# cur-char) (eq ?^ cur-char)
fd72ddf6
RS
492 (and (eq ?\( cur-char)
493 (not (eq prev-char ?\\))
494 (setq paren-level (1+ paren-level)))
495 (if (and (eq ?\) cur-char)
496 (not (eq prev-char ?\\))
497 (> paren-level 0))
498 (setq paren-level (1- paren-level))
499 (and (> paren-level 0)
15ceaba4
KH
500 (< ind fmt-len)))
501 (if (and (<= ?0 cur-char) (>= ?9 cur-char))
502 ;; get format width
503 (let ((field-index ind))
504 (while (progn
505 (setq ind (1+ ind))
506 (setq cur-char (if (< ind fmt-len)
507 (aref format ind)
508 ?\0))
509 (and (<= ?0 cur-char) (>= ?9 cur-char))))
510 (setq field-width (substring format field-index ind))
511 (setq ind (1- ind))
512 t))))
fd72ddf6
RS
513 (setq prev-char cur-char)
514 ;; some characters we actually use
515 (cond ((eq cur-char ?:)
516 (setq alt-form t))
517 ((eq cur-char ?#)
518 (setq change-case t))))
fd72ddf6
RS
519 (setq field-result
520 (cond
521 ((eq cur-char ?%)
55ac4d11 522 "%%")
fd72ddf6
RS
523 ((eq cur-char ?a) ;day of week
524 (if change-case
0c68e5de 525 (format-time-string "%#a" time)
fd72ddf6
RS
526 (or alt-form (not (string-equal field-width ""))
527 (time-stamp-conv-warn "%a" "%:a"))
528 (if (and alt-form (not (string-equal field-width "")))
529 "" ;discourage "%:3a"
530 (format-time-string "%A" time))))
531 ((eq cur-char ?A)
532 (if alt-form
533 (format-time-string "%A" time)
534 (or change-case (not (string-equal field-width ""))
535 (time-stamp-conv-warn "%A" "%#A"))
536 (format-time-string "%#A" time)))
537 ((eq cur-char ?b) ;month name
538 (if change-case
0c68e5de 539 (format-time-string "%#b" time)
fd72ddf6
RS
540 (or alt-form (not (string-equal field-width ""))
541 (time-stamp-conv-warn "%b" "%:b"))
542 (if (and alt-form (not (string-equal field-width "")))
543 "" ;discourage "%:3b"
544 (format-time-string "%B" time))))
545 ((eq cur-char ?B)
546 (if alt-form
547 (format-time-string "%B" time)
548 (or change-case (not (string-equal field-width ""))
549 (time-stamp-conv-warn "%B" "%#B"))
550 (format-time-string "%#B" time)))
551 ((eq cur-char ?d) ;day of month, 1-31
fdac7fba 552 (time-stamp-do-number cur-char alt-form field-width time))
fd72ddf6 553 ((eq cur-char ?H) ;hour, 0-23
fdac7fba 554 (time-stamp-do-number cur-char alt-form field-width time))
fd72ddf6 555 ((eq cur-char ?I) ;hour, 1-12
fdac7fba 556 (time-stamp-do-number cur-char alt-form field-width time))
fd72ddf6 557 ((eq cur-char ?m) ;month number, 1-12
fdac7fba 558 (time-stamp-do-number cur-char alt-form field-width time))
fd72ddf6 559 ((eq cur-char ?M) ;minute, 0-59
fdac7fba 560 (time-stamp-do-number cur-char alt-form field-width time))
fd72ddf6
RS
561 ((eq cur-char ?p) ;am or pm
562 (or change-case
563 (time-stamp-conv-warn "%p" "%#p"))
564 (format-time-string "%#p" time))
565 ((eq cur-char ?P) ;AM or PM
566 (format-time-string "%p" time))
567 ((eq cur-char ?S) ;seconds, 00-60
fdac7fba 568 (time-stamp-do-number cur-char alt-form field-width time))
fd72ddf6
RS
569 ((eq cur-char ?w) ;weekday number, Sunday is 0
570 (format-time-string "%w" time))
571 ((eq cur-char ?y) ;year
572 (or alt-form (not (string-equal field-width ""))
573 (time-stamp-conv-warn "%y" "%:y"))
027a4b6b 574 (string-to-number (format-time-string "%Y" time)))
fd72ddf6 575 ((eq cur-char ?Y) ;4-digit year, new style
027a4b6b 576 (string-to-number (format-time-string "%Y" time)))
fd72ddf6
RS
577 ((eq cur-char ?z) ;time zone lower case
578 (if change-case
579 "" ;discourage %z variations
580 (format-time-string "%#Z" time)))
581 ((eq cur-char ?Z)
582 (if change-case
583 (format-time-string "%#Z" time)
584 (format-time-string "%Z" time)))
585 ((eq cur-char ?f) ;buffer-file-name, base name only
586 (if buffer-file-name
587 (file-name-nondirectory buffer-file-name)
588 time-stamp-no-file))
589 ((eq cur-char ?F) ;buffer-file-name, full path
590 (or buffer-file-name
591 time-stamp-no-file))
592 ((eq cur-char ?s) ;system name
593 (system-name))
594 ((eq cur-char ?u) ;user name
595 (user-login-name))
2c8d5749
RS
596 ((eq cur-char ?U) ;user full name
597 (user-full-name))
55ac4d11
KH
598 ((eq cur-char ?l) ;logname (undocumented user name alt)
599 (user-login-name))
600 ((eq cur-char ?L) ;(undocumented alt user full name)
601 (user-full-name))
fd72ddf6
RS
602 ((eq cur-char ?h) ;mail host name
603 (time-stamp-mail-host-name))
0ec63318
SG
604 ((eq cur-char ?q) ;(undocumented unqual hostname)
605 (let ((qualname (system-name)))
606 (if (string-match "\\." qualname)
607 (substring qualname 0 (match-beginning 0))
608 qualname)))
609 ((eq cur-char ?Q) ;(undocumented fully-qualified host)
610 (system-name))
fd72ddf6 611 ))
b6735035
GM
612 (let ((padded-result
613 (format (format "%%%s%c"
614 field-width
615 (if (numberp field-result) ?d ?s))
616 (or field-result ""))))
617 (let* ((initial-length (length padded-result))
618 (desired-length (if (string-equal field-width "")
619 initial-length
027a4b6b 620 (string-to-number field-width))))
b6735035
GM
621 (if (> initial-length desired-length)
622 ;; truncate strings on right, years on left
623 (if (stringp field-result)
624 (substring padded-result 0 desired-length)
625 (if (eq cur-char ?y)
626 (substring padded-result (- desired-length))
627 padded-result)) ;non-year numbers don't truncate
628 padded-result))))
fd72ddf6
RS
629 (t
630 (char-to-string cur-char)))))
631 (setq ind (1+ ind)))
632 result))
633
fdac7fba 634(defun time-stamp-do-number (format-char alt-form field-width time)
22453b51
DL
635 "Handle compatible FORMAT-CHAR where only default width/padding will change.
636ALT-FORM is whether `#' specified. FIELD-WIDTH is the string
637width specification or \"\". TIME is the time to convert."
fd72ddf6
RS
638 (let ((format-string (concat "%" (char-to-string format-char))))
639 (and (not alt-form) (string-equal field-width "")
640 (time-stamp-conv-warn format-string
641 (format "%%:%c" format-char)))
642 (if (and alt-form (not (string-equal field-width "")))
643 "" ;discourage "%:2d" and the like
027a4b6b 644 (string-to-number (format-time-string format-string time)))))
fd72ddf6
RS
645
646(defvar time-stamp-conversion-warn t
3b5e21df
GM
647 "Warn about soon-to-be-unsupported forms in `time-stamp-format'.
648If nil, these warnings are disabled, which would be a bad idea!
fd72ddf6
RS
649You really need to update your files instead.
650
651The new formats will work with old versions of Emacs.
55ac4d11
KH
652New formats are being recommended now to allow `time-stamp-format'
653to change in the future to be compatible with `format-time-string'.
fd72ddf6
RS
654The new forms being recommended now will continue to work then.")
655
656
657(defun time-stamp-conv-warn (old-form new-form)
33128c29
SG
658 "Display a warning about a soon-to-be-obsolete format.
659Suggests replacing OLD-FORM with NEW-FORM."
fd72ddf6
RS
660 (cond
661 (time-stamp-conversion-warn
7fdbcd83 662 (with-current-buffer (get-buffer-create "*Time-stamp-compatibility*")
fd72ddf6
RS
663 (goto-char (point-max))
664 (if (bobp)
665 (progn
666 (insert
667 "The formats recognized in time-stamp-format will change in a future release\n"
668 "to be compatible with the new, expanded format-time-string function.\n\n"
669 "The following obsolescent time-stamp-format construct(s) were found:\n\n")))
670 (insert "\"" old-form "\" -- use " new-form "\n"))
671 (display-buffer "*Time-stamp-compatibility*"))))
95772e85 672
fd72ddf6 673
b1defad2 674
b1defad2
RS
675(defun time-stamp-mail-host-name ()
676 "Return the name of the host where the user receives mail.
677This is the value of `mail-host-address' if bound and a string,
55ac4d11 678otherwise the value of the function `system-name'."
b1defad2
RS
679 (or (and (boundp 'mail-host-address)
680 (stringp mail-host-address)
681 mail-host-address)
b1defad2
RS
682 (system-name)))
683
684;;; the rest of this file is for version 1 compatibility
9565745a
RS
685
686(defun time-stamp-fconcat (list sep)
e1f40b28 687 "Similar to (mapconcat 'funcall LIST SEP) but LIST allows literals.
9565745a
RS
688If an element of LIST is a symbol, it is funcalled to get the string to use;
689the separator SEP is used between two strings obtained by funcalling a
690symbol. Otherwise the element itself is inserted; no separator is used
691around literals."
692 (let ((return-string "")
693 (insert-sep-p nil))
694 (while list
695 (cond ((symbolp (car list))
696 (if insert-sep-p
697 (setq return-string (concat return-string sep)))
698 (setq return-string (concat return-string (funcall (car list))))
699 (setq insert-sep-p t))
700 (t
701 (setq return-string (concat return-string (car list)))
702 (setq insert-sep-p nil)))
703 (setq list (cdr list)))
704 return-string))
705
9565745a
RS
706(provide 'time-stamp)
707
708;;; time-stamp.el ends here