Add 2010 to copyright years.
[bpt/emacs.git] / lisp / emacs-lisp / copyright.el
CommitLineData
3f61a2e7 1;;; copyright.el --- update the copyright notice in current buffer
d501f516 2
3731a850 3;; Copyright (C) 1991, 1992, 1993, 1994, 1995, 1998, 2001, 2002, 2003,
114f9c96 4;; 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
58142744 5
3e910376 6;; Author: Daniel Pfeiffer <occitan@esperanto.org>
3f61a2e7
KH
7;; Keywords: maint, tools
8
9;; This file is part of GNU Emacs.
10
d6cba7ae 11;; GNU Emacs is free software: you can redistribute it and/or modify
3f61a2e7 12;; it under the terms of the GNU General Public License as published by
d6cba7ae
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
3f61a2e7
KH
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
d6cba7ae 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
3f61a2e7
KH
23
24;;; Commentary:
25
26;; Allows updating the copyright year and above mentioned GPL version manually
f47f5302 27;; or when saving a file.
6b61353c
KH
28;; Do (add-hook 'before-save-hook 'copyright-update), or use
29;; M-x customize-variable RET before-save-hook RET.
0343b087 30
d46bac56
ER
31;;; Code:
32
3f251fcd
AS
33(defgroup copyright nil
34 "Update the copyright notice in current buffer."
35 :group 'tools)
36
37(defcustom copyright-limit 2000
b649d2e4 38 "Don't try to update copyright beyond this position unless interactive.
9c05459c 39A value of nil means to search whole buffer."
3f251fcd
AS
40 :group 'copyright
41 :type '(choice (integer :tag "Limit")
42 (const :tag "No limit")))
3f61a2e7 43
b2c7c56d
GM
44(defcustom copyright-at-end-flag nil
45 "Non-nil means to search backwards from the end of the buffer for copyright.
46This is useful for ChangeLogs."
47 :group 'copyright
48 :type 'boolean
49 :version "23.1")
50
3f251fcd 51(defcustom copyright-regexp
e145a7fe 52 "\\(©\\|@copyright{}\\|[Cc]opyright\\s *:?\\s *\\(?:(C)\\)?\
d260b218 53\\|[Cc]opyright\\s *:?\\s *©\\)\
24a517fc
MB
54\\s *\\(?:[^0-9\n]*\\s *\\)?\
55\\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)"
b649d2e4 56 "What your copyright notice looks like.
3f251fcd
AS
57The second \\( \\) construct must match the years."
58 :group 'copyright
59 :type 'regexp)
3f61a2e7 60
b649d2e4
SM
61(defcustom copyright-names-regexp ""
62 "Regexp matching the names which correspond to the user.
63Only copyright lines where the name matches this regexp will be updated.
8a1dd108 64This allows you to avoid adding years to a copyright notice belonging to
b649d2e4 65someone else or to a group for which you do not work."
44168837 66 :group 'copyright
b649d2e4
SM
67 :type 'regexp)
68
69311816
RS
69(defcustom copyright-years-regexp
70 "\\(\\s *\\)\\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)"
b649d2e4 71 "Match additional copyright notice years.
69311816
RS
72The second \\( \\) construct must match the years."
73 :group 'copyright
74 :type 'regexp)
75
3f61a2e7 76
3f251fcd 77(defcustom copyright-query 'function
b649d2e4 78 "If non-nil, ask user before changing copyright.
3f251fcd
AS
79When this is `function', only ask when called non-interactively."
80 :group 'copyright
81 :type '(choice (const :tag "Do not ask")
e4f0bdfa
AS
82 (const :tag "Ask unless interactive" function)
83 (other :tag "Ask" t)))
3f61a2e7
KH
84
85
a7acbbe4 86;; when modifying this, also modify the comment generated by autoinsert.el
948d9b97 87(defconst copyright-current-gpl-version "3"
9c05459c 88 "String representing the current version of the GPL or nil.")
0343b087 89
fe177a62
GM
90(defvar copyright-update t
91 "The function `copyright-update' sets this to nil after updating a buffer.")
9cfd2eeb 92
b7812d30
EZ
93;; This is a defvar rather than a defconst, because the year can
94;; change during the Emacs session.
9c05459c 95(defvar copyright-current-year (substring (current-time-string) -4)
b7812d30
EZ
96 "String representing the current year.")
97
4168d2c7 98(defsubst copyright-limit () ; re-search-forward BOUND
b2c7c56d
GM
99 (and copyright-limit
100 (if copyright-at-end-flag
101 (- (point) copyright-limit)
102 (+ (point) copyright-limit))))
103
104(defun copyright-re-search (regexp &optional bound noerror count)
105 "Re-search forward or backward depending on `copyright-at-end-flag'."
106 (if copyright-at-end-flag
107 (re-search-backward regexp bound noerror count)
108 (re-search-forward regexp bound noerror count)))
109
110(defun copyright-start-point ()
111 "Return point-min or point-max, depending on `copyright-at-end-flag'."
112 (if copyright-at-end-flag
113 (point-max)
114 (point-min)))
115
116(defun copyright-offset-too-large-p ()
117 "Return non-nil if point is too far from the edge of the buffer."
118 (when copyright-limit
119 (if copyright-at-end-flag
120 (< (point) (- (point-max) copyright-limit))
121 (> (point) (+ (point-min) copyright-limit)))))
4168d2c7 122
f47f5302 123(defun copyright-update-year (replace noquery)
b649d2e4
SM
124 (when
125 (condition-case err
ce12d620
GM
126 ;; (1) Need the extra \\( \\) around copyright-regexp because we
127 ;; goto (match-end 1) below. See note (2) below.
b2c7c56d
GM
128 (copyright-re-search (concat "\\(" copyright-regexp
129 "\\)\\([ \t]*\n\\)?.*\\(?:"
130 copyright-names-regexp "\\)")
131 (copyright-limit)
132 t)
b649d2e4
SM
133 ;; In case the regexp is rejected. This is useful because
134 ;; copyright-update is typically called from before-save-hook where
135 ;; such an error is very inconvenient for the user.
136 (error (message "Can't update copyright: %s" err) nil))
137 (goto-char (match-end 1))
ce12d620 138 ;; If the years are continued onto multiple lines
69311816
RS
139 ;; that are marked as comments, skip to the end of the years anyway.
140 (while (save-excursion
141 (and (eq (following-char) ?,)
142 (progn (forward-char 1) t)
143 (progn (skip-chars-forward " \t") (eolp))
144 comment-start-skip
80817d7b 145 (save-match-data
69311816 146 (forward-line 1)
80817d7b 147 (and (looking-at comment-start-skip)
f5e087f8 148 (goto-char (match-end 0))))
68af6bd6 149 (looking-at-p copyright-years-regexp)))
69311816
RS
150 (forward-line 1)
151 (re-search-forward comment-start-skip)
ce12d620
GM
152 ;; (2) Need the extra \\( \\) so that the years are subexp 3, as
153 ;; they are at note (1) above.
154 (re-search-forward (format "\\(%s\\)" copyright-years-regexp)))
697f502a 155
f47f5302
SM
156 ;; Note that `current-time-string' isn't locale-sensitive.
157 (setq copyright-current-year (substring (current-time-string) -4))
b649d2e4 158 (unless (string= (buffer-substring (- (match-end 3) 2) (match-end 3))
f47f5302
SM
159 (substring copyright-current-year -2))
160 (if (or noquery
4431546e
GM
161 ;; Fixes some point-moving oddness (bug#2209).
162 (save-excursion
163 (y-or-n-p (if replace
164 (concat "Replace copyright year(s) by "
165 copyright-current-year "? ")
166 (concat "Add " copyright-current-year
167 " to copyright? ")))))
f47f5302 168 (if replace
ce12d620 169 (replace-match copyright-current-year t t nil 3)
f47f5302
SM
170 (let ((size (save-excursion (skip-chars-backward "0-9"))))
171 (if (and (eq (% (- (string-to-number copyright-current-year)
172 (string-to-number (buffer-substring
173 (+ (point) size)
174 (point))))
175 100)
176 1)
177 (or (eq (char-after (+ (point) size -1)) ?-)
178 (eq (char-after (+ (point) size -2)) ?-)))
179 ;; This is a range so just replace the end part.
180 (delete-char size)
f47f5302
SM
181 ;; Insert a comma with the preferred number of spaces.
182 (insert
183 (save-excursion
184 (if (re-search-backward "[0-9]\\( *, *\\)[0-9]"
185 (line-beginning-position) t)
186 (match-string 1)
187 ", ")))
188 ;; If people use the '91 '92 '93 scheme, do that as well.
189 (if (eq (char-after (+ (point) size -3)) ?')
190 (insert ?')))
191 ;; Finally insert the new year.
192 (insert (substring copyright-current-year size))))))))
b7812d30 193
0343b087 194;;;###autoload
f47f5302 195(defun copyright-update (&optional arg interactivep)
dbc76957 196 "Update copyright notice to indicate the current year.
9c05459c
RS
197With prefix ARG, replace the years in the notice rather than adding
198the current year after them. If necessary, and
199`copyright-current-gpl-version' is set, any copying permissions
f47f5302
SM
200following the copyright are updated as well.
201If non-nil, INTERACTIVEP tells the function to behave as when it's called
202interactively."
203 (interactive "*P\nd")
204 (when (or copyright-update interactivep)
205 (let ((noquery (or (not copyright-query)
206 (and (eq copyright-query 'function) interactivep))))
3f61a2e7
KH
207 (save-excursion
208 (save-restriction
209 (widen)
b2c7c56d 210 (goto-char (copyright-start-point))
f47f5302 211 (copyright-update-year arg noquery)
b2c7c56d 212 (goto-char (copyright-start-point))
3f61a2e7
KH
213 (and copyright-current-gpl-version
214 ;; match the GPL version comment in .el files, including the
215 ;; bilingual Esperanto one in two-column, and in texinfo.tex
b2c7c56d 216 (copyright-re-search
463dca7e 217 "\\(the Free Software Foundation;\
9c05459c
RS
218 either \\|; a\\^u eldono \\([0-9]+\\)a, ? a\\^u (la\\^u via \\)\
219version \\([0-9]+\\), or (at"
4168d2c7 220 (copyright-limit) t)
2cb250dd
SM
221 ;; Don't update if the file is already using a more recent
222 ;; version than the "current" one.
223 (< (string-to-number (match-string 3))
224 (string-to-number copyright-current-gpl-version))
f47f5302 225 (or noquery
ed2a52e9
SM
226 (save-match-data
227 (y-or-n-p (format "Replace GPL version by %s? "
228 copyright-current-gpl-version))))
3f61a2e7
KH
229 (progn
230 (if (match-end 2)
231 ;; Esperanto bilingual comment in two-column.el
f47f5302
SM
232 (replace-match copyright-current-gpl-version t t nil 2))
233 (replace-match copyright-current-gpl-version t t nil 3))))
3f61a2e7 234 (set (make-local-variable 'copyright-update) nil)))
f47f5302
SM
235 ;; If a write-file-hook returns non-nil, the file is presumed to be written.
236 nil))
0343b087 237
d501f516 238
fe177a62 239;; FIXME should be within 50 years of present (cf calendar).
422032f0
KS
240;;;###autoload
241(defun copyright-fix-years ()
242 "Convert 2 digit years to 4 digit years.
243Uses heuristic: year >= 50 means 19xx, < 50 means 20xx."
244 (interactive)
245 (widen)
b2c7c56d
GM
246 (goto-char (copyright-start-point))
247 (if (copyright-re-search copyright-regexp (copyright-limit) t)
07b41c42
LK
248 (let ((s (match-beginning 2))
249 (e (copy-marker (1+ (match-end 2))))
250 (p (make-marker))
422032f0 251 last)
422032f0 252 (goto-char s)
07b41c42
LK
253 (while (re-search-forward "[0-9]+" e t)
254 (set-marker p (point))
255 (goto-char (match-beginning 0))
256 (let ((sep (char-before))
257 (year (string-to-number (match-string 0))))
258 (when (and sep
259 (/= (char-syntax sep) ?\s)
260 (/= sep ?-))
422032f0 261 (insert " "))
07b41c42
LK
262 (when (< year 100)
263 (insert (if (>= year 50) "19" "20"))))
264 (goto-char p)
265 (setq last p))
422032f0
KS
266 (when last
267 (goto-char last)
02d9d682
RS
268 ;; Don't mess up whitespace after the years.
269 (skip-chars-backward " \t")
270 (save-restriction
b2c7c56d 271 (narrow-to-region (copyright-start-point) (point))
02d9d682 272 (let ((fill-prefix " "))
07b41c42 273 (fill-region s last))))
422032f0 274 (set-marker e nil)
07b41c42 275 (set-marker p nil)
422032f0 276 (copyright-update nil t))
07b41c42 277 (message "No copyright message")))
422032f0 278
3f61a2e7
KH
279;;;###autoload
280(define-skeleton copyright
281 "Insert a copyright by $ORGANIZATION notice at cursor."
282 "Company: "
283 comment-start
b7812d30 284 "Copyright (C) " `(substring (current-time-string) -4) " by "
3f61a2e7
KH
285 (or (getenv "ORGANIZATION")
286 str)
b2c7c56d 287 '(if (copyright-offset-too-large-p)
3f61a2e7 288 (message "Copyright extends beyond `copyright-limit' and won't be updated automatically."))
48a96f51 289 comment-end \n)
3f61a2e7 290
4182531c 291;;;###autoload
470fc354
RS
292(defun copyright-update-directory (directory match)
293 "Update copyright notice for all files in DIRECTORY matching MATCH."
4182531c 294 (interactive "DDirectory: \nMFilenames matching (regexp): ")
470fc354 295 (dolist (file (directory-files directory t match nil))
4182531c 296 (message "Updating file `%s'" file)
470fc354
RS
297 (find-file file)
298 (let ((copyright-query nil))
299 (copyright-update))
300 (save-buffer)
301 (kill-buffer (current-buffer))))
302
896546cd
RS
303(provide 'copyright)
304
bf5f1abd
DL
305;; For the copyright sign:
306;; Local Variables:
d260b218 307;; coding: utf-8
bf5f1abd
DL
308;; End:
309
b649d2e4 310;; arch-tag: b4991afb-b6b1-4590-bebe-e076d9d4aee8
e8af40ee 311;;; copyright.el ends here