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, |
8b72699e | 4 | ;; 2004, 2005, 2006, 2007, 2008 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 | ||
11 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
12 | ;; it under the terms of the GNU General Public License as published by | |
e0085d62 | 13 | ;; the Free Software Foundation; either version 3, or (at your option) |
3f61a2e7 KH |
14 | ;; any later version. |
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 | |
b578f267 | 22 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
3a35cf56 LK |
23 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
24 | ;; Boston, MA 02110-1301, USA. | |
3f61a2e7 KH |
25 | |
26 | ;;; Commentary: | |
27 | ||
28 | ;; Allows updating the copyright year and above mentioned GPL version manually | |
f47f5302 | 29 | ;; or when saving a file. |
6b61353c KH |
30 | ;; Do (add-hook 'before-save-hook 'copyright-update), or use |
31 | ;; M-x customize-variable RET before-save-hook RET. | |
0343b087 | 32 | |
d46bac56 ER |
33 | ;;; Code: |
34 | ||
3f251fcd AS |
35 | (defgroup copyright nil |
36 | "Update the copyright notice in current buffer." | |
37 | :group 'tools) | |
38 | ||
39 | (defcustom copyright-limit 2000 | |
b649d2e4 | 40 | "Don't try to update copyright beyond this position unless interactive. |
9c05459c | 41 | A value of nil means to search whole buffer." |
3f251fcd AS |
42 | :group 'copyright |
43 | :type '(choice (integer :tag "Limit") | |
44 | (const :tag "No limit"))) | |
3f61a2e7 | 45 | |
3f251fcd | 46 | (defcustom copyright-regexp |
e145a7fe | 47 | "\\(©\\|@copyright{}\\|[Cc]opyright\\s *:?\\s *\\(?:(C)\\)?\ |
d260b218 | 48 | \\|[Cc]opyright\\s *:?\\s *©\\)\ |
2e3b4a0b | 49 | \\s *\\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)" |
b649d2e4 | 50 | "What your copyright notice looks like. |
3f251fcd AS |
51 | The second \\( \\) construct must match the years." |
52 | :group 'copyright | |
53 | :type 'regexp) | |
3f61a2e7 | 54 | |
b649d2e4 SM |
55 | (defcustom copyright-names-regexp "" |
56 | "Regexp matching the names which correspond to the user. | |
57 | Only copyright lines where the name matches this regexp will be updated. | |
8a1dd108 | 58 | This allows you to avoid adding years to a copyright notice belonging to |
b649d2e4 | 59 | someone else or to a group for which you do not work." |
44168837 | 60 | :group 'copyright |
b649d2e4 SM |
61 | :type 'regexp) |
62 | ||
69311816 RS |
63 | (defcustom copyright-years-regexp |
64 | "\\(\\s *\\)\\([1-9]\\([-0-9, ';/*%#\n\t]\\|\\s<\\|\\s>\\)*[0-9]+\\)" | |
b649d2e4 | 65 | "Match additional copyright notice years. |
69311816 RS |
66 | The second \\( \\) construct must match the years." |
67 | :group 'copyright | |
68 | :type 'regexp) | |
69 | ||
3f61a2e7 | 70 | |
3f251fcd | 71 | (defcustom copyright-query 'function |
b649d2e4 | 72 | "If non-nil, ask user before changing copyright. |
3f251fcd AS |
73 | When this is `function', only ask when called non-interactively." |
74 | :group 'copyright | |
75 | :type '(choice (const :tag "Do not ask") | |
e4f0bdfa AS |
76 | (const :tag "Ask unless interactive" function) |
77 | (other :tag "Ask" t))) | |
3f61a2e7 KH |
78 | |
79 | ||
a7acbbe4 | 80 | ;; when modifying this, also modify the comment generated by autoinsert.el |
948d9b97 | 81 | (defconst copyright-current-gpl-version "3" |
9c05459c | 82 | "String representing the current version of the GPL or nil.") |
0343b087 | 83 | |
3f61a2e7 | 84 | (defvar copyright-update t) |
9cfd2eeb | 85 | |
b7812d30 EZ |
86 | ;; This is a defvar rather than a defconst, because the year can |
87 | ;; change during the Emacs session. | |
9c05459c | 88 | (defvar copyright-current-year (substring (current-time-string) -4) |
b7812d30 EZ |
89 | "String representing the current year.") |
90 | ||
4168d2c7 | 91 | (defsubst copyright-limit () ; re-search-forward BOUND |
18e08bf7 | 92 | (and copyright-limit (+ (point) copyright-limit))) |
4168d2c7 | 93 | |
f47f5302 | 94 | (defun copyright-update-year (replace noquery) |
b649d2e4 SM |
95 | (when |
96 | (condition-case err | |
ce12d620 GM |
97 | ;; (1) Need the extra \\( \\) around copyright-regexp because we |
98 | ;; goto (match-end 1) below. See note (2) below. | |
b649d2e4 SM |
99 | (re-search-forward (concat "\\(" copyright-regexp |
100 | "\\)\\([ \t]*\n\\)?.*\\(?:" | |
101 | copyright-names-regexp "\\)") | |
4168d2c7 | 102 | (copyright-limit) |
2f12b713 | 103 | t) |
b649d2e4 SM |
104 | ;; In case the regexp is rejected. This is useful because |
105 | ;; copyright-update is typically called from before-save-hook where | |
106 | ;; such an error is very inconvenient for the user. | |
107 | (error (message "Can't update copyright: %s" err) nil)) | |
108 | (goto-char (match-end 1)) | |
ce12d620 | 109 | ;; If the years are continued onto multiple lines |
69311816 RS |
110 | ;; that are marked as comments, skip to the end of the years anyway. |
111 | (while (save-excursion | |
112 | (and (eq (following-char) ?,) | |
113 | (progn (forward-char 1) t) | |
114 | (progn (skip-chars-forward " \t") (eolp)) | |
115 | comment-start-skip | |
80817d7b | 116 | (save-match-data |
69311816 | 117 | (forward-line 1) |
80817d7b | 118 | (and (looking-at comment-start-skip) |
f5e087f8 | 119 | (goto-char (match-end 0)))) |
68af6bd6 | 120 | (looking-at-p copyright-years-regexp))) |
69311816 RS |
121 | (forward-line 1) |
122 | (re-search-forward comment-start-skip) | |
ce12d620 GM |
123 | ;; (2) Need the extra \\( \\) so that the years are subexp 3, as |
124 | ;; they are at note (1) above. | |
125 | (re-search-forward (format "\\(%s\\)" copyright-years-regexp))) | |
697f502a | 126 | |
f47f5302 SM |
127 | ;; Note that `current-time-string' isn't locale-sensitive. |
128 | (setq copyright-current-year (substring (current-time-string) -4)) | |
b649d2e4 | 129 | (unless (string= (buffer-substring (- (match-end 3) 2) (match-end 3)) |
f47f5302 SM |
130 | (substring copyright-current-year -2)) |
131 | (if (or noquery | |
132 | (y-or-n-p (if replace | |
133 | (concat "Replace copyright year(s) by " | |
134 | copyright-current-year "? ") | |
135 | (concat "Add " copyright-current-year | |
136 | " to copyright? ")))) | |
137 | (if replace | |
ce12d620 | 138 | (replace-match copyright-current-year t t nil 3) |
f47f5302 SM |
139 | (let ((size (save-excursion (skip-chars-backward "0-9")))) |
140 | (if (and (eq (% (- (string-to-number copyright-current-year) | |
141 | (string-to-number (buffer-substring | |
142 | (+ (point) size) | |
143 | (point)))) | |
144 | 100) | |
145 | 1) | |
146 | (or (eq (char-after (+ (point) size -1)) ?-) | |
147 | (eq (char-after (+ (point) size -2)) ?-))) | |
148 | ;; This is a range so just replace the end part. | |
149 | (delete-char size) | |
f47f5302 SM |
150 | ;; Insert a comma with the preferred number of spaces. |
151 | (insert | |
152 | (save-excursion | |
153 | (if (re-search-backward "[0-9]\\( *, *\\)[0-9]" | |
154 | (line-beginning-position) t) | |
155 | (match-string 1) | |
156 | ", "))) | |
157 | ;; If people use the '91 '92 '93 scheme, do that as well. | |
158 | (if (eq (char-after (+ (point) size -3)) ?') | |
159 | (insert ?'))) | |
160 | ;; Finally insert the new year. | |
161 | (insert (substring copyright-current-year size)))))))) | |
b7812d30 | 162 | |
0343b087 | 163 | ;;;###autoload |
f47f5302 | 164 | (defun copyright-update (&optional arg interactivep) |
9c05459c RS |
165 | "Update copyright notice at beginning of buffer to indicate the current year. |
166 | With prefix ARG, replace the years in the notice rather than adding | |
167 | the current year after them. If necessary, and | |
168 | `copyright-current-gpl-version' is set, any copying permissions | |
f47f5302 SM |
169 | following the copyright are updated as well. |
170 | If non-nil, INTERACTIVEP tells the function to behave as when it's called | |
171 | interactively." | |
172 | (interactive "*P\nd") | |
173 | (when (or copyright-update interactivep) | |
174 | (let ((noquery (or (not copyright-query) | |
175 | (and (eq copyright-query 'function) interactivep)))) | |
3f61a2e7 KH |
176 | (save-excursion |
177 | (save-restriction | |
178 | (widen) | |
179 | (goto-char (point-min)) | |
f47f5302 | 180 | (copyright-update-year arg noquery) |
3f61a2e7 KH |
181 | (goto-char (point-min)) |
182 | (and copyright-current-gpl-version | |
183 | ;; match the GPL version comment in .el files, including the | |
184 | ;; bilingual Esperanto one in two-column, and in texinfo.tex | |
463dca7e SM |
185 | (re-search-forward |
186 | "\\(the Free Software Foundation;\ | |
9c05459c RS |
187 | either \\|; a\\^u eldono \\([0-9]+\\)a, ? a\\^u (la\\^u via \\)\ |
188 | version \\([0-9]+\\), or (at" | |
4168d2c7 | 189 | (copyright-limit) t) |
2cb250dd SM |
190 | ;; Don't update if the file is already using a more recent |
191 | ;; version than the "current" one. | |
192 | (< (string-to-number (match-string 3)) | |
193 | (string-to-number copyright-current-gpl-version)) | |
f47f5302 | 194 | (or noquery |
2cb250dd SM |
195 | (y-or-n-p (format "Replace GPL version by %s? " |
196 | copyright-current-gpl-version))) | |
3f61a2e7 KH |
197 | (progn |
198 | (if (match-end 2) | |
199 | ;; Esperanto bilingual comment in two-column.el | |
f47f5302 SM |
200 | (replace-match copyright-current-gpl-version t t nil 2)) |
201 | (replace-match copyright-current-gpl-version t t nil 3)))) | |
3f61a2e7 | 202 | (set (make-local-variable 'copyright-update) nil))) |
f47f5302 SM |
203 | ;; If a write-file-hook returns non-nil, the file is presumed to be written. |
204 | nil)) | |
0343b087 | 205 | |
d501f516 | 206 | |
422032f0 KS |
207 | ;;;###autoload |
208 | (defun copyright-fix-years () | |
209 | "Convert 2 digit years to 4 digit years. | |
210 | Uses heuristic: year >= 50 means 19xx, < 50 means 20xx." | |
211 | (interactive) | |
212 | (widen) | |
213 | (goto-char (point-min)) | |
4168d2c7 | 214 | (if (re-search-forward copyright-regexp (copyright-limit) t) |
07b41c42 LK |
215 | (let ((s (match-beginning 2)) |
216 | (e (copy-marker (1+ (match-end 2)))) | |
217 | (p (make-marker)) | |
422032f0 | 218 | last) |
422032f0 | 219 | (goto-char s) |
07b41c42 LK |
220 | (while (re-search-forward "[0-9]+" e t) |
221 | (set-marker p (point)) | |
222 | (goto-char (match-beginning 0)) | |
223 | (let ((sep (char-before)) | |
224 | (year (string-to-number (match-string 0)))) | |
225 | (when (and sep | |
226 | (/= (char-syntax sep) ?\s) | |
227 | (/= sep ?-)) | |
422032f0 | 228 | (insert " ")) |
07b41c42 LK |
229 | (when (< year 100) |
230 | (insert (if (>= year 50) "19" "20")))) | |
231 | (goto-char p) | |
232 | (setq last p)) | |
422032f0 KS |
233 | (when last |
234 | (goto-char last) | |
02d9d682 RS |
235 | ;; Don't mess up whitespace after the years. |
236 | (skip-chars-backward " \t") | |
237 | (save-restriction | |
238 | (narrow-to-region (point-min) (point)) | |
239 | (let ((fill-prefix " ")) | |
07b41c42 | 240 | (fill-region s last)))) |
422032f0 | 241 | (set-marker e nil) |
07b41c42 | 242 | (set-marker p nil) |
422032f0 | 243 | (copyright-update nil t)) |
07b41c42 | 244 | (message "No copyright message"))) |
422032f0 | 245 | |
3f61a2e7 KH |
246 | ;;;###autoload |
247 | (define-skeleton copyright | |
248 | "Insert a copyright by $ORGANIZATION notice at cursor." | |
249 | "Company: " | |
250 | comment-start | |
b7812d30 | 251 | "Copyright (C) " `(substring (current-time-string) -4) " by " |
3f61a2e7 KH |
252 | (or (getenv "ORGANIZATION") |
253 | str) | |
463dca7e | 254 | '(if (and copyright-limit (> (point) (+ (point-min) copyright-limit))) |
3f61a2e7 | 255 | (message "Copyright extends beyond `copyright-limit' and won't be updated automatically.")) |
48a96f51 | 256 | comment-end \n) |
3f61a2e7 | 257 | |
896546cd RS |
258 | (provide 'copyright) |
259 | ||
bf5f1abd DL |
260 | ;; For the copyright sign: |
261 | ;; Local Variables: | |
d260b218 | 262 | ;; coding: utf-8 |
bf5f1abd DL |
263 | ;; End: |
264 | ||
b649d2e4 | 265 | ;; arch-tag: b4991afb-b6b1-4590-bebe-e076d9d4aee8 |
e8af40ee | 266 | ;;; copyright.el ends here |