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