Convert consecutive FSF copyright years to ranges.
[bpt/emacs.git] / lisp / informat.el
CommitLineData
1a06eabd
ER
1;;; informat.el --- info support functions package for Emacs
2
73b0cd50 3;; Copyright (C) 1986, 2001-2011 Free Software Foundation, Inc.
3a801d0c 4
e5167999 5;; Maintainer: FSF
fd7fa35a 6;; Keywords: help
e5167999 7
745bc783
JB
8;; This file is part of GNU Emacs.
9
eb3fa2cf 10;; GNU Emacs is free software: you can redistribute it and/or modify
745bc783 11;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
745bc783
JB
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
eb3fa2cf 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
745bc783 22
54f1a1dd
RS
23;;; Commentary:
24
25;; Nowadays, the Texinfo formatting commands always tagify a buffer
26;; (as does `makeinfo') since @anchor commands need tag tables.
27
e5167999
ER
28;;; Code:
29
745bc783
JB
30(require 'info)
31
2c52d7a3 32(declare-function texinfo-format-refill "texinfmt" ())
004a00f4 33
745bc783 34;;;###autoload
54f1a1dd
RS
35(defun Info-tagify (&optional input-buffer-name)
36 "Create or update Info file tag table in current buffer or in a region."
745bc783
JB
37 (interactive)
38 ;; Save and restore point and restrictions.
39 ;; save-restrictions would not work
40 ;; because it records the old max relative to the end.
41 ;; We record it relative to the beginning.
54f1a1dd
RS
42 (if input-buffer-name
43 (message "Tagifying region in %s ..." input-buffer-name)
44 (message
45 "Tagifying %s ..." (file-name-nondirectory (buffer-file-name))))
745bc783
JB
46 (let ((omin (point-min))
47 (omax (point-max))
48 (nomax (= (point-max) (1+ (buffer-size))))
49 (opoint (point)))
50 (unwind-protect
c69bfc14 51 (progn
289f0da2 52 (widen)
c69bfc14
RS
53 (goto-char (point-min))
54 (if (search-forward "\^_\nIndirect:\n" nil t)
54f1a1dd
RS
55 (message
56 "Cannot tagify split info file. Run this before splitting.")
c69bfc14
RS
57 (let (tag-list
58 refillp
59 (case-fold-search t)
f1180544 60 (regexp
c69bfc14
RS
61 (concat
62 "\\("
63
64
65 "\\("
66 "@anchor" ; match-string 2 matches @anchor
67 "\\)"
68 "\\(-no\\|-yes\\)" ; match-string 3 matches -no or -yes
69 "\\("
70 "-refill"
71 "\\)"
72
73 "\\("
74 "{"
75 "\\)"
76 "\\("
77 "[^}]+" ; match-string 6 matches arg to anchor
78 "\\)"
79 "\\("
80 "}"
81 "\\)"
82
83 "\\|"
84
85 "\\("
289f0da2 86 "\n\^_\\(\^L\\)?"
c69bfc14
RS
87 "\\)"
88
89 "\\("
289f0da2 90 "\n\\(File:[ \t]*\\([^,\n\t]*\\)[,\t\n]+[ \t\n]*\\)?"
c69bfc14
RS
91 "Node:[ \t]*"
92 "\\("
289f0da2 93 "[^,\n\t]*" ; match-string 13 matches arg to node name
c69bfc14
RS
94 "\\)"
95 "[,\t\n]"
96 "\\)"
97
98 "\\)"
99 )))
100 (while (re-search-forward regexp nil t)
101 (if (string-equal "@anchor" (match-string 2))
102 (progn
103 ;; kludge lest lose match-data
104 (if (string-equal "-yes" (match-string 3))
105 (setq refillp t))
106 (setq tag-list
107 (cons (list
108 (concat "Ref: " (match-string 6))
109 (match-beginning 0))
110 tag-list))
111 (if (eq refillp t)
112 ;; set start and end so texinfo-format-refill works
113 (let ((texinfo-command-start (match-beginning 0))
114 (texinfo-command-end (match-end 0)))
115 (texinfo-format-refill))
116 (delete-region (match-beginning 0) (match-end 0))))
117 ;; else this is a Node
118 (setq tag-list
f1180544 119 (cons (list
289f0da2
RS
120 (concat "Node: " (match-string-no-properties 13))
121 (1+ (match-beginning 10)))
c69bfc14
RS
122 tag-list))))
123
745bc783
JB
124 (goto-char (point-max))
125 (forward-line -8)
126 (let ((buffer-read-only nil))
127 (if (search-forward "\^_\nEnd tag table\n" nil t)
128 (let ((end (point)))
129 (search-backward "\nTag table:\n")
130 (beginning-of-line)
131 (delete-region (point) end)))
132 (goto-char (point-max))
289f0da2
RS
133 (or (bolp)
134 (newline))
135 (insert "\^_\f\nTag table:\n")
9304909e
RS
136 (if (eq major-mode 'info-mode)
137 (move-marker Info-tag-table-marker (point)))
c69bfc14
RS
138 (setq tag-list (nreverse tag-list))
139 (while tag-list
140 (insert (car (car tag-list)) ?\177)
54f1a1dd 141 (princ (car (cdr (car tag-list))) (current-buffer))
745bc783 142 (insert ?\n)
c69bfc14 143 (setq tag-list (cdr tag-list)))
745bc783
JB
144 (insert "\^_\nEnd tag table\n")))))
145 (goto-char opoint)
146 (narrow-to-region omin (if nomax (1+ (buffer-size))
147 (min omax (point-max))))))
54f1a1dd 148 (if input-buffer-name
289f0da2 149 (message "Tagifying region in %s done" input-buffer-name)
54f1a1dd 150 (message
289f0da2 151 "Tagifying %s done" (file-name-nondirectory (buffer-file-name)))))
54f1a1dd 152
745bc783 153\f
4b93c9d5
KY
154;;;###autoload
155(defcustom Info-split-threshold 262144
156 "The number of characters by which `Info-split' splits an info file."
157 :type 'integer
158 :version "23.1"
159 :group 'texinfo)
160
745bc783
JB
161;;;###autoload
162(defun Info-split ()
163 "Split an info file into an indirect file plus bounded-size subfiles.
4b93c9d5
KY
164Each subfile will be up to the number of characters that
165`Info-split-threshold' specifies, plus one node.
745bc783
JB
166
167To use this command, first visit a large Info file that has a tag
168table. The buffer is modified into a (small) indirect info file which
169should be saved in place of the original visited file.
170
171The subfiles are written in the same directory the original file is
172in, with names generated by appending `-' and a number to the original
173file name. The indirect file still functions as an Info file, but it
174contains just the tag table and a directory of subfiles."
175
176 (interactive)
4b93c9d5 177 (if (< (buffer-size) (+ 20000 Info-split-threshold))
745bc783
JB
178 (error "This is too small to be worth splitting"))
179 (goto-char (point-min))
180 (search-forward "\^_")
181 (forward-char -1)
182 (let ((start (point))
54f1a1dd 183 (chars-deleted 0)
745bc783
JB
184 subfiles
185 (subfile-number 1)
186 (case-fold-search t)
187 (filename (file-name-sans-versions buffer-file-name)))
188 (goto-char (point-max))
189 (forward-line -8)
190 (setq buffer-read-only nil)
191 (or (search-forward "\^_\nEnd tag table\n" nil t)
192 (error "Tag table required; use M-x Info-tagify"))
193 (search-backward "\nTag table:\n")
194 (if (looking-at "\nTag table:\n\^_")
195 (error "Tag table is just a skeleton; use M-x Info-tagify"))
196 (beginning-of-line)
197 (forward-char 1)
198 (save-restriction
199 (narrow-to-region (point-min) (point))
200 (goto-char (point-min))
201 (while (< (1+ (point)) (point-max))
4b93c9d5 202 (goto-char (min (+ (point) Info-split-threshold) (point-max)))
745bc783
JB
203 (search-forward "\^_" nil 'move)
204 (setq subfiles
54f1a1dd 205 (cons (list (+ start chars-deleted)
745bc783
JB
206 (concat (file-name-nondirectory filename)
207 (format "-%d" subfile-number)))
208 subfiles))
209 ;; Put a newline at end of split file, to make Unix happier.
210 (insert "\n")
211 (write-region (point-min) (point)
212 (concat filename (format "-%d" subfile-number)))
213 (delete-region (1- (point)) (point))
214 ;; Back up over the final ^_.
215 (forward-char -1)
54f1a1dd 216 (setq chars-deleted (+ chars-deleted (- (point) start)))
745bc783
JB
217 (delete-region start (point))
218 (setq subfile-number (1+ subfile-number))))
219 (while subfiles
220 (goto-char start)
221 (insert (nth 1 (car subfiles))
a1a4d0bc 222 (format ": %d" (1- (car (car subfiles))))
745bc783
JB
223 "\n")
224 (setq subfiles (cdr subfiles)))
225 (goto-char start)
226 (insert "\^_\nIndirect:\n")
227 (search-forward "\nTag Table:\n")
228 (insert "(Indirect)\n")))
229\f
c88cd504
RS
230(defvar Info-validate-allnodes)
231(defvar Info-validate-thisnode)
232(defvar Info-validate-lossages)
233
745bc783
JB
234;;;###autoload
235(defun Info-validate ()
236 "Check current buffer for validity as an Info file.
237Check that every node pointer points to an existing node."
238 (interactive)
239 (save-excursion
240 (save-restriction
241 (widen)
242 (goto-char (point-min))
243 (if (search-forward "\nTag table:\n(Indirect)\n" nil t)
244 (error "Don't yet know how to validate indirect info files: \"%s\""
245 (buffer-name (current-buffer))))
246 (goto-char (point-min))
c88cd504 247 (let ((Info-validate-allnodes '(("*")))
745bc783
JB
248 (regexp "Node:[ \t]*\\([^,\n\t]*\\)[,\t\n]")
249 (case-fold-search t)
250 (tags-losing nil)
c88cd504 251 (Info-validate-lossages ()))
745bc783
JB
252 (while (search-forward "\n\^_" nil t)
253 (forward-line 1)
254 (let ((beg (point)))
255 (forward-line 1)
256 (if (re-search-backward regexp beg t)
257 (let ((name (downcase
c88cd504
RS
258 (buffer-substring-no-properties
259 (match-beginning 1)
260 (progn
261 (goto-char (match-end 1))
262 (skip-chars-backward " \t")
263 (point))))))
264 (if (assoc name Info-validate-allnodes)
265 (setq Info-validate-lossages
745bc783 266 (cons (list name "Duplicate node-name" nil)
c88cd504
RS
267 Info-validate-lossages))
268 (setq Info-validate-allnodes
269 (cons (list name
270 (progn
271 (end-of-line)
272 (and (re-search-backward
273 "prev[ious]*:" beg t)
274 (progn
275 (goto-char (match-end 0))
276 (downcase
277 (Info-following-node-name)))))
278 beg)
279 Info-validate-allnodes)))))))
745bc783
JB
280 (goto-char (point-min))
281 (while (search-forward "\n\^_" nil t)
282 (forward-line 1)
283 (let ((beg (point))
c88cd504 284 Info-validate-thisnode next)
745bc783
JB
285 (forward-line 1)
286 (if (re-search-backward regexp beg t)
287 (save-restriction
289f0da2
RS
288 (let ((md (match-data)))
289 (search-forward "\n\^_" nil 'move)
290 (narrow-to-region beg (point))
291 (set-match-data md))
c88cd504
RS
292 (setq Info-validate-thisnode (downcase
293 (buffer-substring-no-properties
294 (match-beginning 1)
295 (progn
296 (goto-char (match-end 1))
297 (skip-chars-backward " \t")
298 (point)))))
745bc783
JB
299 (end-of-line)
300 (and (search-backward "next:" nil t)
301 (setq next (Info-validate-node-name "invalid Next"))
c88cd504
RS
302 (assoc next Info-validate-allnodes)
303 (if (equal (car (cdr (assoc next Info-validate-allnodes)))
304 Info-validate-thisnode)
745bc783 305 ;; allow multiple `next' pointers to one node
c88cd504 306 (let ((tem Info-validate-lossages))
745bc783
JB
307 (while tem
308 (if (and (equal (car (cdr (car tem)))
309 "should have Previous")
310 (equal (car (car tem))
311 next))
c88cd504
RS
312 (setq Info-validate-lossages
313 (delq (car tem) Info-validate-lossages)))
745bc783 314 (setq tem (cdr tem))))
c88cd504 315 (setq Info-validate-lossages
745bc783
JB
316 (cons (list next
317 "should have Previous"
c88cd504
RS
318 Info-validate-thisnode)
319 Info-validate-lossages))))
745bc783
JB
320 (end-of-line)
321 (if (re-search-backward "prev[ious]*:" nil t)
322 (Info-validate-node-name "invalid Previous"))
323 (end-of-line)
324 (if (search-backward "up:" nil t)
325 (Info-validate-node-name "invalid Up"))
326 (if (re-search-forward "\n* Menu:" nil t)
327 (while (re-search-forward "\n\\* " nil t)
328 (Info-validate-node-name
c88cd504
RS
329 (concat "invalid menu item "
330 (buffer-substring (point)
331 (save-excursion
332 (skip-chars-forward "^:")
333 (point))))
334 (Info-extract-menu-node-name))))
745bc783
JB
335 (goto-char (point-min))
336 (while (re-search-forward "\\*note[ \n]*[^:\t]*:" nil t)
337 (goto-char (+ (match-beginning 0) 5))
338 (skip-chars-forward " \n")
339 (Info-validate-node-name
340 (concat "invalid reference "
341 (buffer-substring (point)
342 (save-excursion
343 (skip-chars-forward "^:")
344 (point))))
345 (Info-extract-menu-node-name "Bad format cross-reference")))))))
346 (setq tags-losing (not (Info-validate-tags-table)))
c88cd504 347 (if (or Info-validate-lossages tags-losing)
745bc783 348 (with-output-to-temp-buffer " *problems in info file*"
c88cd504 349 (while Info-validate-lossages
745bc783 350 (princ "In node \"")
c88cd504 351 (princ (car (car Info-validate-lossages)))
745bc783 352 (princ "\", ")
c88cd504 353 (let ((tem (nth 1 (car Info-validate-lossages))))
745bc783
JB
354 (cond ((string-match "\n" tem)
355 (princ (substring tem 0 (match-beginning 0)))
356 (princ "..."))
357 (t
358 (princ tem))))
c88cd504 359 (if (nth 2 (car Info-validate-lossages))
745bc783
JB
360 (progn
361 (princ ": ")
c88cd504 362 (let ((tem (nth 2 (car Info-validate-lossages))))
745bc783
JB
363 (cond ((string-match "\n" tem)
364 (princ (substring tem 0 (match-beginning 0)))
365 (princ "..."))
366 (t
367 (princ tem))))))
368 (terpri)
c88cd504 369 (setq Info-validate-lossages (cdr Info-validate-lossages)))
745bc783
JB
370 (if tags-losing (princ "\nTags table must be recomputed\n")))
371 ;; Here if info file is valid.
372 ;; If we already made a list of problems, clear it out.
373 (save-excursion
374 (if (get-buffer " *problems in info file*")
375 (progn
376 (set-buffer " *problems in info file*")
377 (kill-buffer (current-buffer)))))
378 (message "File appears valid"))))))
379
380(defun Info-validate-node-name (kind &optional name)
381 (if name
382 nil
383 (goto-char (match-end 0))
384 (skip-chars-forward " \t")
385 (if (= (following-char) ?\()
386 nil
387 (setq name
e64d0760 388 (buffer-substring-no-properties
745bc783
JB
389 (point)
390 (progn
c88cd504
RS
391 (skip-chars-forward "^,\t\n")
392 (skip-chars-backward " ")
393 (point))))))
745bc783
JB
394 (if (null name)
395 nil
396 (setq name (downcase name))
397 (or (and (> (length name) 0) (= (aref name 0) ?\())
c88cd504
RS
398 (assoc name Info-validate-allnodes)
399 (setq Info-validate-lossages
400 (cons (list Info-validate-thisnode kind name)
401 Info-validate-lossages))))
745bc783
JB
402 name)
403
404(defun Info-validate-tags-table ()
405 (goto-char (point-min))
406 (if (not (search-forward "\^_\nEnd tag table\n" nil t))
407 t
408 (not (catch 'losing
409 (let* ((end (match-beginning 0))
410 (start (progn (search-backward "\nTag table:\n")
411 (1- (match-end 0))))
412 tem)
c88cd504 413 (setq tem Info-validate-allnodes)
745bc783
JB
414 (while tem
415 (goto-char start)
416 (or (equal (car (car tem)) "*")
417 (search-forward (concat "Node: "
418 (car (car tem))
419 "\177")
420 end t)
421 (throw 'losing 'x))
422 (setq tem (cdr tem)))
423 (goto-char (1+ start))
424 (while (looking-at ".*Node: \\(.*\\)\177\\([0-9]+\\)$")
e64d0760 425 (setq tem (downcase (buffer-substring-no-properties
745bc783
JB
426 (match-beginning 1)
427 (match-end 1))))
c88cd504 428 (setq tem (assoc tem Info-validate-allnodes))
745bc783
JB
429 (if (or (not tem)
430 (< 1000 (progn
431 (goto-char (match-beginning 2))
432 (setq tem (- (car (cdr (cdr tem)))
433 (read (current-buffer))))
434 (if (> tem 0) tem (- tem)))))
e64d0760
RS
435 (throw 'losing 'y))
436 (forward-line 1)))
437 (if (looking-at "\^_\n")
438 (forward-line 1))
745bc783
JB
439 (or (looking-at "End tag table\n")
440 (throw 'losing 'z))
441 nil))))
442\f
443;;;###autoload
444(defun batch-info-validate ()
445 "Runs `Info-validate' on the files remaining on the command line.
446Must be used only with -batch, and kills Emacs on completion.
447Each file will be processed even if an error occurred previously.
448For example, invoke \"emacs -batch -f batch-info-validate $info/ ~/*.info\""
449 (if (not noninteractive)
55535639 450 (error "batch-info-validate may only be used -batch"))
745bc783
JB
451 (let ((version-control t)
452 (auto-save-default nil)
453 (find-file-run-dired nil)
454 (kept-old-versions 259259)
455 (kept-new-versions 259259))
456 (let ((error 0)
457 file
458 (files ()))
459 (while command-line-args-left
460 (setq file (expand-file-name (car command-line-args-left)))
461 (cond ((not (file-exists-p file))
462 (message ">> %s does not exist!" file)
463 (setq error 1
f1180544 464 command-line-args-left (cdr command-line-args-left)))
745bc783
JB
465 ((file-directory-p file)
466 (setq command-line-args-left (nconc (directory-files file)
467 (cdr command-line-args-left))))
468 (t
469 (setq files (cons file files)
470 command-line-args-left (cdr command-line-args-left)))))
471 (while files
472 (setq file (car files)
473 files (cdr files))
474 (let ((lose nil))
475 (condition-case err
476 (progn
477 (if buffer-file-name (kill-buffer (current-buffer)))
478 (find-file file)
479 (buffer-disable-undo (current-buffer))
480 (set-buffer-modified-p nil)
481 (fundamental-mode)
482 (let ((case-fold-search nil))
483 (goto-char (point-max))
484 (cond ((search-backward "\n\^_\^L\nTag table:\n" nil t)
485 (message "%s already tagified" file))
486 ((< (point-max) 30000)
487 (message "%s too small to bother tagifying" file))
488 (t
e8a57935 489 (Info-tagify))))
745bc783
JB
490 (let ((loss-name " *problems in info file*"))
491 (message "Checking validity of info file %s..." file)
492 (if (get-buffer loss-name)
493 (kill-buffer loss-name))
494 (Info-validate)
495 (if (not (get-buffer loss-name))
496 nil ;(message "Checking validity of info file %s... OK" file)
497 (message "----------------------------------------------------------------------")
498 (message ">> PROBLEMS IN INFO FILE %s" file)
7fdbcd83 499 (with-current-buffer loss-name
e64d0760
RS
500 (princ (buffer-substring-no-properties
501 (point-min) (point-max))))
745bc783
JB
502 (message "----------------------------------------------------------------------")
503 (setq error 1 lose t)))
504 (if (and (buffer-modified-p)
505 (not lose))
506 (progn (message "Saving modified %s" file)
507 (save-buffer))))
508 (error (message ">> Error: %s" (prin1-to-string err))))))
509 (kill-emacs error))))
1a06eabd 510
896546cd
RS
511(provide 'informat)
512
1a06eabd 513;;; informat.el ends here