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