Update copyright notices for 2013.
[bpt/emacs.git] / lisp / emacs-lisp / copyright.el
CommitLineData
3f61a2e7 1;;; copyright.el --- update the copyright notice in current buffer
d501f516 2
ab422c4d
PE
3;; Copyright (C) 1991-1995, 1998, 2001-2013 Free Software Foundation,
4;; 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")
0fe719e6 50;;;###autoload(put 'copyright-at-end-flag 'safe-local-variable 'booleanp)
b2c7c56d 51
3f251fcd 52(defcustom copyright-regexp
e145a7fe 53 "\\(©\\|@copyright{}\\|[Cc]opyright\\s *:?\\s *\\(?:(C)\\)?\
d260b218 54\\|[Cc]opyright\\s *:?\\s *©\\)\
24a517fc
MB
55\\s *\\(?:[^0-9\n]*\\s *\\)?\
56\\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)"
b649d2e4 57 "What your copyright notice looks like.
3f251fcd
AS
58The second \\( \\) construct must match the years."
59 :group 'copyright
60 :type 'regexp)
3f61a2e7 61
b649d2e4
SM
62(defcustom copyright-names-regexp ""
63 "Regexp matching the names which correspond to the user.
64Only copyright lines where the name matches this regexp will be updated.
8a1dd108 65This allows you to avoid adding years to a copyright notice belonging to
b649d2e4 66someone else or to a group for which you do not work."
44168837 67 :group 'copyright
b649d2e4
SM
68 :type 'regexp)
69
0fe719e6
GM
70;; The worst that can happen is a malicious regexp that overflows in
71;; the regexp matcher, a minor nuisance. It's a pain to be always
72;; prompted if you want to put this in a dir-locals.el.
73;;;###autoload(put 'copyright-names-regexp 'safe-local-variable 'stringp)
74
69311816
RS
75(defcustom copyright-years-regexp
76 "\\(\\s *\\)\\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)"
b649d2e4 77 "Match additional copyright notice years.
69311816
RS
78The second \\( \\) construct must match the years."
79 :group 'copyright
80 :type 'regexp)
81
0fe719e6
GM
82;; See "Copyright Notices" in maintain.info.
83;; TODO? 'end only for ranges at the end, other for all ranges.
84;; Minimum limit on the size of a range?
85(defcustom copyright-year-ranges nil
86 "Non-nil if individual consecutive years should be replaced with a range.
87For example: 2005, 2006, 2007, 2008 might be replaced with 2005-2008.
88If you use ranges, you should add an explanatory note in a README file.
e2b5bdd7 89The function `copyright-fix-years' respects this variable."
0fe719e6
GM
90 :group 'copyright
91 :type 'boolean
92 :version "24.1")
93
94;;;###autoload(put 'copyright-year-ranges 'safe-local-variable 'booleanp)
3f61a2e7 95
3f251fcd 96(defcustom copyright-query 'function
b649d2e4 97 "If non-nil, ask user before changing copyright.
3f251fcd
AS
98When this is `function', only ask when called non-interactively."
99 :group 'copyright
100 :type '(choice (const :tag "Do not ask")
e4f0bdfa
AS
101 (const :tag "Ask unless interactive" function)
102 (other :tag "Ask" t)))
3f61a2e7
KH
103
104
a7acbbe4 105;; when modifying this, also modify the comment generated by autoinsert.el
948d9b97 106(defconst copyright-current-gpl-version "3"
9c05459c 107 "String representing the current version of the GPL or nil.")
0343b087 108
fe177a62
GM
109(defvar copyright-update t
110 "The function `copyright-update' sets this to nil after updating a buffer.")
9cfd2eeb 111
b7812d30
EZ
112;; This is a defvar rather than a defconst, because the year can
113;; change during the Emacs session.
0bfcf5c5 114(defvar copyright-current-year (format-time-string "%Y")
b7812d30
EZ
115 "String representing the current year.")
116
4168d2c7 117(defsubst copyright-limit () ; re-search-forward BOUND
b2c7c56d
GM
118 (and copyright-limit
119 (if copyright-at-end-flag
120 (- (point) copyright-limit)
121 (+ (point) copyright-limit))))
122
123(defun copyright-re-search (regexp &optional bound noerror count)
124 "Re-search forward or backward depending on `copyright-at-end-flag'."
125 (if copyright-at-end-flag
126 (re-search-backward regexp bound noerror count)
127 (re-search-forward regexp bound noerror count)))
128
129(defun copyright-start-point ()
130 "Return point-min or point-max, depending on `copyright-at-end-flag'."
131 (if copyright-at-end-flag
132 (point-max)
133 (point-min)))
134
135(defun copyright-offset-too-large-p ()
136 "Return non-nil if point is too far from the edge of the buffer."
137 (when copyright-limit
138 (if copyright-at-end-flag
139 (< (point) (- (point-max) copyright-limit))
140 (> (point) (+ (point-min) copyright-limit)))))
4168d2c7 141
0412a5a4
GM
142(defun copyright-find-copyright ()
143 "Return non-nil if a copyright header suitable for updating is found.
144The header must match `copyright-regexp' and `copyright-names-regexp', if set.
145This function sets the match-data that `copyright-update-year' uses."
572bf6f2
GM
146 (widen)
147 (goto-char (copyright-start-point))
0412a5a4
GM
148 (condition-case err
149 ;; (1) Need the extra \\( \\) around copyright-regexp because we
150 ;; goto (match-end 1) below. See note (2) below.
151 (copyright-re-search (concat "\\(" copyright-regexp
152 "\\)\\([ \t]*\n\\)?.*\\(?:"
153 copyright-names-regexp "\\)")
154 (copyright-limit)
155 t)
156 ;; In case the regexp is rejected. This is useful because
157 ;; copyright-update is typically called from before-save-hook where
158 ;; such an error is very inconvenient for the user.
159 (error (message "Can't update copyright: %s" err) nil)))
160
0fe719e6
GM
161(defun copyright-find-end ()
162 "Possibly adjust the search performed by `copyright-find-copyright'.
163If the years continue onto multiple lines that are marked as comments,
164skips to the end of all the years."
0412a5a4
GM
165 (while (save-excursion
166 (and (eq (following-char) ?,)
167 (progn (forward-char 1) t)
168 (progn (skip-chars-forward " \t") (eolp))
169 comment-start-skip
170 (save-match-data
171 (forward-line 1)
172 (and (looking-at comment-start-skip)
173 (goto-char (match-end 0))))
174 (looking-at-p copyright-years-regexp)))
175 (forward-line 1)
176 (re-search-forward comment-start-skip)
177 ;; (2) Need the extra \\( \\) so that the years are subexp 3, as
178 ;; they are at note (1) above.
0fe719e6 179 (re-search-forward (format "\\(%s\\)" copyright-years-regexp))))
0412a5a4 180
0fe719e6
GM
181(defun copyright-update-year (replace noquery)
182 ;; This uses the match-data from copyright-find-copyright/end.
183 (goto-char (match-end 1))
184 (copyright-find-end)
0bfcf5c5 185 (setq copyright-current-year (format-time-string "%Y"))
0412a5a4
GM
186 (unless (string= (buffer-substring (- (match-end 3) 2) (match-end 3))
187 (substring copyright-current-year -2))
188 (if (or noquery
189 (save-window-excursion
190 (switch-to-buffer (current-buffer))
191 ;; Fixes some point-moving oddness (bug#2209).
192 (save-excursion
193 (y-or-n-p (if replace
194 (concat "Replace copyright year(s) by "
195 copyright-current-year "? ")
196 (concat "Add " copyright-current-year
197 " to copyright? "))))))
198 (if replace
199 (replace-match copyright-current-year t t nil 3)
200 (let ((size (save-excursion (skip-chars-backward "0-9"))))
201 (if (and (eq (% (- (string-to-number copyright-current-year)
202 (string-to-number (buffer-substring
203 (+ (point) size)
204 (point))))
205 100)
206 1)
207 (or (eq (char-after (+ (point) size -1)) ?-)
208 (eq (char-after (+ (point) size -2)) ?-)))
209 ;; This is a range so just replace the end part.
210 (delete-char size)
211 ;; Insert a comma with the preferred number of spaces.
212 (insert
213 (save-excursion
214 (if (re-search-backward "[0-9]\\( *, *\\)[0-9]"
215 (line-beginning-position) t)
216 (match-string 1)
217 ", ")))
218 ;; If people use the '91 '92 '93 scheme, do that as well.
219 (if (eq (char-after (+ (point) size -3)) ?')
220 (insert ?')))
221 ;; Finally insert the new year.
222 (insert (substring copyright-current-year size)))))))
b7812d30 223
0343b087 224;;;###autoload
f47f5302 225(defun copyright-update (&optional arg interactivep)
dbc76957 226 "Update copyright notice to indicate the current year.
9c05459c
RS
227With prefix ARG, replace the years in the notice rather than adding
228the current year after them. If necessary, and
229`copyright-current-gpl-version' is set, any copying permissions
f47f5302
SM
230following the copyright are updated as well.
231If non-nil, INTERACTIVEP tells the function to behave as when it's called
232interactively."
233 (interactive "*P\nd")
234 (when (or copyright-update interactivep)
235 (let ((noquery (or (not copyright-query)
236 (and (eq copyright-query 'function) interactivep))))
3f61a2e7
KH
237 (save-excursion
238 (save-restriction
0412a5a4
GM
239 ;; If names-regexp doesn't match, we should not mess with
240 ;; the years _or_ the GPL version.
0fe719e6 241 ;; TODO there may be multiple copyrights we should update.
0412a5a4
GM
242 (when (copyright-find-copyright)
243 (copyright-update-year arg noquery)
244 (goto-char (copyright-start-point))
245 (and copyright-current-gpl-version
246 ;; Match the GPL version comment in .el files.
247 ;; This is sensitive to line-breaks. :(
248 (copyright-re-search
249 "the Free Software Foundation[,;\n].*either version \
250\\([0-9]+\\)\\(?: of the License\\)?, or[ \n].*any later version"
251 (copyright-limit) t)
252 ;; Don't update if the file is already using a more recent
253 ;; version than the "current" one.
254 (< (string-to-number (match-string 1))
255 (string-to-number copyright-current-gpl-version))
256 (or noquery
257 (save-match-data
258 (goto-char (match-end 1))
259 (save-window-excursion
260 (switch-to-buffer (current-buffer))
261 (y-or-n-p
262 (format "Replace GPL version %s with version %s? "
263 (match-string-no-properties 1)
264 copyright-current-gpl-version)))))
265 (replace-match copyright-current-gpl-version t t nil 1))))
3f61a2e7 266 (set (make-local-variable 'copyright-update) nil)))
f47f5302
SM
267 ;; If a write-file-hook returns non-nil, the file is presumed to be written.
268 nil))
0343b087 269
d501f516 270
0fe719e6 271;; FIXME heuristic should be within 50 years of present (cf calendar).
422032f0
KS
272;;;###autoload
273(defun copyright-fix-years ()
274 "Convert 2 digit years to 4 digit years.
0fe719e6
GM
275Uses heuristic: year >= 50 means 19xx, < 50 means 20xx.
276If `copyright-year-ranges' (which see) is non-nil, also
277independently replaces consecutive years with a range."
422032f0 278 (interactive)
0fe719e6 279 ;; TODO there may be multiple copyrights we should fix.
d226ec23 280 (if (copyright-find-copyright)
0fe719e6 281 (let ((s (match-beginning 3))
07b41c42 282 (p (make-marker))
0fe719e6
GM
283 ;; Not line-beg-pos, so we don't mess up leading whitespace.
284 (copystart (match-beginning 0))
285 e last sep year prev-year first-year range-start range-end)
286 ;; In case years are continued over multiple, commented lines.
287 (goto-char (match-end 1))
288 (copyright-find-end)
289 (setq e (copy-marker (1+ (match-end 3))))
422032f0 290 (goto-char s)
07b41c42
LK
291 (while (re-search-forward "[0-9]+" e t)
292 (set-marker p (point))
293 (goto-char (match-beginning 0))
0fe719e6
GM
294 (setq year (string-to-number (match-string 0)))
295 (and (setq sep (char-before))
296 (/= (char-syntax sep) ?\s)
297 (/= sep ?-)
298 (insert " "))
299 (when (< year 100)
300 (insert (if (>= year 50) "19" "20"))
301 (setq year (+ year (if (>= year 50) 1900 2000))))
07b41c42 302 (goto-char p)
0fe719e6
GM
303 (when copyright-year-ranges
304 ;; If the previous thing was a range, don't try to tack more on.
305 ;; Ie not 2000-2005 -> 2000-2005-2007
306 ;; TODO should merge into existing range if possible.
307 (if (eq sep ?-)
308 (setq prev-year nil
309 year nil)
310 (if (and prev-year (= year (1+ prev-year)))
311 (setq range-end (point))
312 (when (and first-year prev-year
313 (> prev-year first-year))
314 (goto-char range-end)
315 (delete-region range-start range-end)
316 (insert (format "-%d" prev-year))
317 (goto-char p))
318 (setq first-year year
319 range-start (point)))))
320 (setq prev-year year
321 last p))
422032f0 322 (when last
0fe719e6
GM
323 (when (and copyright-year-ranges
324 first-year prev-year
325 (> prev-year first-year))
326 (goto-char range-end)
327 (delete-region range-start range-end)
328 (insert (format "-%d" prev-year)))
422032f0 329 (goto-char last)
02d9d682
RS
330 ;; Don't mess up whitespace after the years.
331 (skip-chars-backward " \t")
0fe719e6
GM
332 (save-restriction
333 (narrow-to-region copystart (point))
334 ;; This is clearly wrong, eg what about comment markers?
335 ;;; (let ((fill-prefix " "))
336 ;; TODO do not break copyright owner over lines.
337 (fill-region (point-min) (point-max))))
422032f0 338 (set-marker e nil)
0fe719e6
GM
339 (set-marker p nil))
340 ;; Simply reformatting the years is not copyrightable, so it does
341 ;; not seem right to call this. Also it messes with ranges.
342;;; (copyright-update nil t))
07b41c42 343 (message "No copyright message")))
422032f0 344
3f61a2e7
KH
345;;;###autoload
346(define-skeleton copyright
347 "Insert a copyright by $ORGANIZATION notice at cursor."
348 "Company: "
349 comment-start
0bfcf5c5 350 "Copyright (C) " `(format-time-string "%Y") " by "
3f61a2e7
KH
351 (or (getenv "ORGANIZATION")
352 str)
b2c7c56d 353 '(if (copyright-offset-too-large-p)
3f61a2e7 354 (message "Copyright extends beyond `copyright-limit' and won't be updated automatically."))
48a96f51 355 comment-end \n)
3f61a2e7 356
0fe719e6 357;; TODO: recurse, exclude COPYING etc.
4182531c 358;;;###autoload
0fe719e6
GM
359(defun copyright-update-directory (directory match &optional fix)
360 "Update copyright notice for all files in DIRECTORY matching MATCH.
361If FIX is non-nil, run `copyright-fix-years' instead."
4182531c 362 (interactive "DDirectory: \nMFilenames matching (regexp): ")
470fc354 363 (dolist (file (directory-files directory t match nil))
0fe719e6
GM
364 (unless (file-directory-p file)
365 (message "Updating file `%s'" file)
fbb5e336
GM
366 ;; FIXME we should not use find-file+save+kill.
367 (let ((enable-local-variables :safe)
368 (enable-local-eval nil))
369 (find-file file))
370 (let ((inhibit-read-only t))
0fe719e6
GM
371 (if fix
372 (copyright-fix-years)
373 (copyright-update)))
374 (save-buffer)
375 (kill-buffer (current-buffer)))))
470fc354 376
896546cd
RS
377(provide 'copyright)
378
bf5f1abd
DL
379;; For the copyright sign:
380;; Local Variables:
d260b218 381;; coding: utf-8
bf5f1abd
DL
382;; End:
383
e8af40ee 384;;; copyright.el ends here