Make it easier to control which revisions bzrmerge might skip.
[bpt/emacs.git] / lisp / emacs-lisp / copyright.el
CommitLineData
3f61a2e7 1;;; copyright.el --- update the copyright notice in current buffer
d501f516 2
73b0cd50 3;; Copyright (C) 1991-1995, 1998, 2001-2011
0412a5a4 4;; 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
0412a5a4
GM
123(defun copyright-find-copyright ()
124 "Return non-nil if a copyright header suitable for updating is found.
125The header must match `copyright-regexp' and `copyright-names-regexp', if set.
126This function sets the match-data that `copyright-update-year' uses."
572bf6f2
GM
127 (widen)
128 (goto-char (copyright-start-point))
0412a5a4
GM
129 (condition-case err
130 ;; (1) Need the extra \\( \\) around copyright-regexp because we
131 ;; goto (match-end 1) below. See note (2) below.
132 (copyright-re-search (concat "\\(" copyright-regexp
133 "\\)\\([ \t]*\n\\)?.*\\(?:"
134 copyright-names-regexp "\\)")
135 (copyright-limit)
136 t)
137 ;; In case the regexp is rejected. This is useful because
138 ;; copyright-update is typically called from before-save-hook where
139 ;; such an error is very inconvenient for the user.
140 (error (message "Can't update copyright: %s" err) nil)))
141
f47f5302 142(defun copyright-update-year (replace noquery)
0412a5a4
GM
143 ;; This uses the match-data from copyright-find-copyright.
144 (goto-char (match-end 1))
145 ;; If the years are continued onto multiple lines
146 ;; that are marked as comments, skip to the end of the years anyway.
147 (while (save-excursion
148 (and (eq (following-char) ?,)
149 (progn (forward-char 1) t)
150 (progn (skip-chars-forward " \t") (eolp))
151 comment-start-skip
152 (save-match-data
153 (forward-line 1)
154 (and (looking-at comment-start-skip)
155 (goto-char (match-end 0))))
156 (looking-at-p copyright-years-regexp)))
157 (forward-line 1)
158 (re-search-forward comment-start-skip)
159 ;; (2) Need the extra \\( \\) so that the years are subexp 3, as
160 ;; they are at note (1) above.
161 (re-search-forward (format "\\(%s\\)" copyright-years-regexp)))
162
163 ;; Note that `current-time-string' isn't locale-sensitive.
164 (setq copyright-current-year (substring (current-time-string) -4))
165 (unless (string= (buffer-substring (- (match-end 3) 2) (match-end 3))
166 (substring copyright-current-year -2))
167 (if (or noquery
168 (save-window-excursion
169 (switch-to-buffer (current-buffer))
170 ;; Fixes some point-moving oddness (bug#2209).
171 (save-excursion
172 (y-or-n-p (if replace
173 (concat "Replace copyright year(s) by "
174 copyright-current-year "? ")
175 (concat "Add " copyright-current-year
176 " to copyright? "))))))
177 (if replace
178 (replace-match copyright-current-year t t nil 3)
179 (let ((size (save-excursion (skip-chars-backward "0-9"))))
180 (if (and (eq (% (- (string-to-number copyright-current-year)
181 (string-to-number (buffer-substring
182 (+ (point) size)
183 (point))))
184 100)
185 1)
186 (or (eq (char-after (+ (point) size -1)) ?-)
187 (eq (char-after (+ (point) size -2)) ?-)))
188 ;; This is a range so just replace the end part.
189 (delete-char size)
190 ;; Insert a comma with the preferred number of spaces.
191 (insert
192 (save-excursion
193 (if (re-search-backward "[0-9]\\( *, *\\)[0-9]"
194 (line-beginning-position) t)
195 (match-string 1)
196 ", ")))
197 ;; If people use the '91 '92 '93 scheme, do that as well.
198 (if (eq (char-after (+ (point) size -3)) ?')
199 (insert ?')))
200 ;; Finally insert the new year.
201 (insert (substring copyright-current-year size)))))))
b7812d30 202
0343b087 203;;;###autoload
f47f5302 204(defun copyright-update (&optional arg interactivep)
dbc76957 205 "Update copyright notice to indicate the current year.
9c05459c
RS
206With prefix ARG, replace the years in the notice rather than adding
207the current year after them. If necessary, and
208`copyright-current-gpl-version' is set, any copying permissions
f47f5302
SM
209following the copyright are updated as well.
210If non-nil, INTERACTIVEP tells the function to behave as when it's called
211interactively."
212 (interactive "*P\nd")
213 (when (or copyright-update interactivep)
214 (let ((noquery (or (not copyright-query)
215 (and (eq copyright-query 'function) interactivep))))
3f61a2e7
KH
216 (save-excursion
217 (save-restriction
0412a5a4
GM
218 ;; If names-regexp doesn't match, we should not mess with
219 ;; the years _or_ the GPL version.
220 (when (copyright-find-copyright)
221 (copyright-update-year arg noquery)
222 (goto-char (copyright-start-point))
223 (and copyright-current-gpl-version
224 ;; Match the GPL version comment in .el files.
225 ;; This is sensitive to line-breaks. :(
226 (copyright-re-search
227 "the Free Software Foundation[,;\n].*either version \
228\\([0-9]+\\)\\(?: of the License\\)?, or[ \n].*any later version"
229 (copyright-limit) t)
230 ;; Don't update if the file is already using a more recent
231 ;; version than the "current" one.
232 (< (string-to-number (match-string 1))
233 (string-to-number copyright-current-gpl-version))
234 (or noquery
235 (save-match-data
236 (goto-char (match-end 1))
237 (save-window-excursion
238 (switch-to-buffer (current-buffer))
239 (y-or-n-p
240 (format "Replace GPL version %s with version %s? "
241 (match-string-no-properties 1)
242 copyright-current-gpl-version)))))
243 (replace-match copyright-current-gpl-version t t nil 1))))
3f61a2e7 244 (set (make-local-variable 'copyright-update) nil)))
f47f5302
SM
245 ;; If a write-file-hook returns non-nil, the file is presumed to be written.
246 nil))
0343b087 247
d501f516 248
fe177a62 249;; FIXME should be within 50 years of present (cf calendar).
422032f0
KS
250;;;###autoload
251(defun copyright-fix-years ()
252 "Convert 2 digit years to 4 digit years.
253Uses heuristic: year >= 50 means 19xx, < 50 means 20xx."
254 (interactive)
d226ec23 255 (if (copyright-find-copyright)
07b41c42
LK
256 (let ((s (match-beginning 2))
257 (e (copy-marker (1+ (match-end 2))))
258 (p (make-marker))
422032f0 259 last)
422032f0 260 (goto-char s)
07b41c42
LK
261 (while (re-search-forward "[0-9]+" e t)
262 (set-marker p (point))
263 (goto-char (match-beginning 0))
264 (let ((sep (char-before))
265 (year (string-to-number (match-string 0))))
266 (when (and sep
267 (/= (char-syntax sep) ?\s)
268 (/= sep ?-))
422032f0 269 (insert " "))
07b41c42
LK
270 (when (< year 100)
271 (insert (if (>= year 50) "19" "20"))))
272 (goto-char p)
273 (setq last p))
422032f0
KS
274 (when last
275 (goto-char last)
02d9d682
RS
276 ;; Don't mess up whitespace after the years.
277 (skip-chars-backward " \t")
278 (save-restriction
b2c7c56d 279 (narrow-to-region (copyright-start-point) (point))
02d9d682 280 (let ((fill-prefix " "))
07b41c42 281 (fill-region s last))))
422032f0 282 (set-marker e nil)
07b41c42 283 (set-marker p nil)
422032f0 284 (copyright-update nil t))
07b41c42 285 (message "No copyright message")))
422032f0 286
3f61a2e7
KH
287;;;###autoload
288(define-skeleton copyright
289 "Insert a copyright by $ORGANIZATION notice at cursor."
290 "Company: "
291 comment-start
b7812d30 292 "Copyright (C) " `(substring (current-time-string) -4) " by "
3f61a2e7
KH
293 (or (getenv "ORGANIZATION")
294 str)
b2c7c56d 295 '(if (copyright-offset-too-large-p)
3f61a2e7 296 (message "Copyright extends beyond `copyright-limit' and won't be updated automatically."))
48a96f51 297 comment-end \n)
3f61a2e7 298
4182531c 299;;;###autoload
470fc354
RS
300(defun copyright-update-directory (directory match)
301 "Update copyright notice for all files in DIRECTORY matching MATCH."
4182531c 302 (interactive "DDirectory: \nMFilenames matching (regexp): ")
470fc354 303 (dolist (file (directory-files directory t match nil))
4182531c 304 (message "Updating file `%s'" file)
470fc354
RS
305 (find-file file)
306 (let ((copyright-query nil))
307 (copyright-update))
308 (save-buffer)
309 (kill-buffer (current-buffer))))
310
896546cd
RS
311(provide 'copyright)
312
bf5f1abd
DL
313;; For the copyright sign:
314;; Local Variables:
d260b218 315;; coding: utf-8
bf5f1abd
DL
316;; End:
317
e8af40ee 318;;; copyright.el ends here