New syntax-propertize functionality.
[bpt/emacs.git] / lisp / emacs-lisp / copyright.el
index b53ec9c..43eb61b 100644 (file)
@@ -1,17 +1,17 @@
 ;;; copyright.el --- update the copyright notice in current buffer
 
 ;; Copyright (C) 1991, 1992, 1993, 1994, 1995, 1998, 2001, 2002, 2003,
-;;   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Author: Daniel Pfeiffer <occitan@esperanto.org>
 ;; Keywords: maint, 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 +19,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:
 
@@ -43,12 +41,18 @@ A value of nil means to search whole buffer."
   :type '(choice (integer :tag "Limit")
                 (const :tag "No limit")))
 
-;; The character classes have the Latin-1 version and the Latin-9
-;; version, which is probably enough.
+(defcustom copyright-at-end-flag nil
+  "Non-nil means to search backwards from the end of the buffer for copyright.
+This is useful for ChangeLogs."
+  :group 'copyright
+  :type 'boolean
+  :version "23.1")
+
 (defcustom copyright-regexp
- "\\([\81©\81©]\\|@copyright{}\\|[Cc]opyright\\s *:?\\s *\\(?:(C)\\)?\
-\\|[Cc]opyright\\s *:?\\s *[\81©\81©]\\)\
-\\s *\\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)"
+ "\\(©\\|@copyright{}\\|[Cc]opyright\\s *:?\\s *\\(?:(C)\\)?\
+\\|[Cc]opyright\\s *:?\\s *©\\)\
+\\s *\\(?:[^0-9\n]*\\s *\\)?\
+\\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)"
   "What your copyright notice looks like.
 The second \\( \\) construct must match the years."
   :group 'copyright
@@ -83,7 +87,8 @@ When this is `function', only ask when called non-interactively."
 (defconst copyright-current-gpl-version "3"
   "String representing the current version of the GPL or nil.")
 
-(defvar copyright-update t)
+(defvar copyright-update t
+  "The function `copyright-update' sets this to nil after updating a buffer.")
 
 ;; This is a defvar rather than a defconst, because the year can
 ;; change during the Emacs session.
@@ -91,22 +96,46 @@ When this is `function', only ask when called non-interactively."
   "String representing the current year.")
 
 (defsubst copyright-limit ()            ; re-search-forward BOUND
-  (and copyright-limit (+ (point) copyright-limit)))
+  (and copyright-limit
+       (if copyright-at-end-flag
+          (- (point) copyright-limit)
+        (+ (point) copyright-limit))))
+
+(defun copyright-re-search (regexp &optional bound noerror count)
+  "Re-search forward or backward depending on `copyright-at-end-flag'."
+  (if copyright-at-end-flag
+      (re-search-backward regexp bound noerror count)
+    (re-search-forward regexp bound noerror count)))
+
+(defun copyright-start-point ()
+  "Return point-min or point-max, depending on `copyright-at-end-flag'."
+  (if copyright-at-end-flag
+      (point-max)
+    (point-min)))
+
+(defun copyright-offset-too-large-p ()
+  "Return non-nil if point is too far from the edge of the buffer."
+  (when copyright-limit
+    (if copyright-at-end-flag
+       (< (point) (- (point-max) copyright-limit))
+      (> (point) (+ (point-min) copyright-limit)))))
 
 (defun copyright-update-year (replace noquery)
   (when
       (condition-case err
-         (re-search-forward (concat "\\(" copyright-regexp
-                                    "\\)\\([ \t]*\n\\)?.*\\(?:"
-                                    copyright-names-regexp "\\)")
-                            (copyright-limit)
-                            t)
+         ;; (1) Need the extra \\( \\) around copyright-regexp because we
+         ;; goto (match-end 1) below. See note (2) below.
+         (copyright-re-search (concat "\\(" copyright-regexp
+                                      "\\)\\([ \t]*\n\\)?.*\\(?:"
+                                      copyright-names-regexp "\\)")
+                              (copyright-limit)
+                              t)
        ;; In case the regexp is rejected.  This is useful because
        ;; copyright-update is typically called from before-save-hook where
        ;; such an error is very inconvenient for the user.
        (error (message "Can't update copyright: %s" err) nil))
     (goto-char (match-end 1))
-    ;; If the years are continued onto multiple lined
+    ;; If the years are continued onto multiple lines
     ;; that are marked as comments, skip to the end of the years anyway.
     (while (save-excursion
             (and (eq (following-char) ?,)
@@ -117,24 +146,29 @@ When this is `function', only ask when called non-interactively."
                    (forward-line 1)
                    (and (looking-at comment-start-skip)
                         (goto-char (match-end 0))))
-                 (save-match-data
-                   (looking-at copyright-years-regexp))))
+                 (looking-at-p copyright-years-regexp)))
       (forward-line 1)
       (re-search-forward comment-start-skip)
-      (re-search-forward copyright-years-regexp))
+      ;; (2) Need the extra \\( \\) so that the years are subexp 3, as
+      ;; they are at note (1) above.
+      (re-search-forward (format "\\(%s\\)" copyright-years-regexp)))
 
     ;; Note that `current-time-string' isn't locale-sensitive.
     (setq copyright-current-year (substring (current-time-string) -4))
     (unless (string= (buffer-substring (- (match-end 3) 2) (match-end 3))
                     (substring copyright-current-year -2))
       (if (or noquery
-             (y-or-n-p (if replace
-                           (concat "Replace copyright year(s) by "
-                                   copyright-current-year "? ")
-                         (concat "Add " copyright-current-year
-                                 " to copyright? "))))
+             (save-window-excursion
+               (switch-to-buffer (current-buffer))
+               ;; Fixes some point-moving oddness (bug#2209).
+               (save-excursion
+                 (y-or-n-p (if replace
+                               (concat "Replace copyright year(s) by "
+                                       copyright-current-year "? ")
+                             (concat "Add " copyright-current-year
+                                     " to copyright? "))))))
          (if replace
-             (replace-match copyright-current-year t t nil 2)
+             (replace-match copyright-current-year t t nil 3)
            (let ((size (save-excursion (skip-chars-backward "0-9"))))
              (if (and (eq (% (- (string-to-number copyright-current-year)
                                 (string-to-number (buffer-substring
@@ -161,7 +195,7 @@ When this is `function', only ask when called non-interactively."
 
 ;;;###autoload
 (defun copyright-update (&optional arg interactivep)
-  "Update copyright notice at beginning of buffer to indicate the current year.
+  "Update copyright notice to indicate the current year.
 With prefix ARG, replace the years in the notice rather than adding
 the current year after them.  If necessary, and
 `copyright-current-gpl-version' is set, any copying permissions
@@ -175,13 +209,13 @@ interactively."
       (save-excursion
        (save-restriction
          (widen)
-         (goto-char (point-min))
+         (goto-char (copyright-start-point))
          (copyright-update-year arg noquery)
-         (goto-char (point-min))
+         (goto-char (copyright-start-point))
          (and copyright-current-gpl-version
               ;; match the GPL version comment in .el files, including the
               ;; bilingual Esperanto one in two-column, and in texinfo.tex
-              (re-search-forward
+              (copyright-re-search
                 "\\(the Free Software Foundation;\
  either \\|; a\\^u eldono \\([0-9]+\\)a, ? a\\^u (la\\^u via    \\)\
 version \\([0-9]+\\), or (at"
@@ -191,8 +225,11 @@ version \\([0-9]+\\), or (at"
                (< (string-to-number (match-string 3))
                   (string-to-number copyright-current-gpl-version))
               (or noquery
-                  (y-or-n-p (format "Replace GPL version by %s? "
-                                    copyright-current-gpl-version)))
+                   (save-match-data
+                    (save-window-excursion
+                      (switch-to-buffer (current-buffer))
+                      (y-or-n-p (format "Replace GPL version by %s? "
+                                        copyright-current-gpl-version)))))
               (progn
                 (if (match-end 2)
                     ;; Esperanto bilingual comment in two-column.el
@@ -203,14 +240,15 @@ version \\([0-9]+\\), or (at"
     nil))
 
 
+;; FIXME should be within 50 years of present (cf calendar).
 ;;;###autoload
 (defun copyright-fix-years ()
   "Convert 2 digit years to 4 digit years.
 Uses heuristic: year >= 50 means 19xx, < 50 means 20xx."
   (interactive)
   (widen)
-  (goto-char (point-min))
-  (if (re-search-forward copyright-regexp (copyright-limit) t)
+  (goto-char (copyright-start-point))
+  (if (copyright-re-search copyright-regexp (copyright-limit) t)
       (let ((s (match-beginning 2))
            (e (copy-marker (1+ (match-end 2))))
            (p (make-marker))
@@ -234,7 +272,7 @@ Uses heuristic: year >= 50 means 19xx, < 50 means 20xx."
          ;; Don't mess up whitespace after the years.
          (skip-chars-backward " \t")
          (save-restriction
-           (narrow-to-region (point-min) (point))
+           (narrow-to-region (copyright-start-point) (point))
            (let ((fill-prefix "     "))
              (fill-region s last))))
        (set-marker e nil)
@@ -250,15 +288,27 @@ Uses heuristic: year >= 50 means 19xx, < 50 means 20xx."
   "Copyright (C) " `(substring (current-time-string) -4) " by "
   (or (getenv "ORGANIZATION")
       str)
-  '(if (and copyright-limit (> (point) (+ (point-min) copyright-limit)))
+  '(if (copyright-offset-too-large-p)
        (message "Copyright extends beyond `copyright-limit' and won't be updated automatically."))
   comment-end \n)
 
+;;;###autoload
+(defun copyright-update-directory (directory match)
+  "Update copyright notice for all files in DIRECTORY matching MATCH."
+  (interactive "DDirectory: \nMFilenames matching (regexp): ")
+  (dolist (file (directory-files directory t match nil))
+    (message "Updating file `%s'" file)
+    (find-file file)
+    (let ((copyright-query nil))
+      (copyright-update))
+    (save-buffer)
+    (kill-buffer (current-buffer))))
+
 (provide 'copyright)
 
 ;; For the copyright sign:
 ;; Local Variables:
-;; coding: emacs-mule
+;; coding: utf-8
 ;; End:
 
 ;; arch-tag: b4991afb-b6b1-4590-bebe-e076d9d4aee8