]> code.delx.au - gnu-emacs/blobdiff - lisp/add-log.el
(comment-start, comment-start-skip, comment-end): Made `defvar'.
[gnu-emacs] / lisp / add-log.el
index a459079f6298acfdda7f0d1af8a6065dcf04f81b..a89cbd49f6d5667ab29086daa14df1a9ef55cf2f 100644 (file)
@@ -1,6 +1,6 @@
 ;;; add-log.el --- change log maintenance commands for Emacs
 
 ;;; add-log.el --- change log maintenance commands for Emacs
 
-;; Copyright (C) 1985, 86, 88, 93, 94, 1997 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 86, 88, 93, 94, 97, 98, 2000 Free Software Foundation, Inc.
 
 ;; Keywords: tools
 
 
 ;; Keywords: tools
 
 
 ;;; Code:
 
 
 ;;; Code:
 
+(eval-when-compile
+  (require 'timezone))
+
 (defgroup change-log nil
   "Change log maintenance"
   :group 'tools
 (defgroup change-log nil
   "Change log maintenance"
   :group 'tools
+  :link '(custom-manual "(emacs)Change Log")
   :prefix "change-log-"
   :prefix "add-log-")
 
   :prefix "change-log-"
   :prefix "add-log-")
 
                 string)
   :group 'change-log)
 
                 string)
   :group 'change-log)
 
+(defcustom change-log-mode-hook nil
+  "Normal hook run by `change-log-mode'."
+  :type 'hook
+  :group 'change-log)
+
 (defcustom add-log-current-defun-function nil
 (defcustom add-log-current-defun-function nil
-  "\
-*If non-nil, function to guess name of current function from surrounding text.
-\\[add-change-log-entry] calls this function (if nil, `add-log-current-defun'
-instead) with no arguments.  It returns a string or nil if it cannot guess."
-  :type 'boolean
+  "*If non-nil, function to guess name of surrounding function.
+It is used by `add-log-current-defun' in preference to built-in rules.
+Returns function's name as a string, or nil if outside a function."
+  :type 'function
   :group 'change-log)
 
 ;;;###autoload
 (defcustom add-log-full-name nil
   "*Full name of user, for inclusion in ChangeLog daily headers.
   :group 'change-log)
 
 ;;;###autoload
 (defcustom add-log-full-name nil
   "*Full name of user, for inclusion in ChangeLog daily headers.
-This defaults to the value returned by the `user-full-name' function."
+This defaults to the value returned by the function `user-full-name'."
   :type '(choice (const :tag "Default" nil)
                 string)
   :group 'change-log)
   :type '(choice (const :tag "Default" nil)
                 string)
   :group 'change-log)
@@ -76,22 +84,89 @@ and `current-time-string' are two valid values."
                (function :tag "Other"))
   :group 'change-log)
 
                (function :tag "Other"))
   :group 'change-log)
 
+(defcustom add-log-keep-changes-together nil
+  "*If non-nil, normally keep day's log entries for one file together.
+
+Log entries for a given file made with \\[add-change-log-entry] or
+\\[add-change-log-entry-other-window] will only be added to others \
+for that file made
+today if this variable is non-nil or that file comes first in today's
+entries.  Otherwise another entry for that file will be started.  An
+original log:
+
+       * foo (...): ...
+       * bar (...): change 1
+
+in the latter case, \\[add-change-log-entry-other-window] in a \
+buffer visiting `bar', yields:
+
+       * bar (...): -!-
+       * foo (...): ...
+       * bar (...): change 1
+
+and in the former:
+
+       * foo (...): ...
+       * bar (...): change 1
+       (...): -!-
+
+The NEW-ENTRY arg to `add-change-log-entry' can override the effect of
+this variable."
+  :version "20.3"
+  :type 'boolean
+  :group 'change-log)
+
+(defcustom add-log-file-name-function nil
+  "*If non-nil, function to call to identify the filename for a ChangeLog entry.
+This function is called with one argument, the value of variable
+`buffer-file-name' in that buffer.  If this is nil, the default is to
+use the file's name relative to the directory of the change log file."
+  :type 'function
+  :group 'change-log)
+
+
+(defcustom change-log-version-info-enabled nil
+  "*If non-nil, enable recording version numbers with the changes."
+  :version "21.1"
+  :type 'boolean
+  :group 'change-log)
+
+(defcustom change-log-version-number-regexp-list
+  (let ((re    "\\([0-9]+\.[0-9.]+\\)"))
+    (list
+     ;;  (defconst ad-version "2.15"
+     (concat "^(def[^ \t\n]+[ \t]+[^ \t\n][ \t]\"" re)
+     ;; Revision: pcl-cvs.el,v 1.72 1999/09/05 20:21:54 monnier Exp
+     (concat "^;+ *Revision: +[^ \t\n]+[ \t]+" re)))
+  "*List of regexps to search for version number.
+The version number must be in group 1.
+Note: The search is conducted only within 10%, at the beginning of the file."
+  :version "21.1"
+  :type '(repeat regexp)
+  :group 'change-log)
+
+
 (defvar change-log-font-lock-keywords
   '(;;
     ;; Date lines, new and old styles.
 (defvar change-log-font-lock-keywords
   '(;;
     ;; Date lines, new and old styles.
-    ("^\\sw.........[0-9: ]*"
+    ("^\\sw.........[0-9:+ ]*"
      (0 font-lock-string-face)
      (0 font-lock-string-face)
-     ("\\([^<]+\\)<\\([A-Za-z0-9_.-]+@[A-Za-z0-9_.-]+\\)>" nil nil
-      (1 font-lock-reference-face)
+     ;; Name and e-mail; some people put e-mail in parens, not angles.
+     ("\\([^<(]+\\)[(<]\\([A-Za-z0-9_.-]+@[A-Za-z0-9_.-]+\\)[>)]" nil nil
+      (1 font-lock-constant-face)
       (2 font-lock-variable-name-face)))
     ;;
     ;; File names.
     ("^\t\\* \\([^ ,:([\n]+\\)"
      (1 font-lock-function-name-face)
       (2 font-lock-variable-name-face)))
     ;;
     ;; File names.
     ("^\t\\* \\([^ ,:([\n]+\\)"
      (1 font-lock-function-name-face)
-     ("\\=, \\([^ ,:([\n]+\\)" nil nil (1 font-lock-function-name-face)))
+     ;; Possibly further names in a list:
+     ("\\=, \\([^ ,:([\n]+\\)" nil nil (1 font-lock-function-name-face))
+     ;; Possibly a parenthesized list of names:
+     ("\\= (\\([^) ,:\n]+\\)" nil nil (1 font-lock-keyword-face))
+     ("\\=, *\\([^) ,:\n]+\\)" nil nil (1 font-lock-keyword-face)))
     ;;
     ;; Function or variable names.
     ;;
     ;; Function or variable names.
-    ("(\\([^) ,:\n]+\\)"
+    ("^\t(\\([^) ,:\n]+\\)"
      (1 font-lock-keyword-face)
      ("\\=, *\\([^) ,:\n]+\\)" nil nil (1 font-lock-keyword-face)))
     ;;
      (1 font-lock-keyword-face)
      ("\\=, *\\([^) ,:\n]+\\)" nil nil (1 font-lock-keyword-face)))
     ;;
@@ -99,17 +174,14 @@ and `current-time-string' are two valid values."
     ("\\[!?\\([^]\n]+\\)\\]\\(:\\| (\\)" (1 font-lock-variable-name-face))
     ;;
     ;; Acknowledgements.
     ("\\[!?\\([^]\n]+\\)\\]\\(:\\| (\\)" (1 font-lock-variable-name-face))
     ;;
     ;; Acknowledgements.
-    ("^\t\\(From\\|Patch\\(es\\)? by\\|Reported by\\)"
+    ("^\t\\(From\\|Patch\\(es\\)? by\\|Report\\(ed by\\| from\\)\\|Suggest\\(ed by\\|ion from\\)\\)"
      1 font-lock-comment-face)
      1 font-lock-comment-face)
-    ("  \\(From\\|Patch\\(es\\)? by\\|Reported by\\)"
+    ("  \\(From\\|Patch\\(es\\)? by\\|Report\\(ed by\\| from\\)\\|Suggest\\(ed by\\|ion from\\)\\)"
      1 font-lock-comment-face))
   "Additional expressions to highlight in Change Log mode.")
 
      1 font-lock-comment-face))
   "Additional expressions to highlight in Change Log mode.")
 
-(defvar change-log-mode-map nil
+(defvar change-log-mode-map (make-sparse-keymap)
   "Keymap for Change Log major mode.")
   "Keymap for Change Log major mode.")
-(if change-log-mode-map
-    nil
-  (setq change-log-mode-map (make-sparse-keymap)))
 
 (defvar change-log-time-zone-rule nil
   "Time zone used for calculating change log time stamps.
 
 (defvar change-log-time-zone-rule nil
   "Time zone used for calculating change log time stamps.
@@ -144,6 +216,7 @@ If nil, use local time.")
     (format-time-string "%Y-%m-%d")))
 
 (defun change-log-name ()
     (format-time-string "%Y-%m-%d")))
 
 (defun change-log-name ()
+  "Return (system-dependent) default name for a change log file."
   (or change-log-default-name
       (if (eq system-type 'vax-vms)
          "$CHANGE_LOG$.TXT"
   (or change-log-default-name
       (if (eq system-type 'vax-vms)
          "$CHANGE_LOG$.TXT"
@@ -167,6 +240,35 @@ If nil, use local time.")
                            (file-name-as-directory name))
        name))))
 
                            (file-name-as-directory name))
        name))))
 
+(defun change-log-version-number-search ()
+  "Return version number of current buffer's file.
+This is the value returned by `vc-workfile-version' or, if that is
+nil, by matching `change-log-version-number-regexp-list'."
+  (let* ((size (buffer-size))
+        (end
+         ;; The version number can be anywhere in the file, but
+         ;; restrict search to the file beginning: 10% should be
+         ;; enough to prevent some mishits.
+         ;;
+         ;; Apply percentage only if buffer size is bigger than
+         ;; approx 100 lines.
+         (if (> size (* 100 80))
+             (/ size 10)
+           size))
+        version)
+    (or (and buffer-file-name
+            (vc-workfile-version buffer-file-name))
+       (save-restriction
+         (widen)
+         (let ((regexps change-log-version-number-regexp-list))
+           (while regexps
+             (save-excursion
+               (goto-char (point-min))
+               (when (re-search-forward (pop regexps) end t)
+                 (setq version (match-string 1)
+                       regexps nil)))))))))
+
+
 ;;;###autoload
 (defun find-change-log (&optional file-name)
   "Find a change log file for \\[add-change-log-entry] and return the name.
 ;;;###autoload
 (defun find-change-log (&optional file-name)
   "Find a change log file for \\[add-change-log-entry] and return the name.
@@ -224,16 +326,20 @@ current buffer to the complete file name."
   (set (make-local-variable 'change-log-default-name) file-name)
   file-name)
 
   (set (make-local-variable 'change-log-default-name) file-name)
   file-name)
 
-
 ;;;###autoload
 (defun add-change-log-entry (&optional whoami file-name other-window new-entry)
   "Find change log file and add an entry for today.
 ;;;###autoload
 (defun add-change-log-entry (&optional whoami file-name other-window new-entry)
   "Find change log file and add an entry for today.
-Optional arg (interactive prefix) non-nil means prompt for user name and site.
-Second arg is file name of change log.  If nil, uses `change-log-default-name'.
+Optional arg WHOAMI (interactive prefix) non-nil means prompt for user
+name and site.
+
+Second arg is FILE-NAME of change log.  If nil, uses `change-log-default-name'.
 Third arg OTHER-WINDOW non-nil means visit in other window.
 Fourth arg NEW-ENTRY non-nil means always create a new entry at the front;
 Third arg OTHER-WINDOW non-nil means visit in other window.
 Fourth arg NEW-ENTRY non-nil means always create a new entry at the front;
-never append to an existing entry.  Today's date is calculated according to
-`change-log-time-zone-rule' if non-nil, otherwise in local time."
+never append to an existing entry.  Option `add-log-keep-changes-together'
+otherwise affects whether a new entry is created.
+
+Today's date is calculated according to `change-log-time-zone-rule' if
+non-nil, otherwise in local time."
   (interactive (list current-prefix-arg
                     (prompt-for-change-log-name)))
   (or add-log-full-name
   (interactive (list current-prefix-arg
                     (prompt-for-change-log-name)))
   (or add-log-full-name
@@ -249,9 +355,10 @@ never append to an existing entry.  Today's date is calculated according to
         ;; s/he can edit the full name field in prompter if s/he wants.
        (setq add-log-mailing-address
              (read-input "Mailing address: " add-log-mailing-address))))
         ;; s/he can edit the full name field in prompter if s/he wants.
        (setq add-log-mailing-address
              (read-input "Mailing address: " add-log-mailing-address))))
-  (let ((defun (funcall (or add-log-current-defun-function
-                           'add-log-current-defun)))
-       paragraph-end entry)
+  (let ((defun (add-log-current-defun))
+       (version (and change-log-version-info-enabled
+                     (change-log-version-number-search)))
+       bound entry)
 
     (setq file-name (expand-file-name (find-change-log file-name)))
 
 
     (setq file-name (expand-file-name (find-change-log file-name)))
 
@@ -259,12 +366,21 @@ never append to an existing entry.  Today's date is calculated according to
     (and buffer-file-name
         ;; Never want to add a change log entry for the ChangeLog file itself.
         (not (string= buffer-file-name file-name))
     (and buffer-file-name
         ;; Never want to add a change log entry for the ChangeLog file itself.
         (not (string= buffer-file-name file-name))
-        (setq entry (if (string-match
-                         (concat "^" (regexp-quote (file-name-directory
-                                                    file-name)))
-                         buffer-file-name)
-                        (substring buffer-file-name (match-end 0))
-                      (file-name-nondirectory buffer-file-name))))
+        (if add-log-file-name-function
+            (setq entry
+                  (funcall add-log-file-name-function buffer-file-name))
+          (setq entry
+                (if (string-match
+                     (concat "^" (regexp-quote (file-name-directory
+                                                file-name)))
+                     buffer-file-name)
+                    (substring buffer-file-name (match-end 0))
+                  (file-name-nondirectory buffer-file-name)))
+          ;; If we have a backup file, it's presumably because we're
+          ;; comparing old and new versions (e.g. for deleted
+          ;; functions) and we'll want to use the original name.
+          (if (backup-file-name-p entry)
+              (setq entry (file-name-sans-versions entry)))))
 
     (if (and other-window (not (equal file-name buffer-file-name)))
        (find-file-other-window file-name)
 
     (if (and other-window (not (equal file-name buffer-file-name)))
        (find-file-other-window file-name)
@@ -280,15 +396,17 @@ never append to an existing entry.  Today's date is calculated according to
          (forward-line 1)
        (insert new-entry "\n\n")))
 
          (forward-line 1)
        (insert new-entry "\n\n")))
 
-    ;; Search only within the first paragraph.
-    (if (looking-at "\n*[^\n* \t]")
-       (skip-chars-forward "\n")
-      (forward-paragraph 1))
-    (setq paragraph-end (point))
+    (setq bound
+         (progn
+            (if (looking-at "\n*[^\n* \t]")
+                (skip-chars-forward "\n")
+             (if add-log-keep-changes-together
+                 (forward-page)        ; page delimits entries for date
+               (forward-paragraph)))   ; paragraph delimits entries for file
+           (point)))
     (goto-char (point-min))
     (goto-char (point-min))
-
     ;; Now insert the new line for this entry.
     ;; Now insert the new line for this entry.
-    (cond ((re-search-forward "^\\s *\\*\\s *$" paragraph-end t)
+    (cond ((re-search-forward "^\\s *\\*\\s *$" bound t)
           ;; Put this file name into the existing empty entry.
           (if entry
               (insert entry)))
           ;; Put this file name into the existing empty entry.
           (if entry
               (insert entry)))
@@ -299,13 +417,13 @@ never append to an existing entry.  Today's date is calculated according to
                           ;; Don't accept `foo.bar' when
                           ;; looking for `foo':
                           "\\(\\s \\|[(),:]\\)")
                           ;; Don't accept `foo.bar' when
                           ;; looking for `foo':
                           "\\(\\s \\|[(),:]\\)")
-                  paragraph-end t)))
+                  bound t)))
           ;; Add to the existing entry for the same file.
           (re-search-forward "^\\s *$\\|^\\s \\*")
           (goto-char (match-beginning 0))
           ;; Delete excess empty lines; make just 2.
           (while (and (not (eobp)) (looking-at "^\\s *$"))
           ;; Add to the existing entry for the same file.
           (re-search-forward "^\\s *$\\|^\\s \\*")
           (goto-char (match-beginning 0))
           ;; Delete excess empty lines; make just 2.
           (while (and (not (eobp)) (looking-at "^\\s *$"))
-            (delete-region (point) (save-excursion (forward-line 1) (point))))
+            (delete-region (point) (line-beginning-position 2)))
           (insert "\n\n")
           (forward-line -2)
           (indent-relative-maybe))
           (insert "\n\n")
           (forward-line -2)
           (indent-relative-maybe))
@@ -315,7 +433,7 @@ never append to an existing entry.  Today's date is calculated according to
           (while (looking-at "\\sW")
             (forward-line 1))
           (while (and (not (eobp)) (looking-at "^\\s *$"))
           (while (looking-at "\\sW")
             (forward-line 1))
           (while (and (not (eobp)) (looking-at "^\\s *$"))
-            (delete-region (point) (save-excursion (forward-line 1) (point))))
+            (delete-region (point) (line-beginning-position 2)))
           (insert "\n\n\n")
           (forward-line -2)
           (indent-to left-margin)
           (insert "\n\n\n")
           (forward-line -2)
           (indent-to left-margin)
@@ -327,24 +445,29 @@ never append to an existing entry.  Today's date is calculated according to
        (progn
          ;; Make it easy to get rid of the function name.
          (undo-boundary)
        (progn
          ;; Make it easy to get rid of the function name.
          (undo-boundary)
-         (insert (if (save-excursion
-                       (beginning-of-line 1)
-                       (looking-at "\\s *$"))
-                     ""
-                   " ")
-                 "(" defun "): "))
+         (unless (save-excursion
+                   (beginning-of-line 1)
+                   (looking-at "\\s *$"))
+           (insert ?\ ))
+         (insert "(" defun "): ")
+         (if version
+             (insert version ?\ )))
       ;; No function name, so put in a colon unless we have just a star.
       ;; No function name, so put in a colon unless we have just a star.
-      (if (not (save-excursion
-                (beginning-of-line 1)
-                (looking-at "\\s *\\(\\*\\s *\\)?$")))
-         (insert ": ")))))
+      (unless (save-excursion
+               (beginning-of-line 1)
+               (looking-at "\\s *\\(\\*\\s *\\)?$"))
+       (insert ": ")
+       (if version (insert version ?\ ))))))
 
 ;;;###autoload
 (defun add-change-log-entry-other-window (&optional whoami file-name)
   "Find change log file in other window and add an entry for today.
 
 ;;;###autoload
 (defun add-change-log-entry-other-window (&optional whoami file-name)
   "Find change log file in other window and add an entry for today.
-Optional arg (interactive prefix) non-nil means prompt for user name and site.
-Second arg is file name of change log.  \
-If nil, uses `change-log-default-name'."
+Optional arg WHOAMI (interactive prefix) non-nil means prompt for user
+name and site.
+Second optional arg FILE-NAME is file name of change log.
+If nil, use `change-log-default-name'.
+
+Affected by the same options as `add-change-log-entry'."
   (interactive (if current-prefix-arg
                   (list current-prefix-arg
                         (prompt-for-change-log-name))))
   (interactive (if current-prefix-arg
                   (list current-prefix-arg
                         (prompt-for-change-log-name))))
@@ -370,12 +493,12 @@ Runs `change-log-mode-hook'."
   (use-local-map change-log-mode-map)
   (set (make-local-variable 'fill-paragraph-function)
        'change-log-fill-paragraph)
   (use-local-map change-log-mode-map)
   (set (make-local-variable 'fill-paragraph-function)
        'change-log-fill-paragraph)
-  ;; Let each entry behave as one paragraph:
-  ;; We really do want "^" in paragraph-start below: it is only the lines that
-  ;; begin at column 0 (despite the left-margin of 8) that we are looking for.
+  ;; We really do want "^" in paragraph-start below: it is only the
+  ;; lines that begin at column 0 (despite the left-margin of 8) that
+  ;; we are looking for.  Adding `* ' allows eliding the blank line
+  ;; between entries for different files.
   (set (make-local-variable 'paragraph-start) "\\s *$\\|\f\\|^\\<")
   (set (make-local-variable 'paragraph-start) "\\s *$\\|\f\\|^\\<")
-  (set (make-local-variable 'paragraph-separate) "\\s *$\\|\f\\|^\\<")
-  ;; Let all entries for one day behave as one page.
+  (set (make-local-variable 'paragraph-separate) paragraph-start)
   ;; Match null string on the date-line so that the date-line
   ;; is grouped with what follows.
   (set (make-local-variable 'page-delimiter) "^\\<\\|^\f")
   ;; Match null string on the date-line so that the date-line
   ;; is grouped with what follows.
   (set (make-local-variable 'page-delimiter) "^\\<\\|^\f")
@@ -407,7 +530,7 @@ Prefix arg means justify as well."
 
 ;;;###autoload
 (defvar add-log-lisp-like-modes
 
 ;;;###autoload
 (defvar add-log-lisp-like-modes
-    '(emacs-lisp-mode lisp-mode scheme-mode lisp-interaction-mode)
+    '(emacs-lisp-mode lisp-mode scheme-mode dsssl-mode lisp-interaction-mode)
   "*Modes that look like Lisp to `add-log-current-defun'.")
 
 ;;;###autoload
   "*Modes that look like Lisp to `add-log-current-defun'.")
 
 ;;;###autoload
@@ -425,36 +548,44 @@ Prefix arg means justify as well."
   "Return name of function definition point is in, or nil.
 
 Understands C, Lisp, LaTeX (\"functions\" are chapters, sections, ...),
   "Return name of function definition point is in, or nil.
 
 Understands C, Lisp, LaTeX (\"functions\" are chapters, sections, ...),
-Texinfo (@node titles), Perl, and Fortran.
+Texinfo (@node titles) and Perl.
 
 Other modes are handled by a heuristic that looks in the 10K before
 point for uppercase headings starting in the first column or
 
 Other modes are handled by a heuristic that looks in the 10K before
 point for uppercase headings starting in the first column or
-identifiers followed by `:' or `=', see variable
-`add-log-current-defun-header-regexp'.
+identifiers followed by `:' or `='.  See variables
+`add-log-current-defun-header-regexp' and
+`add-log-current-defun-function'
 
 Has a preference of looking backwards."
   (condition-case nil
       (save-excursion
        (let ((location (point)))
 
 Has a preference of looking backwards."
   (condition-case nil
       (save-excursion
        (let ((location (point)))
-         (cond ((memq major-mode add-log-lisp-like-modes)
+         (cond (add-log-current-defun-function
+                (funcall add-log-current-defun-function))
+               ((memq major-mode add-log-lisp-like-modes)
                 ;; If we are now precisely at the beginning of a defun,
                 ;; make sure beginning-of-defun finds that one
                 ;; rather than the previous one.
                 (or (eobp) (forward-char 1))
                 (beginning-of-defun)
                 ;; If we are now precisely at the beginning of a defun,
                 ;; make sure beginning-of-defun finds that one
                 ;; rather than the previous one.
                 (or (eobp) (forward-char 1))
                 (beginning-of-defun)
-                ;; Make sure we are really inside the defun found, not after it.
-                (if (and (looking-at "\\s(")
-                         (progn (end-of-defun)
-                                (< location (point)))
-                         (progn (forward-sexp -1)
-                                (>= location (point))))
-                    (progn
-                      (if (looking-at "\\s(")
-                          (forward-char 1))
-                      (forward-sexp 1)
-                      (skip-chars-forward " '")
-                      (buffer-substring (point)
-                                        (progn (forward-sexp 1) (point))))))
+                ;; Make sure we are really inside the defun found,
+                ;; not after it.
+                (when (and (looking-at "\\s(")
+                           (progn (end-of-defun)
+                                  (< location (point)))
+                           (progn (forward-sexp -1)
+                                  (>= location (point))))
+                  (if (looking-at "\\s(")
+                      (forward-char 1))
+                  ;; Skip the defining construct name, typically "defun"
+                  ;; or "defvar".
+                  (forward-sexp 1)
+                  ;; The second element is usually a symbol being defined.
+                  ;; If it is not, use the first symbol in it.
+                  (skip-chars-forward " \t\n'(")
+                  (buffer-substring-no-properties (point)
+                                                  (progn (forward-sexp 1)
+                                                         (point)))))
                ((and (memq major-mode add-log-c-like-modes)
                      (save-excursion
                        (beginning-of-line)
                ((and (memq major-mode add-log-c-like-modes)
                      (save-excursion
                        (beginning-of-line)
@@ -470,8 +601,9 @@ Has a preference of looking backwards."
                   (forward-line -1))
                 (search-forward "define")
                 (skip-chars-forward " \t")
                   (forward-line -1))
                 (search-forward "define")
                 (skip-chars-forward " \t")
-                (buffer-substring (point)
-                                  (progn (forward-sexp 1) (point))))
+                (buffer-substring-no-properties (point)
+                                                (progn (forward-sexp 1)
+                                                       (point))))
                ((memq major-mode add-log-c-like-modes)
                 (beginning-of-line)
                 ;; See if we are in the beginning part of a function,
                ((memq major-mode add-log-c-like-modes)
                 (beginning-of-line)
                 ;; See if we are in the beginning part of a function,
@@ -481,167 +613,227 @@ Has a preference of looking backwards."
                 (or (eobp)
                     (forward-char 1))
                 (beginning-of-defun)
                 (or (eobp)
                     (forward-char 1))
                 (beginning-of-defun)
-                (if (progn (end-of-defun)
-                           (< location (point)))
-                    (progn
-                      (backward-sexp 1)
-                      (let (beg tem)
-
-                        (forward-line -1)
-                        ;; Skip back over typedefs of arglist.
-                        (while (and (not (bobp))
-                                    (looking-at "[ \t\n]"))
-                          (forward-line -1))
-                        ;; See if this is using the DEFUN macro used in Emacs,
-                        ;; or the DEFUN macro used by the C library.
-                        (if (condition-case nil
-                                (and (save-excursion
-                                       (end-of-line)
-                                       (while (= (preceding-char) ?\\)
-                                         (end-of-line 2))
-                                       (backward-sexp 1)
-                                       (beginning-of-line)
-                                       (setq tem (point))
-                                       (looking-at "DEFUN\\b"))
-                                     (>= location tem))
-                              (error nil))
-                            (progn
-                              (goto-char tem)
-                              (down-list 1)
-                              (if (= (char-after (point)) ?\")
-                                  (progn
-                                    (forward-sexp 1)
-                                    (skip-chars-forward " ,")))
-                              (buffer-substring (point)
-                                                (progn (forward-sexp 1) (point))))
-                           (if (looking-at "^[+-]")
-                               (get-method-definition)
-                             ;; Ordinary C function syntax.
-                             (setq beg (point))
-                             (if (and (condition-case nil
-                                         ;; Protect against "Unbalanced parens" error.
-                                         (progn
-                                           (down-list 1) ; into arglist
-                                           (backward-up-list 1)
-                                           (skip-chars-backward " \t")
-                                           t)
-                                       (error nil))
-                                     ;; Verify initial pos was after
-                                     ;; real start of function.
-                                     (save-excursion
-                                       (goto-char beg)
-                                       ;; For this purpose, include the line
-                                       ;; that has the decl keywords.  This
-                                       ;; may also include some of the
-                                       ;; comments before the function.
-                                       (while (and (not (bobp))
-                                                   (save-excursion
-                                                     (forward-line -1)
-                                                     (looking-at "[^\n\f]")))
-                                         (forward-line -1))
-                                       (>= location (point)))
-                                          ;; Consistency check: going down and up
-                                          ;; shouldn't take us back before BEG.
-                                          (> (point) beg))
-                                (let (end middle)
-                                  ;; Don't include any final newline
-                                  ;; in the name we use.
-                                  (if (= (preceding-char) ?\n)
-                                      (forward-char -1))
-                                  (setq end (point))
-                                  (backward-sexp 1)
-                                  ;; Now find the right beginning of the name.
-                                  ;; Include certain keywords if they
-                                  ;; precede the name.
-                                  (setq middle (point))
-                                  (forward-word -1)
-                                  ;; Ignore these subparts of a class decl
-                                  ;; and move back to the class name itself.
-                                  (while (looking-at "public \\|private ")
-                                    (skip-chars-backward " \t:")
-                                    (setq end (point))
-                                    (backward-sexp 1)
-                                    (setq middle (point))
-                                    (forward-word -1))
-                                  (and (bolp)
-                                       (looking-at "struct \\|union \\|class ")
-                                       (setq middle (point)))
-                                  (buffer-substring middle end)))))))))
+                (when (progn (end-of-defun)
+                             (< location (point)))
+                  (backward-sexp 1)
+                  (let (beg tem)
+
+                    (forward-line -1)
+                    ;; Skip back over typedefs of arglist.
+                    (while (and (not (bobp))
+                                (looking-at "[ \t\n]"))
+                      (forward-line -1))
+                    ;; See if this is using the DEFUN macro used in Emacs,
+                    ;; or the DEFUN macro used by the C library.
+                    (if (condition-case nil
+                            (and (save-excursion
+                                   (end-of-line)
+                                   (while (= (preceding-char) ?\\)
+                                     (end-of-line 2))
+                                   (backward-sexp 1)
+                                   (beginning-of-line)
+                                   (setq tem (point))
+                                   (looking-at "DEFUN\\b"))
+                                 (>= location tem))
+                          (error nil))
+                        (progn
+                          (goto-char tem)
+                          (down-list 1)
+                          (if (= (char-after (point)) ?\")
+                              (progn
+                                (forward-sexp 1)
+                                (skip-chars-forward " ,")))
+                          (buffer-substring-no-properties
+                           (point)
+                           (progn (forward-sexp 1)
+                                  (point))))
+                      (if (looking-at "^[+-]")
+                          (change-log-get-method-definition)
+                        ;; Ordinary C function syntax.
+                        (setq beg (point))
+                        (if (and
+                             ;; Protect against "Unbalanced parens" error.
+                             (condition-case nil
+                                 (progn
+                                   (down-list 1) ; into arglist
+                                   (backward-up-list 1)
+                                   (skip-chars-backward " \t")
+                                   t)
+                               (error nil))
+                             ;; Verify initial pos was after
+                             ;; real start of function.
+                             (save-excursion
+                               (goto-char beg)
+                               ;; For this purpose, include the line
+                               ;; that has the decl keywords.  This
+                               ;; may also include some of the
+                               ;; comments before the function.
+                               (while (and (not (bobp))
+                                           (save-excursion
+                                             (forward-line -1)
+                                             (looking-at "[^\n\f]")))
+                                 (forward-line -1))
+                               (>= location (point)))
+                             ;; Consistency check: going down and up
+                             ;; shouldn't take us back before BEG.
+                             (> (point) beg))
+                            (let (end middle)
+                              ;; Don't include any final whitespace
+                              ;; in the name we use.
+                              (skip-chars-backward " \t\n")
+                              (setq end (point))
+                              (backward-sexp 1)
+                              ;; Now find the right beginning of the name.
+                              ;; Include certain keywords if they
+                              ;; precede the name.
+                              (setq middle (point))
+                              (forward-word -1)
+                              ;; Ignore these subparts of a class decl
+                              ;; and move back to the class name itself.
+                              (while (looking-at "public \\|private ")
+                                (skip-chars-backward " \t:")
+                                (setq end (point))
+                                (backward-sexp 1)
+                                (setq middle (point))
+                                (forward-word -1))
+                              (and (bolp)
+                                   (looking-at
+                                    "enum \\|struct \\|union \\|class ")
+                                   (setq middle (point)))
+                              (goto-char end)
+                              (when (eq (preceding-char) ?=)
+                                (forward-char -1)
+                                (skip-chars-backward " \t")
+                                (setq end (point)))
+                              (buffer-substring-no-properties
+                               middle end))))))))
                ((memq major-mode add-log-tex-like-modes)
                 (if (re-search-backward
                ((memq major-mode add-log-tex-like-modes)
                 (if (re-search-backward
-                     "\\\\\\(sub\\)*\\(section\\|paragraph\\|chapter\\)" nil t)
+                     "\\\\\\(sub\\)*\\(section\\|paragraph\\|chapter\\)"
+                     nil t)
                     (progn
                       (goto-char (match-beginning 0))
                     (progn
                       (goto-char (match-beginning 0))
-                      (buffer-substring (1+ (point));; without initial backslash
-                                        (progn
-                                          (end-of-line)
-                                          (point))))))
+                      (buffer-substring-no-properties
+                       (1+ (point))    ; without initial backslash
+                       (line-end-position)))))
                ((eq major-mode 'texinfo-mode)
                 (if (re-search-backward "^@node[ \t]+\\([^,\n]+\\)" nil t)
                ((eq major-mode 'texinfo-mode)
                 (if (re-search-backward "^@node[ \t]+\\([^,\n]+\\)" nil t)
-                    (buffer-substring (match-beginning 1)
-                                      (match-end 1))))
+                    (match-string-no-properties 1)))
                ((eq major-mode 'perl-mode)
                 (if (re-search-backward "^sub[ \t]+\\([^ \t\n]+\\)" nil t)
                ((eq major-mode 'perl-mode)
                 (if (re-search-backward "^sub[ \t]+\\([^ \t\n]+\\)" nil t)
-                    (buffer-substring (match-beginning 1)
-                                      (match-end 1))))
-                ((eq major-mode 'fortran-mode)
-                 ;; must be inside function body for this to work
-                 (beginning-of-fortran-subprogram)
-                 (let ((case-fold-search t)) ; case-insensitive
-                   ;; search for fortran subprogram start
-                   (if (re-search-forward
-                        "^[ \t]*\\(program\\|subroutine\\|function\
-\\|[ \ta-z0-9*]*[ \t]+function\\)"
-                        nil t)
-                       (progn
-                         ;; move to EOL or before first left paren
-                         (if (re-search-forward "[(\n]" nil t)
-                            (progn (forward-char -1)
-                                   (skip-chars-backward " \t"))
-                          (end-of-line))
-                        ;; Use the name preceding that.
-                         (buffer-substring (point)
-                                           (progn (forward-sexp -1)
-                                                  (point)))))))
+                    (match-string-no-properties 1)))
+               ;; Emacs's autoconf-mode installs its own
+               ;; `add-log-current-defun-function'.  This applies to
+               ;; a different mode apparently for editing .m4
+               ;; autoconf source.
+                ((eq major-mode 'autoconf-mode)
+                 (if (re-search-backward
+                     "^\\(\\(m4_\\)?define\\|A._DEFUN\\)(\\[?\\([A-Za-z0-9_]+\\)" nil t)
+                     (match-string-no-properties 3)))
                (t
                 ;; If all else fails, try heuristics
                (t
                 ;; If all else fails, try heuristics
-                (let (case-fold-search)
+                (let (case-fold-search
+                      result)
                   (end-of-line)
                   (end-of-line)
-                  (if (re-search-backward add-log-current-defun-header-regexp
-                                          (- (point) 10000)
-                                          t)
-                      (buffer-substring (match-beginning 1)
-                                        (match-end 1))))))))
+                  (when (re-search-backward
+                         add-log-current-defun-header-regexp
+                         (- (point) 10000)
+                         t)
+                    (setq result (or (match-string-no-properties 1)
+                                     (match-string-no-properties 0)))
+                    ;; Strip whitespace away
+                    (when (string-match "\\([^ \t\n\r\f].*[^ \t\n\r\f]\\)"
+                                        result)
+                      (setq result (match-string-no-properties 1 result)))
+                    result))))))
     (error nil)))
 
     (error nil)))
 
-(defvar get-method-definition-md)
+(defvar change-log-get-method-definition-md)
 
 
-;; Subroutine used within get-method-definition.
+;; Subroutine used within change-log-get-method-definition.
 ;; Add the last match in the buffer to the end of `md',
 ;; followed by the string END; move to the end of that match.
 ;; Add the last match in the buffer to the end of `md',
 ;; followed by the string END; move to the end of that match.
-(defun get-method-definition-1 (end)
-  (setq get-method-definition-md
-       (concat get-method-definition-md
-               (buffer-substring (match-beginning 1) (match-end 1))
+(defun change-log-get-method-definition-1 (end)
+  (setq change-log-get-method-definition-md
+       (concat change-log-get-method-definition-md
+               (match-string 1)
                end))
   (goto-char (match-end 0)))
 
                end))
   (goto-char (match-end 0)))
 
-;; For objective C, return the method name if we are in a method.
-(defun get-method-definition ()
-  (let ((get-method-definition-md "["))
+(defun change-log-get-method-definition ()
+"For objective C, return the method name if we are in a method."
+  (let ((change-log-get-method-definition-md "["))
     (save-excursion
       (if (re-search-backward "^@implementation\\s-*\\([A-Za-z_]*\\)" nil t)
     (save-excursion
       (if (re-search-backward "^@implementation\\s-*\\([A-Za-z_]*\\)" nil t)
-         (get-method-definition-1 " ")))
+         (change-log-get-method-definition-1 " ")))
     (save-excursion
       (cond
        ((re-search-forward "^\\([-+]\\)[ \t\n\f\r]*\\(([^)]*)\\)?\\s-*" nil t)
     (save-excursion
       (cond
        ((re-search-forward "^\\([-+]\\)[ \t\n\f\r]*\\(([^)]*)\\)?\\s-*" nil t)
-       (get-method-definition-1 "")
+       (change-log-get-method-definition-1 "")
        (while (not (looking-at "[{;]"))
          (looking-at
           "\\([A-Za-z_]*:?\\)\\s-*\\(([^)]*)\\)?[A-Za-z_]*[ \t\n\f\r]*")
        (while (not (looking-at "[{;]"))
          (looking-at
           "\\([A-Za-z_]*:?\\)\\s-*\\(([^)]*)\\)?[A-Za-z_]*[ \t\n\f\r]*")
-         (get-method-definition-1 ""))
-       (concat get-method-definition-md "]"))))))
+         (change-log-get-method-definition-1 ""))
+       (concat change-log-get-method-definition-md "]"))))))
+\f
+(defun change-log-sortable-date-at ()
+  "Return date of log entry in a consistent form for sorting.
+Point is assumed to be at the start of the entry."
+  (require 'timezone)
+  (if (looking-at "^\\sw.........[0-9:+ ]*")
+      (let ((date (match-string-no-properties 0)))
+       (if date
+           (if (string-match "\\(....\\)-\\(..\\)-\\(..\\)\\s-+" date)
+               (concat (match-string 1 date) (match-string 2 date)
+                       (match-string 3 date))
+             (condition-case nil
+                 (timezone-make-date-sortable date)
+               (error nil)))))
+    (error "Bad date")))
 
 
+;;;###autoload
+(defun change-log-merge (other-log)
+  "Merge the contents of ChangeLog file OTHER-LOG with this buffer.
+Both must be found in Change Log mode (since the merging depends on
+the appropriate motion commands).
+
+Entries are inserted in chronological order.
+
+Both the current and old-style time formats for entries are supported,
+so this command could be used to convert old-style logs by merging
+with an empty log."
+  (interactive "*fLog file name to merge: ")
+  (if (not (eq major-mode 'change-log-mode))
+      (error "Not in Change Log mode"))
+  (let ((other-buf (find-file-noselect other-log))
+       (buf (current-buffer))
+       date1 start end)
+    (save-excursion
+      (goto-char (point-min))
+      (set-buffer other-buf)
+      (goto-char (point-min))
+      (if (not (eq major-mode 'change-log-mode))
+         (error "%s not found in Change Log mode" other-log))
+      ;; Loop through all the entries in OTHER-LOG.
+      (while (not (eobp))
+       (setq date1 (change-log-sortable-date-at))
+       (setq start (point)
+             end (progn (forward-page) (point)))
+       ;; Look for an entry in original buffer that isn't later.
+       (with-current-buffer buf
+         (while (and (not (eobp))
+                     (string< date1 (change-log-sortable-date-at)))
+           (forward-page))
+         (if (not (eobp))
+             (insert-buffer-substring other-buf start end)
+           ;; At the end of the original buffer, insert a newline to
+           ;; separate entries and then the rest of the file being
+           ;; merged.  Move to the end of it to terminate outer loop.
+           (insert "\n")
+           (insert-buffer-substring other-buf start
+                                    (with-current-buffer other-buf
+                                      (goto-char (point-max))
+                                      (point)))))))))
 
 (provide 'add-log)
 
 
 (provide 'add-log)