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