|
|
1.1 root 1: ;; Info support functions package for Emacs
2: ;; Copyright (C) 1986 Free Software Foundation, Inc.
3:
4: ;; This file is part of GNU Emacs.
5:
6: ;; GNU Emacs is free software; you can redistribute it and/or modify
7: ;; it under the terms of the GNU General Public License as published by
8: ;; the Free Software Foundation; either version 1, or (at your option)
9: ;; any later version.
10:
11: ;; GNU Emacs is distributed in the hope that it will be useful,
12: ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13: ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14: ;; GNU General Public License for more details.
15:
16: ;; You should have received a copy of the GNU General Public License
17: ;; along with GNU Emacs; see the file COPYING. If not, write to
18: ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19:
20: (require 'info)
21:
22: (defun Info-tagify ()
23: "Create or update Info-file tag table in current buffer."
24: (interactive)
25: ;; Save and restore point and restrictions.
26: ;; save-restrictions would not work
27: ;; because it records the old max relative to the end.
28: ;; We record it relative to the beginning.
29: (let ((omin (point-min))
30: (omax (point-max))
31: (nomax (= (point-max) (1+ (buffer-size))))
32: (opoint (point)))
33: (unwind-protect
34: (progn
35: (widen)
36: (goto-char (point-min))
37: (if (search-forward "\^_\nIndirect:\n" nil t)
38: (message "Cannot tagify split info file")
39: (let ((regexp "Node:[ \t]*\\([^,\n\t]\\)*[,\t\n]")
40: (case-fold-search t)
41: list)
42: (while (search-forward "\n\^_" nil t)
43: (forward-line 1)
44: (let ((beg (point)))
45: (forward-line 1)
46: (if (re-search-backward regexp beg t)
47: (setq list
48: (cons (list (buffer-substring
49: (match-beginning 1)
50: (match-end 1))
51: beg)
52: list)))))
53: (goto-char (point-max))
54: (forward-line -8)
55: (let ((buffer-read-only nil))
56: (if (search-forward "\^_\nEnd tag table\n" nil t)
57: (let ((end (point)))
58: (search-backward "\nTag table:\n")
59: (beginning-of-line)
60: (delete-region (point) end)))
61: (goto-char (point-max))
62: (insert "\^_\f\nTag table:\n")
63: (move-marker Info-tag-table-marker (point))
64: (setq list (nreverse list))
65: (while list
66: (insert "Node: " (car (car list)) ?\177)
67: (princ (car (cdr (car list))) (current-buffer))
68: (insert ?\n)
69: (setq list (cdr list)))
70: (insert "\^_\nEnd tag table\n")))))
71: (goto-char opoint)
72: (narrow-to-region omin (if nomax (1+ (buffer-size))
73: (min omax (point-max)))))))
74:
75: (defun Info-split ()
76: "Split an info file into an indirect file plus bounded-size subfiles.
77: Each subfile will be up to 50000 characters plus one node.
78:
79: To use this command, first visit a large Info file that has a tag table.
80: The buffer is modified into a (small) indirect info file
81: which should be saved in place of the original visited file.
82:
83: The subfiles are written in the same directory the original file is in,
84: with names generated by appending `-' and a number to the original file name.
85:
86: The indirect file still functions as an Info file, but it contains
87: just the tag table and a directory of subfiles."
88: (interactive)
89: (if (< (buffer-size) 70000)
90: (error "This is too small to be worth splitting"))
91: (goto-char (point-min))
92: (search-forward "\^_")
93: (forward-char -1)
94: (let ((start (point))
95: (chars-deleted 0)
96: subfiles
97: (subfile-number 1)
98: (case-fold-search t)
99: (filename (file-name-sans-versions buffer-file-name)))
100: (goto-char (point-max))
101: (forward-line -8)
102: (setq buffer-read-only nil)
103: (or (search-forward "\^_\nEnd tag table\n" nil t)
104: (error "Tag table required; use M-x Info-tagify"))
105: (search-backward "\nTag table:\n")
106: (if (looking-at "\nTag table:\n\^_")
107: (error "Tag table is just a skeleton; use M-x Info-tagify"))
108: (beginning-of-line)
109: (forward-char 1)
110: (save-restriction
111: (narrow-to-region (point-min) (point))
112: (goto-char (point-min))
113: (while (< (1+ (point)) (point-max))
114: (goto-char (min (+ (point) 50000) (point-max)))
115: (search-forward "\^_" nil 'move)
116: (setq subfiles
117: (cons (list (+ start chars-deleted)
118: (concat (file-name-nondirectory filename)
119: (format "-%d" subfile-number)))
120: subfiles))
121: ;; Put a newline at end of split file, to make Unix happier.
122: (insert "\n")
123: (write-region (point-min) (point)
124: (concat filename (format "-%d" subfile-number)))
125: (delete-region (1- (point)) (point))
126: ;; Back up over the final ^_.
127: (forward-char -1)
128: (setq chars-deleted (+ chars-deleted (- (point) start)))
129: (delete-region start (point))
130: (setq subfile-number (1+ subfile-number))))
131: (while subfiles
132: (goto-char start)
133: (insert (nth 1 (car subfiles))
134: (format ": %d" (car (car subfiles)))
135: "\n")
136: (setq subfiles (cdr subfiles)))
137: (goto-char start)
138: (insert "\^_\nIndirect:\n")
139: (search-forward "\nTag Table:\n")
140: (insert "(Indirect)\n")))
141:
142: (defun Info-validate ()
143: "Check current buffer for validity as an Info file.
144: Check that every node pointer points to an existing node."
145: (interactive)
146: (save-excursion
147: (save-restriction
148: (widen)
149: (goto-char (point-min))
150: (if (search-forward "\nTag table:\n(Indirect)\n" nil t)
151: (error "Don't yet know how to validate indirect info files: \"%s\""
152: (buffer-name (current-buffer))))
153: (goto-char (point-min))
154: (let ((allnodes '(("*")))
155: (regexp "Node:[ \t]*\\([^,\n\t]*\\)[,\t\n]")
156: (case-fold-search t)
157: (tags-losing nil)
158: (lossages ()))
159: (while (search-forward "\n\^_" nil t)
160: (forward-line 1)
161: (let ((beg (point)))
162: (forward-line 1)
163: (if (re-search-backward regexp beg t)
164: (let ((name (downcase
165: (buffer-substring
166: (match-beginning 1)
167: (progn
168: (goto-char (match-end 1))
169: (skip-chars-backward " \t")
170: (point))))))
171: (if (assoc name allnodes)
172: (setq lossages
173: (cons (list name "Duplicate node-name" nil)
174: lossages))
175: (setq allnodes
176: (cons (list name
177: (progn
178: (end-of-line)
179: (and (re-search-backward
180: "prev[ious]*:" beg t)
181: (progn
182: (goto-char (match-end 0))
183: (downcase
184: (Info-following-node-name)))))
185: beg)
186: allnodes)))))))
187: (goto-char (point-min))
188: (while (search-forward "\n\^_" nil t)
189: (forward-line 1)
190: (let ((beg (point))
191: thisnode next)
192: (forward-line 1)
193: (if (re-search-backward regexp beg t)
194: (save-restriction
195: (search-forward "\n\^_" nil 'move)
196: (narrow-to-region beg (point))
197: (setq thisnode (downcase
198: (buffer-substring
199: (match-beginning 1)
200: (progn
201: (goto-char (match-end 1))
202: (skip-chars-backward " \t")
203: (point)))))
204: (end-of-line)
205: (and (search-backward "next:" nil t)
206: (setq next (Info-validate-node-name "invalid Next"))
207: (assoc next allnodes)
208: (if (equal (car (cdr (assoc next allnodes)))
209: thisnode)
210: ;; allow multiple `next' pointers to one node
211: (let ((tem lossages))
212: (while tem
213: (if (and (equal (car (cdr (car tem)))
214: "should have Previous")
215: (equal (car (car tem))
216: next))
217: (setq lossages (delq (car tem) lossages)))
218: (setq tem (cdr tem))))
219: (setq lossages
220: (cons (list next
221: "should have Previous"
222: thisnode)
223: lossages))))
224: (end-of-line)
225: (if (re-search-backward "prev[ious]*:" nil t)
226: (Info-validate-node-name "invalid Previous"))
227: (end-of-line)
228: (if (search-backward "up:" nil t)
229: (Info-validate-node-name "invalid Up"))
230: (if (re-search-forward "\n* Menu:" nil t)
231: (while (re-search-forward "\n\\* " nil t)
232: (Info-validate-node-name
233: (concat "invalid menu item "
234: (buffer-substring (point)
235: (save-excursion
236: (skip-chars-forward "^:")
237: (point))))
238: (Info-extract-menu-node-name))))
239: (goto-char (point-min))
240: (while (re-search-forward "\\*note[ \n]*[^:\t]*:" nil t)
241: (goto-char (+ (match-beginning 0) 5))
242: (skip-chars-forward " \n")
243: (Info-validate-node-name
244: (concat "invalid reference "
245: (buffer-substring (point)
246: (save-excursion
247: (skip-chars-forward "^:")
248: (point))))
249: (Info-extract-menu-node-name "Bad format cross-reference")))))))
250: (setq tags-losing (not (Info-validate-tags-table)))
251: (if (or lossages tags-losing)
252: (with-output-to-temp-buffer " *problems in info file*"
253: (while lossages
254: (princ "In node \"")
255: (princ (car (car lossages)))
256: (princ "\", ")
257: (let ((tem (nth 1 (car lossages))))
258: (cond ((string-match "\n" tem)
259: (princ (substring tem 0 (match-beginning 0)))
260: (princ "..."))
261: (t
262: (princ tem))))
263: (if (nth 2 (car lossages))
264: (progn
265: (princ ": ")
266: (let ((tem (nth 2 (car lossages))))
267: (cond ((string-match "\n" tem)
268: (princ (substring tem 0 (match-beginning 0)))
269: (princ "..."))
270: (t
271: (princ tem))))))
272: (terpri)
273: (setq lossages (cdr lossages)))
274: (if tags-losing (princ "\nTags table must be recomputed\n")))
275: ;; Here if info file is valid.
276: ;; If we already made a list of problems, clear it out.
277: (save-excursion
278: (if (get-buffer " *problems in info file*")
279: (progn
280: (set-buffer " *problems in info file*")
281: (kill-buffer (current-buffer)))))
282: (message "File appears valid"))))))
283:
284: (defun Info-validate-node-name (kind &optional name)
285: (if name
286: nil
287: (goto-char (match-end 0))
288: (skip-chars-forward " \t")
289: (if (= (following-char) ?\()
290: nil
291: (setq name
292: (buffer-substring
293: (point)
294: (progn
295: (skip-chars-forward "^,\t\n")
296: (skip-chars-backward " ")
297: (point))))))
298: (if (null name)
299: nil
300: (setq name (downcase name))
301: (or (and (> (length name) 0) (= (aref name 0) ?\())
302: (assoc name allnodes)
303: (setq lossages
304: (cons (list thisnode kind name) lossages))))
305: name)
306:
307: (defun Info-validate-tags-table ()
308: (goto-char (point-min))
309: (if (not (search-forward "\^_\nEnd tag table\n" nil t))
310: t
311: (not (catch 'losing
312: (let* ((end (match-beginning 0))
313: (start (progn (search-backward "\nTag table:\n")
314: (1- (match-end 0))))
315: tem)
316: (setq tem allnodes)
317: (while tem
318: (goto-char start)
319: (or (equal (car (car tem)) "*")
320: (search-forward (concat "Node: "
321: (car (car tem))
322: "\177")
323: end t)
324: (throw 'losing 'x))
325: (setq tem (cdr tem)))
326: (goto-char (1+ start))
327: (while (looking-at ".*Node: \\(.*\\)\177\\([0-9]+\\)$")
328: (setq tem (downcase (buffer-substring
329: (match-beginning 1)
330: (match-end 1))))
331: (setq tem (assoc tem allnodes))
332: (if (or (not tem)
333: (< 1000 (progn
334: (goto-char (match-beginning 2))
335: (setq tem (- (car (cdr (cdr tem)))
336: (read (current-buffer))))
337: (if (> tem 0) tem (- tem)))))
338: (throw 'losing 'y)))
339: (forward-line 1))
340: (or (looking-at "End tag table\n")
341: (throw 'losing 'z))
342: nil))))
343:
344: (defun batch-info-validate ()
345: "Runs Info-validate on the files remaining on the command line.
346: Must be used only with -batch, and kills emacs on completion.
347: Each file will be processed even if an error occurred previously.
348: For example, invoke \"emacs -batch -f batch-info-validate $info/ ~/*.info\""
349: (if (not noninteractive)
350: (error "batch-info-validate may only be used -batch."))
351: (let ((version-control t)
352: (auto-save-default nil)
353: (find-file-run-dired nil)
354: (kept-old-versions 259259)
355: (kept-new-versions 259259))
356: (let ((error 0)
357: file
358: (files ()))
359: (while command-line-args-left
360: (setq file (expand-file-name (car command-line-args-left)))
361: (cond ((not (file-exists-p file))
362: (message ">> %s does not exist!" file)
363: (setq error 1
364: command-line-args-left (cdr command-line-args-left)))
365: ((file-directory-p file)
366: (setq command-line-args-left (nconc (directory-files file)
367: (cdr command-line-args-left))))
368: (t
369: (setq files (cons file files)
370: command-line-args-left (cdr command-line-args-left)))))
371: (while files
372: (setq file (car files)
373: files (cdr files))
374: (let ((lose nil))
375: (condition-case err
376: (progn
377: (if buffer-file-name (kill-buffer (current-buffer)))
378: (find-file file)
379: (buffer-flush-undo (current-buffer))
380: (set-buffer-modified-p nil)
381: (fundamental-mode)
382: (let ((case-fold-search nil))
383: (goto-char (point-max))
384: (cond ((search-backward "\n\^_\^L\nTag table:\n" nil t)
385: (message "%s already tagified" file))
386: ((< (point-max) 30000)
387: (message "%s too small to bother tagifying" file))
388: (t
389: (message "Tagifying %s..." file)
390: (Info-tagify)
391: (message "Tagifying %s...done" file))))
392: (let ((loss-name " *problems in info file*"))
393: (message "Checking validity of info file %s..." file)
394: (if (get-buffer loss-name)
395: (kill-buffer loss-name))
396: (Info-validate)
397: (if (not (get-buffer loss-name))
398: nil ;(message "Checking validity of info file %s... OK" file)
399: (message "----------------------------------------------------------------------")
400: (message ">> PROBLEMS IN INFO FILE %s" file)
401: (save-excursion
402: (set-buffer loss-name)
403: (princ (buffer-substring (point-min) (point-max))))
404: (message "----------------------------------------------------------------------")
405: (setq error 1 lose t)))
406: (if (and (buffer-modified-p)
407: (not lose))
408: (progn (message "Saving modified %s" file)
409: (save-buffer))))
410: (error (message ">> Error: %s" (prin1-to-string err))))))
411: (kill-emacs error))))
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.