Don't call turn_on_atimers around `connect' (Bug#5723).
[bpt/emacs.git] / lisp / add-log.el
index 33ecd98..ab99f67 100644 (file)
@@ -1,17 +1,18 @@
 ;;; add-log.el --- change log maintenance commands for Emacs
 
 ;; Copyright (C) 1985, 1986, 1988, 1993, 1994, 1997, 1998, 2000, 2001,
-;;   2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+;;   Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: tools
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; 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 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, 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
@@ -19,9 +20,7 @@
 ;; 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., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -54,6 +53,7 @@
   :type '(choice (const :tag "default" nil)
                 string)
   :group 'change-log)
+;;;###autoload
 (put 'change-log-default-name 'safe-local-variable 'string-or-null-p)
 
 (defcustom change-log-mode-hook nil
@@ -163,7 +163,7 @@ use the file's name relative to the directory of the change log file."
   :group 'change-log)
 
 (defcustom change-log-version-number-regexp-list
-  (let ((re    "\\([0-9]+\.[0-9.]+\\)"))
+  (let ((re "\\([0-9]+\.[0-9.]+\\)"))
     (list
      ;;  (defconst ad-version "2.15"
      (concat "^(def[^ \t\n]+[ \t]+[^ \t\n][ \t]\"" re)
@@ -181,67 +181,65 @@ Note: The search is conducted only within 10%, at the beginning of the file."
   "Face used to highlight dates in date lines."
   :version "21.1"
   :group 'change-log)
-;; backward-compatibility alias
-(put 'change-log-date-face 'face-alias 'change-log-date)
+(define-obsolete-face-alias 'change-log-date-face 'change-log-date "22.1")
 
 (defface change-log-name
   '((t (:inherit font-lock-constant-face)))
   "Face for highlighting author names."
   :version "21.1"
   :group 'change-log)
-;; backward-compatibility alias
-(put 'change-log-name-face 'face-alias 'change-log-name)
+(define-obsolete-face-alias 'change-log-name-face 'change-log-name "22.1")
 
 (defface change-log-email
   '((t (:inherit font-lock-variable-name-face)))
   "Face for highlighting author email addresses."
   :version "21.1"
   :group 'change-log)
-;; backward-compatibility alias
-(put 'change-log-email-face 'face-alias 'change-log-email)
+(define-obsolete-face-alias 'change-log-email-face 'change-log-email "22.1")
 
 (defface change-log-file
   '((t (:inherit font-lock-function-name-face)))
   "Face for highlighting file names."
   :version "21.1"
   :group 'change-log)
-;; backward-compatibility alias
-(put 'change-log-file-face 'face-alias 'change-log-file)
+(define-obsolete-face-alias 'change-log-file-face 'change-log-file "22.1")
 
 (defface change-log-list
   '((t (:inherit font-lock-keyword-face)))
   "Face for highlighting parenthesized lists of functions or variables."
   :version "21.1"
   :group 'change-log)
-;; backward-compatibility alias
-(put 'change-log-list-face 'face-alias 'change-log-list)
+(define-obsolete-face-alias 'change-log-list-face 'change-log-list "22.1")
 
 (defface change-log-conditionals
   '((t (:inherit font-lock-variable-name-face)))
   "Face for highlighting conditionals of the form `[...]'."
   :version "21.1"
   :group 'change-log)
-;; backward-compatibility alias
-(put 'change-log-conditionals-face 'face-alias 'change-log-conditionals)
+(define-obsolete-face-alias 'change-log-conditionals-face
+  'change-log-conditionals "22.1")
 
 (defface change-log-function
   '((t (:inherit font-lock-variable-name-face)))
   "Face for highlighting items of the form `<....>'."
   :version "21.1"
   :group 'change-log)
-;; backward-compatibility alias
-(put 'change-log-function-face 'face-alias 'change-log-function)
+(define-obsolete-face-alias 'change-log-function-face
+  'change-log-function "22.1")
 
 (defface change-log-acknowledgement
   '((t (:inherit font-lock-comment-face)))
   "Face for highlighting acknowledgments."
   :version "21.1"
   :group 'change-log)
-;; backward-compatibility alias
-(put 'change-log-acknowledgement-face 'face-alias 'change-log-acknowledgement)
+(define-obsolete-face-alias 'change-log-acknowledgement-face
+  'change-log-acknowledgement "22.1")
+
+(defconst change-log-file-names-re "^\\( +\\|\t\\)\\* \\([^ ,:([\n]+\\)")
+(defconst change-log-start-entry-re "^\\sw.........[0-9:+ ]*")
 
 (defvar change-log-font-lock-keywords
-  '(;;
+  `(;;
     ;; Date lines, new (2000-01-01) and old (Sat Jan  1 00:00:00 2000) styles.
     ;; Fixme: this regepx is just an approximate one and may match
     ;; wrongly with a non-date line existing as a random note.  In
@@ -255,7 +253,7 @@ Note: The search is conducted only within 10%, at the beginning of the file."
       (2 'change-log-email)))
     ;;
     ;; File names.
-    ("^\\( +\\|\t\\)\\* \\([^ ,:([\n]+\\)"
+    (,change-log-file-names-re
      (2 'change-log-file)
      ;; Possibly further names in a list:
      ("\\=, \\([^ ,:([\n]+\\)" nil nil (1 'change-log-file))
@@ -287,10 +285,291 @@ Note: The search is conducted only within 10%, at the beginning of the file."
      3 'change-log-acknowledgement))
   "Additional expressions to highlight in Change Log mode.")
 
+(defun change-log-search-file-name (where)
+  "Return the file-name for the change under point."
+  (save-excursion
+    (goto-char where)
+    (beginning-of-line 1)
+    (if (looking-at change-log-start-entry-re)
+       ;; We are at the start of an entry, search forward for a file
+       ;; name.
+       (progn
+         (re-search-forward change-log-file-names-re nil t)
+         (match-string-no-properties 2))
+      (if (looking-at change-log-file-names-re)
+         ;; We found a file name.
+         (match-string-no-properties 2)
+       ;; Look backwards for either a file name or the log entry start.
+       (if (re-search-backward
+            (concat "\\(" change-log-start-entry-re
+                    "\\)\\|\\("
+                    change-log-file-names-re "\\)") nil t)
+           (if (match-beginning 1)
+               ;; We got the start of the entry, look forward for a
+               ;; file name.
+               (progn
+                 (re-search-forward change-log-file-names-re nil t)
+                 (match-string-no-properties 2))
+             (match-string-no-properties 4))
+         ;; We must be before any file name, look forward.
+         (re-search-forward change-log-file-names-re nil t)
+         (match-string-no-properties 2))))))
+
+(defun change-log-find-file ()
+  "Visit the file for the change under point."
+  (interactive)
+  (let ((file (change-log-search-file-name (point))))
+    (if (and file (file-exists-p file))
+       (find-file file)
+      (message "No such file or directory: %s" file))))
+
+(defun change-log-search-tag-name-1 (&optional from)
+  "Search for a tag name within subexpression 1 of last match.
+Optional argument FROM specifies a buffer position where the tag
+name should be located.  Return value is a cons whose car is the
+string representing the tag and whose cdr is the position where
+the tag was found."
+  (save-restriction
+    (narrow-to-region (match-beginning 1) (match-end 1))
+    (when from (goto-char from))
+    ;; The regexp below skips any symbol near `point' (FROM) followed by
+    ;; whitespace and another symbol.  This should skip, for example,
+    ;; "struct" in a specification like "(struct buffer)" and move to
+    ;; "buffer".  A leading paren is ignored.
+    (when (looking-at
+          "[(]?\\(?:\\(?:\\sw\\|\\s_\\)+\\(?:[ \t]+\\(\\sw\\|\\s_\\)+\\)\\)")
+      (goto-char (match-beginning 1)))
+    (cons (find-tag-default) (point))))
+
+(defconst change-log-tag-re
+  "(\\(\\(?:\\sw\\|\\s_\\)+\\(?:[, \t]+\\(?:\\sw\\|\\s_\\)+\\)*\\))"
+  "Regexp matching a tag name in change log entries.")
+
+(defun change-log-search-tag-name (&optional at)
+  "Search for a tag name near `point'.
+Optional argument AT non-nil means search near buffer position AT.
+Return value is a cons whose car is the string representing
+the tag and whose cdr is the position where the tag was found."
+  (save-excursion
+    (goto-char (setq at (or at (point))))
+    (save-restriction
+      (widen)
+      (or (condition-case nil
+             ;; Within parenthesized list?
+             (save-excursion
+               (backward-up-list)
+               (when (looking-at change-log-tag-re)
+                 (change-log-search-tag-name-1 at)))
+           (error nil))
+         (condition-case nil
+             ;; Before parenthesized list on same line?
+             (save-excursion
+               (when (and (skip-chars-forward " \t")
+                          (looking-at change-log-tag-re))
+                 (change-log-search-tag-name-1)))
+           (error nil))
+         (condition-case nil
+             ;; Near file name?
+             (save-excursion
+               (when (and (progn
+                            (beginning-of-line)
+                            (looking-at change-log-file-names-re))
+                          (goto-char (match-end 0))
+                          (skip-syntax-forward " ")
+                          (looking-at change-log-tag-re))
+                 (change-log-search-tag-name-1)))
+           (error nil))
+         (condition-case nil
+             ;; Anywhere else within current entry?
+             (let ((from
+                    (save-excursion
+                      (end-of-line)
+                      (if (re-search-backward change-log-start-entry-re nil t)
+                          (match-beginning 0)
+                        (point-min))))
+                   (to
+                    (save-excursion
+                      (end-of-line)
+                      (if (re-search-forward change-log-start-entry-re nil t)
+                          (match-beginning 0)
+                        (point-max)))))
+               (when (and (< from to) (<= from at) (<= at to))
+                 (save-restriction
+                   ;; Narrow to current change log entry.
+                   (narrow-to-region from to)
+                   (cond
+                    ((re-search-backward change-log-tag-re nil t)
+                     (narrow-to-region (match-beginning 1) (match-end 1))
+                     (goto-char (point-max))
+                     (cons (find-tag-default) (point-max)))
+                    ((re-search-forward change-log-tag-re nil t)
+                     (narrow-to-region (match-beginning 1) (match-end 1))
+                     (goto-char (point-min))
+                     (cons (find-tag-default) (point-min)))))))
+           (error nil))))))
+
+(defvar change-log-find-head nil)
+(defvar change-log-find-tail nil)
+(defvar change-log-find-window nil)
+
+(defun change-log-goto-source-1 (tag regexp file buffer
+                                    &optional window first last)
+  "Search for tag TAG in buffer BUFFER visiting file FILE.
+REGEXP is a regular expression for TAG.  The remaining arguments
+are optional: WINDOW denotes the window to display the results of
+the search.  FIRST is a position in BUFFER denoting the first
+match from previous searches for TAG.  LAST is the position in
+BUFFER denoting the last match for TAG in the last search."
+  (with-current-buffer buffer
+    (save-excursion
+      (save-restriction
+       (widen)
+       (if last
+           (progn
+             ;; When LAST is set make sure we continue from the next
+             ;; line end to not find the same tag again.
+             (goto-char last)
+             (end-of-line)
+             (condition-case nil
+                 ;; Try to go to the end of the current defun to avoid
+                 ;; false positives within the current defun's body
+                 ;; since these would match `add-log-current-defun'.
+                 (end-of-defun)
+               ;; Don't fall behind when `end-of-defun' fails.
+               (error (progn (goto-char last) (end-of-line))))
+             (setq last nil))
+         ;; When LAST was not set start at beginning of BUFFER.
+         (goto-char (point-min)))
+       (let (current-defun)
+         (while (and (not last) (re-search-forward regexp nil t))
+             ;; Verify that `add-log-current-defun' invoked at the end
+             ;; of the match returns TAG.  This heuristic works well
+             ;; whenever the name of the defun occurs within the first
+             ;; line of the defun.
+             (setq current-defun (add-log-current-defun))
+             (when (and current-defun (string-equal current-defun tag))
+               ;; Record this as last match.
+               (setq last (line-beginning-position))
+               ;; Record this as first match when there's none.
+               (unless first (setq first last)))))))
+    (if (or last first)
+       (with-selected-window
+           (setq change-log-find-window (or window (display-buffer buffer)))
+         (if last
+             (progn
+               (when (or (< last (point-min)) (> last (point-max)))
+                 ;; Widen to show TAG.
+                 (widen))
+               (push-mark)
+               (goto-char last))
+           ;; When there are no more matches go (back) to FIRST.
+           (message "No more matches for tag `%s' in file `%s'" tag file)
+           (setq last first)
+           (goto-char first))
+         ;; Return new "tail".
+         (list (selected-window) first last))
+      (message "Source location of tag `%s' not found in file `%s'" tag file)
+      nil)))
+
+(defun change-log-goto-source ()
+  "Go to source location of \"change log tag\" near `point'.
+A change log tag is a symbol within a parenthesized,
+comma-separated list.  If no suitable tag can be found nearby,
+try to visit the file for the change under `point' instead."
+  (interactive)
+  (if (and (eq last-command 'change-log-goto-source)
+          change-log-find-tail)
+      (setq change-log-find-tail
+           (condition-case nil
+               (apply 'change-log-goto-source-1
+                      (append change-log-find-head change-log-find-tail))
+             (error
+              (format "Cannot find more matches for tag `%s' in file `%s'"
+                      (car change-log-find-head)
+                      (nth 2 change-log-find-head)))))
+    (save-excursion
+      (let* ((at (point))
+            (tag-at (change-log-search-tag-name))
+            (tag (car tag-at))
+            (file (when tag-at (change-log-search-file-name (cdr tag-at))))
+            (file-at (when file (match-beginning 2)))
+            ;; `file-2' is the file `change-log-search-file-name' finds
+            ;; at `point'.  We use `file-2' as a fallback when `tag' or
+            ;; `file' are not suitable for some reason.
+            (file-2 (change-log-search-file-name at))
+            (file-2-at (when file-2 (match-beginning 2))))
+       (cond
+        ((and (or (not tag) (not file) (not (file-exists-p file)))
+              (or (not file-2) (not (file-exists-p file-2))))
+         (error "Cannot find tag or file near `point'"))
+        ((and file-2 (file-exists-p file-2)
+              (or (not tag) (not file) (not (file-exists-p file))
+                  (and (or (and (< file-at file-2-at) (<= file-2-at at))
+                           (and (<= at file-2-at) (< file-2-at file-at))))))
+         ;; We either have not found a suitable file name or `file-2'
+         ;; provides a "better" file name wrt `point'.  Go to the
+         ;; buffer of `file-2' instead.
+         (setq change-log-find-window
+               (display-buffer (find-file-noselect file-2))))
+        (t
+         (setq change-log-find-head
+               (list tag (concat "\\_<" (regexp-quote tag) "\\_>")
+                     file (find-file-noselect file)))
+         (condition-case nil
+             (setq change-log-find-tail
+                   (apply 'change-log-goto-source-1 change-log-find-head))
+           (error
+            (format "Cannot find matches for tag `%s' in file `%s'"
+                    tag file)))))))))
+
+(defun change-log-next-error (&optional argp reset)
+  "Move to the Nth (default 1) next match in a ChangeLog buffer.
+Compatibility function for \\[next-error] invocations."
+  (interactive "p")
+  (let* ((argp (or argp 0))
+        (count (abs argp))             ; how many cycles
+        (down (< argp 0))              ; are we going down? (is argp negative?)
+        (up (not down))
+        (search-function (if up 're-search-forward 're-search-backward)))
+
+    ;; set the starting position
+    (goto-char (cond (reset (point-min))
+                    (down (line-beginning-position))
+                    (up (line-end-position))
+                    ((point))))
+
+    (funcall search-function change-log-file-names-re nil t count))
+
+  (beginning-of-line)
+  ;; if we found a place to visit...
+  (when (looking-at change-log-file-names-re)
+    (let (change-log-find-window)
+      (change-log-goto-source)
+      (when change-log-find-window
+       ;; Select window displaying source file.
+       (select-window change-log-find-window)))))
+
 (defvar change-log-mode-map
-  (let ((map (make-sparse-keymap)))
+  (let ((map (make-sparse-keymap))
+       (menu-map (make-sparse-keymap)))
     (define-key map [?\C-c ?\C-p] 'add-log-edit-prev-comment)
     (define-key map [?\C-c ?\C-n] 'add-log-edit-next-comment)
+    (define-key map [?\C-c ?\C-f] 'change-log-find-file)
+    (define-key map [?\C-c ?\C-c] 'change-log-goto-source)
+    (define-key map [menu-bar changelog] (cons "ChangeLog" menu-map))
+    (define-key menu-map [gs]
+      '(menu-item "Go To Source" change-log-goto-source
+                 :help "Go to source location of ChangeLog tag near point"))
+    (define-key menu-map [ff]
+      '(menu-item "Find File" change-log-find-file
+                 :help "Visit the file for the change under point"))
+    (define-key menu-map [sep] '("--"))
+    (define-key menu-map [nx]
+      '(menu-item "Next Log-Edit Comment" add-log-edit-next-comment
+                 :help "Cycle forward through Log-Edit mode comment history"))
+    (define-key menu-map [pr]
+      '(menu-item "Previous Log-Edit Comment" add-log-edit-prev-comment
+                 :help "Cycle backward through Log-Edit mode comment history"))
     map)
   "Keymap for Change Log major mode.")
 
@@ -330,9 +609,7 @@ If t, use universal time.")
 (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"
-       "ChangeLog")))
+      "ChangeLog"))
 
 (defun add-log-edit-prev-comment (arg)
   "Cycle backward through Log-Edit mode comment history.
@@ -403,6 +680,8 @@ nil, by matching `change-log-version-number-regexp-list'."
                        regexps nil))))
            version)))))
 
+(declare-function diff-find-source-location "diff-mode"
+                 (&optional other-file reverse noprompt))
 
 ;;;###autoload
 (defun find-change-log (&optional file-name buffer-file)
@@ -420,47 +699,54 @@ directory and its successive parents for a file so named.
 Once a file is found, `change-log-default-name' is set locally in the
 current buffer to the complete file name.
 Optional arg BUFFER-FILE overrides `buffer-file-name'."
-  ;; If user specified a file name or if this buffer knows which one to use,
-  ;; just use that.
-  (or file-name
-      (setq file-name (and change-log-default-name
-                          (file-name-directory change-log-default-name)
-                          change-log-default-name))
-      (progn
-       ;; Chase links in the source file
-       ;; and use the change log in the dir where it points.
-       (setq file-name (or (and (or buffer-file buffer-file-name)
-                                (file-name-directory
-                                 (file-chase-links
-                                  (or buffer-file buffer-file-name))))
-                           default-directory))
-       (if (file-directory-p file-name)
-           (setq file-name (expand-file-name (change-log-name) file-name)))
-       ;; Chase links before visiting the file.
-       ;; This makes it easier to use a single change log file
-       ;; for several related directories.
-       (setq file-name (file-chase-links file-name))
-       (setq file-name (expand-file-name file-name))
-       ;; Move up in the dir hierarchy till we find a change log file.
-       (let ((file1 file-name)
-             parent-dir)
-         (while (and (not (or (get-file-buffer file1) (file-exists-p file1)))
-                     (progn (setq parent-dir
+  ;; If we are called from a diff, first switch to the source buffer;
+  ;; in order to respect buffer-local settings of change-log-default-name, etc.
+  (with-current-buffer (let ((buff (if (eq major-mode 'diff-mode)
+                                      (car (ignore-errors
+                                            (diff-find-source-location))))))
+                        (if (buffer-live-p buff) buff
+                          (current-buffer)))
+      ;; If user specified a file name or if this buffer knows which one to use,
+      ;; just use that.
+    (or file-name
+       (setq file-name (and change-log-default-name
+                            (file-name-directory change-log-default-name)
+                            change-log-default-name))
+       (progn
+         ;; Chase links in the source file
+         ;; and use the change log in the dir where it points.
+         (setq file-name (or (and (or buffer-file buffer-file-name)
                                   (file-name-directory
-                                   (directory-file-name
-                                    (file-name-directory file1))))
-                            ;; Give up if we are already at the root dir.
-                            (not (string= (file-name-directory file1)
-                                          parent-dir))))
-           ;; Move up to the parent dir and try again.
-           (setq file1 (expand-file-name
-                        (file-name-nondirectory (change-log-name))
-                        parent-dir)))
-         ;; If we found a change log in a parent, use that.
-         (if (or (get-file-buffer file1) (file-exists-p file1))
-             (setq file-name file1)))))
-  ;; Make a local variable in this buffer so we needn't search again.
-  (set (make-local-variable 'change-log-default-name) file-name)
+                                   (file-chase-links
+                                    (or buffer-file buffer-file-name))))
+                             default-directory))
+         (if (file-directory-p file-name)
+             (setq file-name (expand-file-name (change-log-name) file-name)))
+         ;; Chase links before visiting the file.
+         ;; This makes it easier to use a single change log file
+         ;; for several related directories.
+         (setq file-name (file-chase-links file-name))
+         (setq file-name (expand-file-name file-name))
+         ;; Move up in the dir hierarchy till we find a change log file.
+         (let ((file1 file-name)
+               parent-dir)
+           (while (and (not (or (get-file-buffer file1) (file-exists-p file1)))
+                       (progn (setq parent-dir
+                                    (file-name-directory
+                                     (directory-file-name
+                                      (file-name-directory file1))))
+                              ;; Give up if we are already at the root dir.
+                              (not (string= (file-name-directory file1)
+                                            parent-dir))))
+             ;; Move up to the parent dir and try again.
+             (setq file1 (expand-file-name
+                          (file-name-nondirectory (change-log-name))
+                          parent-dir)))
+           ;; If we found a change log in a parent, use that.
+           (if (or (get-file-buffer file1) (file-exists-p file1))
+               (setq file-name file1)))))
+    ;; Make a local variable in this buffer so we needn't search again.
+    (set (make-local-variable 'change-log-default-name) file-name))
   file-name)
 
 (defun add-log-file-name (buffer-file log-file)
@@ -478,7 +764,8 @@ Optional arg BUFFER-FILE overrides `buffer-file-name'."
        buffer-file))))
 
 ;;;###autoload
-(defun add-change-log-entry (&optional whoami file-name other-window new-entry)
+(defun add-change-log-entry (&optional whoami file-name other-window new-entry
+                                      put-new-entry-on-new-line)
   "Find change log file, and add an entry for today and an item for this file.
 Optional arg WHOAMI (interactive prefix) non-nil means prompt for user
 name and email (stored in `add-log-full-name' and `add-log-mailing-address').
@@ -492,6 +779,10 @@ Fourth arg NEW-ENTRY non-nil means always create a new entry at the front;
 never append to an existing entry.  Option `add-log-keep-changes-together'
 otherwise affects whether a new entry is created.
 
+Fifth arg PUT-NEW-ENTRY-ON-NEW-LINE non-nil means that if a new
+entry is created, put it on a new line by itself, do not put it
+after a comma on an existing line.
+
 Option `add-log-always-start-new-record' non-nil means always create a
 new record, even when the last record was made on the same date and by
 the same person.
@@ -516,9 +807,15 @@ non-nil, otherwise in local time."
         (item (add-log-file-name buffer-file file-name)))
 
     (unless (equal file-name buffer-file-name)
-      (if (or other-window (window-dedicated-p (selected-window)))
-         (find-file-other-window file-name)
-       (find-file file-name)))
+      (cond
+       ((equal file-name (buffer-file-name (window-buffer (selected-window))))
+        ;; If the selected window already shows the desired buffer don't show
+        ;; it again (particularly important if other-window is true).
+        ;; This is important for diff-add-change-log-entries-other-window.
+        (set-buffer (window-buffer (selected-window))))
+       ((or other-window (window-dedicated-p (selected-window)))
+        (find-file-other-window file-name))
+       (t (find-file file-name))))
     (or (derived-mode-p 'change-log-mode)
        (change-log-mode))
     (undo-boundary)
@@ -639,7 +936,8 @@ non-nil, otherwise in local time."
       (let ((pos (point-marker)))
        (skip-syntax-backward " ")
        (skip-chars-backward "):")
-       (if (and (looking-at "):")
+       (if (and (not put-new-entry-on-new-line)
+                (looking-at "):")
                 (let ((pos (save-excursion (backward-sexp 1) (point))))
                   (when (equal (buffer-substring pos (point)) defun)
                     (delete-region pos (point)))
@@ -647,8 +945,8 @@ non-nil, otherwise in local time."
            (progn (skip-chars-backward ", ")
                   (delete-region (point) pos)
                   (unless (memq (char-before) '(?\()) (insert ", ")))
-         (if (looking-at "):")
-             (delete-region (+ 1 (point)) (line-end-position)))
+         (when (and (not put-new-entry-on-new-line) (looking-at "):"))
+           (delete-region (+ 1 (point)) (line-end-position)))
          (goto-char pos)
          (insert "("))
        (set-marker pos nil))
@@ -722,29 +1020,33 @@ the change log file in another window."
 
 
 (defvar smerge-resolve-function)
+(defvar copyright-at-end-flag)
 
 ;;;###autoload
 (define-derived-mode change-log-mode text-mode "Change Log"
-  "Major mode for editing change logs; like Indented Text Mode.
+  "Major mode for editing change logs; like Indented Text mode.
 Prevents numeric backups and sets `left-margin' to 8 and `fill-column' to 74.
 New log entries are usually made with \\[add-change-log-entry] or \\[add-change-log-entry-other-window].
 Each entry behaves as a paragraph, and the entries for one day as a page.
 Runs `change-log-mode-hook'.
-\\{change-log-mode-map}"
+\n\\{change-log-mode-map}"
   (setq left-margin 8
        fill-column 74
        indent-tabs-mode t
        tab-width 8
        show-trailing-whitespace t)
-  (set (make-local-variable 'fill-paragraph-function)
-       'change-log-fill-paragraph)
+  (set (make-local-variable 'fill-forward-paragraph-function)
+       'change-log-fill-forward-paragraph)
+  ;; Make sure we call `change-log-indent' when filling.
+  (set (make-local-variable 'fill-indent-according-to-mode) t)
   ;; Avoid that filling leaves behind a single "*" on a line.
   (add-hook 'fill-nobreak-predicate
            '(lambda ()
-              (looking-back "^\\s *\\*\\s *" (line-beginning-position))) 
+              (looking-back "^\\s *\\*\\s *" (line-beginning-position)))
            nil t)
   (set (make-local-variable 'indent-line-function) 'change-log-indent)
   (set (make-local-variable 'tab-always-indent) nil)
+  (set (make-local-variable 'copyright-at-end-flag) t)
   ;; 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
@@ -760,13 +1062,15 @@ Runs `change-log-mode-hook'.
   (set (make-local-variable 'adaptive-fill-regexp) "\\s *")
   (set (make-local-variable 'font-lock-defaults)
        '(change-log-font-lock-keywords t nil nil backward-paragraph))
-  (set (make-local-variable 'isearch-buffers-next-buffer-function)
+  (set (make-local-variable 'multi-isearch-next-buffer-function)
        'change-log-next-buffer)
-  (set (make-local-variable 'beginning-of-defun-function) 
+  (set (make-local-variable 'beginning-of-defun-function)
        'change-log-beginning-of-defun)
-  (set (make-local-variable 'end-of-defun-function) 
+  (set (make-local-variable 'end-of-defun-function)
        'change-log-end-of-defun)
-  (isearch-buffers-minor-mode))
+  ;; next-error function glue
+  (setq next-error-function 'change-log-next-error)
+  (setq next-error-last-buffer (current-buffer)))
 
 (defun change-log-next-buffer (&optional buffer wrap)
   "Return the next buffer in the series of ChangeLog file buffers.
@@ -778,8 +1082,11 @@ file were isearch was started."
         (files (cons name (sort (file-expand-wildcards
                                  (concat name "[-.][0-9]*"))
                                 (lambda (a b)
-                                  (version< (substring b (length name))
-                                            (substring a (length name)))))))
+                                   ;; The file's extension may not have a valid
+                                   ;; version form (e.g. VC backup revisions).
+                                   (ignore-errors
+                                     (version< (substring b (length name))
+                                               (substring a (length name))))))))
         (files (if isearch-forward files (reverse files))))
     (find-file-noselect
      (if wrap
@@ -787,27 +1094,18 @@ file were isearch was started."
        (cadr (member (file-name-nondirectory (buffer-file-name buffer))
                     files))))))
 
-;; It might be nice to have a general feature to replace this.  The idea I
-;; have is a variable giving a regexp matching text which should not be
-;; moved from bol by filling.  change-log-mode would set this to "^\\s *\\s(".
-;; But I don't feel up to implementing that today.
-(defun change-log-fill-paragraph (&optional justify)
-  "Fill the paragraph, but preserve open parentheses at beginning of lines.
-Prefix arg means justify as well."
-  (interactive "P")
-  (let ((end (progn (forward-paragraph) (point)))
-       (beg (progn (backward-paragraph) (point)))
-       ;; Add lines starting with whitespace followed by a left paren or an
+(defun change-log-fill-forward-paragraph (n)
+  "Cut paragraphs so filling preserves open parentheses at beginning of lines."
+  (let (;; Add lines starting with whitespace followed by a left paren or an
        ;; asterisk.
-       (paragraph-start (concat paragraph-start "\\|\\s *\\(?:\\s(\\|\\*\\)"))
-       ;; Make sure we call `change-log-indent'.
-       (fill-indent-according-to-mode t))
-    (fill-region beg end justify)
-    t))
+       (paragraph-start (concat paragraph-start "\\|\\s *\\(?:\\s(\\|\\*\\)")))
+    (forward-paragraph n)))
 \f
 (defcustom add-log-current-defun-header-regexp
   "^\\([[:upper:]][[:upper:]_ ]*[[:upper:]_]\\|[-_[:alpha:]]+\\)[ \t]*[:=]"
-  "Heuristic regexp used by `add-log-current-defun' for unknown major modes."
+  "Heuristic regexp used by `add-log-current-defun' for unknown major modes.
+The regexp's first submatch is placed in the ChangeLog entry, in
+parentheses."
   :type 'regexp
   :group 'change-log)
 
@@ -826,8 +1124,8 @@ Prefix arg means justify as well."
   '(TeX-mode plain-TeX-mode LaTeX-mode tex-mode)
   "*Modes that look like TeX to `add-log-current-defun'.")
 
-(declare-function c-beginning-of-defun "cc-cmds" (&optional arg))
-(declare-function c-end-of-defun "cc-cmds" (&optional arg))
+(declare-function c-cpp-define-name "cc-cmds" ())
+(declare-function c-defun-name      "cc-cmds" ())
 
 ;;;###autoload
 (defun add-log-current-defun ()
@@ -872,167 +1170,10 @@ Has a preference of looking backwards."
                   (buffer-substring-no-properties (point)
                                                   (progn (forward-sexp 1)
                                                          (point)))))
-               ((and (apply 'derived-mode-p add-log-c-like-modes)
-                     (save-excursion
-                       (beginning-of-line)
-                       ;; Use eq instead of = here to avoid
-                       ;; error when at bob and char-after
-                       ;; returns nil.
-                       (while (eq (char-after (- (point) 2)) ?\\)
-                         (forward-line -1))
-                       (looking-at "[ \t]*#[ \t]*define[ \t]")))
-                ;; Handle a C macro definition.
-                (beginning-of-line)
-                (while (eq (char-after (- (point) 2)) ?\\) ;not =; note above
-                  (forward-line -1))
-                (search-forward "define")
-                (skip-chars-forward " \t")
-                (buffer-substring-no-properties (point)
-                                                (progn (forward-sexp 1)
-                                                       (point))))
                ((apply 'derived-mode-p add-log-c-like-modes)
-                ;; See whether the point is inside a defun.
-                (let (having-previous-defun
-                      having-next-defun
-                      previous-defun-end
-                      next-defun-beginning)
-                    
-                  (save-excursion
-                    (setq having-previous-defun
-                          (c-beginning-of-defun))
-                    (c-end-of-defun)
-                    ;; `c-end-of-defun' moves point to the line after
-                    ;; the function close, but the position we prefer
-                    ;; here is the position after the final }.
-                    (backward-sexp 1)
-                    (forward-sexp 1)
-                     ;; Skip the semicolon ``;'' for
-                    ;; enum/union/struct/class definition.
-                    (if (= (char-after (point)) ?\;)
-                        (forward-char 1))
-                    (setq previous-defun-end (point)))
-
-                  (save-excursion
-                    (setq having-next-defun
-                          (c-end-of-defun))
-                    (c-beginning-of-defun)
-                    (setq next-defun-beginning (point)))
-
-                  (if (and having-next-defun
-                           (< location next-defun-beginning))
-                      (skip-syntax-forward " "))
-                  (if (and having-previous-defun
-                           (> location previous-defun-end))
-                      (skip-syntax-backward " "))
-                  (unless (or
-                           ;; When there is no previous defun, the
-                           ;; point is not in a defun if it is not at
-                           ;; the beginning of the next defun.
-                           (and (not having-previous-defun)
-                                (not (= (point)
-                                        next-defun-beginning)))
-                           ;; When there is no next defun, the point
-                           ;; is not in a defun if it is not at the
-                           ;; end of the previous defun.
-                           (and (not having-next-defun)
-                                (not (= (point)
-                                        previous-defun-end)))
-                           ;; If the point is between two defuns, it
-                           ;; is not in a defun.
-                           (and (> (point) previous-defun-end)
-                                (< (point) next-defun-beginning)))
-                    ;; If the point is already at the beginning of a
-                    ;; defun, there is no need to move point again.
-                    (if (not (= (point) next-defun-beginning))
-                        (c-beginning-of-defun))
-                    ;; Is this a DEFUN construct?  And is LOCATION in it?
-                    (if (and (looking-at "DEFUN\\b")
-                             (>= location (point)))
-                         ;; DEFUN ("file-name-directory", Ffile_name_directory, Sfile_name_directory, ...) ==> Ffile_name_directory
-                         ;; DEFUN(POSIX::STREAM-LOCK, stream lockp &key BLOCK SHARED START LENGTH) ==> POSIX::STREAM-LOCK
-                        (progn
-                          (down-list 1)
-                          (when (= (char-after (point)) ?\")
-                             (forward-sexp 1)
-                             (search-forward ","))
-                           (skip-syntax-forward " ")
-                          (buffer-substring-no-properties
-                           (point)
-                           (progn (search-forward ",")
-                                   (forward-char -1)
-                                   (skip-syntax-backward " ")
-                                  (point))))
-                      (if (looking-at "^[+-]")
-                          ;; Objective-C
-                          (change-log-get-method-definition)
-                        ;; Ordinary C function syntax.
-                        (let ((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))
-                                ;; We tried calling `forward-sexp' in a loop
-                                ;; but it causes inconsistency for C names.
-                                (forward-sexp -1)
-                                ;; Is this C++ method?
-                                (when (and (< 2 middle)
-                                           (string= (buffer-substring (- middle 2)
-                                                                      middle)
-                                                    "::"))
-                                  ;; Include "classname::".
-                                  (setq middle (point)))
-                                ;; 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)))))))))
-               ((apply 'derived-mode-p add-log-tex-like-modes)
+                (or (c-cpp-define-name)
+                    (c-defun-name)))
+               ((memq major-mode add-log-tex-like-modes)
                 (if (re-search-backward
                      "\\\\\\(sub\\)*\\(section\\|paragraph\\|chapter\\)"
                      nil t)
@@ -1101,8 +1242,6 @@ Has a preference of looking backwards."
          (change-log-get-method-definition-1 ""))
        (concat change-log-get-method-definition-md "]"))))))
 \f
-(defconst change-log-start-entry-re "^\\sw.........[0-9:+ ]*")
-
 (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."