Commit | Line | Data |
---|---|---|
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 | 39 | A 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. | |
46 | This 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 |
57 | The 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. | |
63 | Only copyright lines where the name matches this regexp will be updated. | |
8a1dd108 | 64 | This allows you to avoid adding years to a copyright notice belonging to |
b649d2e4 | 65 | someone 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 |
72 | The 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 |
79 | When 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 |
197 | With prefix ARG, replace the years in the notice rather than adding |
198 | the current year after them. If necessary, and | |
199 | `copyright-current-gpl-version' is set, any copying permissions | |
f47f5302 SM |
200 | following the copyright are updated as well. |
201 | If non-nil, INTERACTIVEP tells the function to behave as when it's called | |
202 | interactively." | |
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 \\)\ |
219 | version \\([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. | |
243 | Uses 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 |