Merge changes from emacs-23 branch
[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
c22e18f5
KR
161 (save-window-excursion
162 (switch-to-buffer (current-buffer))
163 ;; Fixes some point-moving oddness (bug#2209).
164 (save-excursion
165 (y-or-n-p (if replace
166 (concat "Replace copyright year(s) by "
167 copyright-current-year "? ")
168 (concat "Add " copyright-current-year
169 " to copyright? "))))))
f47f5302 170 (if replace
ce12d620 171 (replace-match copyright-current-year t t nil 3)
f47f5302
SM
172 (let ((size (save-excursion (skip-chars-backward "0-9"))))
173 (if (and (eq (% (- (string-to-number copyright-current-year)
174 (string-to-number (buffer-substring
175 (+ (point) size)
176 (point))))
177 100)
178 1)
179 (or (eq (char-after (+ (point) size -1)) ?-)
180 (eq (char-after (+ (point) size -2)) ?-)))
181 ;; This is a range so just replace the end part.
182 (delete-char size)
f47f5302
SM
183 ;; Insert a comma with the preferred number of spaces.
184 (insert
185 (save-excursion
186 (if (re-search-backward "[0-9]\\( *, *\\)[0-9]"
187 (line-beginning-position) t)
188 (match-string 1)
189 ", ")))
190 ;; If people use the '91 '92 '93 scheme, do that as well.
191 (if (eq (char-after (+ (point) size -3)) ?')
192 (insert ?')))
193 ;; Finally insert the new year.
194 (insert (substring copyright-current-year size))))))))
b7812d30 195
0343b087 196;;;###autoload
f47f5302 197(defun copyright-update (&optional arg interactivep)
dbc76957 198 "Update copyright notice to indicate the current year.
9c05459c
RS
199With prefix ARG, replace the years in the notice rather than adding
200the current year after them. If necessary, and
201`copyright-current-gpl-version' is set, any copying permissions
f47f5302
SM
202following the copyright are updated as well.
203If non-nil, INTERACTIVEP tells the function to behave as when it's called
204interactively."
205 (interactive "*P\nd")
206 (when (or copyright-update interactivep)
207 (let ((noquery (or (not copyright-query)
208 (and (eq copyright-query 'function) interactivep))))
3f61a2e7
KH
209 (save-excursion
210 (save-restriction
211 (widen)
b2c7c56d 212 (goto-char (copyright-start-point))
f47f5302 213 (copyright-update-year arg noquery)
b2c7c56d 214 (goto-char (copyright-start-point))
3f61a2e7
KH
215 (and copyright-current-gpl-version
216 ;; match the GPL version comment in .el files, including the
217 ;; bilingual Esperanto one in two-column, and in texinfo.tex
b2c7c56d 218 (copyright-re-search
463dca7e 219 "\\(the Free Software Foundation;\
9c05459c
RS
220 either \\|; a\\^u eldono \\([0-9]+\\)a, ? a\\^u (la\\^u via \\)\
221version \\([0-9]+\\), or (at"
4168d2c7 222 (copyright-limit) t)
2cb250dd
SM
223 ;; Don't update if the file is already using a more recent
224 ;; version than the "current" one.
225 (< (string-to-number (match-string 3))
226 (string-to-number copyright-current-gpl-version))
f47f5302 227 (or noquery
ed2a52e9 228 (save-match-data
c22e18f5
KR
229 (save-window-excursion
230 (switch-to-buffer (current-buffer))
231 (y-or-n-p (format "Replace GPL version by %s? "
232 copyright-current-gpl-version)))))
3f61a2e7
KH
233 (progn
234 (if (match-end 2)
235 ;; Esperanto bilingual comment in two-column.el
f47f5302
SM
236 (replace-match copyright-current-gpl-version t t nil 2))
237 (replace-match copyright-current-gpl-version t t nil 3))))
3f61a2e7 238 (set (make-local-variable 'copyright-update) nil)))
f47f5302
SM
239 ;; If a write-file-hook returns non-nil, the file is presumed to be written.
240 nil))
0343b087 241
d501f516 242
fe177a62 243;; FIXME should be within 50 years of present (cf calendar).
422032f0
KS
244;;;###autoload
245(defun copyright-fix-years ()
246 "Convert 2 digit years to 4 digit years.
247Uses heuristic: year >= 50 means 19xx, < 50 means 20xx."
248 (interactive)
249 (widen)
b2c7c56d
GM
250 (goto-char (copyright-start-point))
251 (if (copyright-re-search copyright-regexp (copyright-limit) t)
07b41c42
LK
252 (let ((s (match-beginning 2))
253 (e (copy-marker (1+ (match-end 2))))
254 (p (make-marker))
422032f0 255 last)
422032f0 256 (goto-char s)
07b41c42
LK
257 (while (re-search-forward "[0-9]+" e t)
258 (set-marker p (point))
259 (goto-char (match-beginning 0))
260 (let ((sep (char-before))
261 (year (string-to-number (match-string 0))))
262 (when (and sep
263 (/= (char-syntax sep) ?\s)
264 (/= sep ?-))
422032f0 265 (insert " "))
07b41c42
LK
266 (when (< year 100)
267 (insert (if (>= year 50) "19" "20"))))
268 (goto-char p)
269 (setq last p))
422032f0
KS
270 (when last
271 (goto-char last)
02d9d682
RS
272 ;; Don't mess up whitespace after the years.
273 (skip-chars-backward " \t")
274 (save-restriction
b2c7c56d 275 (narrow-to-region (copyright-start-point) (point))
02d9d682 276 (let ((fill-prefix " "))
07b41c42 277 (fill-region s last))))
422032f0 278 (set-marker e nil)
07b41c42 279 (set-marker p nil)
422032f0 280 (copyright-update nil t))
07b41c42 281 (message "No copyright message")))
422032f0 282
3f61a2e7
KH
283;;;###autoload
284(define-skeleton copyright
285 "Insert a copyright by $ORGANIZATION notice at cursor."
286 "Company: "
287 comment-start
b7812d30 288 "Copyright (C) " `(substring (current-time-string) -4) " by "
3f61a2e7
KH
289 (or (getenv "ORGANIZATION")
290 str)
b2c7c56d 291 '(if (copyright-offset-too-large-p)
3f61a2e7 292 (message "Copyright extends beyond `copyright-limit' and won't be updated automatically."))
48a96f51 293 comment-end \n)
3f61a2e7 294
4182531c 295;;;###autoload
470fc354
RS
296(defun copyright-update-directory (directory match)
297 "Update copyright notice for all files in DIRECTORY matching MATCH."
4182531c 298 (interactive "DDirectory: \nMFilenames matching (regexp): ")
470fc354 299 (dolist (file (directory-files directory t match nil))
4182531c 300 (message "Updating file `%s'" file)
470fc354
RS
301 (find-file file)
302 (let ((copyright-query nil))
303 (copyright-update))
304 (save-buffer)
305 (kill-buffer (current-buffer))))
306
896546cd
RS
307(provide 'copyright)
308
bf5f1abd
DL
309;; For the copyright sign:
310;; Local Variables:
d260b218 311;; coding: utf-8
bf5f1abd
DL
312;; End:
313
b649d2e4 314;; arch-tag: b4991afb-b6b1-4590-bebe-e076d9d4aee8
e8af40ee 315;;; copyright.el ends here