X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/f8d35bf3d139cdec1776761593dfa243339472c2..dc6a28319312fe81f7a1015e363174022313f0bd:/lisp/time-stamp.el diff --git a/lisp/time-stamp.el b/lisp/time-stamp.el dissimilarity index 67% index b9bf1ed1e0..a3f0d66be3 100644 --- a/lisp/time-stamp.el +++ b/lisp/time-stamp.el @@ -1,509 +1,763 @@ -;;; time-stamp.el --- Maintain last change time stamps in files edited by Emacs -;;; Copyright 1989, 1993, 1994, 1995 Free Software Foundation, Inc. -;;; Maintainer's Time-stamp: <95/09/21 12:32:56 gildea> - -;; Maintainer: Stephen Gildea -;; Keywords: tools - -;; This file is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. - -;; This file is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to -;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -;;; Commentary: - -;;; If you put a time stamp template anywhere in the first 8 lines of a file, -;;; it can be updated every time you save the file. See the top of -;;; time-stamp.el for a sample. The template looks like one of the following: -;;; Time-stamp: <> -;;; Time-stamp: " " -;;; The time stamp is written between the brackets or quotes, resulting in -;;; Time-stamp: <95/01/18 10:20:51 gildea> -;;; Here is an example which puts the file name and time stamp in the binary: -;;; static char *time_stamp = "sdmain.c Time-stamp: <>"; - -;;; To activate automatic time stamping in GNU Emacs 19, add this code -;;; to your .emacs file: -;;; (add-hook 'write-file-hooks 'time-stamp) -;;; -;;; In Emacs 18 you will need to do this instead: -;;; (if (not (memq 'time-stamp write-file-hooks)) -;;; (setq write-file-hooks -;;; (cons 'time-stamp write-file-hooks))) -;;; (autoload 'time-stamp "time-stamp" "Update the time stamp in a buffer." t) - -;;; See the documentation for the function `time-stamp' for more details. - -;;; Change Log: - -;;; Originally based on the 19 Dec 88 version of -;;; date.el by John Sturdy -;;; version 2, January 1995: replaced functions with %-escapes -;;; $Id: time-stamp.el,v 1.14 1995/05/31 20:00:40 kwzh Exp kwzh $ - -;;; Code: - -(defvar time-stamp-active t - "*Non-nil to enable time-stamping of buffers by \\[time-stamp]. -Can be toggled by \\[time-stamp-toggle-active]. -See also the variable time-stamp-warn-inactive.") - -(defvar time-stamp-warn-inactive t - "*Non-nil to have \\[time-stamp] warn if a buffer did not get time-stamped. -A warning is printed if time-stamp-active is nil and the buffer contains -a time stamp template that would otherwise have been updated.") - -(defvar time-stamp-format "%02y/%02m/%02d %02H:%02M:%02S %u" - "*Template for the string inserted by \\[time-stamp]. -Value may be a string or a list. (Lists are supported only for -backward compatibility.) A string is used verbatim except -for character sequences beginning with %: - -%a weekday name: `Monday'. %A gives uppercase: `MONDAY' -%b month name: `January'. %B gives uppercase: `JANUARY' -%d day of month -%H 24-hour clock hour -%I 12-hour clock hour -%m month number -%M minute -%p `am' or `pm'. %P gives uppercase: `AM' or `PM' -%S seconds -%w day number of week, Sunday is 0 -%y year: `1995' -%z time zone name: `est'. %Z gives uppercase: `EST' - -Non-date items: -%% a literal percent character: `%' -%f file name without directory %F gives absolute pathname -%s system name -%u user's login name -%h mail host name - -Decimal digits between the % and the type character specify the -field width. Strings are truncated on the right; numbers on the left. -A leading zero causes numbers to be zero-filled. - -For example, to get the format used by the `date' command, -use \"%3a %3b %2d %02H:%02M:%02S %Z %y\"") - - -;;; Do not change time-stamp-line-limit, time-stamp-start, or -;;; time-stamp-end in your .emacs or you will be incompatible -;;; with other people's files! If you must change them, -;;; do so only in the local variables section of the file itself. - -(defvar time-stamp-line-limit 8 ;Do not change! - "Number of lines at the beginning of a file that are searched. -The patterns `time-stamp-start' and `time-stamp-end' must be found on one -of the first `time-stamp-line-limit' lines of the file for the file to -be time-stamped by \\[time-stamp]. - -Do not change `time-stamp-line-limit', `time-stamp-start', or -`time-stamp-end' for yourself or you will be incompatible -with other people's files! If you must change them for some application, -do so in the local variables section of the time-stamped file itself.") - - -(defvar time-stamp-start "Time-stamp:[ \t]+\\\\?[\"<]+" ;Do not change! - "Regexp after which the time stamp is written by \\[time-stamp]. -See also the variables `time-stamp-end' and `time-stamp-line-limit'. - -Do not change `time-stamp-line-limit', `time-stamp-start', or -`time-stamp-end' for yourself or you will be incompatible -with other people's files! If you must change them for some application, -do so in the local variables section of the time-stamped file itself.") - - -(defvar time-stamp-end "\\\\?[\">]" ;Do not change! - "Regexp marking the text after the time stamp. -\\[time-stamp] deletes the text between the first match of `time-stamp-start' -and the following match of `time-stamp-end' on the same line, -then writes the time stamp specified by `time-stamp-format' between them. - -Do not change `time-stamp-line-limit', `time-stamp-start', or -`time-stamp-end' for yourself or you will be incompatible -with other people's files! If you must change them for some application, -do so in the local variables section of the time-stamped file itself.") - - -;;;###autoload -(defun time-stamp () - "Update the time stamp string in the buffer. -If you put a time stamp template anywhere in the first 8 lines of a file, -it can be updated every time you save the file. See the top of -`time-stamp.el' for a sample. The template looks like one of the following: - Time-stamp: <> - Time-stamp: \" \" -The time stamp is written between the brackets or quotes, resulting in - Time-stamp: <95/01/18 10:20:51 gildea> -Only does its thing if the variable time-stamp-active is non-nil. -Typically used on write-file-hooks for automatic time-stamping. -The format of the time stamp is determined by the variable time-stamp-format. -The variables time-stamp-line-limit, time-stamp-start, and time-stamp-end -control finding the template." - (interactive) - (let ((case-fold-search nil) - (need-to-warn nil)) - (if (and (stringp time-stamp-start) - (stringp time-stamp-end)) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (forward-line time-stamp-line-limit) - (let ((start (point-min)) - (search-end (point))) - (goto-char start) - (while - (and (< (point) search-end) - (re-search-forward time-stamp-start search-end 'move)) - (setq start (point)) - (end-of-line) - (let ((line-end (point))) - (goto-char start) - (if (re-search-forward time-stamp-end line-end 'move) - (progn - (if time-stamp-active - (let ((end (match-beginning 0))) - (delete-region start end) - (goto-char start) - (insert (time-stamp-string)) - (setq end (point)) - ;; remove any tabs used to format time stamp - (goto-char start) - (if (search-forward "\t" end t) - (untabify start end))) - (if time-stamp-warn-inactive - ;; do warning outside save-excursion - (setq need-to-warn t))) - (setq search-end (point))))))))) - ;; don't signal an error in a write-file-hook - (message "time-stamp-start or time-stamp-end is not a string") - (sit-for 1)) - (if need-to-warn - (progn - (message "Warning: time-stamp-active is off; did not time-stamp buffer.") - (sit-for 1)))) - ;; be sure to return nil so can be used on write-file-hooks - nil) - -;;;###autoload -(defun time-stamp-toggle-active (&optional arg) - "Toggle time-stamp-active, setting whether \\[time-stamp] updates a buffer. -With arg, turn time stamping on if and only if arg is positive." - (interactive "P") - (setq time-stamp-active - (if (null arg) - (not time-stamp-active) - (> (prefix-numeric-value arg) 0))) - (message "time-stamp is now %s." (if time-stamp-active "active" "off"))) - - -(defun time-stamp-string () - "Generate the new string to be inserted by \\[time-stamp]." - (if (stringp time-stamp-format) - (time-stamp-strftime time-stamp-format) - (time-stamp-fconcat time-stamp-format " "))) ;version 1 compatibility - -(defconst time-stamp-month-numbers - '(("Jan" . 1) ("Feb" . 2) ("Mar" . 3) ("Apr" . 4) ("May" . 5) ("Jun" . 6) - ("Jul" . 7) ("Aug" . 8) ("Sep" . 9) ("Oct" . 10) ("Nov" . 11) ("Dec" . 12)) - "Alist of months and their number.") - -(defconst time-stamp-month-full-names - ["(zero)" "January" "February" "March" "April" "May" "June" - "July" "August" "September" "October" "November" "December"]) - -(defconst time-stamp-weekday-numbers - '(("Sun" . 0) ("Mon" . 1) ("Tue" . 2) ("Wed" . 3) - ("Thu" . 4) ("Fri" . 5) ("Sat" . 6)) - "Alist of weekdays and their number.") - -(defconst time-stamp-weekday-full-names - ["Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"]) - -(defun time-stamp-strftime (format &optional time) - "Uses a FORMAT to format date, time, file, and user information. -Optional second argument TIME will be used instead of the current time. -See the description of the variable `time-stamp-format' for a description -of the format string." - (let ((time-string (cond ((stringp time) - time) - (time - (current-time-string time)) - (t - (current-time-string)))) - (fmt-len (length format)) - (ind 0) - cur-char - (prev-char nil) - (result "") - field-index - field-width - field-result - (paren-level 0)) - (while (< ind fmt-len) - (setq cur-char (aref format ind)) - (setq - result - (concat result - (cond - ((eq cur-char ?%) - (setq field-index (1+ ind)) - (while (progn - (setq ind (1+ ind)) - (setq cur-char (if (< ind fmt-len) - (aref format ind) - ?\0)) - (and (<= ?0 cur-char) (>= ?9 cur-char)))) - (setq field-width (substring format field-index ind)) - ;; eat any additional args to allow for future expansion - (while (or (and (<= ?0 cur-char) (>= ?9 cur-char)) (eq ?. cur-char) - (eq ?, cur-char) (eq ?: cur-char) (eq ?@ cur-char) - (eq ?- cur-char) (eq ?+ cur-char) - (eq ?\ cur-char) (eq ?# cur-char) - (and (eq ?\( cur-char) - (not (eq prev-char ?\\)) - (setq paren-level (1+ paren-level))) - (if (and (eq ?\) cur-char) - (not (eq prev-char ?\\)) - (> paren-level 0)) - (setq paren-level (1- paren-level)) - (and (> paren-level 0) - (< ind fmt-len)))) - (setq ind (1+ ind)) - (setq prev-char cur-char) - (setq cur-char (if (< ind fmt-len) - (aref format ind) - ?\0))) - (setq field-result - (cond - ((eq cur-char ?%) - "%") - ((or (eq cur-char ?a) ;weekday name - (eq cur-char ?A)) - (let ((name - (aref time-stamp-weekday-full-names - (cdr (assoc (substring time-string 0 3) - time-stamp-weekday-numbers))))) - (if (eq cur-char ?a) - name - (upcase name)))) - ((or (eq cur-char ?b) ;month name - (eq cur-char ?B)) - (let ((name - (aref time-stamp-month-full-names - (cdr (assoc (substring time-string 4 7) - time-stamp-month-numbers))))) - (if (eq cur-char ?b) - name - (upcase name)))) - ((eq cur-char ?d) ;day of month, 1-31 - (string-to-int (substring time-string 8 10))) - ((eq cur-char ?H) ;hour, 0-23 - (string-to-int (substring time-string 11 13))) - ((eq cur-char ?I) ;hour, 1-12 - (let ((hour (string-to-int (substring time-string 11 13)))) - (cond ((< hour 1) - (+ hour 12)) - ((> hour 12) - (- hour 12)) - (t - hour)))) - ((eq cur-char ?m) ;month number, 1-12 - (cdr (assoc (substring time-string 4 7) - time-stamp-month-numbers))) - ((eq cur-char ?M) ;minute, 0-59 - (string-to-int (substring time-string 14 16))) - ((or (eq cur-char ?p) ;am or pm - (eq cur-char ?P)) - (let ((name - (if (> 12 (string-to-int (substring time-string 11 13))) - "am" - "pm"))) - (if (eq cur-char ?p) - name - (upcase name)))) - ((eq cur-char ?S) ;seconds, 00-60 - (string-to-int (substring time-string 17 19))) - ((eq cur-char ?w) ;weekday number, Sunday is 0 - (cdr (assoc (substring time-string 0 3) time-stamp-weekday-numbers))) - ((eq cur-char ?y) ;year - (string-to-int (substring time-string -4))) - ((or (eq cur-char ?z) ;time zone - (eq cur-char ?Z)) - (let ((name - (if (fboundp 'current-time-zone) - (car (cdr (current-time-zone time)))))) - (or name (setq name "")) - (if (eq cur-char ?z) - (downcase name) - (upcase name)))) - ((eq cur-char ?f) ;buffer-file-name, base name only - (if buffer-file-name - (file-name-nondirectory buffer-file-name) - "(no file)")) - ((eq cur-char ?F) ;buffer-file-name, full path - (or buffer-file-name - "(no file)")) - ((eq cur-char ?s) ;system name - (system-name)) - ((eq cur-char ?u) ;user name - (user-login-name)) - ((eq cur-char ?h) ;mail host name - (time-stamp-mail-host-name)) - )) - (if (string-equal field-width "") - field-result - (let ((padded-result - (format (format "%%%s%c" - field-width - (if (numberp field-result) ?d ?s)) - (or field-result "")))) - (let ((initial-length (length padded-result)) - (desired-length (string-to-int field-width))) - (if (> initial-length desired-length) - ;; truncate strings on right, numbers on left - (if (stringp field-result) - (substring padded-result 0 desired-length) - (substring padded-result (- desired-length))) - padded-result))))) - (t - (char-to-string cur-char))))) - (setq ind (1+ ind))) - result)) - -(defun time-stamp-mail-host-name () - "Return the name of the host where the user receives mail. -This is the value of `mail-host-address' if bound and a string, -otherwise the value of `time-stamp-mail-host' (for versions of Emacs -before 19.29) otherwise the value of the function system-name. -This function may be usefully referenced by `time-stamp-format'." - (or (and (boundp 'mail-host-address) - (stringp mail-host-address) - mail-host-address) - (and (boundp 'time-stamp-mail-host) ;for backward compatibility - (stringp time-stamp-mail-host) - time-stamp-mail-host) - (system-name))) - -;;; the rest of this file is for version 1 compatibility - -(defun time-stamp-fconcat (list sep) - "Similar to (mapconcat 'funcall LIST SEP) but LIST allows literals. -If an element of LIST is a symbol, it is funcalled to get the string to use; -the separator SEP is used between two strings obtained by funcalling a -symbol. Otherwise the element itself is inserted; no separator is used -around literals." - (let ((return-string "") - (insert-sep-p nil)) - (while list - (cond ((symbolp (car list)) - (if insert-sep-p - (setq return-string (concat return-string sep))) - (setq return-string (concat return-string (funcall (car list)))) - (setq insert-sep-p t)) - (t - (setq return-string (concat return-string (car list))) - (setq insert-sep-p nil))) - (setq list (cdr list))) - return-string)) - - -;;; Some useful functions to use in time-stamp-format - -;;; Could generate most of a message-id with -;;; '(time-stamp-yymmdd "" time-stamp-hhmm "@" time-stamp-mail-host-name) - -;;; pretty form, suitable for a title page - -(defun time-stamp-month-dd-yyyy () - "Return the current date as a string in \"Month DD, YYYY\" form." - (let ((date (current-time-string))) - (format "%s %d, %s" - (aref time-stamp-month-full-names - (cdr (assoc (substring date 4 7) time-stamp-month-numbers))) - (string-to-int (substring date 8 10)) - (substring date -4)))) - -;;; same as __DATE__ in ANSI C - -(defun time-stamp-mon-dd-yyyy () - "Return the current date as a string in \"Mon DD YYYY\" form. -The first character of DD is space if the value is less than 10." - (let ((date (current-time-string))) - (format "%s %2d %s" - (substring date 4 7) - (string-to-int (substring date 8 10)) - (substring date -4)))) - -;;; RFC 822 date - -(defun time-stamp-dd-mon-yy () - "Return the current date as a string in \"DD Mon YY\" form." - (let ((date (current-time-string))) - (format "%02d %s %s" - (string-to-int (substring date 8 10)) - (substring date 4 7) - (substring date -2)))) - -;;; RCS 3 date - -(defun time-stamp-yy/mm/dd () - "Return the current date as a string in \"YY/MM/DD\" form." - (let ((date (current-time-string))) - (format "%s/%02d/%02d" - (substring date -2) - (cdr (assoc (substring date 4 7) time-stamp-month-numbers)) - (string-to-int (substring date 8 10))))) - -;;; RCS 5 date - -(defun time-stamp-yyyy/mm/dd () - "Return the current date as a string in \"YYYY/MM/DD\" form." - (let ((date (current-time-string))) - (format "%s/%02d/%02d" - (substring date -4) - (cdr (assoc (substring date 4 7) time-stamp-month-numbers)) - (string-to-int (substring date 8 10))))) - -;;; ISO 8601 date - -(defun time-stamp-yyyy-mm-dd () - "Return the current date as a string in \"YYYY-MM-DD\" form." - (let ((date (current-time-string))) - (format "%s-%02d-%02d" - (substring date -4) - (cdr (assoc (substring date 4 7) time-stamp-month-numbers)) - (string-to-int (substring date 8 10))))) - -(defun time-stamp-yymmdd () - "Return the current date as a string in \"YYMMDD\" form." - (let ((date (current-time-string))) - (format "%s%02d%02d" - (substring date -2) - (cdr (assoc (substring date 4 7) time-stamp-month-numbers)) - (string-to-int (substring date 8 10))))) - -(defun time-stamp-hh:mm:ss () - "Return the current time as a string in \"HH:MM:SS\" form." - (substring (current-time-string) 11 19)) - -(defun time-stamp-hhmm () - "Return the current time as a string in \"HHMM\" form." - (let ((date (current-time-string))) - (concat (substring date 11 13) - (substring date 14 16)))) - -(provide 'time-stamp) - -;;; time-stamp.el ends here +;;; time-stamp.el --- Maintain last change time stamps in files edited by Emacs + +;; Copyright 1989, 1993, 1994, 1995, 1997, 2000, 2001 +;;; Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; Maintainer's Time-stamp: <2003-02-01 09:26:25 gildea> +;; Maintainer: Stephen Gildea +;; Keywords: tools + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; A template in a file can be updated with a new time stamp when +;; you save the file. For example: +;; static char *ts = "sdmain.c Time-stamp: <2001-08-13 10:20:51 gildea>"; +;; See the top of `time-stamp.el' for another example. + +;; To use time-stamping, add this line to your .emacs file: +;; (add-hook 'write-file-hooks 'time-stamp) +;; Now any time-stamp templates in your files will be updated automatically. + +;; See the documentation for the functions `time-stamp' +;; and `time-stamp-toggle-active' for details. + +;;; Code: + +(defgroup time-stamp nil + "Maintain last change time stamps in files edited by Emacs." + :group 'data + :group 'extensions) + +(defcustom time-stamp-format "%:y-%02m-%02d %02H:%02M:%02S %u" + "*Format of the string inserted by \\[time-stamp]. +The value may be a string or a list. Lists are supported only for +backward compatibility; see variable `time-stamp-old-format-warn'. + +A string is used verbatim except for character sequences beginning +with %, as follows. The values of non-numeric formatted items depend +on the locale setting recorded in `system-time-locale' and +`locale-coding-system'. The examples here are for the default +\(`C') locale. + +%:a weekday name: `Monday'. %#A gives uppercase: `MONDAY' +%3a abbreviated weekday: `Mon'. %3A gives uppercase: `MON' +%:b month name: `January'. %#B gives uppercase: `JANUARY' +%3b abbreviated month: `Jan'. %3B gives uppercase: `JAN' +%02d day of month +%02H 24-hour clock hour +%02I 12-hour clock hour +%02m month number +%02M minute +%#p `am' or `pm'. %P gives uppercase: `AM' or `PM' +%02S seconds +%w day number of week, Sunday is 0 +%02y 2-digit year: `03' %:y 4-digit year: `2003' +%z time zone name: `est'. %Z gives uppercase: `EST' + +Non-date items: +%% a literal percent character: `%' +%f file name without directory %F gives absolute pathname +%s system name +%u user's login name %U user's full name +%h mail host name + +Decimal digits between the % and the type character specify the +field width. Strings are truncated on the right; years on the left. +A leading zero in the field width zero-fills a number. + +For example, to get the format used by the `date' command, +use \"%3a %3b %2d %02H:%02M:%02S %Z %:y\". + +In the future these formats will be aligned more with `format-time-string'. +Because of this transition, the default padding for numeric formats will +change in a future version. Therefore either a padding width should be +specified, or the : modifier should be used to explicitly request the +historical default." + :type 'string + :group 'time-stamp + :version "20.1") + +(defcustom time-stamp-active t + "*Non-nil to enable time-stamping of buffers by \\[time-stamp]. +Can be toggled by \\[time-stamp-toggle-active]. +See also the variable `time-stamp-warn-inactive'." + :type 'boolean + :group 'time-stamp) + +(defcustom time-stamp-warn-inactive t + "Have \\[time-stamp] warn if a buffer did not get time-stamped. +If non-nil, a warning is displayed if `time-stamp-active' has +deactivated time stamping and the buffer contains a template that +otherwise would have been updated." + :type 'boolean + :group 'time-stamp + :version "19.29") + +(defcustom time-stamp-old-format-warn 'ask + "Action if `time-stamp-format' is an old-style list. +If `error', the format is not used. If `ask', the user is queried about +using the time-stamp-format. If `warn', a warning is displayed. +If nil, no notification is given." + :type '(choice (const :tag "Don't use the format" error) + (const ask) + (const warn) + (const :tag "No notification" nil)) + :group 'time-stamp) + +(defcustom time-stamp-time-zone nil + "If non-nil, a string naming the timezone to be used by \\[time-stamp]. +Format is the same as that used by the environment variable TZ on your system." + :type '(choice (const nil) string) + :group 'time-stamp + :version "20.1") + + +;;; Do not change time-stamp-line-limit, time-stamp-start, +;;; time-stamp-end, time-stamp-pattern, time-stamp-inserts-lines, +;;; or time-stamp-count in your .emacs or you will be incompatible +;;; with other people's files! If you must change them, do so only +;;; in the local variables section of the file itself. + + +(defvar time-stamp-line-limit 8 ;Do not change! + "Lines of a file searched; positive counts from start, negative from end. +The patterns `time-stamp-start' and `time-stamp-end' must be found in +the first (last) `time-stamp-line-limit' lines of the file for the +file to be time-stamped by \\[time-stamp]. A value of 0 searches the +entire buffer (use with care). + +Do not change `time-stamp-line-limit', `time-stamp-start', or +`time-stamp-end' for yourself or you will be incompatible +with other people's files! If you must change them for some application, +do so in the local variables section of the time-stamped file itself.") + + +(defvar time-stamp-start "Time-stamp:[ \t]+\\\\?[\"<]+" ;Do not change! + "Regexp after which the time stamp is written by \\[time-stamp]. +See also the variables `time-stamp-end' and `time-stamp-line-limit'. + +Do not change `time-stamp-line-limit', `time-stamp-start', or +`time-stamp-end' for yourself or you will be incompatible +with other people's files! If you must change them for some application, +do so in the local variables section of the time-stamped file itself.") + + +(defvar time-stamp-end "\\\\?[\">]" ;Do not change! + "Regexp marking the text after the time stamp. +\\[time-stamp] deletes the text between the first match of `time-stamp-start' +and the following match of `time-stamp-end', then writes the +time stamp specified by `time-stamp-format' between them. + +The end text normally starts on the same line as the start text ends, +but if there are any newlines in `time-stamp-format', the same number +of newlines must separate the start and end. \\[time-stamp] tries +to not change the number of lines in the buffer. `time-stamp-inserts-lines' +controls this behavior. + +Do not change `time-stamp-line-limit', `time-stamp-start', `time-stamp-end', +or `time-stamp-inserts-lines' for yourself or you will be incompatible +with other people's files! If you must change them for some application, +do so in the local variables section of the time-stamped file itself.") + + +(defvar time-stamp-inserts-lines nil ;Do not change! + "Whether \\[time-stamp] can change the number of lines in a file. +If nil, \\[time-stamp] skips as many lines as there are newlines in +`time-stamp-format' before looking for the `time-stamp-end' pattern, +thus it tries not to change the number of lines in the buffer. +If non-nil, \\[time-stamp] starts looking for the end pattern +immediately after the start pattern. This behavior can cause +unexpected changes in the buffer if used carelessly, but it is useful +for generating repeated time stamps. + +Do not change `time-stamp-end' or `time-stamp-inserts-lines' for +yourself or you will be incompatible with other people's files! +If you must change them for some application, do so in the local +variables section of the time-stamped file itself.") + + +(defvar time-stamp-count 1 ;Do not change! + "How many templates \\[time-stamp] will look for in a buffer. +The same time stamp will be written in each case. + +Do not change `time-stamp-count' for yourself or you will be +incompatible with other people's files! If you must change it for +some application, do so in the local variables section of the +time-stamped file itself.") + + +(defvar time-stamp-pattern nil ;Do not change! + "Convenience variable setting all `time-stamp' location and format values. +This string has four parts, each of which is optional. +These four parts set `time-stamp-line-limit', `time-stamp-start', +`time-stamp-format', and `time-stamp-end'. See the documentation +for each of these variables for details. + +The first part is a number followed by a slash; the number sets the number +of lines at the beginning (negative counts from end) of the file searched +for the time stamp. The number and the slash may be omitted to use the +normal value. + +The second part is a regexp identifying the pattern preceding the time stamp. +This part may be omitted to use the normal pattern. + +The third part specifies the format of the time stamp inserted. See +the documentation for `time-stamp-format' for details. Specify this +part as \"%%\" to use the normal format. + +The fourth part is a regexp identifying the pattern following the time stamp. +This part may be omitted to use the normal pattern. + +Examples: +\"-10/\" +\"-9/^Last modified: %%$\" +\"@set Time-stamp: %:b %:d, %:y$\" +\"newcommand{\\\\\\\\timestamp}{%%}\" + +Do not change `time-stamp-pattern' for yourself or you will be incompatible +with other people's files! Set it only in the local variables section +of the time-stamped file itself.") + + + +;;;###autoload +(defun time-stamp () + "Update the time stamp string(s) in the buffer. +A template in a file can be automatically updated with a new time stamp +every time you save the file. Add this line to your .emacs file: + (add-hook 'write-file-hooks 'time-stamp) +Normally the template must appear in the first 8 lines of a file and +look like one of the following: + Time-stamp: <> + Time-stamp: \" \" +The time stamp is written between the brackets or quotes: + Time-stamp: <2001-02-18 10:20:51 gildea> +The time stamp is updated only if the variable `time-stamp-active' is non-nil. +The format of the time stamp is set by the variable `time-stamp-format'. +The variables `time-stamp-line-limit', `time-stamp-start', `time-stamp-end', +`time-stamp-count', and `time-stamp-inserts-lines' control finding the +template." + (interactive) + (let ((line-limit time-stamp-line-limit) + (ts-start time-stamp-start) + (ts-format time-stamp-format) + (ts-end time-stamp-end) + (ts-count time-stamp-count) + (format-lines 0) + (end-lines 1) + (start nil) + search-limit) + (if (stringp time-stamp-pattern) + (progn + (string-match "\\`\\(\\(-?[0-9]+\\)/\\)?\\([^%]+\\)?\\(\\(%[-.,:@+_ #^()0-9]*[A-Za-z%][^%]*\\)*%[-.,:@+_ #^()0-9]*[A-Za-z%]\\)?\\([^%]+\\)?\\'" time-stamp-pattern) + (and (match-beginning 2) + (setq line-limit + (string-to-int (match-string 2 time-stamp-pattern)))) + (and (match-beginning 3) + (setq ts-start (match-string 3 time-stamp-pattern))) + (and (match-beginning 4) + (not (string-equal (match-string 4 time-stamp-pattern) "%%")) + (setq ts-format (match-string 4 time-stamp-pattern))) + (and (match-beginning 6) + (setq ts-end (match-string 6 time-stamp-pattern))))) + (cond ((not (integerp line-limit)) + (setq line-limit 8) + (message "time-stamp-line-limit is not an integer") + (sit-for 1))) + (cond ((not (integerp ts-count)) + (setq ts-count 1) + (message "time-stamp-count is not an integer") + (sit-for 1)) + ((< ts-count 1) + ;; We need to call time-stamp-once at least once + ;; to output any warnings about time-stamp not being active. + (setq ts-count 1))) + ;; Figure out what lines the end should be on. + (if (stringp ts-format) + (let ((nl-start 0)) + (while (string-match "\n" ts-format nl-start) + (setq format-lines (1+ format-lines) nl-start (match-end 0))))) + (let ((nl-start 0)) + (while (string-match "\n" ts-end nl-start) + (setq end-lines (1+ end-lines) nl-start (match-end 0)))) + ;; Find overall what lines to look at + (save-excursion + (save-restriction + (widen) + (cond ((> line-limit 0) + (goto-char (setq start (point-min))) + (forward-line line-limit) + (setq search-limit (point))) + ((< line-limit 0) + (goto-char (setq search-limit (point-max))) + (forward-line line-limit) + (setq start (point))) + (t ;0 => no limit (use with care!) + (setq start (point-min)) + (setq search-limit (point-max)))))) + (while (and start + (< start search-limit) + (> ts-count 0)) + (setq start (time-stamp-once start search-limit ts-start ts-end + ts-format format-lines end-lines)) + (setq ts-count (1- ts-count)))) + ;; be sure to return nil so can be used on write-file-hooks + nil) + +(defun time-stamp-once (start search-limit ts-start ts-end + ts-format format-lines end-lines) + "Update one time stamp. Internal routine called by \\[time-stamp]. +Returns the end point, which is where `time-stamp' begins the next search." + (let ((case-fold-search nil) + (end nil) + end-search-start + (end-length nil)) + (save-excursion + (save-restriction + (widen) + ;; Find the location of the time stamp. + (while (and (< (goto-char start) search-limit) + (not end) + (re-search-forward ts-start search-limit 'move)) + (setq start (point)) + (if (not time-stamp-inserts-lines) + (forward-line format-lines)) + (setq end-search-start (max start (point))) + (if (= (forward-line end-lines) 0) + (progn + (and (bolp) (backward-char)) + (let ((line-end (min (point) search-limit))) + (if (>= line-end end-search-start) + (progn + (goto-char end-search-start) + (if (re-search-forward ts-end line-end t) + (progn + (setq end (match-beginning 0)) + (setq end-length (- (match-end 0) end)))))))))))) + (if end + (progn + ;; do all warnings outside save-excursion + (cond + ((not time-stamp-active) + (if time-stamp-warn-inactive + ;; don't signal an error in a write-file-hook + (progn + (message "Warning: time-stamp-active is off; did not time-stamp buffer.") + (sit-for 1)))) + ((not (and (stringp ts-start) + (stringp ts-end))) + (message "time-stamp-start or time-stamp-end is not a string") + (sit-for 1)) + (t + (let ((new-time-stamp (time-stamp-string ts-format))) + (if (and (stringp new-time-stamp) + (not (string-equal (buffer-substring start end) + new-time-stamp))) + (save-excursion + (save-restriction + (widen) + (delete-region start end) + (goto-char start) + (insert-and-inherit new-time-stamp) + (setq end (point)) + ;; remove any tabs used to format time stamp + (if (search-backward "\t" start t) + (progn + (untabify start end) + (setq end (point)))))))))))) + ;; return the location after this time stamp, if there was one + (and end end-length + (+ end end-length)))) + + +;;;###autoload +(defun time-stamp-toggle-active (&optional arg) + "Toggle `time-stamp-active', setting whether \\[time-stamp] updates a buffer. +With ARG, turn time stamping on if and only if arg is positive." + (interactive "P") + (setq time-stamp-active + (if (null arg) + (not time-stamp-active) + (> (prefix-numeric-value arg) 0))) + (message "time-stamp is now %s." (if time-stamp-active "active" "off"))) + + +(defun time-stamp-string (&optional ts-format) + "Generate the new string to be inserted by \\[time-stamp]. +Optionally use format TS-FORMAT instead of `time-stamp-format' to +format the string." + (or ts-format + (setq ts-format time-stamp-format)) + (if (stringp ts-format) + (if (stringp time-stamp-time-zone) + (let ((ts-real-time-zone (getenv "TZ"))) + (unwind-protect + (progn + (set-time-zone-rule time-stamp-time-zone) + (format-time-string + (time-stamp-string-preprocess ts-format))) + (set-time-zone-rule ts-real-time-zone))) + (format-time-string + (time-stamp-string-preprocess ts-format))) + ;; handle version 1 compatibility + (cond ((or (eq time-stamp-old-format-warn 'error) + (and (eq time-stamp-old-format-warn 'ask) + (not (y-or-n-p "Use non-string time-stamp-format? ")))) + (message "Warning: no time-stamp: time-stamp-format not a string") + (sit-for 1) + nil) + (t + (cond ((eq time-stamp-old-format-warn 'warn) + (message "Obsolescent time-stamp-format type; should be string") + (sit-for 1))) + (time-stamp-fconcat ts-format " "))))) + +(defconst time-stamp-no-file "(no file)" + "String to use when the buffer is not associated with a file.") + +;;; time-stamp is transitioning to using the new, expanded capabilities +;;; of format-time-string. During the process, this function implements +;;; intermediate, compatible formats and complains about old, soon to +;;; be unsupported, formats. This function will get a lot (a LOT) shorter +;;; when the transition is complete and we can just pass most things +;;; straight through to format-time-string. +;;; At all times, all the formats recommended in the doc string +;;; of time-stamp-format will work not only in the current version of +;;; Emacs, but in all versions that have been released within the past +;;; two years. +;;; The : modifier is a temporary conversion feature used to resolve +;;; ambiguous formats--formats that are changing (over time) incompatibly. +(defun time-stamp-string-preprocess (format &optional time) + "Use a FORMAT to format date, time, file, and user information. +Optional second argument TIME is only for testing. +Implements non-time extensions to `format-time-string' +and all `time-stamp-format' compatibility." + (let ((fmt-len (length format)) + (ind 0) + cur-char + (prev-char nil) + (result "") + field-width + field-result + alt-form change-case require-padding + (paren-level 0)) + (while (< ind fmt-len) + (setq cur-char (aref format ind)) + (setq + result + (concat result + (cond + ((eq cur-char ?%) + ;; eat any additional args to allow for future expansion + (setq alt-form nil change-case nil require-padding nil field-width "") + (while (progn + (setq ind (1+ ind)) + (setq cur-char (if (< ind fmt-len) + (aref format ind) + ?\0)) + (or (eq ?. cur-char) + (eq ?, cur-char) (eq ?: cur-char) (eq ?@ cur-char) + (eq ?- cur-char) (eq ?+ cur-char) (eq ?_ cur-char) + (eq ?\ cur-char) (eq ?# cur-char) (eq ?^ cur-char) + (and (eq ?\( cur-char) + (not (eq prev-char ?\\)) + (setq paren-level (1+ paren-level))) + (if (and (eq ?\) cur-char) + (not (eq prev-char ?\\)) + (> paren-level 0)) + (setq paren-level (1- paren-level)) + (and (> paren-level 0) + (< ind fmt-len))) + (if (and (<= ?0 cur-char) (>= ?9 cur-char)) + ;; get format width + (let ((field-index ind)) + (while (progn + (setq ind (1+ ind)) + (setq cur-char (if (< ind fmt-len) + (aref format ind) + ?\0)) + (and (<= ?0 cur-char) (>= ?9 cur-char)))) + (setq field-width (substring format field-index ind)) + (setq ind (1- ind)) + t)))) + (setq prev-char cur-char) + ;; some characters we actually use + (cond ((eq cur-char ?:) + (setq alt-form t)) + ((eq cur-char ?#) + (setq change-case t)))) + (setq field-result + (cond + ((eq cur-char ?%) + "%%") + ((eq cur-char ?a) ;day of week + (if change-case + (format-time-string "%#a" time) + (or alt-form (not (string-equal field-width "")) + (time-stamp-conv-warn "%a" "%:a")) + (if (and alt-form (not (string-equal field-width ""))) + "" ;discourage "%:3a" + (format-time-string "%A" time)))) + ((eq cur-char ?A) + (if alt-form + (format-time-string "%A" time) + (or change-case (not (string-equal field-width "")) + (time-stamp-conv-warn "%A" "%#A")) + (format-time-string "%#A" time))) + ((eq cur-char ?b) ;month name + (if change-case + (format-time-string "%#b" time) + (or alt-form (not (string-equal field-width "")) + (time-stamp-conv-warn "%b" "%:b")) + (if (and alt-form (not (string-equal field-width ""))) + "" ;discourage "%:3b" + (format-time-string "%B" time)))) + ((eq cur-char ?B) + (if alt-form + (format-time-string "%B" time) + (or change-case (not (string-equal field-width "")) + (time-stamp-conv-warn "%B" "%#B")) + (format-time-string "%#B" time))) + ((eq cur-char ?d) ;day of month, 1-31 + (time-stamp-do-number cur-char alt-form field-width time)) + ((eq cur-char ?H) ;hour, 0-23 + (time-stamp-do-number cur-char alt-form field-width time)) + ((eq cur-char ?I) ;hour, 1-12 + (time-stamp-do-number cur-char alt-form field-width time)) + ((eq cur-char ?m) ;month number, 1-12 + (time-stamp-do-number cur-char alt-form field-width time)) + ((eq cur-char ?M) ;minute, 0-59 + (time-stamp-do-number cur-char alt-form field-width time)) + ((eq cur-char ?p) ;am or pm + (or change-case + (time-stamp-conv-warn "%p" "%#p")) + (format-time-string "%#p" time)) + ((eq cur-char ?P) ;AM or PM + (format-time-string "%p" time)) + ((eq cur-char ?S) ;seconds, 00-60 + (time-stamp-do-number cur-char alt-form field-width time)) + ((eq cur-char ?w) ;weekday number, Sunday is 0 + (format-time-string "%w" time)) + ((eq cur-char ?y) ;year + (or alt-form (not (string-equal field-width "")) + (time-stamp-conv-warn "%y" "%:y")) + (string-to-int (format-time-string "%Y" time))) + ((eq cur-char ?Y) ;4-digit year, new style + (string-to-int (format-time-string "%Y" time))) + ((eq cur-char ?z) ;time zone lower case + (if change-case + "" ;discourage %z variations + (format-time-string "%#Z" time))) + ((eq cur-char ?Z) + (if change-case + (format-time-string "%#Z" time) + (format-time-string "%Z" time))) + ((eq cur-char ?f) ;buffer-file-name, base name only + (if buffer-file-name + (file-name-nondirectory buffer-file-name) + time-stamp-no-file)) + ((eq cur-char ?F) ;buffer-file-name, full path + (or buffer-file-name + time-stamp-no-file)) + ((eq cur-char ?s) ;system name + (system-name)) + ((eq cur-char ?u) ;user name + (user-login-name)) + ((eq cur-char ?U) ;user full name + (user-full-name)) + ((eq cur-char ?l) ;logname (undocumented user name alt) + (user-login-name)) + ((eq cur-char ?L) ;(undocumented alt user full name) + (user-full-name)) + ((eq cur-char ?h) ;mail host name + (time-stamp-mail-host-name)) + )) + (let ((padded-result + (format (format "%%%s%c" + field-width + (if (numberp field-result) ?d ?s)) + (or field-result "")))) + (let* ((initial-length (length padded-result)) + (desired-length (if (string-equal field-width "") + initial-length + (string-to-int field-width)))) + (if (> initial-length desired-length) + ;; truncate strings on right, years on left + (if (stringp field-result) + (substring padded-result 0 desired-length) + (if (eq cur-char ?y) + (substring padded-result (- desired-length)) + padded-result)) ;non-year numbers don't truncate + padded-result)))) + (t + (char-to-string cur-char))))) + (setq ind (1+ ind))) + result)) + +(defun time-stamp-do-number (format-char alt-form field-width time) + "Handle compatible FORMAT-CHAR where only default width/padding will change. +ALT-FORM is whether `#' specified. FIELD-WIDTH is the string +width specification or \"\". TIME is the time to convert." + (let ((format-string (concat "%" (char-to-string format-char)))) + (and (not alt-form) (string-equal field-width "") + (time-stamp-conv-warn format-string + (format "%%:%c" format-char))) + (if (and alt-form (not (string-equal field-width ""))) + "" ;discourage "%:2d" and the like + (string-to-int (format-time-string format-string time))))) + +(defvar time-stamp-conversion-warn t + "Warn about soon-to-be-unsupported forms in `time-stamp-format'. +If nil, these warnings are disabled, which would be a bad idea! +You really need to update your files instead. + +The new formats will work with old versions of Emacs. +New formats are being recommended now to allow `time-stamp-format' +to change in the future to be compatible with `format-time-string'. +The new forms being recommended now will continue to work then.") + + +(defun time-stamp-conv-warn (old-form new-form) + "Display a warning about a soon-to-be-obsolete format. +Suggests replacing OLD-FORM with NEW-FORM." + (cond + (time-stamp-conversion-warn + (save-excursion + (set-buffer (get-buffer-create "*Time-stamp-compatibility*")) + (goto-char (point-max)) + (if (bobp) + (progn + (insert + "The formats recognized in time-stamp-format will change in a future release\n" + "to be compatible with the new, expanded format-time-string function.\n\n" + "The following obsolescent time-stamp-format construct(s) were found:\n\n"))) + (insert "\"" old-form "\" -- use " new-form "\n")) + (display-buffer "*Time-stamp-compatibility*")))) + + + +(defun time-stamp-mail-host-name () + "Return the name of the host where the user receives mail. +This is the value of `mail-host-address' if bound and a string, +otherwise the value of the function `system-name'." + (or (and (boundp 'mail-host-address) + (stringp mail-host-address) + mail-host-address) + (system-name))) + +;;; the rest of this file is for version 1 compatibility + +(defun time-stamp-fconcat (list sep) + "Similar to (mapconcat 'funcall LIST SEP) but LIST allows literals. +If an element of LIST is a symbol, it is funcalled to get the string to use; +the separator SEP is used between two strings obtained by funcalling a +symbol. Otherwise the element itself is inserted; no separator is used +around literals." + (let ((return-string "") + (insert-sep-p nil)) + (while list + (cond ((symbolp (car list)) + (if insert-sep-p + (setq return-string (concat return-string sep))) + (setq return-string (concat return-string (funcall (car list)))) + (setq insert-sep-p t)) + (t + (setq return-string (concat return-string (car list))) + (setq insert-sep-p nil))) + (setq list (cdr list))) + return-string)) + +;;; Some functions used in time-stamp-format + +;;; These functions have been obsolete since 1995 +;;; and will be removed in Emacs 22. +;;; Meanwhile, discourage other packages from using them. +(let ((obsolete-functions '(time-stamp-month-dd-yyyy + time-stamp-dd/mm/yyyy + time-stamp-mon-dd-yyyy + time-stamp-dd-mon-yy + time-stamp-yy/mm/dd + time-stamp-yyyy/mm/dd + time-stamp-yyyy-mm-dd + time-stamp-yymmdd + time-stamp-hh:mm:ss + time-stamp-hhmm))) + (while obsolete-functions + (make-obsolete (car obsolete-functions) + "use time-stamp-string or format-time-string instead." + "20.1") + (setq obsolete-functions (cdr obsolete-functions)))) + +;;; pretty form, suitable for a title page + +(defun time-stamp-month-dd-yyyy () + "Return the current date as a string in \"Month DD, YYYY\" form." + (format-time-string "%B %e, %Y")) + +(defun time-stamp-dd/mm/yyyy () + "Return the current date as a string in \"DD/MM/YYYY\" form." + (format-time-string "%d/%m/%Y")) + +;;; same as __DATE__ in ANSI C + +(defun time-stamp-mon-dd-yyyy () + "Return the current date as a string in \"Mon DD YYYY\" form. +The first character of DD is space if the value is less than 10." + (format-time-string "%b %d %Y")) + +;;; RFC 822 date + +(defun time-stamp-dd-mon-yy () + "Return the current date as a string in \"DD Mon YY\" form." + (format-time-string "%d %b %y")) + +;;; RCS 3 date + +(defun time-stamp-yy/mm/dd () + "Return the current date as a string in \"YY/MM/DD\" form." + (format-time-string "%y/%m/%d")) + +;;; RCS 5 date + +(defun time-stamp-yyyy/mm/dd () + "Return the current date as a string in \"YYYY/MM/DD\" form." + (format-time-string "%Y/%m/%d")) + +;;; ISO 8601 date + +(defun time-stamp-yyyy-mm-dd () + "Return the current date as a string in \"YYYY-MM-DD\" form." + (format-time-string "%Y-%m-%d")) + +(defun time-stamp-yymmdd () + "Return the current date as a string in \"YYMMDD\" form." + (format-time-string "%y%m%d")) + +(defun time-stamp-hh:mm:ss () + "Return the current time as a string in \"HH:MM:SS\" form." + (format-time-string "%T")) + +(defun time-stamp-hhmm () + "Return the current time as a string in \"HHMM\" form." + (format-time-string "%H%M")) + +(provide 'time-stamp) + +;;; time-stamp.el ends here