summaryrefslogtreecommitdiffstats
path: root/contrib/texinfo/emacs
diff options
context:
space:
mode:
authorjmacd <jmacd@FreeBSD.org>1997-01-11 02:12:38 +0000
committerjmacd <jmacd@FreeBSD.org>1997-01-11 02:12:38 +0000
commit58ff11de31aa3832448101bc51ddf144fd5a29b4 (patch)
treecc1b5b231b9a50d0219c9921e490665c5cea9eca /contrib/texinfo/emacs
downloadFreeBSD-src-58ff11de31aa3832448101bc51ddf144fd5a29b4.zip
FreeBSD-src-58ff11de31aa3832448101bc51ddf144fd5a29b4.tar.gz
This is unmodified GNU texinfo-3.9 source. I'll be commiting a few
patches in a bit. -josh
Diffstat (limited to 'contrib/texinfo/emacs')
-rw-r--r--contrib/texinfo/emacs/Makefile.in88
-rw-r--r--contrib/texinfo/emacs/detexinfo.el250
-rwxr-xr-xcontrib/texinfo/emacs/elisp-comp7
-rw-r--r--contrib/texinfo/emacs/info.el1846
-rw-r--r--contrib/texinfo/emacs/informat.el429
-rw-r--r--contrib/texinfo/emacs/makeinfo.el247
-rw-r--r--contrib/texinfo/emacs/new-useful-setqs180
-rw-r--r--contrib/texinfo/emacs/texinfmt.el3979
-rw-r--r--contrib/texinfo/emacs/texinfo.el932
-rw-r--r--contrib/texinfo/emacs/texnfo-tex.el346
-rw-r--r--contrib/texinfo/emacs/texnfo-upd.el2058
11 files changed, 10362 insertions, 0 deletions
diff --git a/contrib/texinfo/emacs/Makefile.in b/contrib/texinfo/emacs/Makefile.in
new file mode 100644
index 0000000..f011b33
--- /dev/null
+++ b/contrib/texinfo/emacs/Makefile.in
@@ -0,0 +1,88 @@
+# Makefile for Texinfo/emacs.
+# Copyright (C) 1995, 96 Free Software Foundation, Inc.
+# $Id: Makefile.in,v 1.4 1996/09/28 21:34:34 karl Exp $
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Author: Brian J. Fox (bfox@ai.mit.edu)
+#
+
+srcdir = @srcdir@
+VPATH = $(srcdir)
+SHELL = /bin/sh
+RM = rm -f
+
+
+ELISP_SRCS = info.el makeinfo.el texinfo.el texnfo-upd.el \
+ texnfo-tex.el texinfmt.el informat.el detexinfo.el
+ELISP_OBJS = info.elc makeinfo.elc texinfo.elc texnfo-upd.elc \
+ texnfo-tex.elc texinfmt.elc informat.elc detexinfo.elc
+
+.SUFFIXES: .el .elc
+
+.el.elc:
+ $(srcdir)/elisp-comp $<
+
+all:
+sub-all: all
+
+elisp: $(ELISP_OBJS)
+.PHONY: elisp
+
+# Nobody likes any of these install targets. Fine. Install it
+# manually, then.
+install:
+ @echo Please install the Emacs Lisp files manually.
+
+uninstall:
+ @echo Please uninstall the Emacs Lisp files manually.
+
+# install: $(ELISP_OBJS)
+# @(echo "(print (car load-path))" >/tmp/elc.$$$$; \
+# lispdir=`emacs -batch -q -l /tmp/elc.$$$$ -nw | grep site-lisp`; \
+# rm /tmp/elc.$$$$; \
+# if [ "$$lispdir" != "" ]; then \
+# lispdir=`echo $$lispdir | sed -e 's/"//g'`; \
+# echo "Installing .elc files in $$lispdir."; \
+# $(CP) $(ELISP_OBJS) $$lispdir; \
+# else \
+# echo "To install the elisp files, please copy *.elc to the"; \
+# echo "emacs site-lisp directory."; \
+# fi)
+#
+# install: $(ELISP_OBJS)
+# for file in $(ELISP_OBJS); do \
+# $(INSTALL_DATA) $$file $(lispdir); \
+# done
+#
+# uninstall: $(ELISP_OBJS)
+# cd $(lispdir) && rm -f $(ELISP_OBJS)
+#
+informat.elc: info.elc
+makeinfo.elc: texinfo.elc
+texinfmt.elc: texinfo.elc
+texinfmt.elc: texnfo-upd.elc
+
+Makefile: $(srcdir)/Makefile.in ../config.status
+ cd .. && sh config.status
+
+realclean distclean: clean
+ $(RM) Makefile *.log
+
+clean: FORCE
+ $(RM) *.elc
+
+FORCE:
+
diff --git a/contrib/texinfo/emacs/detexinfo.el b/contrib/texinfo/emacs/detexinfo.el
new file mode 100644
index 0000000..fda9909
--- /dev/null
+++ b/contrib/texinfo/emacs/detexinfo.el
@@ -0,0 +1,250 @@
+;;; Here is a handy keybinding:
+
+(global-set-key "\C-x\\" 'detexinfo)
+
+;;;;;;;;;;;;;;;; detexinfo.el ;;;;;;;;;;;;;;;;
+;;;
+;;; Remove Texinfo commands from a Texinfo source file.
+;;;
+;;; Copyright (C) 1991, 1992 Free Software Foundation
+;;; Robert J. Chassell
+;;; bugs to bug-texinfo@prep.ai.mit.edu
+;;;
+;;; ==> test version <==
+;;; Fails if Texinfo source file contains formatting errors.
+;;;
+;;; Version 0.05 - 3 Jun 1992
+;;; Add to list of removed commands. Improve messages.
+;;;
+;;; Version 0.04 - 27 Jan 1992
+;;; Rewrite to insert detexinfo'd text into a temporary buffer.
+;;;
+;;; Version 0.03 - 27 Dec 1991
+;;; Improved messages.
+;;;
+;;; Version 0.02 - 13 Nov 1991
+;;; detexinfo-remove-inline-cmd, detexinfo-syntax-table: Handle
+;;; nested commands.
+;;; detexinfo: Handle nested @'s, eg @samp{@}} and @samp{@@};
+;;; replace @TeX{} with TeX.
+;;;
+;;; Version 0.01 - 13 Nov 1991
+;;;
+;;; Based on detex.el, by Bengt Martensson, 4 Oct 1987
+;;;
+;;;;;;;;;;;;;;;;
+
+(defvar detexinfo-buffer-name "*detexinfo*"
+ "*Name of the temporary buffer used by \\[detexinfo].")
+
+(defvar detexinfo-syntax-table nil)
+
+(if detexinfo-syntax-table
+ nil
+ (setq detexinfo-syntax-table (make-syntax-table))
+ (modify-syntax-entry ?\[ "." detexinfo-syntax-table)
+ (modify-syntax-entry ?\] "." detexinfo-syntax-table)
+ (modify-syntax-entry ?\" "." detexinfo-syntax-table)
+ (modify-syntax-entry ?\\ "." detexinfo-syntax-table)
+ (modify-syntax-entry ?\( "." detexinfo-syntax-table)
+ (modify-syntax-entry ?\) "." detexinfo-syntax-table)
+ (modify-syntax-entry ?{ "(}" detexinfo-syntax-table)
+ (modify-syntax-entry ?} "){" detexinfo-syntax-table))
+
+(defun detexinfo ()
+ "Remove Texinfo commands from current buffer, copying result to new buffer.
+BUG: Fails if Texinfo source file contains formatting errors."
+ (interactive)
+ (let ((input-buffer (current-buffer)))
+ ;; Find a buffer to use.
+ (switch-to-buffer (get-buffer-create detexinfo-buffer-name))
+ (setq major-mode 'detexinfo-mode)
+ (set-syntax-table detexinfo-syntax-table)
+ (erase-buffer)
+ (insert-buffer-substring input-buffer)
+
+ ;; Replace @{ and @} with %#* and *#% temporarily, so @samp{@{} works.
+ ;; What is a better way of doing this??
+ (goto-char (point-min))
+ (while (search-forward "@{" nil t) ; e.g., @samp{@{}
+ (replace-match "%#*"))
+ (goto-char (point-min))
+ (while (search-forward "@}" nil t)
+ (forward-char -3) ; e.g., @samp{@@}
+ (if (looking-at "@") ; Two @@ in a row
+ (progn
+ (delete-char 2)
+ (insert "%&%#"))
+ (forward-char 1)
+ (delete-char 2)
+ (insert "*#%")))
+
+ (goto-char (point-min))
+ ;; Remove @refill, the only inline command without braces.
+ (while (search-forward "@refill" nil t)
+ (replace-match ""))
+ ;; Replace @TeX{} with TeX
+ (goto-char (point-min))
+ (while (search-forward "@TeX{}" nil t) (replace-match "TeX" t t))
+
+ (detexinfo-remove-line-cmds-without-arg)
+ (detexinfo-remove-inline-cmds-without-arg)
+ (detexinfo-remove-inline-cmds-keep-arg)
+ (detexinfo-remove-line-cmds-deletable-arg)
+ (detexinfo-remove-line-cmds-maybe-delete-arg)
+ (detexinfo-remove-line-cmds-keep-arg)
+
+ ;; Now replace %#*, *#%, and %&%# with {, }, and @@.
+ (goto-char (point-min))
+ (while (search-forward "%#*" nil t)
+ (replace-match "{"))
+ (goto-char (point-min))
+ (while (search-forward "*#%" nil t)
+ (replace-match "}"))
+ (goto-char (point-min))
+ (while (search-forward "%&%#" nil t)
+ (replace-match "@@"))
+
+ ;; Scan for remaining two character @-commands
+ (goto-char (point-min))
+ (while (search-forward "@" nil t)
+ (cond ((looking-at "[*:]")
+ (delete-region (1- (point)) (1+ (point))))
+ ((looking-at "[{}^@.'`]\"?!")
+ (delete-region (1- (point)) (point)))))
+
+ (goto-char (point-min))
+ (message "Done...removed Texinfo commands from buffer. You may save it.")))
+
+(defun detexinfo-remove-whole-line (cmd)
+ "Delete Texinfo line command CMD at beginning of line and rest of line."
+ (goto-char (point-min))
+ (while
+ (re-search-forward
+ (concat "^@" cmd "[ \n]+") (point-max) t)
+ (goto-char (match-beginning 0))
+ (delete-region
+ (point) (save-excursion (end-of-line) (1+ (point))))))
+
+(defun detexinfo-remove-inline-cmd (cmd)
+ "Delete Texinfo inline command CMD, eg. @point, @code."
+ (goto-char (point-min))
+ (while
+ (re-search-forward (concat "@" cmd "{") (point-max) t)
+ (save-excursion
+ (forward-char -1)
+ (forward-sexp 1)
+ (delete-char -1)) ; delete right brace
+ (delete-region (point) (match-beginning 0))))
+
+;;;;;;;;;;;;;;;;
+
+;;; 1. @setfilename and other line commands with args to delete
+
+(defvar detexinfo-line-cmds-deletable-arg
+ '("enumerate" "ftable" "vtable" "itemize" "table"
+ "setfilename" "settitle" "setchapternewpage"
+ "footnotestyle" "paragraphindent"
+ "include" "need" "sp"
+ "clear" "ifclear" "ifset" "set"
+ "defcodeindex" "defindex" "syncodeindex" "synindex")
+ "List of Texinfo commands whose arguments should be deleted.")
+
+(defun detexinfo-remove-line-cmds-deletable-arg ()
+ "Delete Texinfo line commands together with their args, eg @setfilename."
+ (message "Removing commands such as @enumerate...with their arguments...")
+ (mapcar 'detexinfo-remove-whole-line
+ detexinfo-line-cmds-deletable-arg))
+
+;;; 2. @cindex and other cmds with args that may be deleted
+;;; This list is here just to make it easier to revise the
+;;; categories. In particular, you might want to keep the index entries.
+
+(defvar detexinfo-line-cmds-maybe-delete-arg
+ '("cindex" "findex" "kindex" "pindex" "tindex" "vindex" "node"
+ "c" "comment" "end" "headings" "printindex" "vskip"
+ "evenfooting" "evenheading" "everyfooting" "everyheading"
+ "oddfooting" "oddheading")
+ "List of Texinfo commands whose arguments may possibly be deleted.")
+
+(defun detexinfo-remove-line-cmds-maybe-delete-arg ()
+ "Delete Texinfo line commands together with their arguments, eg, @cindex."
+ (message "Removing commands such as @cindex...with their arguments...")
+ (mapcar 'detexinfo-remove-whole-line
+ detexinfo-line-cmds-maybe-delete-arg))
+
+;;; 3. @chapter and other line cmds with args to keep.
+
+(defvar detexinfo-line-cmds-keep-arg
+ '("top" "chapter" "section" "subsection" "subsubsection"
+ "unnumbered" "unnumberedsec" "unnumberedsubsec" "unnumberedsubsubsec"
+ "majorheading" "chapheading" "heading" "subheading" "subsubheading"
+ "appendix" "appendixsec" "appendixsubsec" "appendixsubsubsec"
+ "item" "itemx"
+ "title" "subtitle" "center" "author" "exdent"
+ "defcv" "deffn" "defivar" "defmac" "defmethod" "defop" "defopt"
+ "defspec" "deftp" "deftypefn" "deftypefun" "deftypvr"
+ "deftypevar" "defun" "defvar" "defvr")
+ "List of Texinfo line commands whose arguments should be kept.")
+
+(defun detexinfo-remove-line-cmds-keep-arg ()
+ "Delete Texinfo line commands but keep their arguments, eg @chapter."
+ (message "Removing commands such as @chapter...but not their arguments...")
+ (mapcar 'detexinfo-remove-line-cmd-keep-arg
+ detexinfo-line-cmds-keep-arg))
+
+(defun detexinfo-remove-line-cmd-keep-arg (cmd)
+ "Delete Texinfo line command CMD but keep its argument, eg @chapter."
+ (goto-char (point-min))
+ (while
+ (re-search-forward
+ (concat "^@" cmd "[ \n]+") (point-max) t)
+ (delete-region (match-beginning 0) (match-end 0))))
+
+;;; 4. @bye and other line commands without args.
+
+(defvar detexinfo-line-cmds-without-arg
+ '("bye" "contents" "display" "example" "finalout"
+ "flushleft" "flushright" "format" "group" "ifhtml" "ifinfo" "iftex"
+ "ignore" "lisp" "menu" "noindent" "page" "quotation"
+ "shortcontents" "smallbook" "smallexample" "smalllisp"
+ "summarycontents" "tex" "thischapter" "thischaptername"
+ "thisfile" "thispage" "thissection" "thistitle" "titlepage")
+ "List of Texinfo commands without arguments that should be deleted.")
+
+(defun detexinfo-remove-line-cmds-without-arg ()
+ "Delete line Texinfo commands that lack args, eg. @example."
+ (message "Removing commands such as @example...that lack arguments...")
+ (mapcar 'detexinfo-remove-whole-line
+ detexinfo-line-cmds-without-arg))
+
+;;; 5. @equiv and other inline cmds without args.
+
+(defvar detexinfo-inline-cmds-without-arg
+ '("equiv" "error" "expansion" "point" "print" "result"
+ "asis" "br" "bullet" "dots" "minus" "today")
+ "List of Texinfo inline commands without arguments that should be deleted.")
+
+(defun detexinfo-remove-inline-cmds-without-arg ()
+ "Delete Texinfo inline commands in that lack arguments."
+ (message "Removing within line commands such as @result...")
+ (mapcar 'detexinfo-remove-inline-cmd
+ detexinfo-inline-cmds-without-arg))
+
+;;; 6. @code and other inline cmds with args to keep
+
+(defvar detexinfo-inline-cmds-keep-arg
+ '("b" "cartouche" "cite" "code" "copyright" "ctrl" "dfn" "dmn"
+ "emph" "file" "footnote" "i" "inforef"
+ "kbd" "key" "pxref" "r" "ref" "samp" "sc" "titlefont"
+ "strong" "t" "var" "w" "xref")
+ "List of Texinfo inline commands with arguments that should be kept.")
+
+(defun detexinfo-remove-inline-cmds-keep-arg ()
+ "Delete Texinfo inline commands but keep its arg, eg. @code."
+ (message
+ "Removing within line commands such as @code...but not their arguments...")
+ (mapcar 'detexinfo-remove-inline-cmd
+ detexinfo-inline-cmds-keep-arg))
+
+;;;;;;;;;;;;;;;; end detexinfo.el ;;;;;;;;;;;;;;;;
diff --git a/contrib/texinfo/emacs/elisp-comp b/contrib/texinfo/emacs/elisp-comp
new file mode 100755
index 0000000..6505826
--- /dev/null
+++ b/contrib/texinfo/emacs/elisp-comp
@@ -0,0 +1,7 @@
+#!/bin/sh
+# $Id: elisp-comp,v 1.2 1996/09/26 23:41:08 karl Exp $
+# Trivial script to compile the Elisp files.
+setpath=${TMPDIR-/tmp}/elc.$$
+echo "(setq load-path (cons nil load-path))" > $setpath
+emacs -batch -l $setpath -f batch-byte-compile "$@"
+rm -f $setpath
diff --git a/contrib/texinfo/emacs/info.el b/contrib/texinfo/emacs/info.el
new file mode 100644
index 0000000..ead6ab92
--- /dev/null
+++ b/contrib/texinfo/emacs/info.el
@@ -0,0 +1,1846 @@
+;;; info.el --- info package for Emacs.
+
+;; Copyright (C) 1985, 1986, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+;; Maintainer: FSF
+;; Keywords: help
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Note that nowadays we expect info files to be made using makeinfo.
+
+;;; Code:
+
+(defvar Info-history nil
+ "List of info nodes user has visited.
+Each element of list is a list (FILENAME NODENAME BUFFERPOS).")
+
+(defvar Info-enable-edit nil
+ "*Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node.
+This is convenient if you want to write info files by hand.
+However, we recommend that you not do this.
+It is better to write a Texinfo file and generate the Info file from that,
+because that gives you a printed manual as well.")
+
+(defvar Info-enable-active-nodes nil
+ "Non-nil allows Info to execute Lisp code associated with nodes.
+The Lisp code is executed when the node is selected.")
+(put 'Info-enable-active-nodes 'risky-local-variable t)
+
+(defvar Info-fontify t
+ "*Non-nil enables highlighting and fonts in Info nodes.")
+
+(defvar Info-fontify-maximum-menu-size 30000
+ "*Maximum size of menu to fontify if `Info-fontify' is non-nil.")
+
+(defvar Info-directory-list
+ (let ((path (getenv "INFOPATH"))
+ ;; This is for older Emacs versions
+ ;; which might get this info.el from the Texinfo distribution.
+ (path-separator (if (boundp 'path-separator) path-separator
+ (if (eq system-type 'ms-dos) ";" ":")))
+ (source (expand-file-name "info/" source-directory))
+ (sibling (if installation-directory
+ (expand-file-name "info/" installation-directory)))
+ alternative)
+ (if path
+ (let ((list nil)
+ idx)
+ (while (> (length path) 0)
+ (setq idx (or (string-match path-separator path) (length path))
+ list (cons (substring path 0 idx) list)
+ path (substring path (min (1+ idx)
+ (length path)))))
+ (nreverse list))
+ (if (and sibling (file-exists-p sibling))
+ (setq alternative sibling)
+ (setq alternative source))
+ (if (or (member alternative Info-default-directory-list)
+ (not (file-exists-p alternative))
+ ;; On DOS/NT, we use movable executables always,
+ ;; and we must always find the Info dir at run time.
+ (if (or (eq system-type 'ms-dos) (eq system-type 'windows-nt))
+ nil
+ ;; Use invocation-directory for Info only if we used it for
+ ;; exec-directory also.
+ (not (string= exec-directory
+ (expand-file-name "lib-src/"
+ installation-directory)))))
+ Info-default-directory-list
+ (reverse (cons alternative
+ (cdr (reverse Info-default-directory-list)))))))
+ "List of directories to search for Info documentation files.
+nil means not yet initialized. In this case, Info uses the environment
+variable INFOPATH to initialize it, or `Info-default-directory-list'
+if there is no INFOPATH variable in the environment.
+The last element of `Info-default-directory-list' is the directory
+where Emacs installs the Info files that come with it.
+
+If you run the Emacs executable from the `src' directory in the Emacs
+source tree, the `info' directory in the source tree is used as the last
+element, in place of the installation Info directory. This is useful
+when you run a version of Emacs without installing it.")
+
+(defvar Info-additional-directory-list nil
+ "List of additional directories to search for Info documentation files.
+These directories are not searched for merging the `dir' file.")
+
+(defvar Info-current-file nil
+ "Info file that Info is now looking at, or nil.
+This is the name that was specified in Info, not the actual file name.
+It doesn't contain directory names or file name extensions added by Info.")
+
+(defvar Info-current-subfile nil
+ "Info subfile that is actually in the *info* buffer now,
+or nil if current info file is not split into subfiles.")
+
+(defvar Info-current-node nil
+ "Name of node that Info is now looking at, or nil.")
+
+(defvar Info-tag-table-marker (make-marker)
+ "Marker pointing at beginning of current Info file's tag table.
+Marker points nowhere if file has no tag table.")
+
+(defvar Info-current-file-completions nil
+ "Cached completion list for current Info file.")
+
+(defvar Info-index-alternatives nil
+ "List of possible matches for last Info-index command.")
+
+(defvar Info-standalone nil
+ "Non-nil if Emacs was started solely as an Info browser.")
+
+(defvar Info-suffix-list
+ (if (eq system-type 'ms-dos)
+ '( (".gz" . "gunzip")
+ (".z" . "gunzip")
+ (".inf" . nil)
+ ("" . nil))
+ '( (".info.Z" . "uncompress")
+ (".info.Y" . "unyabba")
+ (".info.gz" . "gunzip")
+ (".info.z" . "gunzip")
+ (".info" . nil)
+ (".Z" . "uncompress")
+ (".Y" . "unyabba")
+ (".gz" . "gunzip")
+ (".z" . "gunzip")
+ ("" . nil)))
+ "List of file name suffixes and associated decoding commands.
+Each entry should be (SUFFIX . STRING); the file is given to
+the command as standard input. If STRING is nil, no decoding is done.
+Because the SUFFIXes are tried in order, the empty string should
+be last in the list.")
+
+;; Concatenate SUFFIX onto FILENAME. SUFFIX should start with a dot.
+;; First, on ms-dos, delete some of the extension in FILENAME
+;; to make room.
+(defun info-insert-file-contents-1 (filename suffix)
+ (if (not (eq system-type 'ms-dos))
+ (concat filename suffix)
+ (let* ((sans-exts (file-name-sans-extension filename))
+ ;; How long is the extension in FILENAME (not counting the dot).
+ (ext-len (max 0 (- (length filename) (length sans-exts) 1)))
+ ext-left)
+ ;; SUFFIX starts with a dot. If FILENAME already has one,
+ ;; get rid of the one in SUFFIX (unless suffix is empty).
+ (or (and (<= ext-len 0)
+ (not (eq (aref filename (1- (length filename))) ?.)))
+ (= (length suffix) 0)
+ (setq suffix (substring suffix 1)))
+ ;; How many chars of that extension should we keep?
+ (setq ext-left (min ext-len (max 0 (- 3 (length suffix)))))
+ ;; Get rid of the rest of the extension, and add SUFFIX.
+ (concat (substring filename 0 (- (length filename)
+ (- ext-len ext-left)))
+ suffix))))
+
+(defun info-insert-file-contents (filename &optional visit)
+ "Insert the contents of an info file in the current buffer.
+Do the right thing if the file has been compressed or zipped."
+ (let ((tail Info-suffix-list)
+ fullname decoder)
+ (if (file-exists-p filename)
+ ;; FILENAME exists--see if that name contains a suffix.
+ ;; If so, set DECODE accordingly.
+ (progn
+ (while (and tail
+ (not (string-match
+ (concat (regexp-quote (car (car tail))) "$")
+ filename)))
+ (setq tail (cdr tail)))
+ (setq fullname filename
+ decoder (cdr (car tail))))
+ ;; Try adding suffixes to FILENAME and see if we can find something.
+ (while (and tail
+ (not (file-exists-p (info-insert-file-contents-1
+ filename (car (car tail))))))
+ (setq tail (cdr tail)))
+ ;; If we found a file with a suffix, set DECODER according to the suffix
+ ;; and set FULLNAME to the file's actual name.
+ (setq fullname (info-insert-file-contents-1 filename (car (car tail)))
+ decoder (cdr (car tail)))
+ (or tail
+ (error "Can't find %s or any compressed version of it" filename)))
+ ;; check for conflict with jka-compr
+ (if (and (featurep 'jka-compr)
+ (jka-compr-installed-p)
+ (jka-compr-get-compression-info fullname))
+ (setq decoder nil))
+ (insert-file-contents fullname visit)
+ (if decoder
+ (let ((buffer-read-only nil)
+ (default-directory (or (file-name-directory fullname)
+ default-directory)))
+ (call-process-region (point-min) (point-max) decoder t t)))))
+
+;;;###autoload (add-hook 'same-window-buffer-names "*info*")
+
+;;;###autoload
+(defun info (&optional file)
+ "Enter Info, the documentation browser.
+Optional argument FILE specifies the file to examine;
+the default is the top-level directory of Info.
+
+In interactive use, a prefix argument directs this command
+to read a file name from the minibuffer.
+
+The search path for Info files is in the variable `Info-directory-list'.
+The top-level Info directory is made by combining all the files named `dir'
+in all the directories in that path."
+ (interactive (if current-prefix-arg
+ (list (read-file-name "Info file name: " nil nil t))))
+ (if file
+ (Info-goto-node (concat "(" file ")"))
+ (if (get-buffer "*info*")
+ (pop-to-buffer "*info*")
+ (Info-directory))))
+
+;;;###autoload
+(defun info-standalone ()
+ "Run Emacs as a standalone Info reader.
+Usage: emacs -f info-standalone [filename]
+In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
+ (setq Info-standalone t)
+ (if (and command-line-args-left
+ (not (string-match "^-" (car command-line-args-left))))
+ (condition-case err
+ (progn
+ (info (car command-line-args-left))
+ (setq command-line-args-left (cdr command-line-args-left)))
+ (error (send-string-to-terminal
+ (format "%s\n" (if (eq (car-safe err) 'error)
+ (nth 1 err) err)))
+ (save-buffers-kill-emacs)))
+ (info)))
+
+;; Go to an info node specified as separate filename and nodename.
+;; no-going-back is non-nil if recovering from an error in this function;
+;; it says do not attempt further (recursive) error recovery.
+(defun Info-find-node (filename nodename &optional no-going-back)
+ ;; Convert filename to lower case if not found as specified.
+ ;; Expand it.
+ (if filename
+ (let (temp temp-downcase found)
+ (setq filename (substitute-in-file-name filename))
+ (if (string= (downcase filename) "dir")
+ (setq found t)
+ (let ((dirs (if (string-match "^\\./" filename)
+ ;; If specified name starts with `./'
+ ;; then just try current directory.
+ '("./")
+ (if (file-name-absolute-p filename)
+ ;; No point in searching for an
+ ;; absolute file name
+ '(nil)
+ (if Info-additional-directory-list
+ (append Info-directory-list
+ Info-additional-directory-list)
+ Info-directory-list)))))
+ ;; Search the directory list for file FILENAME.
+ (while (and dirs (not found))
+ (setq temp (expand-file-name filename (car dirs)))
+ (setq temp-downcase
+ (expand-file-name (downcase filename) (car dirs)))
+ ;; Try several variants of specified name.
+ (let ((suffix-list Info-suffix-list))
+ (while (and suffix-list (not found))
+ (cond ((file-exists-p
+ (info-insert-file-contents-1
+ temp (car (car suffix-list))))
+ (setq found temp))
+ ((file-exists-p
+ (info-insert-file-contents-1
+ temp-downcase (car (car suffix-list))))
+ (setq found temp-downcase)))
+ (setq suffix-list (cdr suffix-list))))
+ (setq dirs (cdr dirs)))))
+ (if found
+ (setq filename found)
+ (error "Info file %s does not exist" filename))))
+ ;; Record the node we are leaving.
+ (if (and Info-current-file (not no-going-back))
+ (setq Info-history
+ (cons (list Info-current-file Info-current-node (point))
+ Info-history)))
+ ;; Go into info buffer.
+ (switch-to-buffer "*info*")
+ (buffer-disable-undo (current-buffer))
+ (or (eq major-mode 'Info-mode)
+ (Info-mode))
+ (widen)
+ (setq Info-current-node nil)
+ (unwind-protect
+ (progn
+ ;; Switch files if necessary
+ (or (null filename)
+ (equal Info-current-file filename)
+ (let ((buffer-read-only nil))
+ (setq Info-current-file nil
+ Info-current-subfile nil
+ Info-current-file-completions nil
+ Info-index-alternatives nil
+ buffer-file-name nil)
+ (erase-buffer)
+ (if (eq filename t)
+ (Info-insert-dir)
+ (info-insert-file-contents filename t)
+ (setq default-directory (file-name-directory filename)))
+ (set-buffer-modified-p nil)
+ ;; See whether file has a tag table. Record the location if yes.
+ (set-marker Info-tag-table-marker nil)
+ (goto-char (point-max))
+ (forward-line -8)
+ ;; Use string-equal, not equal, to ignore text props.
+ (or (string-equal nodename "*")
+ (not (search-forward "\^_\nEnd tag table\n" nil t))
+ (let (pos)
+ ;; We have a tag table. Find its beginning.
+ ;; Is this an indirect file?
+ (search-backward "\nTag table:\n")
+ (setq pos (point))
+ (if (save-excursion
+ (forward-line 2)
+ (looking-at "(Indirect)\n"))
+ ;; It is indirect. Copy it to another buffer
+ ;; and record that the tag table is in that buffer.
+ (save-excursion
+ (let ((buf (current-buffer)))
+ (set-buffer (get-buffer-create " *info tag table*"))
+ (buffer-disable-undo (current-buffer))
+ (setq case-fold-search t)
+ (erase-buffer)
+ (insert-buffer-substring buf)
+ (set-marker Info-tag-table-marker
+ (match-end 0))))
+ (set-marker Info-tag-table-marker pos))))
+ (setq Info-current-file
+ (if (eq filename t) "dir" filename))))
+ ;; Use string-equal, not equal, to ignore text props.
+ (if (string-equal nodename "*")
+ (progn (setq Info-current-node nodename)
+ (Info-set-mode-line))
+ ;; Search file for a suitable node.
+ (let ((guesspos (point-min))
+ (regexp (concat "Node: *" (regexp-quote nodename) " *[,\t\n\177]")))
+ ;; First get advice from tag table if file has one.
+ ;; Also, if this is an indirect info file,
+ ;; read the proper subfile into this buffer.
+ (if (marker-position Info-tag-table-marker)
+ (save-excursion
+ (set-buffer (marker-buffer Info-tag-table-marker))
+ (goto-char Info-tag-table-marker)
+ (if (re-search-forward regexp nil t)
+ (progn
+ (setq guesspos (read (current-buffer)))
+ ;; If this is an indirect file,
+ ;; determine which file really holds this node
+ ;; and read it in.
+ (if (not (eq (current-buffer) (get-buffer "*info*")))
+ (setq guesspos
+ (Info-read-subfile guesspos))))
+ (error "No such node: %s" nodename))))
+ (goto-char (max (point-min) (- guesspos 1000)))
+ ;; Now search from our advised position (or from beg of buffer)
+ ;; to find the actual node.
+ (catch 'foo
+ (while (search-forward "\n\^_" nil t)
+ (forward-line 1)
+ (let ((beg (point)))
+ (forward-line 1)
+ (if (re-search-backward regexp beg t)
+ (throw 'foo t))))
+ (error "No such node: %s" nodename)))
+ (Info-select-node)))
+ ;; If we did not finish finding the specified node,
+ ;; go back to the previous one.
+ (or Info-current-node no-going-back (null Info-history)
+ (let ((hist (car Info-history)))
+ (setq Info-history (cdr Info-history))
+ (Info-find-node (nth 0 hist) (nth 1 hist) t)
+ (goto-char (nth 2 hist)))))
+ (goto-char (point-min)))
+
+;; Cache the contents of the (virtual) dir file, once we have merged
+;; it for the first time, so we can save time subsequently.
+(defvar Info-dir-contents nil)
+
+;; Cache for the directory we decided to use for the default-directory
+;; of the merged dir text.
+(defvar Info-dir-contents-directory nil)
+
+;; Record the file attributes of all the files from which we
+;; constructed Info-dir-contents.
+(defvar Info-dir-file-attributes nil)
+
+;; Construct the Info directory node by merging the files named `dir'
+;; from various directories. Set the *info* buffer's
+;; default-directory to the first directory we actually get any text
+;; from.
+(defun Info-insert-dir ()
+ (if (and Info-dir-contents Info-dir-file-attributes
+ ;; Verify that none of the files we used has changed
+ ;; since we used it.
+ (eval (cons 'and
+ (mapcar '(lambda (elt)
+ (let ((curr (file-attributes (car elt))))
+ ;; Don't compare the access time.
+ (if curr (setcar (nthcdr 4 curr) 0))
+ (setcar (nthcdr 4 (cdr elt)) 0)
+ (equal (cdr elt) curr)))
+ Info-dir-file-attributes))))
+ (insert Info-dir-contents)
+ (let ((dirs Info-directory-list)
+ buffers buffer others nodes dirs-done)
+
+ (setq Info-dir-file-attributes nil)
+
+ ;; Search the directory list for the directory file.
+ (while dirs
+ (let ((truename (file-truename (expand-file-name (car dirs)))))
+ (or (member truename dirs-done)
+ (member (directory-file-name truename) dirs-done)
+ ;; Try several variants of specified name.
+ ;; Try upcasing, appending `.info', or both.
+ (let* (file
+ (attrs
+ (or
+ (progn (setq file (expand-file-name "dir" truename))
+ (file-attributes file))
+ (progn (setq file (expand-file-name "DIR" truename))
+ (file-attributes file))
+ (progn (setq file (expand-file-name "dir.info" truename))
+ (file-attributes file))
+ (progn (setq file (expand-file-name "DIR.INFO" truename))
+ (file-attributes file)))))
+ (setq dirs-done
+ (cons truename
+ (cons (directory-file-name truename)
+ dirs-done)))
+ (if attrs
+ (save-excursion
+ (or buffers
+ (message "Composing main Info directory..."))
+ (set-buffer (generate-new-buffer "info dir"))
+ (insert-file-contents file)
+ (setq buffers (cons (current-buffer) buffers)
+ Info-dir-file-attributes
+ (cons (cons file attrs)
+ Info-dir-file-attributes))))))
+ (or (cdr dirs) (setq Info-dir-contents-directory (car dirs)))
+ (setq dirs (cdr dirs))))
+
+ (or buffers
+ (error "Can't find the Info directory node"))
+ ;; Distinguish the dir file that comes with Emacs from all the
+ ;; others. Yes, that is really what this is supposed to do.
+ ;; If it doesn't work, fix it.
+ (setq buffer (car buffers)
+ others (cdr buffers))
+
+ ;; Insert the entire original dir file as a start; note that we've
+ ;; already saved its default directory to use as the default
+ ;; directory for the whole concatenation.
+ (insert-buffer buffer)
+
+ ;; Look at each of the other buffers one by one.
+ (while others
+ (let ((other (car others)))
+ ;; In each, find all the menus.
+ (save-excursion
+ (set-buffer other)
+ (goto-char (point-min))
+ ;; Find each menu, and add an elt to NODES for it.
+ (while (re-search-forward "^\\* Menu:" nil t)
+ (let (beg nodename end)
+ (forward-line 1)
+ (setq beg (point))
+ (search-backward "\n\^_")
+ (search-forward "Node: ")
+ (setq nodename (Info-following-node-name))
+ (search-forward "\n\^_" nil 'move)
+ (beginning-of-line)
+ (setq end (point))
+ (setq nodes (cons (list nodename other beg end) nodes))))))
+ (setq others (cdr others)))
+ ;; Add to the main menu a menu item for each other node.
+ (re-search-forward "^\\* Menu:")
+ (forward-line 1)
+ (let ((menu-items '("top"))
+ (nodes nodes)
+ (case-fold-search t)
+ (end (save-excursion (search-forward "\^_" nil t) (point))))
+ (while nodes
+ (let ((nodename (car (car nodes))))
+ (save-excursion
+ (or (member (downcase nodename) menu-items)
+ (re-search-forward (concat "^\\* "
+ (regexp-quote nodename)
+ "::")
+ end t)
+ (progn
+ (insert "* " nodename "::" "\n")
+ (setq menu-items (cons nodename menu-items))))))
+ (setq nodes (cdr nodes))))
+ ;; Now take each node of each of the other buffers
+ ;; and merge it into the main buffer.
+ (while nodes
+ (let ((nodename (car (car nodes))))
+ (goto-char (point-min))
+ ;; Find the like-named node in the main buffer.
+ (if (re-search-forward (concat "\n\^_.*\n.*Node: "
+ (regexp-quote nodename)
+ "[,\n\t]")
+ nil t)
+ (progn
+ (search-forward "\n\^_" nil 'move)
+ (beginning-of-line)
+ (insert "\n"))
+ ;; If none exists, add one.
+ (goto-char (point-max))
+ (insert "\^_\nFile: dir\tNode: " nodename "\n\n* Menu:\n\n"))
+ ;; Merge the text from the other buffer's menu
+ ;; into the menu in the like-named node in the main buffer.
+ (apply 'insert-buffer-substring (cdr (car nodes))))
+ (setq nodes (cdr nodes)))
+ ;; Kill all the buffers we just made.
+ (while buffers
+ (kill-buffer (car buffers))
+ (setq buffers (cdr buffers)))
+ (message "Composing main Info directory...done"))
+ (setq Info-dir-contents (buffer-string)))
+ (setq default-directory Info-dir-contents-directory))
+
+(defun Info-read-subfile (nodepos)
+ (set-buffer (marker-buffer Info-tag-table-marker))
+ (goto-char (point-min))
+ (search-forward "\n\^_")
+ (let (lastfilepos
+ lastfilename)
+ (forward-line 2)
+ (catch 'foo
+ (while (not (looking-at "\^_"))
+ (if (not (eolp))
+ (let ((beg (point))
+ thisfilepos thisfilename)
+ (search-forward ": ")
+ (setq thisfilename (buffer-substring beg (- (point) 2)))
+ (setq thisfilepos (read (current-buffer)))
+ ;; read in version 19 stops at the end of number.
+ ;; Advance to the next line.
+ (forward-line 1)
+ (if (> thisfilepos nodepos)
+ (throw 'foo t))
+ (setq lastfilename thisfilename)
+ (setq lastfilepos thisfilepos))
+ (forward-line 1))))
+ (set-buffer (get-buffer "*info*"))
+ (or (equal Info-current-subfile lastfilename)
+ (let ((buffer-read-only nil))
+ (setq buffer-file-name nil)
+ (widen)
+ (erase-buffer)
+ (info-insert-file-contents lastfilename)
+ (set-buffer-modified-p nil)
+ (setq Info-current-subfile lastfilename)))
+ (goto-char (point-min))
+ (search-forward "\n\^_")
+ (+ (- nodepos lastfilepos) (point))))
+
+;; Select the info node that point is in.
+(defun Info-select-node ()
+ (save-excursion
+ ;; Find beginning of node.
+ (search-backward "\n\^_")
+ (forward-line 2)
+ ;; Get nodename spelled as it is in the node.
+ (re-search-forward "Node:[ \t]*")
+ (setq Info-current-node
+ (buffer-substring-no-properties (point)
+ (progn
+ (skip-chars-forward "^,\t\n")
+ (point))))
+ (Info-set-mode-line)
+ ;; Find the end of it, and narrow.
+ (beginning-of-line)
+ (let (active-expression)
+ (narrow-to-region (point)
+ (if (re-search-forward "\n[\^_\f]" nil t)
+ (prog1
+ (1- (point))
+ (if (looking-at "[\n\^_\f]*execute: ")
+ (progn
+ (goto-char (match-end 0))
+ (setq active-expression
+ (read (current-buffer))))))
+ (point-max)))
+ (if Info-enable-active-nodes (eval active-expression))
+ (if Info-fontify (Info-fontify-node))
+ (run-hooks 'Info-selection-hook))))
+
+(defun Info-set-mode-line ()
+ (setq mode-line-buffer-identification
+ (concat
+ "Info: ("
+ (if Info-current-file
+ (file-name-nondirectory Info-current-file)
+ "")
+ ")"
+ (or Info-current-node ""))))
+
+;; Go to an info node specified with a filename-and-nodename string
+;; of the sort that is found in pointers in nodes.
+
+(defun Info-goto-node (nodename)
+ "Go to info node named NAME. Give just NODENAME or (FILENAME)NODENAME."
+ (interactive (list (Info-read-node-name "Goto node: ")))
+ (let (filename)
+ (string-match "\\s *\\((\\s *\\([^\t)]*\\)\\s *)\\s *\\|\\)\\(.*\\)"
+ nodename)
+ (setq filename (if (= (match-beginning 1) (match-end 1))
+ ""
+ (substring nodename (match-beginning 2) (match-end 2)))
+ nodename (substring nodename (match-beginning 3) (match-end 3)))
+ (let ((trim (string-match "\\s *\\'" filename)))
+ (if trim (setq filename (substring filename 0 trim))))
+ (let ((trim (string-match "\\s *\\'" nodename)))
+ (if trim (setq nodename (substring nodename 0 trim))))
+ (if transient-mark-mode (deactivate-mark))
+ (Info-find-node (if (equal filename "") nil filename)
+ (if (equal nodename "") "Top" nodename))))
+
+;; This function is used as the "completion table" while reading a node name.
+;; It does completion using the alist in completion-table
+;; unless STRING starts with an open-paren.
+(defun Info-read-node-name-1 (string predicate code)
+ (let ((no-completion (and (> (length string) 0) (eq (aref string 0) ?\())))
+ (cond ((eq code nil)
+ (if no-completion
+ string
+ (try-completion string completion-table predicate)))
+ ((eq code t)
+ (if no-completion
+ nil
+ (all-completions string completion-table predicate)))
+ ((eq code 'lambda)
+ (if no-completion
+ t
+ (assoc string completion-table))))))
+
+(defun Info-read-node-name (prompt &optional default)
+ (let* ((completion-ignore-case t)
+ (completion-table (Info-build-node-completions))
+ (nodename (completing-read prompt 'Info-read-node-name-1)))
+ (if (equal nodename "")
+ (or default
+ (Info-read-node-name prompt))
+ nodename)))
+
+(defun Info-build-node-completions ()
+ (or Info-current-file-completions
+ (let ((compl nil))
+ (save-excursion
+ (save-restriction
+ (if (marker-buffer Info-tag-table-marker)
+ (progn
+ (set-buffer (marker-buffer Info-tag-table-marker))
+ (widen)
+ (goto-char Info-tag-table-marker)
+ (while (re-search-forward "\nNode: \\(.*\\)\177" nil t)
+ (setq compl
+ (cons (list (buffer-substring (match-beginning 1)
+ (match-end 1)))
+ compl))))
+ (widen)
+ (goto-char (point-min))
+ (while (search-forward "\n\^_" nil t)
+ (forward-line 1)
+ (let ((beg (point)))
+ (forward-line 1)
+ (if (re-search-backward "Node: *\\([^,\n]*\\) *[,\n\t]"
+ beg t)
+ (setq compl
+ (cons (list (buffer-substring (match-beginning 1)
+ (match-end 1)))
+ compl))))))))
+ (setq Info-current-file-completions compl))))
+
+(defun Info-restore-point (hl)
+ "If this node has been visited, restore the point value when we left."
+ (while hl
+ (if (and (equal (nth 0 (car hl)) Info-current-file)
+ ;; Use string-equal, not equal, to ignore text props.
+ (string-equal (nth 1 (car hl)) Info-current-node))
+ (progn
+ (goto-char (nth 2 (car hl)))
+ (setq hl nil)) ;terminate the while at next iter
+ (setq hl (cdr hl)))))
+
+(defvar Info-last-search nil
+ "Default regexp for \\<Info-mode-map>\\[Info-search] command to search for.")
+
+(defun Info-search (regexp)
+ "Search for REGEXP, starting from point, and select node it's found in."
+ (interactive "sSearch (regexp): ")
+ (if transient-mark-mode (deactivate-mark))
+ (if (equal regexp "")
+ (setq regexp Info-last-search)
+ (setq Info-last-search regexp))
+ (let ((found ()) current
+ (onode Info-current-node)
+ (ofile Info-current-file)
+ (opoint (point))
+ (osubfile Info-current-subfile))
+ (save-excursion
+ (save-restriction
+ (widen)
+ (if (null Info-current-subfile)
+ (progn (re-search-forward regexp) (setq found (point)))
+ (condition-case err
+ (progn (re-search-forward regexp) (setq found (point)))
+ (search-failed nil)))))
+ (if (not found) ;can only happen in subfile case -- else would have erred
+ (unwind-protect
+ (let ((list ()))
+ (set-buffer (marker-buffer Info-tag-table-marker))
+ (goto-char (point-min))
+ (search-forward "\n\^_\nIndirect:")
+ (save-restriction
+ (narrow-to-region (point)
+ (progn (search-forward "\n\^_")
+ (1- (point))))
+ (goto-char (point-min))
+ (search-forward (concat "\n" osubfile ": "))
+ (beginning-of-line)
+ (while (not (eobp))
+ (re-search-forward "\\(^.*\\): [0-9]+$")
+ (goto-char (+ (match-end 1) 2))
+ (setq list (cons (cons (read (current-buffer))
+ (buffer-substring (match-beginning 1)
+ (match-end 1)))
+ list))
+ (goto-char (1+ (match-end 0))))
+ (setq list (nreverse list)
+ current (car (car list))
+ list (cdr list)))
+ (while list
+ (message "Searching subfile %s..." (cdr (car list)))
+ (Info-read-subfile (car (car list)))
+ (setq list (cdr list))
+;; (goto-char (point-min))
+ (if (re-search-forward regexp nil t)
+ (setq found (point) list ())))
+ (if found
+ (message "")
+ (signal 'search-failed (list regexp))))
+ (if (not found)
+ (progn (Info-read-subfile opoint)
+ (goto-char opoint)
+ (Info-select-node)))))
+ (widen)
+ (goto-char found)
+ (Info-select-node)
+ ;; Use string-equal, not equal, to ignore text props.
+ (or (and (string-equal onode Info-current-node)
+ (equal ofile Info-current-file))
+ (setq Info-history (cons (list ofile onode opoint)
+ Info-history)))))
+
+;; Extract the value of the node-pointer named NAME.
+;; If there is none, use ERRORNAME in the error message;
+;; if ERRORNAME is nil, just return nil.
+(defun Info-extract-pointer (name &optional errorname)
+ (save-excursion
+ (goto-char (point-min))
+ (forward-line 1)
+ (if (re-search-backward (concat name ":") nil t)
+ (progn
+ (goto-char (match-end 0))
+ (Info-following-node-name))
+ (if (eq errorname t)
+ nil
+ (error "Node has no %s" (capitalize (or errorname name)))))))
+
+;; Return the node name in the buffer following point.
+;; ALLOWEDCHARS, if non-nil, goes within [...] to make a regexp
+;; saying which chas may appear in the node name.
+(defun Info-following-node-name (&optional allowedchars)
+ (skip-chars-forward " \t")
+ (buffer-substring-no-properties
+ (point)
+ (progn
+ (while (looking-at (concat "[" (or allowedchars "^,\t\n") "]"))
+ (skip-chars-forward (concat (or allowedchars "^,\t\n") "("))
+ (if (looking-at "(")
+ (skip-chars-forward "^)")))
+ (skip-chars-backward " ")
+ (point))))
+
+(defun Info-next ()
+ "Go to the next node of this node."
+ (interactive)
+ (Info-goto-node (Info-extract-pointer "next")))
+
+(defun Info-prev ()
+ "Go to the previous node of this node."
+ (interactive)
+ (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")))
+
+(defun Info-up ()
+ "Go to the superior node of this node."
+ (interactive)
+ (Info-goto-node (Info-extract-pointer "up"))
+ (Info-restore-point Info-history))
+
+(defun Info-last ()
+ "Go back to the last node visited."
+ (interactive)
+ (or Info-history
+ (error "This is the first Info node you looked at"))
+ (let (filename nodename opoint)
+ (setq filename (car (car Info-history)))
+ (setq nodename (car (cdr (car Info-history))))
+ (setq opoint (car (cdr (cdr (car Info-history)))))
+ (setq Info-history (cdr Info-history))
+ (Info-find-node filename nodename)
+ (setq Info-history (cdr Info-history))
+ (goto-char opoint)))
+
+(defun Info-directory ()
+ "Go to the Info directory node."
+ (interactive)
+ (Info-find-node "dir" "top"))
+
+(defun Info-follow-reference (footnotename)
+ "Follow cross reference named NAME to the node it refers to.
+NAME may be an abbreviation of the reference name."
+ (interactive
+ (let ((completion-ignore-case t)
+ completions default alt-default (start-point (point)) str i bol eol)
+ (save-excursion
+ ;; Store end and beginning of line.
+ (end-of-line)
+ (setq eol (point))
+ (beginning-of-line)
+ (setq bol (point))
+
+ (goto-char (point-min))
+ (while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
+ (setq str (buffer-substring
+ (match-beginning 1)
+ (1- (point))))
+ ;; See if this one should be the default.
+ (and (null default)
+ (<= (match-beginning 0) start-point)
+ (<= start-point (point))
+ (setq default t))
+ ;; See if this one should be the alternate default.
+ (and (null alt-default)
+ (and (<= bol (match-beginning 0))
+ (<= (point) eol))
+ (setq alt-default t))
+ (setq i 0)
+ (while (setq i (string-match "[ \n\t]+" str i))
+ (setq str (concat (substring str 0 i) " "
+ (substring str (match-end 0))))
+ (setq i (1+ i)))
+ ;; Record as a completion and perhaps as default.
+ (if (eq default t) (setq default str))
+ (if (eq alt-default t) (setq alt-default str))
+ (setq completions
+ (cons (cons str nil)
+ completions))))
+ ;; If no good default was found, try an alternate.
+ (or default
+ (setq default alt-default))
+ ;; If only one cross-reference found, then make it default.
+ (if (eq (length completions) 1)
+ (setq default (car (car completions))))
+ (if completions
+ (let ((input (completing-read (if default
+ (concat "Follow reference named: ("
+ default ") ")
+ "Follow reference named: ")
+ completions nil t)))
+ (list (if (equal input "")
+ default input)))
+ (error "No cross-references in this node"))))
+ (let (target beg i (str (concat "\\*note " (regexp-quote footnotename))))
+ (while (setq i (string-match " " str i))
+ (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i))))
+ (setq i (+ i 6)))
+ (save-excursion
+ (goto-char (point-min))
+ (or (re-search-forward str nil t)
+ (error "No cross-reference named %s" footnotename))
+ (goto-char (+ (match-beginning 0) 5))
+ (setq target
+ (Info-extract-menu-node-name "Bad format cross reference" t)))
+ (while (setq i (string-match "[ \t\n]+" target i))
+ (setq target (concat (substring target 0 i) " "
+ (substring target (match-end 0))))
+ (setq i (+ i 1)))
+ (Info-goto-node target)))
+
+(defun Info-extract-menu-node-name (&optional errmessage multi-line)
+ (skip-chars-forward " \t\n")
+ (let ((beg (point))
+ str i)
+ (skip-chars-forward "^:")
+ (forward-char 1)
+ (setq str
+ (if (looking-at ":")
+ (buffer-substring-no-properties beg (1- (point)))
+ (skip-chars-forward " \t\n")
+ (Info-following-node-name (if multi-line "^.,\t" "^.,\t\n"))))
+ (while (setq i (string-match "\n" str i))
+ (aset str i ?\ ))
+ ;; Collapse multiple spaces.
+ (while (string-match " +" str)
+ (setq str (replace-match " " t t str)))
+ str))
+
+;; No one calls this.
+;;(defun Info-menu-item-sequence (list)
+;; (while list
+;; (Info-menu (car list))
+;; (setq list (cdr list))))
+
+(defun Info-complete-menu-item (string predicate action)
+ (let ((case-fold-search t))
+ (cond ((eq action nil)
+ (let (completions
+ (pattern (concat "\n\\* \\("
+ (regexp-quote string)
+ "[^:\t\n]*\\):")))
+ (save-excursion
+ (set-buffer Info-complete-menu-buffer)
+ (goto-char (point-min))
+ (search-forward "\n* Menu:")
+ (while (re-search-forward pattern nil t)
+ (setq completions (cons (cons (format "%s"
+ (buffer-substring
+ (match-beginning 1)
+ (match-end 1)))
+ (match-beginning 1))
+ completions))))
+ (try-completion string completions predicate)))
+ ((eq action t)
+ (let (completions
+ (pattern (concat "\n\\* \\("
+ (regexp-quote string)
+ "[^:\t\n]*\\):")))
+ (save-excursion
+ (set-buffer Info-complete-menu-buffer)
+ (goto-char (point-min))
+ (search-forward "\n* Menu:")
+ (while (re-search-forward pattern nil t)
+ (setq completions (cons (cons (format "%s"
+ (buffer-substring
+ (match-beginning 1)
+ (match-end 1)))
+ (match-beginning 1))
+ completions))))
+ (all-completions string completions predicate)))
+ (t
+ (save-excursion
+ (set-buffer Info-complete-menu-buffer)
+ (goto-char (point-min))
+ (search-forward "\n* Menu:")
+ (re-search-forward (concat "\n\\* "
+ (regexp-quote string)
+ ":")
+ nil t))))))
+
+
+(defun Info-menu (menu-item)
+ "Go to node for menu item named (or abbreviated) NAME.
+Completion is allowed, and the menu item point is on is the default."
+ (interactive
+ (let ((completions '())
+ ;; If point is within a menu item, use that item as the default
+ (default nil)
+ (p (point))
+ beg
+ (last nil))
+ (save-excursion
+ (goto-char (point-min))
+ (if (not (search-forward "\n* menu:" nil t))
+ (error "No menu in this node"))
+ (setq beg (point))
+ (and (< (point) p)
+ (save-excursion
+ (goto-char p)
+ (end-of-line)
+ (re-search-backward "\n\\* \\([^:\t\n]*\\):" beg t)
+ (setq default (format "%s" (buffer-substring
+ (match-beginning 1)
+ (match-end 1)))))))
+ (let ((item nil))
+ (while (null item)
+ (setq item (let ((completion-ignore-case t)
+ (Info-complete-menu-buffer (current-buffer)))
+ (completing-read (if default
+ (format "Menu item (default %s): "
+ default)
+ "Menu item: ")
+ 'Info-complete-menu-item nil t)))
+ ;; we rely on the fact that completing-read accepts an input
+ ;; of "" even when the require-match argument is true and ""
+ ;; is not a valid possibility
+ (if (string= item "")
+ (if default
+ (setq item default)
+ ;; ask again
+ (setq item nil))))
+ (list item))))
+ ;; there is a problem here in that if several menu items have the same
+ ;; name you can only go to the node of the first with this command.
+ (Info-goto-node (Info-extract-menu-item menu-item)))
+
+(defun Info-extract-menu-item (menu-item)
+ (setq menu-item (regexp-quote menu-item))
+ (save-excursion
+ (goto-char (point-min))
+ (or (search-forward "\n* menu:" nil t)
+ (error "No menu in this node"))
+ (or (re-search-forward (concat "\n\\* " menu-item ":") nil t)
+ (re-search-forward (concat "\n\\* " menu-item) nil t)
+ (error "No such item in menu"))
+ (beginning-of-line)
+ (forward-char 2)
+ (Info-extract-menu-node-name)))
+
+;; If COUNT is nil, use the last item in the menu.
+(defun Info-extract-menu-counting (count)
+ (save-excursion
+ (goto-char (point-min))
+ (or (search-forward "\n* menu:" nil t)
+ (error "No menu in this node"))
+ (if count
+ (or (search-forward "\n* " nil t count)
+ (error "Too few items in menu"))
+ (while (search-forward "\n* " nil t)
+ nil))
+ (Info-extract-menu-node-name)))
+
+(defun Info-nth-menu-item ()
+ "Go to the node of the Nth menu item.
+N is the digit argument used to invoke this command."
+ (interactive)
+ (Info-goto-node
+ (Info-extract-menu-counting
+ (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0))))
+
+(defun Info-top-node ()
+ "Go to the Top node of this file."
+ (interactive)
+ (Info-goto-node "Top"))
+
+(defun Info-final-node ()
+ "Go to the final node in this file."
+ (interactive)
+ (Info-goto-node "Top")
+ (let (Info-history)
+ ;; Go to the last node in the menu of Top.
+ (Info-goto-node (Info-extract-menu-counting nil))
+ ;; If the last node in the menu is not last in pointer structure,
+ ;; move forward until we can't go any farther.
+ (while (Info-forward-node t t) nil)
+ ;; Then keep moving down to last subnode, unless we reach an index.
+ (while (and (not (string-match "\\<index\\>" Info-current-node))
+ (save-excursion (search-forward "\n* Menu:" nil t)))
+ (Info-goto-node (Info-extract-menu-counting nil)))))
+
+(defun Info-forward-node (&optional not-down no-error)
+ "Go forward one node, considering all nodes as forming one sequence."
+ (interactive)
+ (goto-char (point-min))
+ (forward-line 1)
+ ;; three possibilities, in order of priority:
+ ;; 1. next node is in a menu in this node (but not in an index)
+ ;; 2. next node is next at same level
+ ;; 3. next node is up and next
+ (cond ((and (not not-down)
+ (save-excursion (search-forward "\n* menu:" nil t))
+ (not (string-match "\\<index\\>" Info-current-node)))
+ (Info-goto-node (Info-extract-menu-counting 1))
+ t)
+ ((save-excursion (search-backward "next:" nil t))
+ (Info-next)
+ t)
+ ((and (save-excursion (search-backward "up:" nil t))
+ ;; Use string-equal, not equal, to ignore text props.
+ (not (string-equal (downcase (Info-extract-pointer "up"))
+ "top")))
+ (let ((old-node Info-current-node))
+ (Info-up)
+ (let (Info-history success)
+ (unwind-protect
+ (setq success (Info-forward-node t no-error))
+ (or success (Info-goto-node old-node))))))
+ (no-error nil)
+ (t (error "No pointer forward from this node"))))
+
+(defun Info-backward-node ()
+ "Go backward one node, considering all nodes as forming one sequence."
+ (interactive)
+ (let ((prevnode (Info-extract-pointer "prev[ious]*" t))
+ (upnode (Info-extract-pointer "up" t)))
+ (cond ((and upnode (string-match "(" upnode))
+ (error "First node in file"))
+ ((and upnode (or (null prevnode)
+ ;; Use string-equal, not equal,
+ ;; to ignore text properties.
+ (string-equal (downcase prevnode)
+ (downcase upnode))))
+ (Info-up))
+ (prevnode
+ ;; If we move back at the same level,
+ ;; go down to find the last subnode*.
+ (Info-prev)
+ (let (Info-history)
+ (while (and (not (string-match "\\<index\\>" Info-current-node))
+ (save-excursion (search-forward "\n* Menu:" nil t)))
+ (Info-goto-node (Info-extract-menu-counting nil)))))
+ (t
+ (error "No pointer backward from this node")))))
+
+(defun Info-exit ()
+ "Exit Info by selecting some other buffer."
+ (interactive)
+ (if Info-standalone
+ (save-buffers-kill-emacs)
+ (switch-to-buffer (prog1 (other-buffer (current-buffer))
+ (bury-buffer (current-buffer))))))
+
+(defun Info-next-menu-item ()
+ (interactive)
+ (save-excursion
+ (forward-line -1)
+ (search-forward "\n* menu:" nil t)
+ (or (search-forward "\n* " nil t)
+ (error "No more items in menu"))
+ (Info-goto-node (Info-extract-menu-node-name))))
+
+(defun Info-last-menu-item ()
+ (interactive)
+ (save-excursion
+ (forward-line 1)
+ (let ((beg (save-excursion
+ (and (search-backward "\n* menu:" nil t)
+ (point)))))
+ (or (and beg (search-backward "\n* " beg t))
+ (error "No previous items in menu")))
+ (Info-goto-node (save-excursion
+ (goto-char (match-end 0))
+ (Info-extract-menu-node-name)))))
+
+(defmacro Info-no-error (&rest body)
+ (list 'condition-case nil (cons 'progn (append body '(t))) '(error nil)))
+
+(defun Info-next-preorder ()
+ "Go to the next subnode or the next node, or go up a level."
+ (interactive)
+ (cond ((Info-no-error (Info-next-menu-item)))
+ ((Info-no-error (Info-next)))
+ ((Info-no-error (Info-up))
+ ;; Since we have already gone thru all the items in this menu,
+ ;; go up to the end of this node.
+ (goto-char (point-max))
+ ;; Since logically we are done with the node with that menu,
+ ;; move on from it.
+ (Info-next-preorder))
+ (t
+ (error "No more nodes"))))
+
+(defun Info-last-preorder ()
+ "Go to the last node, popping up a level if there is none."
+ (interactive)
+ (cond ((Info-no-error
+ (Info-last-menu-item)
+ ;; If we go down a menu item, go to the end of the node
+ ;; so we can scroll back through it.
+ (goto-char (point-max)))
+ ;; Keep going down, as long as there are nested menu nodes.
+ (while (Info-no-error
+ (Info-last-menu-item)
+ ;; If we go down a menu item, go to the end of the node
+ ;; so we can scroll back through it.
+ (goto-char (point-max))))
+ (recenter -1))
+ ((Info-no-error (Info-prev))
+ (goto-char (point-max))
+ (while (Info-no-error
+ (Info-last-menu-item)
+ ;; If we go down a menu item, go to the end of the node
+ ;; so we can scroll back through it.
+ (goto-char (point-max))))
+ (recenter -1))
+ ((Info-no-error (Info-up))
+ (goto-char (point-min))
+ (or (search-forward "\n* Menu:" nil t)
+ (goto-char (point-max))))
+ (t (error "No previous nodes"))))
+
+(defun Info-scroll-up ()
+ "Scroll one screenful forward in Info, considering all nodes as one sequence.
+Once you scroll far enough in a node that its menu appears on the screen,
+the next scroll moves into its first subnode. When you scroll past
+the end of a node, that goes to the next node or back up to the parent node."
+ (interactive)
+ (if (or (< (window-start) (point-min))
+ (> (window-start) (point-max)))
+ (set-window-start (selected-window) (point)))
+ (let ((virtual-end (save-excursion
+ (goto-char (point-min))
+ (if (search-forward "\n* Menu:" nil t)
+ (point)
+ (point-max)))))
+ (if (or (< virtual-end (window-start))
+ (pos-visible-in-window-p virtual-end))
+ (Info-next-preorder)
+ (scroll-up))))
+
+(defun Info-scroll-down ()
+ "Scroll one screenful back in Info, considering all nodes as one sequence.
+Within the menu of a node, this goes to its last subnode.
+When you scroll past the beginning of a node, that goes to the
+previous node or back up to the parent node."
+ (interactive)
+ (if (or (< (window-start) (point-min))
+ (> (window-start) (point-max)))
+ (set-window-start (selected-window) (point)))
+ (let* ((current-point (point))
+ (virtual-end (save-excursion
+ (beginning-of-line)
+ (setq current-point (point))
+ (goto-char (point-min))
+ (search-forward "\n* Menu:"
+ current-point
+ t))))
+ (if (or virtual-end (pos-visible-in-window-p (point-min)))
+ (Info-last-preorder)
+ (scroll-down))))
+
+(defun Info-next-reference (&optional recur)
+ "Move cursor to the next cross-reference or menu item in the node."
+ (interactive)
+ (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
+ (old-pt (point)))
+ (or (eobp) (forward-char 1))
+ (or (re-search-forward pat nil t)
+ (progn
+ (goto-char (point-min))
+ (or (re-search-forward pat nil t)
+ (progn
+ (goto-char old-pt)
+ (error "No cross references in this node")))))
+ (goto-char (match-beginning 0))
+ (if (looking-at "\\* Menu:")
+ (if recur
+ (error "No cross references in this node")
+ (Info-next-reference t)))))
+
+(defun Info-prev-reference (&optional recur)
+ "Move cursor to the previous cross-reference or menu item in the node."
+ (interactive)
+ (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
+ (old-pt (point)))
+ (or (re-search-backward pat nil t)
+ (progn
+ (goto-char (point-max))
+ (or (re-search-backward pat nil t)
+ (progn
+ (goto-char old-pt)
+ (error "No cross references in this node")))))
+ (goto-char (match-beginning 0))
+ (if (looking-at "\\* Menu:")
+ (if recur
+ (error "No cross references in this node")
+ (Info-prev-reference t)))))
+
+(defun Info-index (topic)
+ "Look up a string in the index for this file.
+The index is defined as the first node in the top-level menu whose
+name contains the word \"Index\", plus any immediately following
+nodes whose names also contain the word \"Index\".
+If there are no exact matches to the specified topic, this chooses
+the first match which is a case-insensitive substring of a topic.
+Use the `,' command to see the other matches.
+Give a blank topic name to go to the Index node itself."
+ (interactive "sIndex topic: ")
+ (let ((orignode Info-current-node)
+ (rnode nil)
+ (pattern (format "\n\\* \\([^\n:]*%s[^\n:]*\\):[ \t]*\\([^.\n]*\\)\\.[ \t]*\\([0-9]*\\)"
+ (regexp-quote topic)))
+ node)
+ (Info-goto-node "Top")
+ (or (search-forward "\n* menu:" nil t)
+ (error "No index"))
+ (or (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t)
+ (error "No index"))
+ (goto-char (match-beginning 1))
+ ;; Here, and subsequently in this function,
+ ;; we bind Info-history to nil for internal node-switches
+ ;; so that we don't put junk in the history.
+ ;; In the first Info-goto-node call, above, we do update the history
+ ;; because that is what the user's previous node choice into it.
+ (let ((Info-history nil))
+ (Info-goto-node (Info-extract-menu-node-name)))
+ (or (equal topic "")
+ (let ((matches nil)
+ (exact nil)
+ (Info-history nil)
+ found)
+ (while
+ (progn
+ (goto-char (point-min))
+ (while (re-search-forward pattern nil t)
+ (setq matches
+ (cons (list (buffer-substring (match-beginning 1)
+ (match-end 1))
+ (buffer-substring (match-beginning 2)
+ (match-end 2))
+ Info-current-node
+ (string-to-int (concat "0"
+ (buffer-substring
+ (match-beginning 3)
+ (match-end 3)))))
+ matches)))
+ (and (setq node (Info-extract-pointer "next" t))
+ (string-match "\\<Index\\>" node)))
+ (Info-goto-node node))
+ (or matches
+ (progn
+ (Info-goto-node orignode)
+ (error "No `%s' in index" topic)))
+ ;; Here it is a feature that assoc is case-sensitive.
+ (while (setq found (assoc topic matches))
+ (setq exact (cons found exact)
+ matches (delq found matches)))
+ (setq Info-index-alternatives (nconc exact (nreverse matches)))
+ (Info-index-next 0)))))
+
+(defun Info-index-next (num)
+ "Go to the next matching index item from the last `i' command."
+ (interactive "p")
+ (or Info-index-alternatives
+ (error "No previous `i' command in this file"))
+ (while (< num 0)
+ (setq num (+ num (length Info-index-alternatives))))
+ (while (> num 0)
+ (setq Info-index-alternatives
+ (nconc (cdr Info-index-alternatives)
+ (list (car Info-index-alternatives)))
+ num (1- num)))
+ (Info-goto-node (nth 1 (car Info-index-alternatives)))
+ (if (> (nth 3 (car Info-index-alternatives)) 0)
+ (forward-line (nth 3 (car Info-index-alternatives)))
+ (forward-line 3) ; don't search in headers
+ (let ((name (car (car Info-index-alternatives))))
+ (Info-find-index-name name)))
+ (message "Found `%s' in %s. %s"
+ (car (car Info-index-alternatives))
+ (nth 2 (car Info-index-alternatives))
+ (if (cdr Info-index-alternatives)
+ "(Press `,' for more)"
+ "(Only match)")))
+
+(defun Info-find-index-name (name)
+ "Move point to the place within the current node where NAME is defined."
+ (if (or (re-search-forward (format
+ "[a-zA-Z]+: %s\\( \\|$\\)"
+ (regexp-quote name)) nil t)
+ (search-forward (format "`%s'" name) nil t)
+ (and (string-match "\\`.*\\( (.*)\\)\\'" name)
+ (search-forward
+ (format "`%s'" (substring name 0 (match-beginning 1)))
+ nil t))
+ (search-forward name nil t))
+ (beginning-of-line)
+ (goto-char (point-min))))
+
+(defun Info-undefined ()
+ "Make command be undefined in Info."
+ (interactive)
+ (ding))
+
+(defun Info-help ()
+ "Enter the Info tutorial."
+ (interactive)
+ (delete-other-windows)
+ (Info-find-node "info"
+ (if (< (window-height) 23)
+ "Help-Small-Screen"
+ "Help")))
+
+(defun Info-summary ()
+ "Display a brief summary of all Info commands."
+ (interactive)
+ (save-window-excursion
+ (switch-to-buffer "*Help*")
+ (erase-buffer)
+ (insert (documentation 'Info-mode))
+ (help-mode)
+ (goto-char (point-min))
+ (let (ch flag)
+ (while (progn (setq flag (not (pos-visible-in-window-p (point-max))))
+ (message (if flag "Type Space to see more"
+ "Type Space to return to Info"))
+ (if (not (eq ?\ (setq ch (read-event))))
+ (progn (setq unread-command-events (list ch)) nil)
+ flag))
+ (scroll-up)))
+ (bury-buffer "*Help*")))
+
+(defun Info-get-token (pos start all &optional errorstring)
+ "Return the token around POS,
+POS must be somewhere inside the token
+START is a regular expression which will match the
+ beginning of the tokens delimited string
+ALL is a regular expression with a single
+ parenthesized subpattern which is the token to be
+ returned. E.g. '{\(.*\)}' would return any string
+ enclosed in braces around POS.
+SIG optional fourth argument, controls action on no match
+ nil: return nil
+ t: beep
+ a string: signal an error, using that string."
+ (save-excursion
+ (goto-char pos)
+ (re-search-backward start (max (point-min) (- pos 200)) 'yes)
+ (let (found)
+ (while (and (re-search-forward all (min (point-max) (+ pos 200)) 'yes)
+ (not (setq found (and (<= (match-beginning 0) pos)
+ (> (match-end 0) pos))))))
+ (if (and found (<= (match-beginning 0) pos)
+ (> (match-end 0) pos))
+ (buffer-substring (match-beginning 1) (match-end 1))
+ (cond ((null errorstring)
+ nil)
+ ((eq errorstring t)
+ (beep)
+ nil)
+ (t
+ (error "No %s around position %d" errorstring pos)))))))
+
+(defun Info-mouse-follow-nearest-node (click)
+ "\\<Info-mode-map>Follow a node reference near point.
+Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where you click.
+At end of the node's text, moves to the next node, or up if none."
+ (interactive "e")
+ (let* ((start (event-start click))
+ (window (car start))
+ (pos (car (cdr start))))
+ (select-window window)
+ (goto-char pos))
+ (and (not (Info-try-follow-nearest-node))
+ (save-excursion (forward-line 1) (eobp))
+ (Info-next-preorder)))
+
+(defun Info-follow-nearest-node ()
+ "\\<Info-mode-map>Follow a node reference near point.
+Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where point is.
+If no reference to follow, moves to the next node, or up if none."
+ (interactive)
+ (or (Info-try-follow-nearest-node)
+ (Info-next-preorder)))
+
+;; Common subroutine.
+(defun Info-try-follow-nearest-node ()
+ "Follow a node reference near point. Return non-nil if successful."
+ (let (node)
+ (cond
+ ((setq node (Info-get-token (point) "\\*note[ \n]"
+ "\\*note[ \n]\\([^:]*\\):"))
+ (Info-follow-reference node))
+ ((setq node (Info-get-token (point) "\\* " "\\* \\([^:]*\\)::"))
+ (Info-goto-node node))
+ ((setq node (Info-get-token (point) "\\* " "\\* \\([^:]*\\):"))
+ (Info-menu node))
+ ((setq node (Info-get-token (point) "Up: " "Up: \\([^,\n\t]*\\)"))
+ (Info-goto-node node))
+ ((setq node (Info-get-token (point) "Next: " "Next: \\([^,\n\t]*\\)"))
+ (Info-goto-node node))
+ ((setq node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)"))
+ (Info-goto-node "Top"))
+ ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)"))
+ (Info-goto-node node)))
+ node))
+
+(defvar Info-mode-map nil
+ "Keymap containing Info commands.")
+(if Info-mode-map
+ nil
+ (setq Info-mode-map (make-keymap))
+ (suppress-keymap Info-mode-map)
+ (define-key Info-mode-map "." 'beginning-of-buffer)
+ (define-key Info-mode-map " " 'Info-scroll-up)
+ (define-key Info-mode-map "\C-m" 'Info-follow-nearest-node)
+ (define-key Info-mode-map "\t" 'Info-next-reference)
+ (define-key Info-mode-map "\e\t" 'Info-prev-reference)
+ (define-key Info-mode-map "1" 'Info-nth-menu-item)
+ (define-key Info-mode-map "2" 'Info-nth-menu-item)
+ (define-key Info-mode-map "3" 'Info-nth-menu-item)
+ (define-key Info-mode-map "4" 'Info-nth-menu-item)
+ (define-key Info-mode-map "5" 'Info-nth-menu-item)
+ (define-key Info-mode-map "6" 'Info-nth-menu-item)
+ (define-key Info-mode-map "7" 'Info-nth-menu-item)
+ (define-key Info-mode-map "8" 'Info-nth-menu-item)
+ (define-key Info-mode-map "9" 'Info-nth-menu-item)
+ (define-key Info-mode-map "0" 'undefined)
+ (define-key Info-mode-map "?" 'Info-summary)
+ (define-key Info-mode-map "]" 'Info-forward-node)
+ (define-key Info-mode-map "[" 'Info-backward-node)
+ (define-key Info-mode-map "<" 'Info-top-node)
+ (define-key Info-mode-map ">" 'Info-final-node)
+ (define-key Info-mode-map "b" 'beginning-of-buffer)
+ (define-key Info-mode-map "d" 'Info-directory)
+ (define-key Info-mode-map "e" 'Info-edit)
+ (define-key Info-mode-map "f" 'Info-follow-reference)
+ (define-key Info-mode-map "g" 'Info-goto-node)
+ (define-key Info-mode-map "h" 'Info-help)
+ (define-key Info-mode-map "i" 'Info-index)
+ (define-key Info-mode-map "l" 'Info-last)
+ (define-key Info-mode-map "m" 'Info-menu)
+ (define-key Info-mode-map "n" 'Info-next)
+ (define-key Info-mode-map "p" 'Info-prev)
+ (define-key Info-mode-map "q" 'Info-exit)
+ (define-key Info-mode-map "s" 'Info-search)
+ ;; For consistency with Rmail.
+ (define-key Info-mode-map "\M-s" 'Info-search)
+ (define-key Info-mode-map "t" 'Info-top-node)
+ (define-key Info-mode-map "u" 'Info-up)
+ (define-key Info-mode-map "," 'Info-index-next)
+ (define-key Info-mode-map "\177" 'Info-scroll-down)
+ (define-key Info-mode-map [mouse-2] 'Info-mouse-follow-nearest-node)
+ )
+
+;; Info mode is suitable only for specially formatted data.
+(put 'info-mode 'mode-class 'special)
+
+(defun Info-mode ()
+ "\\<Info-mode-map>
+Info mode provides commands for browsing through the Info documentation tree.
+Documentation in Info is divided into \"nodes\", each of which discusses
+one topic and contains references to other nodes which discuss related
+topics. Info has commands to follow the references and show you other nodes.
+
+\\[Info-help] Invoke the Info tutorial.
+
+Selecting other nodes:
+\\[Info-mouse-follow-nearest-node]
+ Follow a node reference you click on.
+ This works with menu items, cross references, and
+ the \"next\", \"previous\" and \"up\", depending on where you click.
+\\[Info-next] Move to the \"next\" node of this node.
+\\[Info-prev] Move to the \"previous\" node of this node.
+\\[Info-up] Move \"up\" from this node.
+\\[Info-menu] Pick menu item specified by name (or abbreviation).
+ Picking a menu item causes another node to be selected.
+\\[Info-directory] Go to the Info directory node.
+\\[Info-follow-reference] Follow a cross reference. Reads name of reference.
+\\[Info-last] Move to the last node you were at.
+\\[Info-index] Look up a topic in this file's Index and move to that node.
+\\[Info-index-next] (comma) Move to the next match from a previous `i' command.
+
+Moving within a node:
+\\[Info-scroll-up] Normally, scroll forward a full screen. If the end of the buffer is
+already visible, try to go to the next menu entry, or up if there is none.
+\\[Info-scroll-down] Normally, scroll backward. If the beginning of the buffer is
+already visible, try to go to the previous menu entry, or up if there is none.
+\\[beginning-of-buffer] Go to beginning of node.
+
+Advanced commands:
+\\[Info-exit] Quit Info: reselect previously selected buffer.
+\\[Info-edit] Edit contents of selected node.
+1 Pick first item in node's menu.
+2, 3, 4, 5 Pick second ... fifth item in node's menu.
+\\[Info-goto-node] Move to node specified by name.
+ You may include a filename as well, as (FILENAME)NODENAME.
+\\[universal-argument] \\[info] Move to new Info file with completion.
+\\[Info-search] Search through this Info file for specified regexp,
+ and select the node in which the next occurrence is found.
+\\[Info-next-reference] Move cursor to next cross-reference or menu item.
+\\[Info-prev-reference] Move cursor to previous cross-reference or menu item."
+ (kill-all-local-variables)
+ (setq major-mode 'Info-mode)
+ (setq mode-name "Info")
+ (use-local-map Info-mode-map)
+ (set-syntax-table text-mode-syntax-table)
+ (setq local-abbrev-table text-mode-abbrev-table)
+ (setq case-fold-search t)
+ (setq buffer-read-only t)
+ (make-local-variable 'Info-current-file)
+ (make-local-variable 'Info-current-subfile)
+ (make-local-variable 'Info-current-node)
+ (make-local-variable 'Info-tag-table-marker)
+ (make-local-variable 'Info-history)
+ (make-local-variable 'Info-index-alternatives)
+ (if (memq (framep (selected-frame)) '(x pc))
+ (progn
+ (make-face 'info-node)
+ (make-face 'info-menu-5)
+ (make-face 'info-xref)
+ (or (face-differs-from-default-p 'info-node)
+ (if (face-differs-from-default-p 'bold-italic)
+ (copy-face 'bold-italic 'info-node)
+ (copy-face 'bold 'info-node)))
+ (or (face-differs-from-default-p 'info-menu-5)
+ (set-face-underline-p 'info-menu-5 t))
+ (or (face-differs-from-default-p 'info-xref)
+ (copy-face 'bold 'info-xref)))
+ (setq Info-fontify nil))
+ (Info-set-mode-line)
+ (run-hooks 'Info-mode-hook))
+
+(defvar Info-edit-map nil
+ "Local keymap used within `e' command of Info.")
+(if Info-edit-map
+ nil
+ (setq Info-edit-map (nconc (make-sparse-keymap) text-mode-map))
+ (define-key Info-edit-map "\C-c\C-c" 'Info-cease-edit))
+
+;; Info-edit mode is suitable only for specially formatted data.
+(put 'info-edit-mode 'mode-class 'special)
+
+(defun Info-edit-mode ()
+ "Major mode for editing the contents of an Info node.
+Like text mode with the addition of `Info-cease-edit'
+which returns to Info mode for browsing.
+\\{Info-edit-map}"
+ (use-local-map Info-edit-map)
+ (setq major-mode 'Info-edit-mode)
+ (setq mode-name "Info Edit")
+ (kill-local-variable 'mode-line-buffer-identification)
+ (setq buffer-read-only nil)
+ (force-mode-line-update)
+ (buffer-enable-undo (current-buffer))
+ (run-hooks 'Info-edit-mode-hook))
+
+(defun Info-edit ()
+ "Edit the contents of this Info node.
+Allowed only if variable `Info-enable-edit' is non-nil."
+ (interactive)
+ (or Info-enable-edit
+ (error "Editing info nodes is not enabled"))
+ (Info-edit-mode)
+ (message "%s" (substitute-command-keys
+ "Editing: Type \\<Info-edit-map>\\[Info-cease-edit] to return to info")))
+
+(defun Info-cease-edit ()
+ "Finish editing Info node; switch back to Info proper."
+ (interactive)
+ ;; Do this first, so nothing has changed if user C-g's at query.
+ (and (buffer-modified-p)
+ (y-or-n-p "Save the file? ")
+ (save-buffer))
+ (use-local-map Info-mode-map)
+ (setq major-mode 'Info-mode)
+ (setq mode-name "Info")
+ (Info-set-mode-line)
+ (setq buffer-read-only t)
+ (force-mode-line-update)
+ (and (marker-position Info-tag-table-marker)
+ (buffer-modified-p)
+ (message "Tags may have changed. Use Info-tagify if necessary")))
+
+(defvar Info-file-list-for-emacs
+ '("ediff" "forms" "gnus" "info" ("mh" . "mh-e") "sc")
+ "List of Info files that describe Emacs commands.
+An element can be a file name, or a list of the form (PREFIX . FILE)
+where PREFIX is a name prefix and FILE is the file to look in.
+If the element is just a file name, the file name also serves as the prefix.")
+
+(defun Info-find-emacs-command-nodes (command)
+ "Return a list of locations documenting COMMAND.
+The `info-file' property of COMMAND says which Info manual to search.
+If COMMAND has no property, the variable `Info-file-list-for-emacs'
+defines heuristics for which Info manual to try.
+The locations are of the format used in Info-history, i.e.
+\(FILENAME NODENAME BUFFERPOS\)."
+ (let ((where '())
+ (cmd-desc (concat "^\\* " (regexp-quote (symbol-name command))
+ ":\\s *\\(.*\\)\\.$"))
+ (info-file "emacs")) ;default
+ ;; Determine which info file this command is documented in.
+ (if (get command 'info-file)
+ (setq info-file (get command 'info-file))
+ ;; If it doesn't say explicitly, test its name against
+ ;; various prefixes that we know.
+ (let ((file-list Info-file-list-for-emacs))
+ (while file-list
+ (let* ((elt (car file-list))
+ (name (if (consp elt)
+ (car elt)
+ elt))
+ (file (if (consp elt) (cdr elt) elt))
+ (regexp (concat "\\`" (regexp-quote name)
+ "\\(\\'\\|-\\)")))
+ (if (string-match regexp (symbol-name command))
+ (setq info-file file file-list nil))
+ (setq file-list (cdr file-list))))))
+ (save-excursion
+ (condition-case nil
+ (Info-find-node info-file "Command Index")
+ ;; Some manuals may not have a separate Command Index node,
+ ;; so try just Index instead.
+ (error
+ (Info-find-node info-file "Index")))
+ ;; Take the index node off the Info history.
+ (setq Info-history (cdr Info-history))
+ (goto-char (point-max))
+ (while (re-search-backward cmd-desc nil t)
+ (setq where (cons (list Info-current-file
+ (buffer-substring
+ (match-beginning 1)
+ (match-end 1))
+ 0)
+ where)))
+ where)))
+
+;;;###autoload
+(defun Info-goto-emacs-command-node (command)
+ "Go to the Info node in the Emacs manual for command COMMAND.
+The command is found by looking up in Emacs manual's Command Index
+or in another manual found via COMMAND's `info-file' property or
+the variable `Info-file-list-for-emacs'."
+ (interactive "CFind documentation for command: ")
+ (or (commandp command)
+ (signal 'wrong-type-argument (list 'commandp command)))
+ (let ((where (Info-find-emacs-command-nodes command)))
+ (if where
+ (let ((num-matches (length where)))
+ ;; Get Info running, and pop to it in another window.
+ (save-window-excursion
+ (info))
+ (pop-to-buffer "*info*")
+ (Info-find-node (car (car where))
+ (car (cdr (car where))))
+ (if (> num-matches 1)
+ (progn
+ ;; Info-find-node already pushed (car where) onto
+ ;; Info-history. Put the other nodes that were found on
+ ;; the history.
+ (setq Info-history (nconc (cdr where) Info-history))
+ (message "Found %d other entr%s. Use %s to see %s."
+ (1- num-matches)
+ (if (> num-matches 2) "ies" "y")
+ (substitute-command-keys "\\[Info-last]")
+ (if (> num-matches 2) "them" "it")))))
+ (error "Couldn't find documentation for %s" command))))
+
+;;;###autoload
+(defun Info-goto-emacs-key-command-node (key)
+ "Go to the Info node in the Emacs manual the command bound to KEY, a string.
+Interactively, if the binding is execute-extended-command, a command is read.
+The command is found by looking up in Emacs manual's Command Index
+or in another manual found via COMMAND's `info-file' property or
+the variable `Info-file-list-for-emacs'."
+ (interactive "kFind documentation for key:")
+ (let ((command (key-binding key)))
+ (cond ((null command)
+ (message "%s is undefined" (key-description key)))
+ ((and (interactive-p)
+ (eq command 'execute-extended-command))
+ (Info-goto-emacs-command-node
+ (read-command "Find documentation for command: ")))
+ (t
+ (Info-goto-emacs-command-node command)))))
+
+(defvar Info-title-face-alist
+ '((?* bold underline)
+ (?= bold-italic underline)
+ (?- italic underline))
+ "*Alist of face or list of faces to use for pseudo-underlined titles.
+The alist key is the character the title is underlined with (?*, ?= or ?-).")
+
+(defun Info-fontify-node ()
+ (save-excursion
+ (let ((buffer-read-only nil))
+ (goto-char (point-min))
+ (if (looking-at "^File: [^,: \t]+,?[ \t]+")
+ (progn
+ (goto-char (match-end 0))
+ (while
+ (looking-at "[ \t]*[^:, \t\n]+:[ \t]+\\([^:,\t\n]+\\),?")
+ (goto-char (match-end 0))
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'face 'info-xref)
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'mouse-face 'highlight))))
+ (goto-char (point-min))
+ (while (re-search-forward "\n\\([^ \t\n].+\\)\n\\(\\*+\\|=+\\|-+\\)$"
+ nil t)
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'face
+ (cdr (assq (preceding-char) Info-title-face-alist)))
+ (put-text-property (match-end 1) (match-end 2)
+ 'invisible t))
+ (goto-char (point-min))
+ (while (re-search-forward "\\*Note[ \n\t]+\\([^:]*\\):" nil t)
+ (if (= (char-after (1- (match-beginning 0))) ?\") ; hack
+ nil
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'face 'info-xref)
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'mouse-face 'highlight)))
+ (goto-char (point-min))
+ (if (and (search-forward "\n* Menu:" nil t)
+ (not (string-match "\\<Index\\>" Info-current-node))
+ ;; Don't take time to annotate huge menus
+ (< (- (point-max) (point)) Info-fontify-maximum-menu-size))
+ (let ((n 0))
+ (while (re-search-forward "^\\* \\([^:\t\n]*\\):" nil t)
+ (setq n (1+ n))
+ (if (memq n '(5 9)) ; visual aids to help with 1-9 keys
+ (put-text-property (match-beginning 0)
+ (1+ (match-beginning 0))
+ 'face 'info-menu-5))
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'face 'info-node)
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'mouse-face 'highlight))))
+ (set-buffer-modified-p nil))))
+
+(provide 'info)
+
+;;; info.el ends here
diff --git a/contrib/texinfo/emacs/informat.el b/contrib/texinfo/emacs/informat.el
new file mode 100644
index 0000000..0b195b9
--- /dev/null
+++ b/contrib/texinfo/emacs/informat.el
@@ -0,0 +1,429 @@
+;;; informat.el --- info support functions package for Emacs
+
+;; Copyright (C) 1986 Free Software Foundation, Inc.
+
+;; Maintainer: FSF
+;; Keywords: help
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Code:
+
+(require 'info)
+
+;;;###autoload
+(defun Info-tagify ()
+ "Create or update Info-file tag table in current buffer."
+ (interactive)
+ ;; Save and restore point and restrictions.
+ ;; save-restrictions would not work
+ ;; because it records the old max relative to the end.
+ ;; We record it relative to the beginning.
+ (message "Tagifying %s ..." (file-name-nondirectory (buffer-file-name)))
+ (let ((omin (point-min))
+ (omax (point-max))
+ (nomax (= (point-max) (1+ (buffer-size))))
+ (opoint (point)))
+ (unwind-protect
+ (progn
+ (widen)
+ (goto-char (point-min))
+ (if (search-forward "\^_\nIndirect:\n" nil t)
+ (message "Cannot tagify split info file")
+ (let ((regexp "Node:[ \t]*\\([^,\n\t]*\\)[,\t\n]")
+ (case-fold-search t)
+ list)
+ (while (search-forward "\n\^_" nil t)
+ ;; We want the 0-origin character position of the ^_.
+ ;; That is the same as the Emacs (1-origin) position
+ ;; of the newline before it.
+ (let ((beg (match-beginning 0)))
+ (forward-line 2)
+ (if (re-search-backward regexp beg t)
+ (setq list
+ (cons (list (buffer-substring-no-properties
+ (match-beginning 1)
+ (match-end 1))
+ beg)
+ list)))))
+ (goto-char (point-max))
+ (forward-line -8)
+ (let ((buffer-read-only nil))
+ (if (search-forward "\^_\nEnd tag table\n" nil t)
+ (let ((end (point)))
+ (search-backward "\nTag table:\n")
+ (beginning-of-line)
+ (delete-region (point) end)))
+ (goto-char (point-max))
+ (insert "\^_\f\nTag table:\n")
+ (move-marker Info-tag-table-marker (point))
+ (setq list (nreverse list))
+ (while list
+ (insert "Node: " (car (car list)) ?\177)
+ (princ (car (cdr (car list))) (current-buffer))
+ (insert ?\n)
+ (setq list (cdr list)))
+ (insert "\^_\nEnd tag table\n")))))
+ (goto-char opoint)
+ (narrow-to-region omin (if nomax (1+ (buffer-size))
+ (min omax (point-max))))))
+ (message "Tagifying %s ... done" (file-name-nondirectory (buffer-file-name))))
+
+;;;###autoload
+(defun Info-split ()
+ "Split an info file into an indirect file plus bounded-size subfiles.
+Each subfile will be up to 50,000 characters plus one node.
+
+To use this command, first visit a large Info file that has a tag
+table. The buffer is modified into a (small) indirect info file which
+should be saved in place of the original visited file.
+
+The subfiles are written in the same directory the original file is
+in, with names generated by appending `-' and a number to the original
+file name. The indirect file still functions as an Info file, but it
+contains just the tag table and a directory of subfiles."
+
+ (interactive)
+ (if (< (buffer-size) 70000)
+ (error "This is too small to be worth splitting"))
+ (goto-char (point-min))
+ (search-forward "\^_")
+ (forward-char -1)
+ (let ((start (point))
+ (chars-deleted 0)
+ subfiles
+ (subfile-number 1)
+ (case-fold-search t)
+ (filename (file-name-sans-versions buffer-file-name)))
+ (goto-char (point-max))
+ (forward-line -8)
+ (setq buffer-read-only nil)
+ (or (search-forward "\^_\nEnd tag table\n" nil t)
+ (error "Tag table required; use M-x Info-tagify"))
+ (search-backward "\nTag table:\n")
+ (if (looking-at "\nTag table:\n\^_")
+ (error "Tag table is just a skeleton; use M-x Info-tagify"))
+ (beginning-of-line)
+ (forward-char 1)
+ (save-restriction
+ (narrow-to-region (point-min) (point))
+ (goto-char (point-min))
+ (while (< (1+ (point)) (point-max))
+ (goto-char (min (+ (point) 50000) (point-max)))
+ (search-forward "\^_" nil 'move)
+ (setq subfiles
+ (cons (list (+ start chars-deleted)
+ (concat (file-name-nondirectory filename)
+ (format "-%d" subfile-number)))
+ subfiles))
+ ;; Put a newline at end of split file, to make Unix happier.
+ (insert "\n")
+ (write-region (point-min) (point)
+ (concat filename (format "-%d" subfile-number)))
+ (delete-region (1- (point)) (point))
+ ;; Back up over the final ^_.
+ (forward-char -1)
+ (setq chars-deleted (+ chars-deleted (- (point) start)))
+ (delete-region start (point))
+ (setq subfile-number (1+ subfile-number))))
+ (while subfiles
+ (goto-char start)
+ (insert (nth 1 (car subfiles))
+ (format ": %d" (1- (car (car subfiles))))
+ "\n")
+ (setq subfiles (cdr subfiles)))
+ (goto-char start)
+ (insert "\^_\nIndirect:\n")
+ (search-forward "\nTag Table:\n")
+ (insert "(Indirect)\n")))
+
+;;;###autoload
+(defun Info-validate ()
+ "Check current buffer for validity as an Info file.
+Check that every node pointer points to an existing node."
+ (interactive)
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (if (search-forward "\nTag table:\n(Indirect)\n" nil t)
+ (error "Don't yet know how to validate indirect info files: \"%s\""
+ (buffer-name (current-buffer))))
+ (goto-char (point-min))
+ (let ((allnodes '(("*")))
+ (regexp "Node:[ \t]*\\([^,\n\t]*\\)[,\t\n]")
+ (case-fold-search t)
+ (tags-losing nil)
+ (lossages ()))
+ (while (search-forward "\n\^_" nil t)
+ (forward-line 1)
+ (let ((beg (point)))
+ (forward-line 1)
+ (if (re-search-backward regexp beg t)
+ (let ((name (downcase
+ (buffer-substring-no-properties
+ (match-beginning 1)
+ (progn
+ (goto-char (match-end 1))
+ (skip-chars-backward " \t")
+ (point))))))
+ (if (assoc name allnodes)
+ (setq lossages
+ (cons (list name "Duplicate node-name" nil)
+ lossages))
+ (setq allnodes
+ (cons (list name
+ (progn
+ (end-of-line)
+ (and (re-search-backward
+ "prev[ious]*:" beg t)
+ (progn
+ (goto-char (match-end 0))
+ (downcase
+ (Info-following-node-name)))))
+ beg)
+ allnodes)))))))
+ (goto-char (point-min))
+ (while (search-forward "\n\^_" nil t)
+ (forward-line 1)
+ (let ((beg (point))
+ thisnode next)
+ (forward-line 1)
+ (if (re-search-backward regexp beg t)
+ (save-restriction
+ (search-forward "\n\^_" nil 'move)
+ (narrow-to-region beg (point))
+ (setq thisnode (downcase
+ (buffer-substring-no-properties
+ (match-beginning 1)
+ (progn
+ (goto-char (match-end 1))
+ (skip-chars-backward " \t")
+ (point)))))
+ (end-of-line)
+ (and (search-backward "next:" nil t)
+ (setq next (Info-validate-node-name "invalid Next"))
+ (assoc next allnodes)
+ (if (equal (car (cdr (assoc next allnodes)))
+ thisnode)
+ ;; allow multiple `next' pointers to one node
+ (let ((tem lossages))
+ (while tem
+ (if (and (equal (car (cdr (car tem)))
+ "should have Previous")
+ (equal (car (car tem))
+ next))
+ (setq lossages (delq (car tem) lossages)))
+ (setq tem (cdr tem))))
+ (setq lossages
+ (cons (list next
+ "should have Previous"
+ thisnode)
+ lossages))))
+ (end-of-line)
+ (if (re-search-backward "prev[ious]*:" nil t)
+ (Info-validate-node-name "invalid Previous"))
+ (end-of-line)
+ (if (search-backward "up:" nil t)
+ (Info-validate-node-name "invalid Up"))
+ (if (re-search-forward "\n* Menu:" nil t)
+ (while (re-search-forward "\n\\* " nil t)
+ (Info-validate-node-name
+ (concat "invalid menu item "
+ (buffer-substring (point)
+ (save-excursion
+ (skip-chars-forward "^:")
+ (point))))
+ (Info-extract-menu-node-name))))
+ (goto-char (point-min))
+ (while (re-search-forward "\\*note[ \n]*[^:\t]*:" nil t)
+ (goto-char (+ (match-beginning 0) 5))
+ (skip-chars-forward " \n")
+ (Info-validate-node-name
+ (concat "invalid reference "
+ (buffer-substring (point)
+ (save-excursion
+ (skip-chars-forward "^:")
+ (point))))
+ (Info-extract-menu-node-name "Bad format cross-reference")))))))
+ (setq tags-losing (not (Info-validate-tags-table)))
+ (if (or lossages tags-losing)
+ (with-output-to-temp-buffer " *problems in info file*"
+ (while lossages
+ (princ "In node \"")
+ (princ (car (car lossages)))
+ (princ "\", ")
+ (let ((tem (nth 1 (car lossages))))
+ (cond ((string-match "\n" tem)
+ (princ (substring tem 0 (match-beginning 0)))
+ (princ "..."))
+ (t
+ (princ tem))))
+ (if (nth 2 (car lossages))
+ (progn
+ (princ ": ")
+ (let ((tem (nth 2 (car lossages))))
+ (cond ((string-match "\n" tem)
+ (princ (substring tem 0 (match-beginning 0)))
+ (princ "..."))
+ (t
+ (princ tem))))))
+ (terpri)
+ (setq lossages (cdr lossages)))
+ (if tags-losing (princ "\nTags table must be recomputed\n")))
+ ;; Here if info file is valid.
+ ;; If we already made a list of problems, clear it out.
+ (save-excursion
+ (if (get-buffer " *problems in info file*")
+ (progn
+ (set-buffer " *problems in info file*")
+ (kill-buffer (current-buffer)))))
+ (message "File appears valid"))))))
+
+(defun Info-validate-node-name (kind &optional name)
+ (if name
+ nil
+ (goto-char (match-end 0))
+ (skip-chars-forward " \t")
+ (if (= (following-char) ?\()
+ nil
+ (setq name
+ (buffer-substring-no-properties
+ (point)
+ (progn
+ (skip-chars-forward "^,\t\n")
+ (skip-chars-backward " ")
+ (point))))))
+ (if (null name)
+ nil
+ (setq name (downcase name))
+ (or (and (> (length name) 0) (= (aref name 0) ?\())
+ (assoc name allnodes)
+ (setq lossages
+ (cons (list thisnode kind name) lossages))))
+ name)
+
+(defun Info-validate-tags-table ()
+ (goto-char (point-min))
+ (if (not (search-forward "\^_\nEnd tag table\n" nil t))
+ t
+ (not (catch 'losing
+ (let* ((end (match-beginning 0))
+ (start (progn (search-backward "\nTag table:\n")
+ (1- (match-end 0))))
+ tem)
+ (setq tem allnodes)
+ (while tem
+ (goto-char start)
+ (or (equal (car (car tem)) "*")
+ (search-forward (concat "Node: "
+ (car (car tem))
+ "\177")
+ end t)
+ (throw 'losing 'x))
+ (setq tem (cdr tem)))
+ (goto-char (1+ start))
+ (while (looking-at ".*Node: \\(.*\\)\177\\([0-9]+\\)$")
+ (setq tem (downcase (buffer-substring-no-properties
+ (match-beginning 1)
+ (match-end 1))))
+ (setq tem (assoc tem allnodes))
+ (if (or (not tem)
+ (< 1000 (progn
+ (goto-char (match-beginning 2))
+ (setq tem (- (car (cdr (cdr tem)))
+ (read (current-buffer))))
+ (if (> tem 0) tem (- tem)))))
+ (throw 'losing 'y))
+ (forward-line 1)))
+ (if (looking-at "\^_\n")
+ (forward-line 1))
+ (or (looking-at "End tag table\n")
+ (throw 'losing 'z))
+ nil))))
+
+;;;###autoload
+(defun batch-info-validate ()
+ "Runs `Info-validate' on the files remaining on the command line.
+Must be used only with -batch, and kills Emacs on completion.
+Each file will be processed even if an error occurred previously.
+For example, invoke \"emacs -batch -f batch-info-validate $info/ ~/*.info\""
+ (if (not noninteractive)
+ (error "batch-info-validate may only be used -batch."))
+ (let ((version-control t)
+ (auto-save-default nil)
+ (find-file-run-dired nil)
+ (kept-old-versions 259259)
+ (kept-new-versions 259259))
+ (let ((error 0)
+ file
+ (files ()))
+ (while command-line-args-left
+ (setq file (expand-file-name (car command-line-args-left)))
+ (cond ((not (file-exists-p file))
+ (message ">> %s does not exist!" file)
+ (setq error 1
+ command-line-args-left (cdr command-line-args-left)))
+ ((file-directory-p file)
+ (setq command-line-args-left (nconc (directory-files file)
+ (cdr command-line-args-left))))
+ (t
+ (setq files (cons file files)
+ command-line-args-left (cdr command-line-args-left)))))
+ (while files
+ (setq file (car files)
+ files (cdr files))
+ (let ((lose nil))
+ (condition-case err
+ (progn
+ (if buffer-file-name (kill-buffer (current-buffer)))
+ (find-file file)
+ (buffer-disable-undo (current-buffer))
+ (set-buffer-modified-p nil)
+ (fundamental-mode)
+ (let ((case-fold-search nil))
+ (goto-char (point-max))
+ (cond ((search-backward "\n\^_\^L\nTag table:\n" nil t)
+ (message "%s already tagified" file))
+ ((< (point-max) 30000)
+ (message "%s too small to bother tagifying" file))
+ (t
+ (Info-tagify))))
+ (let ((loss-name " *problems in info file*"))
+ (message "Checking validity of info file %s..." file)
+ (if (get-buffer loss-name)
+ (kill-buffer loss-name))
+ (Info-validate)
+ (if (not (get-buffer loss-name))
+ nil ;(message "Checking validity of info file %s... OK" file)
+ (message "----------------------------------------------------------------------")
+ (message ">> PROBLEMS IN INFO FILE %s" file)
+ (save-excursion
+ (set-buffer loss-name)
+ (princ (buffer-substring-no-properties
+ (point-min) (point-max))))
+ (message "----------------------------------------------------------------------")
+ (setq error 1 lose t)))
+ (if (and (buffer-modified-p)
+ (not lose))
+ (progn (message "Saving modified %s" file)
+ (save-buffer))))
+ (error (message ">> Error: %s" (prin1-to-string err))))))
+ (kill-emacs error))))
+
+;;; informat.el ends here
diff --git a/contrib/texinfo/emacs/makeinfo.el b/contrib/texinfo/emacs/makeinfo.el
new file mode 100644
index 0000000..a649d52
--- /dev/null
+++ b/contrib/texinfo/emacs/makeinfo.el
@@ -0,0 +1,247 @@
+;;; makeinfo.el --- run makeinfo conveniently
+
+;; Copyright (C) 1991, 1993 Free Software Foundation, Inc.
+
+;; Author: Robert J. Chassell
+;; Maintainer: FSF
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;;; The Texinfo mode `makeinfo' related commands are:
+
+;; makeinfo-region to run makeinfo on the current region.
+;; makeinfo-buffer to run makeinfo on the current buffer, or
+;; with optional prefix arg, on current region
+;; kill-compilation to kill currently running makeinfo job
+;; makeinfo-recenter-makeinfo-buffer to redisplay *compilation* buffer
+
+;;; Keybindings (defined in `texinfo.el')
+
+;; makeinfo bindings
+; (define-key texinfo-mode-map "\C-c\C-m\C-r" 'makeinfo-region)
+; (define-key texinfo-mode-map "\C-c\C-m\C-b" 'makeinfo-buffer)
+; (define-key texinfo-mode-map "\C-c\C-m\C-k" 'kill-compilation)
+; (define-key texinfo-mode-map "\C-c\C-m\C-l"
+; 'makeinfo-recenter-compilation-buffer)
+
+;;; Code:
+
+;;; Variables used by `makeinfo'
+
+(require 'compile)
+
+(defvar makeinfo-run-command "makeinfo"
+ "*Command used to run `makeinfo' subjob.
+The name of the file is appended to this string, separated by a space.")
+
+(defvar makeinfo-options "--fill-column=70"
+ "*String containing options for running `makeinfo'.
+Do not include `--footnote-style' or `--paragraph-indent';
+the proper way to specify those is with the Texinfo commands
+`@footnotestyle` and `@paragraphindent'.")
+
+(require 'texinfo)
+
+(defvar makeinfo-compilation-process nil
+ "Process that runs `makeinfo'. Should start out nil.")
+
+(defvar makeinfo-temp-file nil
+ "Temporary file name used for text being sent as input to `makeinfo'.")
+
+(defvar makeinfo-output-file-name nil
+ "Info file name used for text output by `makeinfo'.")
+
+
+;;; The `makeinfo' function definitions
+
+(defun makeinfo-region (region-beginning region-end)
+ "Make Info file from region of current Texinfo file, and switch to it.
+
+This command does not offer the `next-error' feature since it would
+apply to a temporary file, not the original; use the `makeinfo-buffer'
+command to gain use of `next-error'."
+
+ (interactive "r")
+ (let (filename-or-header
+ filename-or-header-beginning
+ filename-or-header-end)
+ ;; Cannot use `let' for makeinfo-temp-file or
+ ;; makeinfo-output-file-name since `makeinfo-compilation-sentinel'
+ ;; needs them.
+
+ (setq makeinfo-temp-file
+ (concat
+ (make-temp-name
+ (substring (buffer-file-name)
+ 0
+ (or (string-match "\\.tex" (buffer-file-name))
+ (length (buffer-file-name)))))
+ ".texinfo"))
+
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (let ((search-end (save-excursion (forward-line 100) (point))))
+ ;; Find and record the Info filename,
+ ;; or else explain that a filename is needed.
+ (if (re-search-forward
+ "^@setfilename[ \t]+\\([^ \t\n]+\\)[ \t]*"
+ search-end t)
+ (setq makeinfo-output-file-name
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (error
+ "The texinfo file needs a line saying: @setfilename <name>"))
+
+ ;; Find header and specify its beginning and end.
+ (goto-char (point-min))
+ (if (and
+ (prog1
+ (search-forward tex-start-of-header search-end t)
+ (beginning-of-line)
+ ;; Mark beginning of header.
+ (setq filename-or-header-beginning (point)))
+ (prog1
+ (search-forward tex-end-of-header nil t)
+ (beginning-of-line)
+ ;; Mark end of header
+ (setq filename-or-header-end (point))))
+
+ ;; Insert the header into the temporary file.
+ (write-region
+ (min filename-or-header-beginning region-beginning)
+ filename-or-header-end
+ makeinfo-temp-file nil nil)
+
+ ;; Else no header; insert @filename line into temporary file.
+ (goto-char (point-min))
+ (search-forward "@setfilename" search-end t)
+ (beginning-of-line)
+ (setq filename-or-header-beginning (point))
+ (forward-line 1)
+ (setq filename-or-header-end (point))
+ (write-region
+ (min filename-or-header-beginning region-beginning)
+ filename-or-header-end
+ makeinfo-temp-file nil nil))
+
+ ;; Insert the region into the file.
+ (write-region
+ (max region-beginning filename-or-header-end)
+ region-end
+ makeinfo-temp-file t nil)
+
+ ;; Run the `makeinfo-compile' command in the *compilation* buffer
+ (save-excursion
+ (makeinfo-compile
+ (concat makeinfo-run-command
+ " "
+ makeinfo-options
+ " "
+ makeinfo-temp-file)
+ "Use `makeinfo-buffer' to gain use of the `next-error' command"
+ nil)))))))
+
+;;; Actually run makeinfo. COMMAND is the command to run.
+;;; ERROR-MESSAGE is what to say when next-error can't find another error.
+;;; If PARSE-ERRORS is non-nil, do try to parse error messages.
+(defun makeinfo-compile (command error-message parse-errors)
+ (let ((buffer
+ (compile-internal command error-message nil
+ (and (not parse-errors)
+ ;; If we do want to parse errors, pass nil.
+ ;; Otherwise, use this function, which won't
+ ;; ever find any errors.
+ '(lambda (&rest ignore)
+ (setq compilation-error-list nil))))))
+ (set-process-sentinel (get-buffer-process buffer)
+ 'makeinfo-compilation-sentinel)))
+
+;; Delete makeinfo-temp-file after processing is finished,
+;; and visit Info file.
+;; This function is called when the compilation process changes state.
+;; Based on `compilation-sentinel' in compile.el
+(defun makeinfo-compilation-sentinel (proc msg)
+ (compilation-sentinel proc msg)
+ (if (and makeinfo-temp-file (file-exists-p makeinfo-temp-file))
+ (delete-file makeinfo-temp-file))
+ ;; Always use the version on disk.
+ (if (get-file-buffer makeinfo-output-file-name)
+ (progn (set-buffer makeinfo-output-file-name)
+ (revert-buffer t t))
+ (find-file makeinfo-output-file-name))
+ (goto-char (point-min)))
+
+(defun makeinfo-buffer ()
+ "Make Info file from current buffer.
+
+Use the \\[next-error] command to move to the next error
+\(if there are errors\)."
+
+ (interactive)
+ (cond ((null buffer-file-name)
+ (error "Buffer not visiting any file"))
+ ((buffer-modified-p)
+ (if (y-or-n-p "Buffer modified; do you want to save it? ")
+ (save-buffer))))
+
+ ;; Find and record the Info filename,
+ ;; or else explain that a filename is needed.
+ (save-excursion
+ (goto-char (point-min))
+ (let ((search-end (save-excursion (forward-line 100) (point))))
+ (if (re-search-forward
+ "^@setfilename[ \t]+\\([^ \t\n]+\\)[ \t]*"
+ search-end t)
+ (setq makeinfo-output-file-name
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (error
+ "The texinfo file needs a line saying: @setfilename <name>"))))
+
+ (save-excursion
+ (makeinfo-compile
+ (concat makeinfo-run-command " " makeinfo-options
+ " " buffer-file-name)
+ "No more errors."
+ t)))
+
+(defun makeinfo-recenter-compilation-buffer (linenum)
+ "Redisplay `*compilation*' buffer so most recent output can be seen.
+The last line of the buffer is displayed on
+line LINE of the window, or centered if LINE is nil."
+ (interactive "P")
+ (let ((makeinfo-buffer (get-buffer "*compilation*"))
+ (old-buffer (current-buffer)))
+ (if (null makeinfo-buffer)
+ (message "No *compilation* buffer")
+ (pop-to-buffer makeinfo-buffer)
+ (bury-buffer makeinfo-buffer)
+ (goto-char (point-max))
+ (recenter (if linenum
+ (prefix-numeric-value linenum)
+ (/ (window-height) 2)))
+ (pop-to-buffer old-buffer)
+ )))
+
+;;; Place `provide' at end of file.
+(provide 'makeinfo)
+
+;;; makeinfo.el ends here
+
diff --git a/contrib/texinfo/emacs/new-useful-setqs b/contrib/texinfo/emacs/new-useful-setqs
new file mode 100644
index 0000000..4241ae4
--- /dev/null
+++ b/contrib/texinfo/emacs/new-useful-setqs
@@ -0,0 +1,180 @@
+;; -*- Mode: Emacs-Lisp -*-
+
+;; This is the `new-useful-setqs' file
+;; This overrides old defvars since they were revised.
+
+(setq texinfmt-version "2.35 of 10 September 1996")
+
+(setq texinfo-master-menu-header
+ "\n@detailmenu\n --- The Detailed Node Listing ---\n")
+
+(setq texinfo-environment-regexp
+ (concat
+ "^@"
+ "\\("
+ "cartouche\\|"
+ "display\\|"
+ "end\\|"
+ "enumerate\\|"
+ "example\\|"
+ "f?table\\|"
+ "flushleft\\|"
+ "flushright\\|"
+ "format\\|"
+ "group\\|"
+ "ifhtml\\|"
+ "ifinfo\\|"
+ "iftex\\|"
+ "ignore\\|"
+ "itemize\\|"
+ "lisp\\|"
+ "macro\\|"
+ "multitable\\|"
+ "quotation\\|"
+ "smallexample\\|"
+ "smalllisp\\|"
+ "tex"
+ "\\)")
+)
+
+(setq texinfo-no-refill-regexp
+ (concat
+ "^@"
+ "\\("
+ "example\\|"
+ "smallexample\\|"
+ "lisp\\|"
+ "smalllisp\\|"
+ "display\\|"
+ "format\\|"
+ "flushleft\\|"
+ "flushright\\|"
+ "menu\\|"
+ "multitable\\|"
+ "titlepage\\|"
+ "iftex\\|"
+ "ifhtml\\|"
+ "tex\\|"
+ "html"
+ "\\)"))
+
+
+(setq texinfo-accent-commands
+ (concat
+ "@OE\\|"
+ "@oe\\|"
+ "@AA\\|"
+ "@aa\\|"
+ "@AE\\|"
+ "@ae\\|"
+ "@ss\\|"
+ "@^\\|"
+ "@`\\|"
+ "@'\\|"
+ "@\"\\|"
+ "@,\\|"
+ "@=\\|"
+ "@~\\|"
+ "@questiondown{\\|"
+ "@exclamdown{\\|"
+ "@L{\\|"
+ "@l{\\|"
+ "@O{\\|"
+ "@o{\\|"
+ "@dotaccent{\\|"
+ "@ubaraccent{\\|"
+ "@d{\\|"
+ "@H{\\|"
+ "@ringaccent{\\|"
+ "@tieaccent{\\|"
+ "@u{\\|"
+ "@v{\\|"
+ "@dotless{"
+ ))
+
+(setq texinfo-part-of-para-regexp
+ (concat
+ "^@"
+ "\\("
+ "b{\\|"
+ "bullet{\\|"
+ "cite{\\|"
+ "code{\\|"
+ "emph{\\|"
+ "equiv{\\|"
+ "error{\\|"
+ "expansion{\\|"
+ "file{\\|"
+ "i{\\|"
+ "inforef{\\|"
+ "kbd{\\|"
+ "key{\\|"
+ "lisp{\\|"
+ "email{\\|"
+ "minus{\\|"
+ "point{\\|"
+ "print{\\|"
+ "pxref{\\|"
+ "r{\\|"
+ "ref{\\|"
+ "result{\\|"
+ "samp{\\|"
+ "sc{\\|"
+ "t{\\|"
+ "TeX{\\|"
+ "today{\\|"
+ "url{\\|"
+ "var{\\|"
+ "w{\\|"
+ "xref{\\|"
+ "@-\\|" ; @- is a descretionary hyphen (not an accent) (a noop).
+ texinfo-accent-commands
+ "\\)"
+ ))
+
+(setq texinfo-raisesections-alist
+ '((@chapter . @chapter) ; Cannot go higher
+ (@unnumbered . @unnumbered)
+ (@centerchap . @unnumbered)
+
+ (@majorheading . @majorheading)
+ (@chapheading . @chapheading)
+ (@appendix . @appendix)
+
+ (@section . @chapter)
+ (@unnumberedsec . @unnumbered)
+ (@heading . @chapheading)
+ (@appendixsec . @appendix)
+
+ (@subsection . @section)
+ (@unnumberedsubsec . @unnumberedsec)
+ (@subheading . @heading)
+ (@appendixsubsec . @appendixsec)
+
+ (@subsubsection . @subsection)
+ (@unnumberedsubsubsec . @unnumberedsubsec)
+ (@subsubheading . @subheading)
+ (@appendixsubsubsec . @appendixsubsec)))
+
+(setq texinfo-lowersections-alist
+ '((@chapter . @section)
+ (@unnumbered . @unnumberedsec)
+ (@centerchap . @unnumberedsec)
+ (@majorheading . @heading)
+ (@chapheading . @heading)
+ (@appendix . @appendixsec)
+
+ (@section . @subsection)
+ (@unnumberedsec . @unnumberedsubsec)
+ (@heading . @subheading)
+ (@appendixsec . @appendixsubsec)
+
+ (@subsection . @subsubsection)
+ (@unnumberedsubsec . @unnumberedsubsubsec)
+ (@subheading . @subsubheading)
+ (@appendixsubsec . @appendixsubsubsec)
+
+ (@subsubsection . @subsubsection) ; Cannot go lower.
+ (@unnumberedsubsubsec . @unnumberedsubsubsec)
+ (@subsubheading . @subsubheading)
+ (@appendixsubsubsec . @appendixsubsubsec)))
diff --git a/contrib/texinfo/emacs/texinfmt.el b/contrib/texinfo/emacs/texinfmt.el
new file mode 100644
index 0000000..c0d0963
--- /dev/null
+++ b/contrib/texinfo/emacs/texinfmt.el
@@ -0,0 +1,3979 @@
+;;; texinfmt.el --- format Texinfo files into Info files.
+
+;; Copyright (C) 1985, 1986, 1988, 1990, 1991,
+;; 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+
+;; Author: Robert J. Chassell
+;; Date: 10 Sep 1996
+;; Maintainer: Robert J. Chassell <bug-texinfo@prep.ai.mit.edu>
+;; Keywords: maint, tex, docs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Code:
+
+;;; Emacs lisp functions to convert Texinfo files to Info files.
+
+(defvar texinfmt-version "2.35 of 10 September 1996")
+
+(defun texinfmt-version (&optional here)
+ "Show the version of texinfmt.el in the minibuffer.
+If optional argument HERE is non-nil, insert info at point."
+ (interactive "P")
+ (let ((version-string
+ (format "Version of \`texinfmt.el\': %s" texinfmt-version)))
+ (if here
+ (insert version-string)
+ (if (interactive-p)
+ (message "%s" version-string)
+ version-string))))
+
+
+;;; Variable definitions
+
+(require 'texinfo) ; So `texinfo-footnote-style' is defined.
+(require 'texnfo-upd) ; So `texinfo-section-types-regexp' is defined.
+
+(defvar texinfo-format-syntax-table nil)
+
+(defvar texinfo-vindex)
+(defvar texinfo-findex)
+(defvar texinfo-cindex)
+(defvar texinfo-pindex)
+(defvar texinfo-tindex)
+(defvar texinfo-kindex)
+(defvar texinfo-last-node)
+(defvar texinfo-node-names)
+(defvar texinfo-enclosure-list)
+(defvar texinfo-alias-list)
+
+(defvar texinfo-command-start)
+(defvar texinfo-command-end)
+(defvar texinfo-command-name)
+(defvar texinfo-defun-type)
+(defvar texinfo-last-node-pos)
+(defvar texinfo-stack)
+(defvar texinfo-short-index-cmds-alist)
+(defvar texinfo-short-index-format-cmds-alist)
+(defvar texinfo-format-filename)
+(defvar texinfo-footnote-number)
+(defvar texinfo-start-of-header)
+(defvar texinfo-end-of-header)
+(defvar texinfo-raisesections-alist)
+(defvar texinfo-lowersections-alist)
+
+;;; Syntax table
+
+(if texinfo-format-syntax-table
+ nil
+ (setq texinfo-format-syntax-table (make-syntax-table))
+ (modify-syntax-entry ?\" " " texinfo-format-syntax-table)
+ (modify-syntax-entry ?\\ " " texinfo-format-syntax-table)
+ (modify-syntax-entry ?@ "\\" texinfo-format-syntax-table)
+ (modify-syntax-entry ?\^q "\\" texinfo-format-syntax-table)
+ (modify-syntax-entry ?\[ "." texinfo-format-syntax-table)
+ (modify-syntax-entry ?\] "." texinfo-format-syntax-table)
+ (modify-syntax-entry ?\( "." texinfo-format-syntax-table)
+ (modify-syntax-entry ?\) "." texinfo-format-syntax-table)
+ (modify-syntax-entry ?{ "(}" texinfo-format-syntax-table)
+ (modify-syntax-entry ?} "){" texinfo-format-syntax-table)
+ (modify-syntax-entry ?\' "." texinfo-format-syntax-table))
+
+
+;;; Top level buffer and region formatting functions
+
+;;;###autoload
+(defun texinfo-format-buffer (&optional notagify)
+ "Process the current buffer as texinfo code, into an Info file.
+The Info file output is generated in a buffer visiting the Info file
+names specified in the @setfilename command.
+
+Non-nil argument (prefix, if interactive) means don't make tag table
+and don't split the file if large. You can use Info-tagify and
+Info-split to do these manually."
+ (interactive "P")
+ (let ((lastmessage "Formatting Info file..."))
+ (message lastmessage)
+ (texinfo-format-buffer-1)
+ (if notagify
+ nil
+ (if (> (buffer-size) 30000)
+ (progn
+ (message (setq lastmessage "Making tags table for Info file..."))
+ (Info-tagify)))
+ (if (> (buffer-size) 100000)
+ (progn
+ (message (setq lastmessage "Splitting Info file..."))
+ (Info-split))))
+ (message (concat lastmessage
+ (if (interactive-p) "done. Now save it." "done.")))))
+
+(defvar texinfo-region-buffer-name "*Info Region*"
+ "*Name of the temporary buffer used by \\[texinfo-format-region].")
+
+;;;###autoload
+(defun texinfo-format-region (region-beginning region-end)
+ "Convert the current region of the Texinfo file to Info format.
+This lets you see what that part of the file will look like in Info.
+The command is bound to \\[texinfo-format-region]. The text that is
+converted to Info is stored in a temporary buffer."
+ (interactive "r")
+ (message "Converting region to Info format...")
+ (let (texinfo-command-start
+ texinfo-command-end
+ texinfo-command-name
+ texinfo-vindex
+ texinfo-findex
+ texinfo-cindex
+ texinfo-pindex
+ texinfo-tindex
+ texinfo-kindex
+ texinfo-stack
+ (texinfo-format-filename "")
+ texinfo-example-start
+ texinfo-last-node-pos
+ texinfo-last-node
+ texinfo-node-names
+ (texinfo-footnote-number 0)
+ last-input-buffer
+ (fill-column-for-info fill-column)
+ (input-buffer (current-buffer))
+ (input-directory default-directory)
+ (header-text "")
+ (header-beginning 1)
+ (header-end 1))
+
+;;; Copy lines between beginning and end of header lines,
+;;; if any, or else copy the `@setfilename' line, if any.
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (let ((search-end (save-excursion (forward-line 100) (point))))
+ (if (or
+ ;; Either copy header text.
+ (and
+ (prog1
+ (search-forward tex-start-of-header search-end t)
+ (forward-line 1)
+ ;; Mark beginning of header.
+ (setq header-beginning (point)))
+ (prog1
+ (search-forward tex-end-of-header nil t)
+ (beginning-of-line)
+ ;; Mark end of header
+ (setq header-end (point))))
+ ;; Or copy @filename line.
+ (prog2
+ (goto-char (point-min))
+ (search-forward "@setfilename" search-end t)
+ (beginning-of-line)
+ (setq header-beginning (point))
+ (forward-line 1)
+ (setq header-end (point))))
+
+ ;; Copy header
+ (setq header-text
+ (buffer-substring
+ (min header-beginning region-beginning)
+ header-end))))))
+
+;;; Find a buffer to use.
+ (switch-to-buffer (get-buffer-create texinfo-region-buffer-name))
+ (erase-buffer)
+ ;; Insert the header into the buffer.
+ (insert header-text)
+ ;; Insert the region into the buffer.
+ (insert-buffer-substring
+ input-buffer
+ (max region-beginning header-end)
+ region-end)
+ ;; Make sure region ends in a newline.
+ (or (= (preceding-char) ?\n)
+ (insert "\n"))
+
+ (goto-char (point-min))
+ (texinfo-mode)
+ (message "Converting region to Info format...")
+ (setq fill-column fill-column-for-info)
+ ;; Install a syntax table useful for scanning command operands.
+ (set-syntax-table texinfo-format-syntax-table)
+
+ ;; Insert @include files so `texinfo-raise-lower-sections' can
+ ;; work on them without losing track of multiple
+ ;; @raise/@lowersections commands.
+ (while (re-search-forward "^@include" nil t)
+ (setq texinfo-command-end (point))
+ (let ((filename (concat input-directory
+ (texinfo-parse-line-arg))))
+ (re-search-backward "^@include")
+ (delete-region (point) (save-excursion (forward-line 1) (point)))
+ (message "Reading included file: %s" filename)
+ (save-excursion
+ (save-restriction
+ (narrow-to-region
+ (point)
+ (+ (point) (car (cdr (insert-file-contents filename)))))
+ (goto-char (point-min))
+ ;; Remove `@setfilename' line from included file, if any,
+ ;; so @setfilename command not duplicated.
+ (if (re-search-forward
+ "^@setfilename" (save-excursion (forward-line 100) (point)) t)
+ (progn
+ (beginning-of-line)
+ (delete-region
+ (point) (save-excursion (forward-line 1) (point)))))))))
+
+ ;; Raise or lower level of each section, if necessary.
+ (goto-char (point-min))
+ (texinfo-raise-lower-sections)
+ ;; Append @refill to appropriate paragraphs for filling.
+ (goto-char (point-min))
+ (texinfo-append-refill)
+ ;; If the region includes the effective end of the data,
+ ;; discard everything after that.
+ (goto-char (point-max))
+ (if (re-search-backward "^@bye" nil t)
+ (delete-region (point) (point-max)))
+ ;; Make sure buffer ends in a newline.
+ (or (= (preceding-char) ?\n)
+ (insert "\n"))
+ ;; Don't use a previous value of texinfo-enclosure-list.
+ (setq texinfo-enclosure-list nil)
+ (setq texinfo-alias-list nil)
+
+ (goto-char (point-min))
+ (if (looking-at "\\\\input[ \t]+texinfo")
+ (delete-region (point) (save-excursion (forward-line 1) (point))))
+
+ ;; Insert Info region title text.
+ (goto-char (point-min))
+ (if (search-forward
+ "@setfilename" (save-excursion (forward-line 100) (point)) t)
+ (progn
+ (setq texinfo-command-end (point))
+ (beginning-of-line)
+ (setq texinfo-command-start (point))
+ (let ((arg (texinfo-parse-arg-discard)))
+ (insert " "
+ texinfo-region-buffer-name
+ " buffer for: `")
+ (insert (file-name-nondirectory (expand-file-name arg)))
+ (insert "', -*-Text-*-\n")))
+ ;; Else no `@setfilename' line
+ (insert " "
+ texinfo-region-buffer-name
+ " buffer -*-Text-*-\n"))
+ (insert "produced by `texinfo-format-region'\n"
+ "from a region in: "
+ (if (buffer-file-name input-buffer)
+ (concat "`"
+ (file-name-sans-versions
+ (file-name-nondirectory
+ (buffer-file-name input-buffer)))
+ "'")
+ (concat "buffer `" (buffer-name input-buffer) "'"))
+ "\nusing `texinfmt.el' version "
+ texinfmt-version
+ ".\n\n")
+
+ ;; Now convert for real.
+ (goto-char (point-min))
+ (texinfo-format-scan)
+ (goto-char (point-min))
+
+ (message "Done.")))
+
+
+;;; Primary internal formatting function for the whole buffer.
+
+(defun texinfo-format-buffer-1 ()
+ (let (texinfo-format-filename
+ texinfo-example-start
+ texinfo-command-start
+ texinfo-command-end
+ texinfo-command-name
+ texinfo-last-node
+ texinfo-last-node-pos
+ texinfo-vindex
+ texinfo-findex
+ texinfo-cindex
+ texinfo-pindex
+ texinfo-tindex
+ texinfo-kindex
+ texinfo-stack
+ texinfo-node-names
+ (texinfo-footnote-number 0)
+ last-input-buffer
+ outfile
+ (fill-column-for-info fill-column)
+ (input-buffer (current-buffer))
+ (input-directory default-directory))
+ (setq texinfo-enclosure-list nil)
+ (setq texinfo-alias-list nil)
+ (save-excursion
+ (goto-char (point-min))
+ (or (search-forward "@setfilename" nil t)
+ (error "Texinfo file needs an `@setfilename FILENAME' line."))
+ (setq texinfo-command-end (point))
+ (setq outfile (texinfo-parse-line-arg)))
+ (find-file outfile)
+ (texinfo-mode)
+ (setq fill-column fill-column-for-info)
+ (set-syntax-table texinfo-format-syntax-table)
+ (erase-buffer)
+ (insert-buffer-substring input-buffer)
+ (message "Converting %s to Info format..." (buffer-name input-buffer))
+
+ ;; Insert @include files so `texinfo-raise-lower-sections' can
+ ;; work on them without losing track of multiple
+ ;; @raise/@lowersections commands.
+ (goto-char (point-min))
+ (while (re-search-forward "^@include" nil t)
+ (setq texinfo-command-end (point))
+ (let ((filename (concat input-directory
+ (texinfo-parse-line-arg))))
+ (re-search-backward "^@include")
+ (delete-region (point) (save-excursion (forward-line 1) (point)))
+ (message "Reading included file: %s" filename)
+ (save-excursion
+ (save-restriction
+ (narrow-to-region
+ (point)
+ (+ (point) (car (cdr (insert-file-contents filename)))))
+ (goto-char (point-min))
+ ;; Remove `@setfilename' line from included file, if any,
+ ;; so @setfilename command not duplicated.
+ (if (re-search-forward
+ "^@setfilename"
+ (save-excursion (forward-line 100) (point)) t)
+ (progn
+ (beginning-of-line)
+ (delete-region
+ (point) (save-excursion (forward-line 1) (point)))))))))
+ ;; Raise or lower level of each section, if necessary.
+ (goto-char (point-min))
+ (texinfo-raise-lower-sections)
+ ;; Append @refill to appropriate paragraphs
+ (goto-char (point-min))
+ (texinfo-append-refill)
+ (goto-char (point-min))
+ (search-forward "@setfilename")
+ (beginning-of-line)
+ (delete-region (point-min) (point))
+ ;; Remove @bye at end of file, if it is there.
+ (goto-char (point-max))
+ (if (search-backward "@bye" nil t)
+ (delete-region (point) (point-max)))
+ ;; Make sure buffer ends in a newline.
+ (or (= (preceding-char) ?\n)
+ (insert "\n"))
+ ;; Scan the whole buffer, converting to Info format.
+ (texinfo-format-scan)
+ ;; Return data for indices.
+ (goto-char (point-min))
+ (list outfile
+ texinfo-vindex texinfo-findex texinfo-cindex
+ texinfo-pindex texinfo-tindex texinfo-kindex)))
+
+
+;;; Perform non-@-command file conversions: quotes and hyphens
+
+(defun texinfo-format-convert (min max)
+ ;; Convert left and right quotes to typewriter font quotes.
+ (goto-char min)
+ (while (search-forward "``" max t)
+ (replace-match "\""))
+ (goto-char min)
+ (while (search-forward "''" max t)
+ (replace-match "\""))
+ ;; Convert three hyphens in a row to two.
+ (goto-char min)
+ (while (re-search-forward "\\( \\|\\w\\)\\(---\\)\\( \\|\\w\\)" max t)
+ (delete-region (1+ (match-beginning 2)) (+ 2 (match-beginning
+ 2)))))
+
+
+;;; Handle paragraph filling
+
+;; Keep as concatinated lists for ease of maintenance
+
+(defvar texinfo-no-refill-regexp
+ (concat
+ "^@"
+ "\\("
+ "example\\|"
+ "smallexample\\|"
+ "lisp\\|"
+ "smalllisp\\|"
+ "display\\|"
+ "format\\|"
+ "flushleft\\|"
+ "flushright\\|"
+ "menu\\|"
+ "multitable\\|"
+ "titlepage\\|"
+ "iftex\\|"
+ "ifhtml\\|"
+ "tex\\|"
+ "html"
+ "\\)")
+ "Regexp specifying environments in which paragraphs are not filled.")
+
+(defvar texinfo-accent-commands
+ (concat
+ "@^\\|"
+ "@`\\|"
+ "@'\\|"
+ "@\"\\|"
+ "@,\\|"
+ "@=\\|"
+ "@~\\|"
+ "@OE{\\|"
+ "@oe{\\|"
+ "@AA{\\|"
+ "@aa{\\|"
+ "@AE{\\|"
+ "@ae{\\|"
+ "@ss{\\|"
+ "@questiondown{\\|"
+ "@exclamdown{\\|"
+ "@L{\\|"
+ "@l{\\|"
+ "@O{\\|"
+ "@o{\\|"
+ "@dotaccent{\\|"
+ "@ubaraccent{\\|"
+ "@d{\\|"
+ "@H{\\|"
+ "@ringaccent{\\|"
+ "@tieaccent{\\|"
+ "@u{\\|"
+ "@v{\\|"
+ "@dotless{"
+ ))
+
+(defvar texinfo-part-of-para-regexp
+ (concat
+ "^@"
+ "\\("
+ "b{\\|"
+ "bullet{\\|"
+ "cite{\\|"
+ "code{\\|"
+ "emph{\\|"
+ "equiv{\\|"
+ "error{\\|"
+ "expansion{\\|"
+ "file{\\|"
+ "i{\\|"
+ "inforef{\\|"
+ "kbd{\\|"
+ "key{\\|"
+ "lisp{\\|"
+ "email{\\|"
+ "minus{\\|"
+ "point{\\|"
+ "print{\\|"
+ "pxref{\\|"
+ "r{\\|"
+ "ref{\\|"
+ "result{\\|"
+ "samp{\\|"
+ "sc{\\|"
+ "t{\\|"
+ "TeX{\\|"
+ "today{\\|"
+ "url{\\|"
+ "var{\\|"
+ "w{\\|"
+ "xref{\\|"
+ "@-\\|" ; @- is a descretionary hyphen (not an accent) (a noop).
+ texinfo-accent-commands
+ "\\)"
+ )
+ "Regexp specifying @-commands found within paragraphs.")
+
+(defun texinfo-append-refill ()
+ "Append @refill at end of each paragraph that should be filled.
+Do not append @refill to paragraphs within @example and similar environments.
+Do not append @refill to paragraphs containing @w{TEXT} or @*."
+
+ ;; It is necessary to append @refill before other processing because
+ ;; the other processing removes information that tells Texinfo
+ ;; whether the text should or should not be filled.
+
+ (while (< (point) (point-max))
+ (let ((refill-blank-lines "^[ \t\n]*$")
+ (case-fold-search nil)) ; Don't confuse @TeX and @tex....
+ (beginning-of-line)
+ ;; 1. Skip over blank lines;
+ ;; skip over lines beginning with @-commands,
+ ;; but do not skip over lines
+ ;; that are no-refill environments such as @example or
+ ;; that begin with within-paragraph @-commands such as @code.
+ (while (and (looking-at (concat "^@\\|^\\\\\\|" refill-blank-lines))
+ (not (looking-at
+ (concat
+ "\\("
+ texinfo-no-refill-regexp
+ "\\|"
+ texinfo-part-of-para-regexp
+ "\\)")))
+ (< (point) (point-max)))
+ (forward-line 1))
+ ;; 2. Skip over @example and similar no-refill environments.
+ (if (looking-at texinfo-no-refill-regexp)
+ (let ((environment
+ (buffer-substring (match-beginning 1) (match-end 1))))
+ (progn (re-search-forward (concat "^@end " environment) nil t)
+ (forward-line 1)))
+ ;; Else
+ ;; 3. Do not refill a paragraph containing @w or @*, or ending
+ ;; with @<newline> followed by a newline.
+ (if (or
+ (>= (point) (point-max))
+ (re-search-forward
+ "@w{\\|@\\*\\|@\n\n"
+ (save-excursion
+ (forward-paragraph)
+ (forward-line 1)
+ (point)) t))
+ ;; Go to end of paragraph and do nothing.
+ (forward-paragraph)
+ ;; 4. Else go to end of paragraph and insert @refill
+ (forward-paragraph)
+ (forward-line -1)
+ (end-of-line)
+ (delete-region
+ (point)
+ (save-excursion (skip-chars-backward " \t") (point)))
+ ;; `looking-at-backward' not available in v. 18.57
+ ;; (if (not (looking-at-backward "@refill\\|@bye")) ;)
+ (if (not (re-search-backward
+ "@refill\\|@bye"
+ (save-excursion (beginning-of-line) (point))
+ t))
+ (insert "@refill"))
+ (forward-line 1))))))
+
+
+;;; Handle `@raisesections' and `@lowersections' commands
+
+;; These commands change the hierarchical level of chapter structuring
+;; commands.
+;;
+;; @raisesections changes @subsection to @section,
+;; @section to @chapter,
+;; etc.
+;;
+;; @lowersections changes @chapter to @section
+;; @subsection to @subsubsection,
+;; etc.
+;;
+;; An @raisesections/@lowersections command changes only those
+;; structuring commands that follow the @raisesections/@lowersections
+;; command.
+;;
+;; Repeated @raisesections/@lowersections continue to raise or lower
+;; the heading level.
+;;
+;; An @lowersections command cancels an @raisesections command, and
+;; vice versa.
+;;
+;; You cannot raise or lower "beyond" chapters or subsubsections, but
+;; trying to do so does not elicit an error---you just get more
+;; headings that mean the same thing as you keep raising or lowering
+;; (for example, after a single @raisesections, both @chapter and
+;; @section produce chapter headings).
+
+(defun texinfo-raise-lower-sections ()
+ "Raise or lower the hierarchical level of chapters, sections, etc.
+
+This function acts according to `@raisesections' and `@lowersections'
+commands in the Texinfo file.
+
+For example, an `@lowersections' command is useful if you wish to
+include what is written as an outer or standalone Texinfo file in
+another Texinfo file as an inner, included file. The `@lowersections'
+command changes chapters to sections, sections to subsections and so
+on.
+
+@raisesections changes @subsection to @section,
+ @section to @chapter,
+ @heading to @chapheading,
+ etc.
+
+@lowersections changes @chapter to @section,
+ @subsection to @subsubsection,
+ @heading to @subheading,
+ etc.
+
+An `@raisesections' or `@lowersections' command changes only those
+structuring commands that follow the `@raisesections' or
+`@lowersections' command.
+
+An `@lowersections' command cancels an `@raisesections' command, and
+vice versa.
+
+Repeated use of the commands continue to raise or lower the hierarchical
+level a step at a time.
+
+An attempt to raise above `chapters' reproduces chapter commands; an
+attempt to lower below subsubsections reproduces subsubsection
+commands."
+
+ ;; `texinfo-section-types-regexp' is defined in `texnfo-upd.el';
+ ;; it is a regexp matching chapter, section, other headings
+ ;; (but not the top node).
+
+ (let (type (level 0))
+ (while
+ (re-search-forward
+ (concat
+ "\\(\\(^@\\(raise\\|lower\\)sections\\)\\|\\("
+ texinfo-section-types-regexp
+ "\\)\\)")
+ nil t)
+ (beginning-of-line)
+ (save-excursion (setq type (read (current-buffer))))
+ (cond
+
+ ;; 1. Increment level
+ ((eq type '@raisesections)
+ (setq level (1+ level))
+ (delete-region
+ (point) (save-excursion (forward-line 1) (point))))
+
+ ;; 2. Decrement level
+ ((eq type '@lowersections)
+ (setq level (1- level))
+ (delete-region
+ (point) (save-excursion (forward-line 1) (point))))
+
+ ;; Now handle structuring commands
+ ((cond
+
+ ;; 3. Raise level when positive
+ ((> level 0)
+ (let ((count level)
+ (new-level type))
+ (while (> count 0)
+ (setq new-level
+ (cdr (assq new-level texinfo-raisesections-alist)))
+ (setq count (1- count)))
+ (kill-word 1)
+ (insert (symbol-name new-level))))
+
+ ;; 4. Do nothing except move point when level is zero
+ ((= level 0) (forward-line 1))
+
+ ;; 5. Lower level when positive
+ ((< level 0)
+ (let ((count level)
+ (new-level type))
+ (while (< count 0)
+ (setq new-level
+ (cdr (assq new-level texinfo-lowersections-alist)))
+ (setq count (1+ count)))
+ (kill-word 1)
+ (insert (symbol-name new-level))))))))))
+
+(defvar texinfo-raisesections-alist
+ '((@chapter . @chapter) ; Cannot go higher
+ (@unnumbered . @unnumbered)
+ (@centerchap . @unnumbered)
+
+ (@majorheading . @majorheading)
+ (@chapheading . @chapheading)
+ (@appendix . @appendix)
+
+ (@section . @chapter)
+ (@unnumberedsec . @unnumbered)
+ (@heading . @chapheading)
+ (@appendixsec . @appendix)
+
+ (@subsection . @section)
+ (@unnumberedsubsec . @unnumberedsec)
+ (@subheading . @heading)
+ (@appendixsubsec . @appendixsec)
+
+ (@subsubsection . @subsection)
+ (@unnumberedsubsubsec . @unnumberedsubsec)
+ (@subsubheading . @subheading)
+ (@appendixsubsubsec . @appendixsubsec))
+ "*An alist of next higher levels for chapters, sections. etc.
+For example, section to chapter, subsection to section.
+Used by `texinfo-raise-lower-sections'.
+The keys specify types of section; the values correspond to the next
+higher types.")
+
+(defvar texinfo-lowersections-alist
+ '((@chapter . @section)
+ (@unnumbered . @unnumberedsec)
+ (@centerchap . @unnumberedsec)
+ (@majorheading . @heading)
+ (@chapheading . @heading)
+ (@appendix . @appendixsec)
+
+ (@section . @subsection)
+ (@unnumberedsec . @unnumberedsubsec)
+ (@heading . @subheading)
+ (@appendixsec . @appendixsubsec)
+
+ (@subsection . @subsubsection)
+ (@unnumberedsubsec . @unnumberedsubsubsec)
+ (@subheading . @subsubheading)
+ (@appendixsubsec . @appendixsubsubsec)
+
+ (@subsubsection . @subsubsection) ; Cannot go lower.
+ (@unnumberedsubsubsec . @unnumberedsubsubsec)
+ (@subsubheading . @subsubheading)
+ (@appendixsubsubsec . @appendixsubsubsec))
+ "*An alist of next lower levels for chapters, sections. etc.
+For example, chapter to section, section to subsection.
+Used by `texinfo-raise-lower-sections'.
+The keys specify types of section; the values correspond to the next
+lower types.")
+
+
+;;; Perform those texinfo-to-info conversions that apply to the whole input
+;;; uniformly.
+
+(defun texinfo-format-scan ()
+ (texinfo-format-convert (point-min) (point-max))
+ ;; Scan for @-commands.
+ (goto-char (point-min))
+ (while (search-forward "@" nil t)
+ ;;
+ ;; These are the single-character accent commands: @^ @` @' @" @= @~
+ ;; In Info, they are simply quoted and the @ deleted.
+ ;; Other single-character commands:
+ ;; @* forces a line break,
+ ;; @- is a discretionary hyphenation point; does nothing in Info.
+ ;; @<space>, @<tab>, @<newline> each produce a single space,
+ ;; unless followed by a newline.
+ ;;
+ ;; Old version 2.34 expression: (looking-at "[@{}^'` *\"?!]")
+ (if (looking-at "[@{}^'`\"=~ \t\n*?!-]")
+ ;; @*, causes a line break.
+ (cond
+ ;; @*, a line break
+ ((= (following-char) ?*)
+ ;; remove command
+ (delete-region (1- (point)) (1+ (point)))
+ ;; insert return if not at end of line;
+ ;; else line is already broken.
+ (if (not (= (following-char) ?\n))
+ (insert ?\n)))
+ ;; @-, deleted
+ ((= (following-char) ?-)
+ (delete-region (1- (point)) (1+ (point))))
+ ;; @<space>, @<tab>, @<newline>: produce a single space,
+ ;; unless followed by a newline.
+ ((= (following-char) ? )
+ (delete-region (1- (point)) (1+ (point)))
+ ;; insert single space if not at end of line;
+ ;; else line is already broken.
+ (if (not (= (following-char) ?\n))
+ (insert ? )))
+ ((= (following-char) ?\t)
+ (delete-region (1- (point)) (1+ (point)))
+ ;; insert single space if not at end of line;
+ ;; else line is already broken.
+ (if (not (= (following-char) ?\n))
+ (insert ? )))
+ ;; following char is a carriage return
+ ((= (following-char) ?
+)
+ ;; remove command
+ (delete-region (1- (point)) (1+ (point)))
+ ;; insert single space if not at end of line;
+ ;; else line is already broken.
+ (if (not (= (following-char) ?\n))
+ (insert ? )))
+ ;; Otherwise: the other characters are simply quoted. Delete the @.
+ (t
+ (delete-char -1)
+ (forward-char 1)))
+ ;; @ is followed by a command-word; find the end of the word.
+ (setq texinfo-command-start (1- (point)))
+ (if (= (char-syntax (following-char)) ?w)
+ (forward-word 1)
+ (forward-char 1))
+ (setq texinfo-command-end (point))
+ ;; Handle let aliasing
+ (setq texinfo-command-name
+ (let (trial
+ (cmdname
+ (buffer-substring
+ (1+ texinfo-command-start) texinfo-command-end)))
+ (while (setq trial (assoc cmdname texinfo-alias-list))
+ (setq cmdname (cdr trial)))
+ (intern cmdname)))
+ ;; Call the handler for this command.
+ (let ((enclosure-type
+ (assoc
+ (symbol-name texinfo-command-name)
+ texinfo-enclosure-list)))
+ (if enclosure-type
+ (progn
+ (insert
+ (car (car (cdr enclosure-type)))
+ (texinfo-parse-arg-discard)
+ (car (cdr (car (cdr enclosure-type)))))
+ (goto-char texinfo-command-start))
+ (let ((cmd (get texinfo-command-name 'texinfo-format)))
+ (if cmd (funcall cmd) (texinfo-unsupported)))))))
+
+ (cond (texinfo-stack
+ (goto-char (nth 2 (car texinfo-stack)))
+ (error "Unterminated @%s" (car (car texinfo-stack))))))
+
+(put 'begin 'texinfo-format 'texinfo-format-begin)
+(defun texinfo-format-begin ()
+ (texinfo-format-begin-end 'texinfo-format))
+
+(put 'end 'texinfo-format 'texinfo-format-end)
+(defun texinfo-format-end ()
+ (texinfo-format-begin-end 'texinfo-end))
+
+(defun texinfo-format-begin-end (prop)
+ (setq texinfo-command-name (intern (texinfo-parse-line-arg)))
+ (let ((cmd (get texinfo-command-name prop)))
+ (if cmd (funcall cmd)
+ (texinfo-unsupported))))
+
+;;; Parsing functions
+
+(defun texinfo-parse-line-arg ()
+ "Return argument of @-command as string.
+Argument is separated from command either by a space or by a brace.
+If a space, return rest of line, with beginning and ending white
+space removed. If a brace, return string between braces.
+Leave point after argument."
+ (goto-char texinfo-command-end)
+ (let ((start (point)))
+ (cond ((looking-at " ")
+ (skip-chars-forward " ")
+ (setq start (point))
+ (end-of-line)
+ (skip-chars-backward " ")
+ (delete-region (point) (progn (end-of-line) (point)))
+ (setq texinfo-command-end (1+ (point))))
+ ((looking-at "{")
+ (setq start (1+ (point)))
+ (forward-list 1)
+ (setq texinfo-command-end (point))
+ (forward-char -1))
+ (t
+ (error "Invalid texinfo command arg format")))
+ (prog1 (buffer-substring start (point))
+ (if (eolp) (forward-char 1)))))
+
+(defun texinfo-parse-expanded-arg ()
+ (goto-char texinfo-command-end)
+ (let ((start (point))
+ marker)
+ (cond ((looking-at " ")
+ (skip-chars-forward " ")
+ (setq start (point))
+ (end-of-line)
+ (setq texinfo-command-end (1+ (point))))
+ ((looking-at "{")
+ (setq start (1+ (point)))
+ (forward-list 1)
+ (setq texinfo-command-end (point))
+ (forward-char -1))
+ (t
+ (error "Invalid texinfo command arg format")))
+ (setq marker (move-marker (make-marker) texinfo-command-end))
+ (texinfo-format-expand-region start (point))
+ (setq texinfo-command-end (marker-position marker))
+ (move-marker marker nil)
+ (prog1 (buffer-substring start (point))
+ (if (eolp) (forward-char 1)))))
+
+(defun texinfo-format-expand-region (start end)
+ (save-restriction
+ (narrow-to-region start end)
+ (let (texinfo-command-start
+ texinfo-command-end
+ texinfo-command-name
+ texinfo-stack)
+ (texinfo-format-scan))
+ (goto-char (point-max))))
+
+(defun texinfo-parse-arg-discard ()
+ "Delete command and argument; return argument of command."
+ (prog1 (texinfo-parse-line-arg)
+ (texinfo-discard-command)))
+
+(defun texinfo-discard-command ()
+ (delete-region texinfo-command-start texinfo-command-end))
+
+(defun texinfo-optional-braces-discard ()
+ "Discard braces following command, if any."
+ (goto-char texinfo-command-end)
+ (let ((start (point)))
+ (cond ((looking-at "[ \t]*\n")) ; do nothing
+ ((looking-at "{") ; remove braces, if any
+ (forward-list 1)
+ (setq texinfo-command-end (point)))
+ (t
+ (error
+ "Invalid `texinfo-optional-braces-discard' format \(need braces?\)")))
+ (delete-region texinfo-command-start texinfo-command-end)))
+
+(defun texinfo-format-parse-line-args ()
+ (let ((start (1- (point)))
+ next beg end
+ args)
+ (skip-chars-forward " ")
+ (while (not (eolp))
+ (setq beg (point))
+ (re-search-forward "[\n,]")
+ (setq next (point))
+ (if (bolp) (setq next (1- next)))
+ (forward-char -1)
+ (skip-chars-backward " ")
+ (setq end (point))
+ (setq args (cons (if (> end beg) (buffer-substring beg end))
+ args))
+ (goto-char next)
+ (skip-chars-forward " "))
+ (if (eolp) (forward-char 1))
+ (setq texinfo-command-end (point))
+ (nreverse args)))
+
+(defun texinfo-format-parse-args ()
+ (let ((start (1- (point)))
+ next beg end
+ args)
+ (search-forward "{")
+ (save-excursion
+ (texinfo-format-expand-region
+ (point)
+ (save-excursion (up-list 1) (1- (point)))))
+ ;; The following does not handle cross references of the form:
+ ;; `@xref{bullet, , @code{@@bullet}@{@}}.' because the
+ ;; re-search-forward finds the first right brace after the second
+ ;; comma.
+ (while (/= (preceding-char) ?\})
+ (skip-chars-forward " \t\n")
+ (setq beg (point))
+ (re-search-forward "[},]")
+ (setq next (point))
+ (forward-char -1)
+ (skip-chars-backward " \t\n")
+ (setq end (point))
+ (cond ((< beg end)
+ (goto-char beg)
+ (while (search-forward "\n" end t)
+ (replace-match " "))))
+ (setq args (cons (if (> end beg) (buffer-substring beg end))
+ args))
+ (goto-char next))
+ (if (eolp) (forward-char 1))
+ (setq texinfo-command-end (point))
+ (nreverse args)))
+
+(defun texinfo-format-parse-defun-args ()
+ (goto-char texinfo-command-end)
+ (let ((start (point)))
+ (end-of-line)
+ (setq texinfo-command-end (1+ (point)))
+ (let ((marker (move-marker (make-marker) texinfo-command-end)))
+ (texinfo-format-expand-region start (point))
+ (setq texinfo-command-end (marker-position marker))
+ (move-marker marker nil))
+ (goto-char start)
+ (let ((args '())
+ beg end)
+ (skip-chars-forward " ")
+ (while (not (eolp))
+ (cond ((looking-at "{")
+ (setq beg (1+ (point)))
+ (forward-list 1)
+ (setq end (1- (point))))
+ (t
+ (setq beg (point))
+ (re-search-forward "[\n ]")
+ (forward-char -1)
+ (setq end (point))))
+ (setq args (cons (buffer-substring beg end) args))
+ (skip-chars-forward " "))
+ (forward-char 1)
+ (nreverse args))))
+
+(defun texinfo-discard-line ()
+ (goto-char texinfo-command-end)
+ (skip-chars-forward " \t")
+ (or (eolp)
+ (error "Extraneous text at end of command line."))
+ (goto-char texinfo-command-start)
+ (or (bolp)
+ (error "Extraneous text at beginning of command line."))
+ (delete-region (point) (progn (forward-line 1) (point))))
+
+(defun texinfo-discard-line-with-args ()
+ (goto-char texinfo-command-start)
+ (delete-region (point) (progn (forward-line 1) (point))))
+
+
+;;; @setfilename
+
+;; Only `texinfo-format-buffer' handles @setfilename with this
+;; definition; `texinfo-format-region' handles @setfilename, if any,
+;; specially.
+(put 'setfilename 'texinfo-format 'texinfo-format-setfilename)
+(defun texinfo-format-setfilename ()
+ (let ((arg (texinfo-parse-arg-discard)))
+ (message "Formatting Info file: %s" arg)
+ (setq texinfo-format-filename
+ (file-name-nondirectory (expand-file-name arg)))
+ (insert "Info file: "
+ texinfo-format-filename ", -*-Text-*-\n"
+ ;; Date string removed so that regression testing is easier.
+ ;; "produced on "
+ ;; (substring (current-time-string) 8 10) " "
+ ;; (substring (current-time-string) 4 7) " "
+ ;; (substring (current-time-string) -4) " "
+ "produced by `texinfo-format-buffer'\n"
+ "from file"
+ (if (buffer-file-name input-buffer)
+ (concat " `"
+ (file-name-sans-versions
+ (file-name-nondirectory
+ (buffer-file-name input-buffer)))
+ "'")
+ (concat "buffer `" (buffer-name input-buffer) "'"))
+ "\nusing `texinfmt.el' version "
+ texinfmt-version
+ ".\n\n")))
+
+;;; @node, @menu, @detailmenu
+
+(put 'node 'texinfo-format 'texinfo-format-node)
+(put 'nwnode 'texinfo-format 'texinfo-format-node)
+(defun texinfo-format-node ()
+ (let* ((args (texinfo-format-parse-line-args))
+ (name (nth 0 args))
+ (next (nth 1 args))
+ (prev (nth 2 args))
+ (up (nth 3 args)))
+ (texinfo-discard-command)
+ (setq texinfo-last-node name)
+ (let ((tem (downcase name)))
+ (if (assoc tem texinfo-node-names)
+ (error "Duplicate node name: %s" name)
+ (setq texinfo-node-names (cons (list tem) texinfo-node-names))))
+ (setq texinfo-footnote-number 0)
+ ;; insert "\n\^_" unconditionally since this is what info is looking for
+ (insert "\n\^_\nFile: " texinfo-format-filename
+ ", Node: " name)
+ (if next
+ (insert ", Next: " next))
+ (if prev
+ (insert ", Prev: " prev))
+ (if up
+ (insert ", Up: " up))
+ (insert ?\n)
+ (setq texinfo-last-node-pos (point))))
+
+(put 'menu 'texinfo-format 'texinfo-format-menu)
+(defun texinfo-format-menu ()
+ (texinfo-discard-line)
+ (insert "* Menu:\n\n"))
+
+(put 'menu 'texinfo-end 'texinfo-discard-command)
+
+;; The @detailmenu should be removed eventually.
+
+;; According to Karl Berry, 31 August 1996:
+;;
+;; You don't like, I don't like it. I agree, it would be better just to
+;; fix the bug [in `makeinfo']. .. At this point, since inserting those
+;; two commands in the Elisp fn is trivial, I don't especially want to
+;; expend more effort...
+;;
+;; I added a couple sentences of documentation to the manual (putting the
+;; blame on makeinfo where it belongs :-().
+
+(put 'detailmenu 'texinfo-format 'texinfo-discard-line)
+(put 'detailmenu 'texinfo-end 'texinfo-discard-command)
+
+;; (Also see `texnfo-upd.el')
+
+
+;;; Cross references
+
+;; @xref {NODE, FNAME, NAME, FILE, DOCUMENT}
+;; -> *Note FNAME: (FILE)NODE
+;; If FILE is missing,
+;; *Note FNAME: NODE
+;; If FNAME is empty and NAME is present
+;; *Note NAME: Node
+;; If both NAME and FNAME are missing
+;; *Note NODE::
+;; texinfo ignores the DOCUMENT argument.
+;; -> See section <xref to NODE> [NAME, else NODE], page <xref to NODE>
+;; If FILE is specified, (FILE)NODE is used for xrefs.
+;; If fifth argument DOCUMENT is specified, produces
+;; See section <xref to NODE> [NAME, else NODE], page <xref to NODE>
+;; of DOCUMENT
+
+;; @ref a reference that does not put `See' or `see' in
+;; the hardcopy and is the same as @xref in Info
+(put 'ref 'texinfo-format 'texinfo-format-xref)
+
+(put 'xref 'texinfo-format 'texinfo-format-xref)
+(defun texinfo-format-xref ()
+ (let ((args (texinfo-format-parse-args)))
+ (texinfo-discard-command)
+ (insert "*Note ")
+ (let ((fname (or (nth 1 args) (nth 2 args))))
+ (if (null (or fname (nth 3 args)))
+ (insert (car args) "::")
+ (insert (or fname (car args)) ": ")
+ (if (nth 3 args)
+ (insert "(" (nth 3 args) ")"))
+ (insert (car args))))))
+
+(put 'pxref 'texinfo-format 'texinfo-format-pxref)
+(defun texinfo-format-pxref ()
+ (texinfo-format-xref)
+ (or (save-excursion
+ (forward-char -2)
+ (looking-at "::"))
+ (insert ".")))
+
+;; @inforef{NODE, FNAME, FILE}
+;; Like @xref{NODE, FNAME,,FILE} in texinfo.
+;; In Tex, generates "See Info file FILE, node NODE"
+(put 'inforef 'texinfo-format 'texinfo-format-inforef)
+(defun texinfo-format-inforef ()
+ (let ((args (texinfo-format-parse-args)))
+ (texinfo-discard-command)
+ (if (nth 1 args)
+ (insert "*Note " (nth 1 args) ": (" (nth 2 args) ")" (car args))
+ (insert "*Note " "(" (nth 2 args) ")" (car args) "::"))))
+
+
+;;; Section headings
+
+(put 'majorheading 'texinfo-format 'texinfo-format-chapter)
+(put 'chapheading 'texinfo-format 'texinfo-format-chapter)
+(put 'ichapter 'texinfo-format 'texinfo-format-chapter)
+(put 'chapter 'texinfo-format 'texinfo-format-chapter)
+(put 'iappendix 'texinfo-format 'texinfo-format-chapter)
+(put 'appendix 'texinfo-format 'texinfo-format-chapter)
+(put 'iunnumbered 'texinfo-format 'texinfo-format-chapter)
+(put 'top 'texinfo-format 'texinfo-format-chapter)
+(put 'unnumbered 'texinfo-format 'texinfo-format-chapter)
+(put 'centerchap 'texinfo-format 'texinfo-format-chapter)
+(defun texinfo-format-chapter ()
+ (texinfo-format-chapter-1 ?*))
+
+(put 'heading 'texinfo-format 'texinfo-format-section)
+(put 'isection 'texinfo-format 'texinfo-format-section)
+(put 'section 'texinfo-format 'texinfo-format-section)
+(put 'iappendixsection 'texinfo-format 'texinfo-format-section)
+(put 'appendixsection 'texinfo-format 'texinfo-format-section)
+(put 'iappendixsec 'texinfo-format 'texinfo-format-section)
+(put 'appendixsec 'texinfo-format 'texinfo-format-section)
+(put 'iunnumberedsec 'texinfo-format 'texinfo-format-section)
+(put 'unnumberedsec 'texinfo-format 'texinfo-format-section)
+(defun texinfo-format-section ()
+ (texinfo-format-chapter-1 ?=))
+
+(put 'subheading 'texinfo-format 'texinfo-format-subsection)
+(put 'isubsection 'texinfo-format 'texinfo-format-subsection)
+(put 'subsection 'texinfo-format 'texinfo-format-subsection)
+(put 'iappendixsubsec 'texinfo-format 'texinfo-format-subsection)
+(put 'appendixsubsec 'texinfo-format 'texinfo-format-subsection)
+(put 'iunnumberedsubsec 'texinfo-format 'texinfo-format-subsection)
+(put 'unnumberedsubsec 'texinfo-format 'texinfo-format-subsection)
+(defun texinfo-format-subsection ()
+ (texinfo-format-chapter-1 ?-))
+
+(put 'subsubheading 'texinfo-format 'texinfo-format-subsubsection)
+(put 'isubsubsection 'texinfo-format 'texinfo-format-subsubsection)
+(put 'subsubsection 'texinfo-format 'texinfo-format-subsubsection)
+(put 'iappendixsubsubsec 'texinfo-format 'texinfo-format-subsubsection)
+(put 'appendixsubsubsec 'texinfo-format 'texinfo-format-subsubsection)
+(put 'iunnumberedsubsubsec 'texinfo-format 'texinfo-format-subsubsection)
+(put 'unnumberedsubsubsec 'texinfo-format 'texinfo-format-subsubsection)
+(defun texinfo-format-subsubsection ()
+ (texinfo-format-chapter-1 ?.))
+
+(defun texinfo-format-chapter-1 (belowchar)
+ (let ((arg (texinfo-parse-arg-discard)))
+ (message "Formatting: %s ... " arg) ; So we can see where we are.
+ (insert ?\n arg ?\n "@SectionPAD " belowchar ?\n)
+ (forward-line -2)))
+
+(put 'SectionPAD 'texinfo-format 'texinfo-format-sectionpad)
+(defun texinfo-format-sectionpad ()
+ (let ((str (texinfo-parse-arg-discard)))
+ (forward-char -1)
+ (let ((column (current-column)))
+ (forward-char 1)
+ (while (> column 0)
+ (insert str)
+ (setq column (1- column))))
+ (insert ?\n)))
+
+
+;;; Space controlling commands: @. and @:, and the soft hyphen.
+
+(put '\. 'texinfo-format 'texinfo-format-\.)
+(defun texinfo-format-\. ()
+ (texinfo-discard-command)
+ (insert "."))
+
+(put '\: 'texinfo-format 'texinfo-format-\:)
+(defun texinfo-format-\: ()
+ (texinfo-discard-command))
+
+(put '\- 'texinfo-format 'texinfo-format-soft-hyphen)
+(defun texinfo-format-soft-hyphen ()
+ (texinfo-discard-command))
+
+
+;;; @center, @sp, and @br
+
+(put 'center 'texinfo-format 'texinfo-format-center)
+(defun texinfo-format-center ()
+ (let ((arg (texinfo-parse-expanded-arg)))
+ (texinfo-discard-command)
+ (insert arg)
+ (insert ?\n)
+ (save-restriction
+ (goto-char (1- (point)))
+ (let ((indent-tabs-mode nil))
+ (center-line)))))
+
+(put 'sp 'texinfo-format 'texinfo-format-sp)
+(defun texinfo-format-sp ()
+ (let* ((arg (texinfo-parse-arg-discard))
+ (num (read arg)))
+ (insert-char ?\n num)))
+
+(put 'br 'texinfo-format 'texinfo-format-paragraph-break)
+(defun texinfo-format-paragraph-break ()
+ "Force a paragraph break.
+If used within a line, follow `@br' with braces."
+ (texinfo-optional-braces-discard)
+ ;; insert one return if at end of line;
+ ;; else insert two returns, to generate a blank line.
+ (if (= (following-char) ?\n)
+ (insert ?\n)
+ (insert-char ?\n 2)))
+
+
+;;; @footnote and @footnotestyle
+
+;; In Texinfo, footnotes are created with the `@footnote' command.
+;; This command is followed immediately by a left brace, then by the text of
+;; the footnote, and then by a terminating right brace. The
+;; template for a footnote is:
+;;
+;; @footnote{TEXT}
+;;
+;; Info has two footnote styles:
+;;
+;; * In the End of node style, all the footnotes for a single node
+;; are placed at the end of that node. The footnotes are
+;; separated from the rest of the node by a line of dashes with
+;; the word `Footnotes' within it.
+;;
+;; * In the Separate node style, all the footnotes for a single node
+;; are placed in an automatically constructed node of their own.
+
+;; Footnote style is specified by the @footnotestyle command, either
+;; @footnotestyle separate
+;; or
+;; @footnotestyle end
+;;
+;; The default is separate
+
+(defvar texinfo-footnote-style "separate"
+ "Footnote style, either separate or end.")
+
+(put 'footnotestyle 'texinfo-format 'texinfo-footnotestyle)
+(defun texinfo-footnotestyle ()
+ "Specify whether footnotes are at end of node or in separate nodes.
+Argument is either end or separate."
+ (setq texinfo-footnote-style (texinfo-parse-arg-discard)))
+
+(defvar texinfo-footnote-number)
+
+(put 'footnote 'texinfo-format 'texinfo-format-footnote)
+(defun texinfo-format-footnote ()
+ "Format a footnote in either end of node or separate node style.
+The texinfo-footnote-style variable controls which style is used."
+ (setq texinfo-footnote-number (1+ texinfo-footnote-number))
+ (cond ((string= texinfo-footnote-style "end")
+ (texinfo-format-end-node))
+ ((string= texinfo-footnote-style "separate")
+ (texinfo-format-separate-node))))
+
+(defun texinfo-format-separate-node ()
+ "Format footnote in Separate node style, with notes in own node.
+The node is constructed automatically."
+ (let* (start
+ (arg (texinfo-parse-line-arg))
+ (node-name-beginning
+ (save-excursion
+ (re-search-backward
+ "^File: \\w+\\(\\w\\|\\s_\\|\\.\\|,\\)*[ \t]+Node:")
+ (match-end 0)))
+ (node-name
+ (save-excursion
+ (buffer-substring
+ (progn (goto-char node-name-beginning) ; skip over node command
+ (skip-chars-forward " \t") ; and over spaces
+ (point))
+ (if (search-forward
+ ","
+ (save-excursion (end-of-line) (point)) t) ; bound search
+ (1- (point))
+ (end-of-line) (point))))))
+ (texinfo-discard-command) ; remove or insert whitespace, as needed
+ (delete-region (save-excursion (skip-chars-backward " \t\n") (point))
+ (point))
+ (insert (format " (%d) (*Note %s-Footnotes::)"
+ texinfo-footnote-number node-name))
+ (fill-paragraph nil)
+ (save-excursion
+ (if (re-search-forward "^@node" nil 'move)
+ (forward-line -1))
+
+ ;; two cases: for the first footnote, we must insert a node header;
+ ;; for the second and subsequent footnotes, we need only insert
+ ;; the text of the footnote.
+
+ (if (save-excursion
+ (re-search-backward
+ (concat node-name "-Footnotes, Up: ")
+ node-name-beginning
+ t))
+ (progn ; already at least one footnote
+ (setq start (point))
+ (insert (format "\n(%d) %s\n" texinfo-footnote-number arg))
+ (fill-region start (point)))
+ ;; else not yet a footnote
+ (insert "\n\^_\nFile: " texinfo-format-filename
+ " Node: " node-name "-Footnotes, Up: " node-name "\n")
+ (setq start (point))
+ (insert (format "\n(%d) %s\n" texinfo-footnote-number arg))
+ (fill-region start (point))))))
+
+(defun texinfo-format-end-node ()
+ "Format footnote in the End of node style, with notes at end of node."
+ (let (start
+ (arg (texinfo-parse-line-arg)))
+ (texinfo-discard-command) ; remove or insert whitespace, as needed
+ (delete-region (save-excursion (skip-chars-backward " \t\n") (point))
+ (point))
+ (insert (format " (%d) " texinfo-footnote-number))
+ (fill-paragraph nil)
+ (save-excursion
+ (if (search-forward "\n--------- Footnotes ---------\n" nil t)
+ (progn ; already have footnote, put new one before end of node
+ (if (re-search-forward "^@node" nil 'move)
+ (forward-line -1))
+ (setq start (point))
+ (insert (format "\n(%d) %s\n" texinfo-footnote-number arg))
+ (fill-region start (point)))
+ ;; else no prior footnote
+ (if (re-search-forward "^@node" nil 'move)
+ (forward-line -1))
+ (insert "\n--------- Footnotes ---------\n")
+ (setq start (point))
+ (insert (format "\n(%d) %s\n" texinfo-footnote-number arg))))))
+
+
+;;; @itemize, @enumerate, and similar commands
+
+;; @itemize pushes (itemize "COMMANDS" STARTPOS) on texinfo-stack.
+;; @enumerate pushes (enumerate 0 STARTPOS).
+;; @item dispatches to the texinfo-item prop of the first elt of the list.
+;; For itemize, this puts in and rescans the COMMANDS.
+;; For enumerate, this increments the number and puts it in.
+;; In either case, it puts a Backspace at the front of the line
+;; which marks it not to be indented later.
+;; All other lines get indented by 5 when the @end is reached.
+
+(defvar texinfo-stack-depth 0
+ "Count of number of unpopped texinfo-push-stack calls.
+Used by @refill indenting command to avoid indenting within lists, etc.")
+
+(defun texinfo-push-stack (check arg)
+ (setq texinfo-stack-depth (1+ texinfo-stack-depth))
+ (setq texinfo-stack
+ (cons (list check arg texinfo-command-start)
+ texinfo-stack)))
+
+(defun texinfo-pop-stack (check)
+ (setq texinfo-stack-depth (1- texinfo-stack-depth))
+ (if (null texinfo-stack)
+ (error "Unmatched @end %s" check))
+ (if (not (eq (car (car texinfo-stack)) check))
+ (error "@end %s matches @%s"
+ check (car (car texinfo-stack))))
+ (prog1 (cdr (car texinfo-stack))
+ (setq texinfo-stack (cdr texinfo-stack))))
+
+(put 'itemize 'texinfo-format 'texinfo-itemize)
+(defun texinfo-itemize ()
+ (texinfo-push-stack
+ 'itemize
+ (progn (skip-chars-forward " \t")
+ (if (eolp)
+ "@bullet"
+ (texinfo-parse-line-arg))))
+ (texinfo-discard-line-with-args)
+ (setq fill-column (- fill-column 5)))
+
+(put 'itemize 'texinfo-end 'texinfo-end-itemize)
+(defun texinfo-end-itemize ()
+ (setq fill-column (+ fill-column 5))
+ (texinfo-discard-command)
+ (let ((stacktop
+ (texinfo-pop-stack 'itemize)))
+ (texinfo-do-itemize (nth 1 stacktop))))
+
+(put 'enumerate 'texinfo-format 'texinfo-enumerate)
+(defun texinfo-enumerate ()
+ (texinfo-push-stack
+ 'enumerate
+ (progn (skip-chars-forward " \t")
+ (if (eolp)
+ 1
+ (read (current-buffer)))))
+ (if (and (symbolp (car (cdr (car texinfo-stack))))
+ (> 1 (length (symbol-name (car (cdr (car texinfo-stack)))))))
+ (error
+ "@enumerate: Use a number or letter, eg: 1, A, a, 3, B, or d." ))
+ (texinfo-discard-line-with-args)
+ (setq fill-column (- fill-column 5)))
+
+(put 'enumerate 'texinfo-end 'texinfo-end-enumerate)
+(defun texinfo-end-enumerate ()
+ (setq fill-column (+ fill-column 5))
+ (texinfo-discard-command)
+ (let ((stacktop
+ (texinfo-pop-stack 'enumerate)))
+ (texinfo-do-itemize (nth 1 stacktop))))
+
+;; @alphaenumerate never became a standard part of Texinfo
+(put 'alphaenumerate 'texinfo-format 'texinfo-alphaenumerate)
+(defun texinfo-alphaenumerate ()
+ (texinfo-push-stack 'alphaenumerate (1- ?a))
+ (setq fill-column (- fill-column 5))
+ (texinfo-discard-line))
+
+(put 'alphaenumerate 'texinfo-end 'texinfo-end-alphaenumerate)
+(defun texinfo-end-alphaenumerate ()
+ (setq fill-column (+ fill-column 5))
+ (texinfo-discard-command)
+ (let ((stacktop
+ (texinfo-pop-stack 'alphaenumerate)))
+ (texinfo-do-itemize (nth 1 stacktop))))
+
+;; @capsenumerate never became a standard part of Texinfo
+(put 'capsenumerate 'texinfo-format 'texinfo-capsenumerate)
+(defun texinfo-capsenumerate ()
+ (texinfo-push-stack 'capsenumerate (1- ?A))
+ (setq fill-column (- fill-column 5))
+ (texinfo-discard-line))
+
+(put 'capsenumerate 'texinfo-end 'texinfo-end-capsenumerate)
+(defun texinfo-end-capsenumerate ()
+ (setq fill-column (+ fill-column 5))
+ (texinfo-discard-command)
+ (let ((stacktop
+ (texinfo-pop-stack 'capsenumerate)))
+ (texinfo-do-itemize (nth 1 stacktop))))
+
+;; At the @end, indent all the lines within the construct
+;; except those marked with backspace. FROM says where
+;; construct started.
+(defun texinfo-do-itemize (from)
+ (save-excursion
+ (while (progn (forward-line -1)
+ (>= (point) from))
+ (if (= (following-char) ?\b)
+ (save-excursion
+ (delete-char 1)
+ (end-of-line)
+ (delete-char 6))
+ (if (not (looking-at "[ \t]*$"))
+ (save-excursion (insert " ")))))))
+
+(put 'item 'texinfo-format 'texinfo-item)
+(put 'itemx 'texinfo-format 'texinfo-item)
+(defun texinfo-item ()
+ (funcall (get (car (car texinfo-stack)) 'texinfo-item)))
+
+(put 'itemize 'texinfo-item 'texinfo-itemize-item)
+(defun texinfo-itemize-item ()
+ ;; (texinfo-discard-line) ; Did not handle text on same line as @item.
+ (delete-region (1+ (point)) (save-excursion (beginning-of-line) (point)))
+ (if (looking-at "[ \t]*[^ \t\n]+")
+ ;; Text on same line as @item command.
+ (insert "\b " (nth 1 (car texinfo-stack)) " \n")
+ ;; Else text on next line.
+ (insert "\b " (nth 1 (car texinfo-stack)) " "))
+ (forward-line -1))
+
+(put 'enumerate 'texinfo-item 'texinfo-enumerate-item)
+(defun texinfo-enumerate-item ()
+ (texinfo-discard-line)
+ (let (enumerating-symbol)
+ (cond ((integerp (car (cdr (car texinfo-stack))))
+ (setq enumerating-symbol (car (cdr (car texinfo-stack))))
+ (insert ?\b (format "%3d. " enumerating-symbol) ?\n)
+ (setcar (cdr (car texinfo-stack)) (1+ enumerating-symbol)))
+ ((symbolp (car (cdr (car texinfo-stack))))
+ (setq enumerating-symbol
+ (symbol-name (car (cdr (car texinfo-stack)))))
+ (if (or (equal ?\[ (string-to-char enumerating-symbol))
+ (equal ?\{ (string-to-char enumerating-symbol)))
+ (error
+ "Too many items in enumerated list; alphabet ends at Z."))
+ (insert ?\b (format "%3s. " enumerating-symbol) ?\n)
+ (setcar (cdr (car texinfo-stack))
+ (make-symbol
+ (char-to-string
+ (1+
+ (string-to-char enumerating-symbol))))))
+ (t
+ (error
+ "@enumerate: Use a number or letter, eg: 1, A, a, 3, B or d." )))
+ (forward-line -1)))
+
+(put 'alphaenumerate 'texinfo-item 'texinfo-alphaenumerate-item)
+(defun texinfo-alphaenumerate-item ()
+ (texinfo-discard-line)
+ (let ((next (1+ (car (cdr (car texinfo-stack))))))
+ (if (> next ?z)
+ (error "More than 26 items in @alphaenumerate; get a bigger alphabet."))
+ (setcar (cdr (car texinfo-stack)) next)
+ (insert "\b " next ". \n"))
+ (forward-line -1))
+
+(put 'capsenumerate 'texinfo-item 'texinfo-capsenumerate-item)
+(defun texinfo-capsenumerate-item ()
+ (texinfo-discard-line)
+ (let ((next (1+ (car (cdr (car texinfo-stack))))))
+ (if (> next ?Z)
+ (error "More than 26 items in @capsenumerate; get a bigger alphabet."))
+ (setcar (cdr (car texinfo-stack)) next)
+ (insert "\b " next ". \n"))
+ (forward-line -1))
+
+
+;;; @table
+
+;; The `@table' command produces two-column tables.
+
+(put 'table 'texinfo-format 'texinfo-table)
+(defun texinfo-table ()
+ (texinfo-push-stack
+ 'table
+ (progn (skip-chars-forward " \t")
+ (if (eolp)
+ "@asis"
+ (texinfo-parse-line-arg))))
+ (texinfo-discard-line-with-args)
+ (setq fill-column (- fill-column 5)))
+
+(put 'table 'texinfo-item 'texinfo-table-item)
+(defun texinfo-table-item ()
+ (let ((arg (texinfo-parse-arg-discard))
+ (itemfont (car (cdr (car texinfo-stack)))))
+ (insert ?\b itemfont ?\{ arg "}\n \n"))
+ (forward-line -2))
+
+(put 'table 'texinfo-end 'texinfo-end-table)
+(defun texinfo-end-table ()
+ (setq fill-column (+ fill-column 5))
+ (texinfo-discard-command)
+ (let ((stacktop
+ (texinfo-pop-stack 'table)))
+ (texinfo-do-itemize (nth 1 stacktop))))
+
+;; @description appears to be an undocumented variant on @table that
+;; does not require an arg. It fails in texinfo.tex 2.58 and is not
+;; part of makeinfo.c The command appears to be a relic of the past.
+(put 'description 'texinfo-end 'texinfo-end-table)
+(put 'description 'texinfo-format 'texinfo-description)
+(defun texinfo-description ()
+ (texinfo-push-stack 'table "@asis")
+ (setq fill-column (- fill-column 5))
+ (texinfo-discard-line))
+
+
+;;; @ftable, @vtable
+
+;; The `@ftable' and `@vtable' commands are like the `@table' command
+;; but they also insert each entry in the first column of the table
+;; into the function or variable index.
+
+;; Handle the @ftable and @vtable commands:
+
+(put 'ftable 'texinfo-format 'texinfo-ftable)
+(put 'vtable 'texinfo-format 'texinfo-vtable)
+
+(defun texinfo-ftable () (texinfo-indextable 'ftable))
+(defun texinfo-vtable () (texinfo-indextable 'vtable))
+
+(defun texinfo-indextable (table-type)
+ (texinfo-push-stack table-type (texinfo-parse-arg-discard))
+ (setq fill-column (- fill-column 5)))
+
+;; Handle the @item commands within ftable and vtable:
+
+(put 'ftable 'texinfo-item 'texinfo-ftable-item)
+(put 'vtable 'texinfo-item 'texinfo-vtable-item)
+
+(defun texinfo-ftable-item () (texinfo-indextable-item 'texinfo-findex))
+(defun texinfo-vtable-item () (texinfo-indextable-item 'texinfo-vindex))
+
+(defun texinfo-indextable-item (index-type)
+ (let ((item (texinfo-parse-arg-discard))
+ (itemfont (car (cdr (car texinfo-stack))))
+ (indexvar index-type))
+ (insert ?\b itemfont ?\{ item "}\n \n")
+ (set indexvar
+ (cons
+ (list item texinfo-last-node)
+ (symbol-value indexvar)))
+ (forward-line -2)))
+
+;; Handle @end ftable, @end vtable
+
+(put 'ftable 'texinfo-end 'texinfo-end-ftable)
+(put 'vtable 'texinfo-end 'texinfo-end-vtable)
+
+(defun texinfo-end-ftable () (texinfo-end-indextable 'ftable))
+(defun texinfo-end-vtable () (texinfo-end-indextable 'vtable))
+
+(defun texinfo-end-indextable (table-type)
+ (setq fill-column (+ fill-column 5))
+ (texinfo-discard-command)
+ (let ((stacktop
+ (texinfo-pop-stack table-type)))
+ (texinfo-do-itemize (nth 1 stacktop))))
+
+
+;;; @multitable ... @end multitable
+
+;; Produce a multi-column table, with as many columns as desired.
+;;
+;; A multi-column table has this template:
+;;
+;; @multitable {A1} {A2} {A3}
+;; @item A1 @tab A2 @tab A3
+;; @item B1 @tab B2 @tab B3
+;; @item C1 @tab C2 @tab C3
+;; @end multitable
+;;
+;; where the width of the text in brackets specifies the width of the
+;; respective column.
+;;
+;; Or else:
+;;
+;; @multitable @columnfractions .25 .3 .45
+;; @item A1 @tab A2 @tab A3
+;; @item B1 @tab B2 @tab B3
+;; @end multitable
+;;
+;; where the fractions specify the width of each column as a percent
+;; of the current width of the text (i.e., of the fill-column).
+;;
+;; Long lines of text are filled within columns.
+;;
+;; Using the Emacs Lisp formatter, texinfmt.el,
+;; the whitespace between columns can be increased by setting
+;; `extra-inter-column-width' to a value greater than 0. By default,
+;; there is at least one blank space between columns.
+;;
+;; The Emacs Lisp formatter, texinfmt.el, ignores the following four
+;; commands that are defined in texinfo.tex for printed output.
+;;
+;; @multitableparskip,
+;; @multitableparindent,
+;; @multitablecolmargin,
+;; @multitablelinespace.
+
+;; How @multitable works.
+;; =====================
+;;
+;; `texinfo-multitable' reads the @multitable line and determines from it
+;; how wide each column should be.
+;;
+;; Also, it pushes this information, along with an identifying symbol,
+;; onto the `texinfo-stack'. At the @end multitable command, the stack
+;; is checked for its matching @multitable command, and then popped, or
+;; else an error is signaled. Also, this command pushes the location of
+;; the start of the table onto the stack.
+;;
+;; `texinfo-end-multitable' checks the `texinfo-stack' that the @end
+;; multitable truly is ending a corresponding beginning, and if it is,
+;; pops the stack.
+;;
+;; `texinfo-multitable-widths' is called by `texinfo-multitable'.
+;; The function returns a list of the widths of each column in a
+;; multi-column table, based on the information supplied by the arguments
+;; to the @multitable command (by arguments, I mean the text on the rest
+;; of the @multitable line, not the remainder of the multi-column table
+;; environment).
+;;
+;; `texinfo-multitable-item' formats a row within a multicolumn table.
+;; This command is executed when texinfmt sees @item inside @multitable.
+;; Cells in row are separated by `@tab's. Widths of cells are specified
+;; by the arguments in the @multitable line. Cells are filled. All cells
+;; are made to be the same height by padding their bottoms, as needed,
+;; with blanks.
+;;
+;; `texinfo-multitable-extract-row' is called by `texinfo-multitable-item'.
+;; This function returns the text in a multitable row, as a string.
+;; The start of a row is marked by an @item and the end of row is the
+;; beginning of next @item or beginning of the @end multitable line.
+;; Cells within a row are separated by @tab.
+;;
+;; Note that @tab, the cell separators, are not treated as independent
+;; Texinfo commands.
+
+(defvar extra-inter-column-width 0
+"*Insert NUMBER of additional columns of whitespace between entries of
+a multi-column table.")
+
+(defvar multitable-temp-buffer-name "*multitable-temporary-buffer*")
+(defvar multitable-temp-rectangle-name "texinfo-multitable-temp-")
+
+;; These commands are defined in texinfo.tex for printed output.
+(put 'multitableparskip 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'multitableparindent 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'multitablecolmargin 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'multitablelinespace 'texinfo-format 'texinfo-discard-line-with-args)
+
+(put 'multitable 'texinfo-format 'texinfo-multitable)
+(defun texinfo-multitable ()
+ "Produce multi-column tables.
+
+A multi-column table has this template:
+
+ @multitable {A1} {A2} {A3}
+ @item A1 @tab A2 @tab A3
+ @item B1 @tab B2 @tab B3
+ @item C1 @tab C2 @tab C3
+ @end multitable
+
+where the width of the text in brackets specifies the width of the
+respective column.
+
+Or else:
+
+ @multitable @columnfractions .25 .3 .45
+ @item A1 @tab A2 @tab A3
+ @item B1 @tab B2 @tab B3
+ @end multitable
+
+where the fractions specify the width of each column as a percent
+of the current width of the text (i.e., of the fill-column).
+
+Long lines of text are filled within columns.
+
+Using the Emacs Lisp formatter, texinfmt.el,
+the whitespace between columns can be increased by setting
+`extra-inter-column-width' to a value greater than 0. By default,
+there is at least one blank space between columns.
+
+The Emacs Lisp formatter, texinfmt.el, ignores the following four
+commands that are defined in texinfo.tex for printed output.
+
+ @multitableparskip,
+ @multitableparindent,
+ @multitablecolmargin,
+ @multitablelinespace."
+
+;; This function pushes information onto the `texinfo-stack'.
+;; A stack element consists of:
+;; - type-of-command, i.e., multitable
+;; - the information about column widths, and
+;; - the position of texinfo-command-start.
+;; e.g., ('multitable (1 2 3 4) 123)
+;; The command line is then deleted.
+ (texinfo-push-stack
+ 'multitable
+ ;; push width information on stack
+ (texinfo-multitable-widths))
+ (texinfo-discard-line-with-args))
+
+(put 'multitable 'texinfo-end 'texinfo-end-multitable)
+(defun texinfo-end-multitable ()
+ "Discard the @end multitable line and pop the stack of multitable."
+ (texinfo-discard-command)
+ (texinfo-pop-stack 'multitable))
+
+(defun texinfo-multitable-widths ()
+ "Return list of widths of each column in a multi-column table."
+ (let (texinfo-multitable-width-list)
+ ;; Fractions format:
+ ;; @multitable @columnfractions .25 .3 .45
+ ;;
+ ;; Template format:
+ ;; @multitable {Column 1 template} {Column 2} {Column 3 example}
+ ;; Place point before first argument
+ (skip-chars-forward " \t")
+ (cond
+ ;; Check for common misspelling
+ ((looking-at "@columnfraction ")
+ (error "In @multitable, @columnfractions misspelled"))
+ ;; Case 1: @columnfractions .25 .3 .45
+ ((looking-at "@columnfractions")
+ (forward-word 1)
+ (while (not (eolp))
+ (setq texinfo-multitable-width-list
+ (cons
+ (truncate
+ (1-
+ (* fill-column (read (get-buffer (current-buffer))))))
+ texinfo-multitable-width-list))))
+ ;;
+ ;; Case 2: {Column 1 template} {Column 2} {Column 3 example}
+ ((looking-at "{")
+ (let ((start-of-templates (point)))
+ (while (not (eolp))
+ (skip-chars-forward " \t")
+ (let* ((start-of-template (1+ (point)))
+ (end-of-template
+ ;; forward-sexp works with braces in Texinfo mode
+ (progn (forward-sexp 1) (1- (point)))))
+ (setq texinfo-multitable-width-list
+ (cons (- end-of-template start-of-template)
+ texinfo-multitable-width-list))
+ ;; Remove carriage return from within a template, if any.
+ ;; This helps those those who want to use more than
+ ;; one line's worth of words in @multitable line.
+ (narrow-to-region start-of-template end-of-template)
+ (goto-char (point-min))
+ (while (search-forward "
+" nil t)
+ (delete-char -1))
+ (goto-char (point-max))
+ (widen)
+ (forward-char 1)))))
+ ;;
+ ;; Case 3: Trouble
+ (t
+ (error
+ "You probably need to specify column widths for @multitable correctly.")))
+ ;; Check whether columns fit on page.
+ (let ((desired-columns
+ (+
+ ;; between column spaces
+ (length texinfo-multitable-width-list)
+ ;; additional between column spaces, if any
+ extra-inter-column-width
+ ;; sum of spaces for each entry
+ (apply '+ texinfo-multitable-width-list))))
+ (if (> desired-columns fill-column)
+ (error
+ (format
+ "Multi-column table width, %d chars, is greater than page width, %d chars."
+ desired-columns fill-column))))
+ texinfo-multitable-width-list))
+
+;; @item A1 @tab A2 @tab A3
+(defun texinfo-multitable-extract-row ()
+ "Return multitable row, as a string.
+End of row is beginning of next @item or beginning of @end.
+Cells within rows are separated by @tab."
+ (skip-chars-forward " \t")
+ (let* ((start (point))
+ (end (progn
+ (re-search-forward "@item\\|@end")
+ (match-beginning 0)))
+ (row (progn (goto-char end)
+ (skip-chars-backward " ")
+ ;; remove whitespace at end of argument
+ (delete-region (point) end)
+ (buffer-substring start (point)))))
+ (delete-region texinfo-command-start end)
+ row))
+
+(put 'multitable 'texinfo-item 'texinfo-multitable-item)
+(defun texinfo-multitable-item ()
+ "Format a row within a multicolumn table.
+Cells in row are separated by @tab.
+Widths of cells are specified by the arguments in the @multitable line.
+All cells are made to be the same height.
+This command is executed when texinfmt sees @item inside @multitable."
+ (let ((original-buffer (current-buffer))
+ (table-widths (reverse (car (cdr (car texinfo-stack)))))
+ (existing-fill-column fill-column)
+ start
+ end
+ (table-column 0)
+ (table-entry-height 0)
+ ;; unformatted row looks like: A1 @tab A2 @tab A3
+ ;; extract-row command deletes the source line in the table.
+ (unformated-row (texinfo-multitable-extract-row)))
+ ;; Use a temporary buffer
+ (set-buffer (get-buffer-create multitable-temp-buffer-name))
+ (delete-region (point-min) (point-max))
+ (insert unformated-row)
+ (goto-char (point-min))
+;; 1. Check for correct number of @tab in line.
+ (let ((tab-number 1)) ; one @tab between two columns
+ (while (search-forward "@tab" nil t)
+ (setq tab-number (1+ tab-number)))
+ (if (/= tab-number (length table-widths))
+ (error "Wrong number of @tab's in a @multitable row.")))
+ (goto-char (point-min))
+;; 2. Format each cell, and copy to a rectangle
+ ;; buffer looks like this: A1 @tab A2 @tab A3
+ ;; Cell #1: format up to @tab
+ ;; Cell #2: format up to @tab
+ ;; Cell #3: format up to eob
+ (while (not (eobp))
+ (setq start (point))
+ (setq end (save-excursion
+ (if (search-forward "@tab" nil 'move)
+ ;; Delete the @tab command, including the @-sign
+ (delete-region
+ (point)
+ (progn (forward-word -1) (1- (point)))))
+ (point)))
+ ;; Set fill-column *wider* than needed to produce inter-column space
+ (setq fill-column (+ 1
+ extra-inter-column-width
+ (nth table-column table-widths)))
+ (narrow-to-region start end)
+ ;; Remove whitespace before and after entry.
+ (skip-chars-forward " ")
+ (delete-region (point) (save-excursion (beginning-of-line) (point)))
+ (goto-char (point-max))
+ (skip-chars-backward " ")
+ (delete-region (point) (save-excursion (end-of-line) (point)))
+ ;; Temorarily set texinfo-stack to nil so texinfo-format-scan
+ ;; does not see an unterminated @multitable.
+ (let (texinfo-stack) ; nil
+ (texinfo-format-scan))
+ (let (fill-prefix) ; no fill prefix
+ (fill-region (point-min) (point-max)))
+ (setq table-entry-height
+ (max table-entry-height (count-lines (point-min) (point-max))))
+;; 3. Move point to end of bottom line, and pad that line to fill column.
+ (goto-char (point-min))
+ (forward-line (1- table-entry-height))
+ (let* ((beg (point)) ; beginning of line
+ ;; add one more space for inter-column spacing
+ (needed-whitespace
+ (1+
+ (- fill-column
+ (-
+ (progn (end-of-line) (point)) ; end of existing line
+ beg)))))
+ (insert (make-string
+ (if (> needed-whitespace 0) needed-whitespace 1)
+ ? )))
+ ;; now, put formatted cell into a rectangle
+ (set (intern (concat multitable-temp-rectangle-name
+ (int-to-string table-column)))
+ (extract-rectangle (point-min) (point)))
+ (delete-region (point-min) (point))
+ (goto-char (point-max))
+ (setq table-column (1+ table-column))
+ (widen))
+;; 4. Add extra lines to rectangles so all are of same height
+ (let ((total-number-of-columns table-column)
+ (column-number 0)
+ here)
+ (while (> table-column 0)
+ (let ((this-rectangle (int-to-string table-column)))
+ (while (< (length this-rectangle) table-entry-height)
+ (setq this-rectangle (append this-rectangle '("")))))
+ (setq table-column (1- table-column)))
+;; 5. Insert formatted rectangles in original buffer
+ (switch-to-buffer original-buffer)
+ (open-line table-entry-height)
+ (while (< column-number total-number-of-columns)
+ (setq here (point))
+ (insert-rectangle
+ (eval (intern
+ (concat multitable-temp-rectangle-name
+ (int-to-string column-number)))))
+ (goto-char here)
+ (end-of-line)
+ (setq column-number (1+ column-number))))
+ (kill-buffer multitable-temp-buffer-name)
+ (setq fill-column existing-fill-column)))
+
+
+;;; @ifinfo, @iftex, @tex, @ifhtml, @html
+
+(put 'ifinfo 'texinfo-format 'texinfo-discard-line)
+(put 'ifinfo 'texinfo-end 'texinfo-discard-command)
+
+(put 'iftex 'texinfo-format 'texinfo-format-iftex)
+(defun texinfo-format-iftex ()
+ (delete-region texinfo-command-start
+ (progn (re-search-forward "@end iftex[ \t]*\n")
+ (point))))
+
+(put 'ifhtml 'texinfo-format 'texinfo-format-ifhtml)
+(defun texinfo-format-ifhtml ()
+ (delete-region texinfo-command-start
+ (progn (re-search-forward "@end ifhtml[ \t]*\n")
+ (point))))
+
+(put 'tex 'texinfo-format 'texinfo-format-tex)
+(defun texinfo-format-tex ()
+ (delete-region texinfo-command-start
+ (progn (re-search-forward "@end tex[ \t]*\n")
+ (point))))
+
+(put 'html 'texinfo-format 'texinfo-format-html)
+(defun texinfo-format-html ()
+ (delete-region texinfo-command-start
+ (progn (re-search-forward "@end html[ \t]*\n")
+ (point))))
+
+
+;;; @titlepage
+
+(put 'titlepage 'texinfo-format 'texinfo-format-titlepage)
+(defun texinfo-format-titlepage ()
+ (delete-region texinfo-command-start
+ (progn (re-search-forward "@end titlepage[ \t]*\n")
+ (point))))
+
+(put 'endtitlepage 'texinfo-format 'texinfo-discard-line)
+
+;; @titlespec an alternative titling command; ignored by Info
+
+(put 'titlespec 'texinfo-format 'texinfo-format-titlespec)
+(defun texinfo-format-titlespec ()
+ (delete-region texinfo-command-start
+ (progn (re-search-forward "@end titlespec[ \t]*\n")
+ (point))))
+
+(put 'endtitlespec 'texinfo-format 'texinfo-discard-line)
+
+
+;;; @today
+
+(put 'today 'texinfo-format 'texinfo-format-today)
+
+;; Produces Day Month Year style of output. eg `1 Jan 1900'
+;; The `@today{}' command requires a pair of braces, like `@dots{}'.
+(defun texinfo-format-today ()
+ (texinfo-parse-arg-discard)
+ (insert (format "%s %s %s"
+ (substring (current-time-string) 8 10)
+ (substring (current-time-string) 4 7)
+ (substring (current-time-string) -4))))
+
+
+;;; @ignore
+
+(put 'ignore 'texinfo-format 'texinfo-format-ignore)
+(defun texinfo-format-ignore ()
+ (delete-region texinfo-command-start
+ (progn (re-search-forward "@end ignore[ \t]*\n")
+ (point))))
+
+(put 'endignore 'texinfo-format 'texinfo-discard-line)
+
+
+;;; Define the Info enclosure command: @definfoenclose
+
+;; A `@definfoenclose' command may be used to define a highlighting
+;; command for Info, but not for TeX. A command defined using
+;; `@definfoenclose' marks text by enclosing it in strings that precede
+;; and follow the text.
+;;
+;; Presumably, if you define a command with `@definfoenclose` for Info,
+;; you will also define the same command in the TeX definitions file,
+;; `texinfo.tex' in a manner appropriate for typesetting.
+;;
+;; Write a `@definfoenclose' command on a line and follow it with three
+;; arguments separated by commas (commas are used as separators in an
+;; `@node' line in the same way). The first argument to
+;; `@definfoenclose' is the @-command name \(without the `@'\); the
+;; second argument is the Info start delimiter string; and the third
+;; argument is the Info end delimiter string. The latter two arguments
+;; enclose the highlighted text in the Info file. A delimiter string
+;; may contain spaces. Neither the start nor end delimiter is
+;; required. However, if you do not provide a start delimiter, you
+;; must follow the command name with two commas in a row; otherwise,
+;; the Info formatting commands will misinterpret the end delimiter
+;; string as a start delimiter string.
+;;
+;; If you do a @definfoenclose{} on the name of a pre-defined macro (such
+;; as @emph{}, @strong{}, @tt{}, or @i{}) the enclosure definition will
+;; override the built-in definition.
+;;
+;; An enclosure command defined this way takes one argument in braces.
+;;
+;; For example, you can write:
+;;
+;; @ifinfo
+;; @definfoenclose phoo, //, \\
+;; @end ifinfo
+;;
+;; near the beginning of a Texinfo file at the beginning of the lines
+;; to define `@phoo' as an Info formatting command that inserts `//'
+;; before and `\\' after the argument to `@phoo'. You can then write
+;; `@phoo{bar}' wherever you want `//bar\\' highlighted in Info.
+;;
+;; Also, for TeX formatting, you could write
+;;
+;; @iftex
+;; @global@let@phoo=@i
+;; @end iftex
+;;
+;; to define `@phoo' as a command that causes TeX to typeset
+;; the argument to `@phoo' in italics.
+;;
+;; Note that each definition applies to its own formatter: one for TeX,
+;; the other for texinfo-format-buffer or texinfo-format-region.
+;;
+;; Here is another example: write
+;;
+;; @definfoenclose headword, , :
+;;
+;; near the beginning of the file, to define `@headword' as an Info
+;; formatting command that inserts nothing before and a colon after the
+;; argument to `@headword'.
+
+(put 'definfoenclose 'texinfo-format 'texinfo-define-info-enclosure)
+(defun texinfo-define-info-enclosure ()
+ (let* ((args (texinfo-format-parse-line-args))
+ (command-name (nth 0 args))
+ (beginning-delimiter (or (nth 1 args) ""))
+ (end-delimiter (or (nth 2 args) "")))
+ (texinfo-discard-command)
+ (setq texinfo-enclosure-list
+ (cons
+ (list command-name
+ (list
+ beginning-delimiter
+ end-delimiter))
+ texinfo-enclosure-list))))
+
+
+;;; @var, @code and the like
+
+(put 'var 'texinfo-format 'texinfo-format-var)
+;; @sc a small caps font for TeX; formatted as `var' in Info
+(put 'sc 'texinfo-format 'texinfo-format-var)
+(defun texinfo-format-var ()
+ (insert (upcase (texinfo-parse-arg-discard)))
+ (goto-char texinfo-command-start))
+
+(put 'url 'texinfo-format 'texinfo-format-code)
+(put 'cite 'texinfo-format 'texinfo-format-code)
+(put 'code 'texinfo-format 'texinfo-format-code)
+(put 'file 'texinfo-format 'texinfo-format-code)
+(put 'samp 'texinfo-format 'texinfo-format-code)
+(defun texinfo-format-code ()
+ (insert "`" (texinfo-parse-arg-discard) "'")
+ (goto-char texinfo-command-start))
+
+(put 'emph 'texinfo-format 'texinfo-format-emph)
+(put 'strong 'texinfo-format 'texinfo-format-emph)
+(defun texinfo-format-emph ()
+ (insert "*" (texinfo-parse-arg-discard) "*")
+ (goto-char texinfo-command-start))
+
+(put 'dfn 'texinfo-format 'texinfo-format-defn)
+(put 'defn 'texinfo-format 'texinfo-format-defn)
+(defun texinfo-format-defn ()
+ (insert "\"" (texinfo-parse-arg-discard) "\"")
+ (goto-char texinfo-command-start))
+
+(put 'email 'texinfo-format 'texinfo-format-key)
+(put 'key 'texinfo-format 'texinfo-format-key)
+(defun texinfo-format-key ()
+ (insert "<" (texinfo-parse-arg-discard) ">")
+ (goto-char texinfo-command-start))
+
+(put 'bullet 'texinfo-format 'texinfo-format-bullet)
+(defun texinfo-format-bullet ()
+ "Insert an asterisk.
+If used within a line, follow `@bullet' with braces."
+ (texinfo-optional-braces-discard)
+ (insert "*"))
+
+
+;;; @kbd
+
+;; Inside of @example ... @end example and similar environments,
+;; @kbd does nothing; but outside of such environments, it places
+;; single quotation markes around its argument.
+
+(defvar texinfo-format-kbd-regexp
+ (concat
+ "^@"
+ "\\("
+ "example\\|"
+ "smallexample\\|"
+ "lisp\\|"
+ "smalllisp"
+ "\\)")
+ "Regexp specifying environments in which @kbd does not put `...'
+ around argument.")
+
+(defvar texinfo-format-kbd-end-regexp
+ (concat
+ "^@end "
+ "\\("
+ "example\\|"
+ "smallexample\\|"
+ "lisp\\|"
+ "smalllisp"
+ "\\)")
+ "Regexp specifying end of environments in which @kbd does not put `...'
+ around argument. (See `texinfo-format-kbd-regexp')")
+
+(put 'kbd 'texinfo-format 'texinfo-format-kbd)
+(defun texinfo-format-kbd ()
+ "Place single quote marks around arg, except in @example and similar."
+ ;; Search forward for @end example closer than an @example.
+ ;; Can stop search at nearest @node or texinfo-section-types-regexp
+ (let* ((stop
+ (save-excursion
+ (re-search-forward
+ (concat "^@node\\|\\(" texinfo-section-types-regexp "\\)")
+ nil
+ 'move-to-end) ; if necessary, return point at end of buffer
+ (point)))
+ (example-location
+ (save-excursion
+ (re-search-forward texinfo-format-kbd-regexp stop 'move-to-end)
+ (point)))
+ (end-example-location
+ (save-excursion
+ (re-search-forward texinfo-format-kbd-end-regexp stop 'move-to-end)
+ (point))))
+ ;; If inside @example, @end example will be closer than @example
+ ;; or end of search i.e., end-example-location less than example-location
+ (if (>= end-example-location example-location)
+ ;; outside an @example or equivalent
+ (insert "`" (texinfo-parse-arg-discard) "'")
+ ;; else, in @example; do not surround with `...'
+ (insert (texinfo-parse-arg-discard)))
+ (goto-char texinfo-command-start)))
+
+
+;;; @example, @lisp, @quotation, @display, @smalllisp, @smallexample
+
+(put 'display 'texinfo-format 'texinfo-format-example)
+(put 'example 'texinfo-format 'texinfo-format-example)
+(put 'lisp 'texinfo-format 'texinfo-format-example)
+(put 'quotation 'texinfo-format 'texinfo-format-example)
+(put 'smallexample 'texinfo-format 'texinfo-format-example)
+(put 'smalllisp 'texinfo-format 'texinfo-format-example)
+(defun texinfo-format-example ()
+ (texinfo-push-stack 'example nil)
+ (setq fill-column (- fill-column 5))
+ (texinfo-discard-line))
+
+(put 'example 'texinfo-end 'texinfo-end-example)
+(put 'display 'texinfo-end 'texinfo-end-example)
+(put 'lisp 'texinfo-end 'texinfo-end-example)
+(put 'quotation 'texinfo-end 'texinfo-end-example)
+(put 'smallexample 'texinfo-end 'texinfo-end-example)
+(put 'smalllisp 'texinfo-end 'texinfo-end-example)
+(defun texinfo-end-example ()
+ (setq fill-column (+ fill-column 5))
+ (texinfo-discard-command)
+ (let ((stacktop
+ (texinfo-pop-stack 'example)))
+ (texinfo-do-itemize (nth 1 stacktop))))
+
+(put 'exdent 'texinfo-format 'texinfo-format-exdent)
+(defun texinfo-format-exdent ()
+ (texinfo-discard-command)
+ (delete-region (point)
+ (progn
+ (skip-chars-forward " ")
+ (point)))
+ (insert ?\b)
+ ;; Cancel out the deletion that texinfo-do-itemize
+ ;; is going to do at the end of this line.
+ (save-excursion
+ (end-of-line)
+ (insert "\n ")))
+
+
+;;; @cartouche
+
+;; The @cartouche command is a noop in Info; in a printed manual,
+;; it makes a box with rounded corners.
+
+(put 'cartouche 'texinfo-format 'texinfo-discard-line)
+(put 'cartouche 'texinfo-end 'texinfo-discard-command)
+
+
+;;; @flushleft and @format
+
+;; The @flushleft command left justifies every line but leaves the
+;; right end ragged. As far as Info is concerned, @flushleft is a
+;; `do-nothing' command
+
+;; The @format command is similar to @example except that it does not
+;; indent; this means that in Info, @format is similar to @flushleft.
+
+(put 'format 'texinfo-format 'texinfo-format-flushleft)
+(put 'flushleft 'texinfo-format 'texinfo-format-flushleft)
+(defun texinfo-format-flushleft ()
+ (texinfo-discard-line))
+
+(put 'format 'texinfo-end 'texinfo-end-flushleft)
+(put 'flushleft 'texinfo-end 'texinfo-end-flushleft)
+(defun texinfo-end-flushleft ()
+ (texinfo-discard-command))
+
+
+;;; @flushright
+
+;; The @flushright command right justifies every line but leaves the
+;; left end ragged. Spaces and tabs at the right ends of lines are
+;; removed so that visible text lines up on the right side.
+
+(put 'flushright 'texinfo-format 'texinfo-format-flushright)
+(defun texinfo-format-flushright ()
+ (texinfo-push-stack 'flushright nil)
+ (texinfo-discard-line))
+
+(put 'flushright 'texinfo-end 'texinfo-end-flushright)
+(defun texinfo-end-flushright ()
+ (texinfo-discard-command)
+
+ (let ((stacktop
+ (texinfo-pop-stack 'flushright)))
+
+ (texinfo-do-flushright (nth 1 stacktop))))
+
+(defun texinfo-do-flushright (from)
+ (save-excursion
+ (while (progn (forward-line -1)
+ (>= (point) from))
+
+ (beginning-of-line)
+ (insert
+ (make-string
+ (- fill-column
+ (save-excursion
+ (end-of-line)
+ (skip-chars-backward " \t")
+ (delete-region (point) (progn (end-of-line) (point)))
+ (current-column)))
+ ? )))))
+
+
+;;; @ctrl, @TeX, @copyright, @minus, @dots, @enddots, @pounds
+
+(put 'ctrl 'texinfo-format 'texinfo-format-ctrl)
+(defun texinfo-format-ctrl ()
+ (let ((str (texinfo-parse-arg-discard)))
+ (insert (logand 31 (aref str 0)))))
+
+(put 'TeX 'texinfo-format 'texinfo-format-TeX)
+(defun texinfo-format-TeX ()
+ (texinfo-parse-arg-discard)
+ (insert "TeX"))
+
+(put 'copyright 'texinfo-format 'texinfo-format-copyright)
+(defun texinfo-format-copyright ()
+ (texinfo-parse-arg-discard)
+ (insert "(C)"))
+
+(put 'minus 'texinfo-format 'texinfo-format-minus)
+(defun texinfo-format-minus ()
+ "Insert a minus sign.
+If used within a line, follow `@minus' with braces."
+ (texinfo-optional-braces-discard)
+ (insert "-"))
+
+(put 'dots 'texinfo-format 'texinfo-format-dots)
+(defun texinfo-format-dots ()
+ (texinfo-parse-arg-discard)
+ (insert "..."))
+
+(put 'enddots 'texinfo-format 'texinfo-format-enddots)
+(defun texinfo-format-enddots ()
+ (texinfo-parse-arg-discard)
+ (insert "...."))
+
+(put 'pounds 'texinfo-format 'texinfo-format-pounds)
+(defun texinfo-format-pounds ()
+ (texinfo-parse-arg-discard)
+ (insert "#"))
+
+
+;;; Refilling and indenting: @refill, @paragraphindent, @noindent
+
+;;; Indent only those paragraphs that are refilled as a result of an
+;;; @refill command.
+
+;; * If the value is `asis', do not change the existing indentation at
+;; the starts of paragraphs.
+
+;; * If the value zero, delete any existing indentation.
+
+;; * If the value is greater than zero, indent each paragraph by that
+;; number of spaces.
+
+;;; But do not refill paragraphs with an @refill command that are
+;;; preceded by @noindent or are part of a table, list, or deffn.
+
+(defvar texinfo-paragraph-indent "asis"
+ "Number of spaces for @refill to indent a paragraph; else to leave as is.")
+
+(put 'paragraphindent 'texinfo-format 'texinfo-paragraphindent)
+
+(defun texinfo-paragraphindent ()
+ "Specify the number of spaces for @refill to indent a paragraph.
+Default is to leave the number of spaces as is."
+ (let ((arg (texinfo-parse-arg-discard)))
+ (if (string= "asis" arg)
+ (setq texinfo-paragraph-indent "asis")
+ (setq texinfo-paragraph-indent (string-to-int arg)))))
+
+(put 'refill 'texinfo-format 'texinfo-format-refill)
+(defun texinfo-format-refill ()
+ "Refill paragraph. Also, indent first line as set by @paragraphindent.
+Default is to leave paragraph indentation as is."
+ (texinfo-discard-command)
+ (forward-paragraph -1)
+ (if (looking-at "[ \t\n]*$") (forward-line 1))
+ ;; Do not indent if an entry in a list, table, or deffn,
+ ;; or if paragraph is preceded by @noindent.
+ ;; Otherwise, indent
+ (cond
+ ;; delete a @noindent line and do not indent paragraph
+ ((save-excursion (forward-line -1)
+ (looking-at "^@noindent"))
+ (forward-line -1)
+ (delete-region (point) (progn (forward-line 1) (point))))
+ ;; do nothing if "asis"
+ ((equal texinfo-paragraph-indent "asis"))
+ ;; do no indenting in list, etc.
+ ((> texinfo-stack-depth 0))
+ ;; otherwise delete existing whitespace and indent
+ (t
+ (delete-region (point) (progn (skip-chars-forward " \t") (point)))
+ (insert (make-string texinfo-paragraph-indent ? ))))
+ (forward-paragraph 1)
+ (forward-line -1)
+ (end-of-line)
+ ;; Do not fill a section title line with asterisks, hyphens, etc. that
+ ;; are used to underline it. This could occur if the line following
+ ;; the underlining is not an index entry and has text within it.
+ (let* ((previous-paragraph-separate paragraph-separate)
+ (paragraph-separate
+ (concat paragraph-separate "\\|[-=.]+\\|\\*\\*+"))
+ (previous-paragraph-start paragraph-start)
+ (paragraph-start
+ (concat paragraph-start "\\|[-=.]+\\|\\*\\*+")))
+ (unwind-protect
+ (fill-paragraph nil)
+ (setq paragraph-separate previous-paragraph-separate)
+ (setq paragraph-start previous-paragraph-start))))
+
+(put 'noindent 'texinfo-format 'texinfo-noindent)
+(defun texinfo-noindent ()
+ (save-excursion
+ (forward-paragraph 1)
+ (if (search-backward "@refill"
+ (save-excursion (forward-line -1) (point)) t)
+ () ; leave @noindent command so @refill command knows not to indent
+ ;; else
+ (texinfo-discard-line))))
+
+
+;;; Index generation
+
+(put 'vindex 'texinfo-format 'texinfo-format-vindex)
+(defun texinfo-format-vindex ()
+ (texinfo-index 'texinfo-vindex))
+
+(put 'cindex 'texinfo-format 'texinfo-format-cindex)
+(defun texinfo-format-cindex ()
+ (texinfo-index 'texinfo-cindex))
+
+(put 'findex 'texinfo-format 'texinfo-format-findex)
+(defun texinfo-format-findex ()
+ (texinfo-index 'texinfo-findex))
+
+(put 'pindex 'texinfo-format 'texinfo-format-pindex)
+(defun texinfo-format-pindex ()
+ (texinfo-index 'texinfo-pindex))
+
+(put 'tindex 'texinfo-format 'texinfo-format-tindex)
+(defun texinfo-format-tindex ()
+ (texinfo-index 'texinfo-tindex))
+
+(put 'kindex 'texinfo-format 'texinfo-format-kindex)
+(defun texinfo-format-kindex ()
+ (texinfo-index 'texinfo-kindex))
+
+(defun texinfo-index (indexvar)
+ (let ((arg (texinfo-parse-expanded-arg)))
+ (texinfo-discard-command)
+ (set indexvar
+ (cons (list arg
+ texinfo-last-node
+ ;; Region formatting may not provide last node position.
+ (if texinfo-last-node-pos
+ (1+ (count-lines texinfo-last-node-pos (point)))
+ 1))
+ (symbol-value indexvar)))))
+
+(defconst texinfo-indexvar-alist
+ '(("cp" . texinfo-cindex)
+ ("fn" . texinfo-findex)
+ ("vr" . texinfo-vindex)
+ ("tp" . texinfo-tindex)
+ ("pg" . texinfo-pindex)
+ ("ky" . texinfo-kindex)))
+
+
+;;; @defindex @defcodeindex
+(put 'defindex 'texinfo-format 'texinfo-format-defindex)
+(put 'defcodeindex 'texinfo-format 'texinfo-format-defindex)
+
+(defun texinfo-format-defindex ()
+ (let* ((index-name (texinfo-parse-arg-discard)) ; eg: `aa'
+ (indexing-command (intern (concat index-name "index")))
+ (index-formatting-command ; eg: `texinfo-format-aaindex'
+ (intern (concat "texinfo-format-" index-name "index")))
+ (index-alist-name ; eg: `texinfo-aaindex'
+ (intern (concat "texinfo-" index-name "index"))))
+
+ (set index-alist-name nil)
+
+ (put indexing-command ; eg, aaindex
+ 'texinfo-format
+ index-formatting-command) ; eg, texinfo-format-aaindex
+
+ ;; eg: "aa" . texinfo-aaindex
+ (or (assoc index-name texinfo-indexvar-alist)
+ (setq texinfo-indexvar-alist
+ (cons
+ (cons index-name
+ index-alist-name)
+ texinfo-indexvar-alist)))
+
+ (fset index-formatting-command
+ (list 'lambda 'nil
+ (list 'texinfo-index
+ (list 'quote index-alist-name))))))
+
+
+;;; @synindex @syncodeindex
+
+(put 'synindex 'texinfo-format 'texinfo-format-synindex)
+(put 'syncodeindex 'texinfo-format 'texinfo-format-synindex)
+
+(defun texinfo-format-synindex ()
+ (let* ((args (texinfo-parse-arg-discard))
+ (second (cdr (read-from-string args)))
+ (joiner (symbol-name (car (read-from-string args))))
+ (joined (symbol-name (car (read-from-string args second)))))
+
+ (if (assoc joiner texinfo-short-index-cmds-alist)
+ (put
+ (cdr (assoc joiner texinfo-short-index-cmds-alist))
+ 'texinfo-format
+ (or (cdr (assoc joined texinfo-short-index-format-cmds-alist))
+ (intern (concat "texinfo-format-" joined "index"))))
+ (put
+ (intern (concat joiner "index"))
+ 'texinfo-format
+ (or (cdr(assoc joined texinfo-short-index-format-cmds-alist))
+ (intern (concat "texinfo-format-" joined "index")))))))
+
+(defconst texinfo-short-index-cmds-alist
+ '(("cp" . cindex)
+ ("fn" . findex)
+ ("vr" . vindex)
+ ("tp" . tindex)
+ ("pg" . pindex)
+ ("ky" . kindex)))
+
+(defconst texinfo-short-index-format-cmds-alist
+ '(("cp" . texinfo-format-cindex)
+ ("fn" . texinfo-format-findex)
+ ("vr" . texinfo-format-vindex)
+ ("tp" . texinfo-format-tindex)
+ ("pg" . texinfo-format-pindex)
+ ("ky" . texinfo-format-kindex)))
+
+
+;;; Sort and index (for VMS)
+
+;; Sort an index which is in the current buffer between START and END.
+;; Used on VMS, where the `sort' utility is not available.
+(defun texinfo-sort-region (start end)
+ (require 'sort)
+ (save-restriction
+ (narrow-to-region start end)
+ (sort-subr nil 'forward-line 'end-of-line 'texinfo-sort-startkeyfun)))
+
+;; Subroutine for sorting an index.
+;; At start of a line, return a string to sort the line under.
+(defun texinfo-sort-startkeyfun ()
+ (let ((line
+ (buffer-substring (point) (save-excursion (end-of-line) (point)))))
+ ;; Canonicalize whitespace and eliminate funny chars.
+ (while (string-match "[ \t][ \t]+\\|[^a-z0-9 ]+" line)
+ (setq line (concat (substring line 0 (match-beginning 0))
+ " "
+ (substring line (match-end 0) (length line)))))
+ line))
+
+
+;;; @printindex
+
+(put 'printindex 'texinfo-format 'texinfo-format-printindex)
+
+(defun texinfo-format-printindex ()
+ (let ((indexelts (symbol-value
+ (cdr (assoc (texinfo-parse-arg-discard)
+ texinfo-indexvar-alist))))
+ opoint)
+ (insert "\n* Menu:\n\n")
+ (setq opoint (point))
+ (texinfo-print-index nil indexelts)
+
+ (if (memq system-type '(vax-vms windows-nt ms-dos))
+ (texinfo-sort-region opoint (point))
+ (shell-command-on-region opoint (point) "sort -fd" 1))))
+
+(defun texinfo-print-index (file indexelts)
+ (while indexelts
+ (if (stringp (car (car indexelts)))
+ (progn
+ (insert "* " (car (car indexelts)) ": " )
+ (indent-to 32)
+ (insert
+ (if file (concat "(" file ")") "")
+ (nth 1 (car indexelts)) ".")
+ (indent-to 54)
+ (insert
+ (if (nth 2 (car indexelts))
+ (format " %d." (nth 2 (car indexelts)))
+ "")
+ "\n"))
+ ;; index entries from @include'd file
+ (texinfo-print-index (nth 1 (car indexelts))
+ (nth 2 (car indexelts))))
+ (setq indexelts (cdr indexelts))))
+
+
+;;; Glyphs: @equiv, @error, etc
+
+;; @equiv to show that two expressions are equivalent
+;; @error to show an error message
+;; @expansion to show what a macro expands to
+;; @point to show the location of point in an example
+;; @print to show what an evaluated expression prints
+;; @result to indicate the value returned by an expression
+
+(put 'equiv 'texinfo-format 'texinfo-format-equiv)
+(defun texinfo-format-equiv ()
+ (texinfo-parse-arg-discard)
+ (insert "=="))
+
+(put 'error 'texinfo-format 'texinfo-format-error)
+(defun texinfo-format-error ()
+ (texinfo-parse-arg-discard)
+ (insert "error-->"))
+
+(put 'expansion 'texinfo-format 'texinfo-format-expansion)
+(defun texinfo-format-expansion ()
+ (texinfo-parse-arg-discard)
+ (insert "==>"))
+
+(put 'point 'texinfo-format 'texinfo-format-point)
+(defun texinfo-format-point ()
+ (texinfo-parse-arg-discard)
+ (insert "-!-"))
+
+(put 'print 'texinfo-format 'texinfo-format-print)
+(defun texinfo-format-print ()
+ (texinfo-parse-arg-discard)
+ (insert "-|"))
+
+(put 'result 'texinfo-format 'texinfo-format-result)
+(defun texinfo-format-result ()
+ (texinfo-parse-arg-discard)
+ (insert "=>"))
+
+
+;;; Accent commands
+
+;; Info presumes a plain ASCII output, so the accented characters do
+;; not look as they would if typeset, or output with a different
+;; character set.
+
+;; See the `texinfo-accent-commands' variable
+;; in the section for `texinfo-append-refill'.
+;; Also, see the defun for `texinfo-format-scan'
+;; for single-character accent commands.
+
+;; Command Info output Name
+
+;; These do not have braces:
+;; @^ ==> ^ circumflex accent
+;; @` ==> ` grave accent
+;; @' ==> ' acute accent
+;; @" ==> " umlaut accent
+;; @= ==> = overbar accent
+;; @~ ==> ~ tilde accent
+
+;; These have braces, but take no argument:
+;; @OE{} ==> OE French-OE-ligature
+;; @oe{} ==> oe
+;; @AA{} ==> AA Scandinavian-A-with-circle
+;; @aa{} ==> aa
+;; @AE{} ==> AE Latin-Scandinavian-AE
+;; @ae{} ==> ae
+;; @ss{} ==> ss German-sharp-S
+
+;; @questiondown{} ==> ? upside-down-question-mark
+;; @exclamdown{} ==> ! upside-down-exclamation-mark
+;; @L{} ==> L/ Polish suppressed-L (Lslash)
+;; @l{} ==> l/ Polish suppressed-L (Lslash) (lower case)
+;; @O{} ==> O/ Scandinavian O-with-slash
+;; @o{} ==> o/ Scandinavian O-with-slash (lower case)
+
+;; These have braces, and take an argument:
+;; @,{c} ==> c, cedilla accent
+;; @dotaccent{o} ==> .o overdot-accent
+;; @ubaraccent{o} ==> _o underbar-accent
+;; @udotaccent{o} ==> o-. underdot-accent
+;; @H{o} ==> ""o long Hungarian umlaut
+;; @ringaccent{o} ==> *o ring accent
+;; @tieaccent{oo} ==> [oo tie after accent
+;; @u{o} ==> (o breve accent
+;; @v{o} ==> <o hacek accent
+;; @dotless{i} ==> i dotless i and dotless j
+
+;; ==========
+
+;; Note: The defun texinfo-format-scan
+;; looks at "[@{}^'`\",=~ *?!-]"
+;; In the case of @*, a line break is inserted;
+;; in the other cases, the characters are simply quoted and the @ is deleted.
+;; Thus, `texinfo-format-scan' handles the following
+;; single-character accent commands: @^ @` @' @" @, @- @= @~
+
+;; @^ ==> ^ circumflex accent
+;; (put '^ 'texinfo-format 'texinfo-format-circumflex-accent)
+;; (defun texinfo-format-circumflex-accent ()
+;; (texinfo-discard-command)
+;; (insert "^"))
+;;
+;; @` ==> ` grave accent
+;; (put '\` 'texinfo-format 'texinfo-format-grave-accent)
+;; (defun texinfo-format-grave-accent ()
+;; (texinfo-discard-command)
+;; (insert "\`"))
+;;
+;; @' ==> ' acute accent
+;; (put '\' 'texinfo-format 'texinfo-format-acute-accent)
+;; (defun texinfo-format-acute-accent ()
+;; (texinfo-discard-command)
+;; (insert "'"))
+;;
+;; @" ==> " umlaut accent
+;; (put '\" 'texinfo-format 'texinfo-format-umlaut-accent)
+;; (defun texinfo-format-umlaut-accent ()
+;; (texinfo-discard-command)
+;; (insert "\""))
+;;
+;; @= ==> = overbar accent
+;; (put '= 'texinfo-format 'texinfo-format-overbar-accent)
+;; (defun texinfo-format-overbar-accent ()
+;; (texinfo-discard-command)
+;; (insert "="))
+;;
+;; @~ ==> ~ tilde accent
+;; (put '~ 'texinfo-format 'texinfo-format-tilde-accent)
+;; (defun texinfo-format-tilde-accent ()
+;; (texinfo-discard-command)
+;; (insert "~"))
+
+;; @OE{} ==> OE French-OE-ligature
+(put 'OE 'texinfo-format 'texinfo-format-French-OE-ligature)
+(defun texinfo-format-French-OE-ligature ()
+ (insert "OE" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @oe{} ==> oe
+(put 'oe 'texinfo-format 'texinfo-format-French-oe-ligature)
+(defun texinfo-format-French-oe-ligature () ; lower case
+ (insert "oe" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @AA{} ==> AA Scandinavian-A-with-circle
+(put 'AA 'texinfo-format 'texinfo-format-Scandinavian-A-with-circle)
+(defun texinfo-format-Scandinavian-A-with-circle ()
+ (insert "AA" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @aa{} ==> aa
+(put 'aa 'texinfo-format 'texinfo-format-Scandinavian-a-with-circle)
+(defun texinfo-format-Scandinavian-a-with-circle () ; lower case
+ (insert "aa" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @AE{} ==> AE Latin-Scandinavian-AE
+(put 'AE 'texinfo-format 'texinfo-format-Latin-Scandinavian-AE)
+(defun texinfo-format-Latin-Scandinavian-AE ()
+ (insert "AE" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @ae{} ==> ae
+(put 'ae 'texinfo-format 'texinfo-format-Latin-Scandinavian-ae)
+(defun texinfo-format-Latin-Scandinavian-ae () ; lower case
+ (insert "ae" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @ss{} ==> ss German-sharp-S
+(put 'ss 'texinfo-format 'texinfo-format-German-sharp-S)
+(defun texinfo-format-German-sharp-S ()
+ (insert "ss" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @questiondown{} ==> ? upside-down-question-mark
+(put 'questiondown 'texinfo-format 'texinfo-format-upside-down-question-mark)
+(defun texinfo-format-upside-down-question-mark ()
+ (insert "?" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @exclamdown{} ==> ! upside-down-exclamation-mark
+(put 'exclamdown 'texinfo-format 'texinfo-format-upside-down-exclamation-mark)
+(defun texinfo-format-upside-down-exclamation-mark ()
+ (insert "!" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @L{} ==> L/ Polish suppressed-L (Lslash)
+(put 'L 'texinfo-format 'texinfo-format-Polish-suppressed-L)
+(defun texinfo-format-Polish-suppressed-L ()
+ (insert (texinfo-parse-arg-discard) "/L")
+ (goto-char texinfo-command-start))
+
+;; @l{} ==> l/ Polish suppressed-L (Lslash) (lower case)
+(put 'l 'texinfo-format 'texinfo-format-Polish-suppressed-l-lower-case)
+(defun texinfo-format-Polish-suppressed-l-lower-case ()
+ (insert (texinfo-parse-arg-discard) "/l")
+ (goto-char texinfo-command-start))
+
+
+;; @O{} ==> O/ Scandinavian O-with-slash
+(put 'O 'texinfo-format 'texinfo-format-Scandinavian-O-with-slash)
+(defun texinfo-format-Scandinavian-O-with-slash ()
+ (insert (texinfo-parse-arg-discard) "O/")
+ (goto-char texinfo-command-start))
+
+;; @o{} ==> o/ Scandinavian O-with-slash (lower case)
+(put 'o 'texinfo-format 'texinfo-format-Scandinavian-o-with-slash-lower-case)
+(defun texinfo-format-Scandinavian-o-with-slash-lower-case ()
+ (insert (texinfo-parse-arg-discard) "o/")
+ (goto-char texinfo-command-start))
+
+;; Take arguments
+
+;; @,{c} ==> c, cedilla accent
+(put ', 'texinfo-format 'texinfo-format-cedilla-accent)
+(defun texinfo-format-cedilla-accent ()
+ (insert (texinfo-parse-arg-discard) ",")
+ (goto-char texinfo-command-start))
+
+
+;; @dotaccent{o} ==> .o overdot-accent
+(put 'dotaccent 'texinfo-format 'texinfo-format-overdot-accent)
+(defun texinfo-format-overdot-accent ()
+ (insert "." (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @ubaraccent{o} ==> _o underbar-accent
+(put 'ubaraccent 'texinfo-format 'texinfo-format-underbar-accent)
+(defun texinfo-format-underbar-accent ()
+ (insert "_" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @udotaccent{o} ==> o-. underdot-accent
+(put 'udotaccent 'texinfo-format 'texinfo-format-underdot-accent)
+(defun texinfo-format-underdot-accent ()
+ (insert (texinfo-parse-arg-discard) "-.")
+ (goto-char texinfo-command-start))
+
+;; @H{o} ==> ""o long Hungarian umlaut
+(put 'H 'texinfo-format 'texinfo-format-long-Hungarian-umlaut)
+(defun texinfo-format-long-Hungarian-umlaut ()
+ (insert "\"\"" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @ringaccent{o} ==> *o ring accent
+(put 'ringaccent 'texinfo-format 'texinfo-format-ring-accent)
+(defun texinfo-format-ring-accent ()
+ (insert "*" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @tieaccent{oo} ==> [oo tie after accent
+(put 'tieaccent 'texinfo-format 'texinfo-format-tie-after-accent)
+(defun texinfo-format-tie-after-accent ()
+ (insert "[" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+
+;; @u{o} ==> (o breve accent
+(put 'u 'texinfo-format 'texinfo-format-breve-accent)
+(defun texinfo-format-breve-accent ()
+ (insert "(" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @v{o} ==> <o hacek accent
+(put 'v 'texinfo-format 'texinfo-format-hacek-accent)
+(defun texinfo-format-hacek-accent ()
+ (insert "<" (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+
+;; @dotless{i} ==> i dotless i and dotless j
+(put 'dotless 'texinfo-format 'texinfo-format-dotless)
+(defun texinfo-format-dotless ()
+ (insert (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+
+;;; Definition formatting: @deffn, @defun, etc
+
+;; What definition formatting produces:
+;;
+;; @deffn category name args...
+;; In Info, `Category: name ARGS'
+;; In index: name: node. line#.
+;;
+;; @defvr category name
+;; In Info, `Category: name'
+;; In index: name: node. line#.
+;;
+;; @deftp category name attributes...
+;; `category name attributes...' Note: @deftp args in lower case.
+;; In index: name: node. line#.
+;;
+;; Specialized function-like or variable-like entity:
+;;
+;; @defun, @defmac, @defspec, @defvar, @defopt
+;;
+;; @defun name args In Info, `Function: name ARGS'
+;; @defmac name args In Info, `Macro: name ARGS'
+;; @defvar name In Info, `Variable: name'
+;; etc.
+;; In index: name: node. line#.
+;;
+;; Generalized typed-function-like or typed-variable-like entity:
+;; @deftypefn category data-type name args...
+;; In Info, `Category: data-type name args...'
+;; @deftypevr category data-type name
+;; In Info, `Category: data-type name'
+;; In index: name: node. line#.
+;;
+;; Specialized typed-function-like or typed-variable-like entity:
+;; @deftypefun data-type name args...
+;; In Info, `Function: data-type name ARGS'
+;; In index: name: node. line#.
+;;
+;; @deftypevar data-type name
+;; In Info, `Variable: data-type name'
+;; In index: name: node. line#. but include args after name!?
+;;
+;; Generalized object oriented entity:
+;; @defop category class name args...
+;; In Info, `Category on class: name ARG'
+;; In index: name on class: node. line#.
+;;
+;; @defcv category class name
+;; In Info, `Category of class: name'
+;; In index: name of class: node. line#.
+;;
+;; Specialized object oriented entity:
+;; @defmethod class name args...
+;; In Info, `Method on class: name ARGS'
+;; In index: name on class: node. line#.
+;;
+;; @defivar class name
+;; In Info, `Instance variable of class: name'
+;; In index: name of class: node. line#.
+
+
+;;; The definition formatting functions
+
+(defun texinfo-format-defun ()
+ (texinfo-push-stack 'defun nil)
+ (setq fill-column (- fill-column 5))
+ (texinfo-format-defun-1 t))
+
+(defun texinfo-end-defun ()
+ (setq fill-column (+ fill-column 5))
+ (texinfo-discard-command)
+ (let ((start (nth 1 (texinfo-pop-stack 'defun))))
+ (texinfo-do-itemize start)
+ ;; Delete extra newline inserted after header.
+ (save-excursion
+ (goto-char start)
+ (delete-char -1))))
+
+(defun texinfo-format-defunx ()
+ (texinfo-format-defun-1 nil))
+
+(defun texinfo-format-defun-1 (first-p)
+ (let ((parse-args (texinfo-format-parse-defun-args))
+ (texinfo-defun-type (get texinfo-command-name 'texinfo-defun-type)))
+ (texinfo-discard-command)
+ ;; Delete extra newline inserted after previous header line.
+ (if (not first-p)
+ (delete-char -1))
+ (funcall
+ (get texinfo-command-name 'texinfo-deffn-formatting-property) parse-args)
+ ;; Insert extra newline so that paragraph filling does not mess
+ ;; with header line.
+ (insert "\n\n")
+ (rplaca (cdr (cdr (car texinfo-stack))) (point))
+ (funcall
+ (get texinfo-command-name 'texinfo-defun-indexing-property) parse-args)))
+
+;;; Formatting the first line of a definition
+
+;; @deffn, @defvr, @deftp
+(put 'deffn 'texinfo-deffn-formatting-property 'texinfo-format-deffn)
+(put 'deffnx 'texinfo-deffn-formatting-property 'texinfo-format-deffn)
+(put 'defvr 'texinfo-deffn-formatting-property 'texinfo-format-deffn)
+(put 'defvrx 'texinfo-deffn-formatting-property 'texinfo-format-deffn)
+(put 'deftp 'texinfo-deffn-formatting-property 'texinfo-format-deffn)
+(put 'deftpx 'texinfo-deffn-formatting-property 'texinfo-format-deffn)
+(defun texinfo-format-deffn (parsed-args)
+ ;; Generalized function-like, variable-like, or generic data-type entity:
+ ;; @deffn category name args...
+ ;; In Info, `Category: name ARGS'
+ ;; @deftp category name attributes...
+ ;; `category name attributes...' Note: @deftp args in lower case.
+ (let ((category (car parsed-args))
+ (name (car (cdr parsed-args)))
+ (args (cdr (cdr parsed-args))))
+ (insert " -- " category ": " name)
+ (while args
+ (insert " "
+ (if (or (= ?& (aref (car args) 0))
+ (eq (eval (car texinfo-defun-type)) 'deftp-type))
+ (car args)
+ (upcase (car args))))
+ (setq args (cdr args)))))
+
+;; @defun, @defmac, @defspec, @defvar, @defopt: Specialized, simple
+(put 'defun 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(put 'defunx 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(put 'defmac 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(put 'defmacx 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(put 'defspec 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(put 'defspecx 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(put 'defvar 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(put 'defvarx 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(put 'defopt 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(put 'defoptx 'texinfo-deffn-formatting-property
+ 'texinfo-format-specialized-defun)
+(defun texinfo-format-specialized-defun (parsed-args)
+ ;; Specialized function-like or variable-like entity:
+ ;; @defun name args In Info, `Function: Name ARGS'
+ ;; @defmac name args In Info, `Macro: Name ARGS'
+ ;; @defvar name In Info, `Variable: Name'
+ ;; Use cdr of texinfo-defun-type to determine category:
+ (let ((category (car (cdr texinfo-defun-type)))
+ (name (car parsed-args))
+ (args (cdr parsed-args)))
+ (insert " -- " category ": " name)
+ (while args
+ (insert " "
+ (if (= ?& (aref (car args) 0))
+ (car args)
+ (upcase (car args))))
+ (setq args (cdr args)))))
+
+;; @deftypefn, @deftypevr: Generalized typed
+(put 'deftypefn 'texinfo-deffn-formatting-property 'texinfo-format-deftypefn)
+(put 'deftypefnx 'texinfo-deffn-formatting-property 'texinfo-format-deftypefn)
+(put 'deftypevr 'texinfo-deffn-formatting-property 'texinfo-format-deftypefn)
+(put 'deftypevrx 'texinfo-deffn-formatting-property 'texinfo-format-deftypefn)
+(defun texinfo-format-deftypefn (parsed-args)
+ ;; Generalized typed-function-like or typed-variable-like entity:
+ ;; @deftypefn category data-type name args...
+ ;; In Info, `Category: data-type name args...'
+ ;; @deftypevr category data-type name
+ ;; In Info, `Category: data-type name'
+ ;; Note: args in lower case, unless modified in command line.
+ (let ((category (car parsed-args))
+ (data-type (car (cdr parsed-args)))
+ (name (car (cdr (cdr parsed-args))))
+ (args (cdr (cdr (cdr parsed-args)))))
+ (insert " -- " category ": " data-type " " name)
+ (while args
+ (insert " " (car args))
+ (setq args (cdr args)))))
+
+;; @deftypefun, @deftypevar: Specialized typed
+(put 'deftypefun 'texinfo-deffn-formatting-property 'texinfo-format-deftypefun)
+(put 'deftypefunx 'texinfo-deffn-formatting-property
+ 'texinfo-format-deftypefun)
+(put 'deftypevar 'texinfo-deffn-formatting-property 'texinfo-format-deftypefun)
+(put 'deftypevarx 'texinfo-deffn-formatting-property
+ 'texinfo-format-deftypefun)
+(defun texinfo-format-deftypefun (parsed-args)
+ ;; Specialized typed-function-like or typed-variable-like entity:
+ ;; @deftypefun data-type name args...
+ ;; In Info, `Function: data-type name ARGS'
+ ;; @deftypevar data-type name
+ ;; In Info, `Variable: data-type name'
+ ;; Note: args in lower case, unless modified in command line.
+ ;; Use cdr of texinfo-defun-type to determine category:
+ (let ((category (car (cdr texinfo-defun-type)))
+ (data-type (car parsed-args))
+ (name (car (cdr parsed-args)))
+ (args (cdr (cdr parsed-args))))
+ (insert " -- " category ": " data-type " " name)
+ (while args
+ (insert " " (car args))
+ (setq args (cdr args)))))
+
+;; @defop: Generalized object-oriented
+(put 'defop 'texinfo-deffn-formatting-property 'texinfo-format-defop)
+(put 'defopx 'texinfo-deffn-formatting-property 'texinfo-format-defop)
+(defun texinfo-format-defop (parsed-args)
+ ;; Generalized object oriented entity:
+ ;; @defop category class name args...
+ ;; In Info, `Category on class: name ARG'
+ ;; Note: args in upper case; use of `on'
+ (let ((category (car parsed-args))
+ (class (car (cdr parsed-args)))
+ (name (car (cdr (cdr parsed-args))))
+ (args (cdr (cdr (cdr parsed-args)))))
+ (insert " -- " category " on " class ": " name)
+ (while args
+ (insert " " (upcase (car args)))
+ (setq args (cdr args)))))
+
+;; @defcv: Generalized object-oriented
+(put 'defcv 'texinfo-deffn-formatting-property 'texinfo-format-defcv)
+(put 'defcvx 'texinfo-deffn-formatting-property 'texinfo-format-defcv)
+(defun texinfo-format-defcv (parsed-args)
+ ;; Generalized object oriented entity:
+ ;; @defcv category class name
+ ;; In Info, `Category of class: name'
+ ;; Note: args in upper case; use of `of'
+ (let ((category (car parsed-args))
+ (class (car (cdr parsed-args)))
+ (name (car (cdr (cdr parsed-args))))
+ (args (cdr (cdr (cdr parsed-args)))))
+ (insert " -- " category " of " class ": " name)
+ (while args
+ (insert " " (upcase (car args)))
+ (setq args (cdr args)))))
+
+;; @defmethod: Specialized object-oriented
+(put 'defmethod 'texinfo-deffn-formatting-property 'texinfo-format-defmethod)
+(put 'defmethodx 'texinfo-deffn-formatting-property 'texinfo-format-defmethod)
+(defun texinfo-format-defmethod (parsed-args)
+ ;; Specialized object oriented entity:
+ ;; @defmethod class name args...
+ ;; In Info, `Method on class: name ARGS'
+ ;; Note: args in upper case; use of `on'
+ ;; Use cdr of texinfo-defun-type to determine category:
+ (let ((category (car (cdr texinfo-defun-type)))
+ (class (car parsed-args))
+ (name (car (cdr parsed-args)))
+ (args (cdr (cdr parsed-args))))
+ (insert " -- " category " on " class ": " name)
+ (while args
+ (insert " " (upcase (car args)))
+ (setq args (cdr args)))))
+
+;; @defivar: Specialized object-oriented
+(put 'defivar 'texinfo-deffn-formatting-property 'texinfo-format-defivar)
+(put 'defivarx 'texinfo-deffn-formatting-property 'texinfo-format-defivar)
+(defun texinfo-format-defivar (parsed-args)
+ ;; Specialized object oriented entity:
+ ;; @defivar class name
+ ;; In Info, `Instance variable of class: name'
+ ;; Note: args in upper case; use of `of'
+ ;; Use cdr of texinfo-defun-type to determine category:
+ (let ((category (car (cdr texinfo-defun-type)))
+ (class (car parsed-args))
+ (name (car (cdr parsed-args)))
+ (args (cdr (cdr parsed-args))))
+ (insert " -- " category " of " class ": " name)
+ (while args
+ (insert " " (upcase (car args)))
+ (setq args (cdr args)))))
+
+
+;;; Indexing for definitions
+
+;; An index entry has three parts: the `entry proper', the node name, and the
+;; line number. Depending on the which command is used, the entry is
+;; formatted differently:
+;;
+;; @defun,
+;; @defmac,
+;; @defspec,
+;; @defvar,
+;; @defopt all use their 1st argument as the entry-proper
+;;
+;; @deffn,
+;; @defvr,
+;; @deftp
+;; @deftypefun
+;; @deftypevar all use their 2nd argument as the entry-proper
+;;
+;; @deftypefn,
+;; @deftypevr both use their 3rd argument as the entry-proper
+;;
+;; @defmethod uses its 2nd and 1st arguments as an entry-proper
+;; formatted: NAME on CLASS
+
+;; @defop uses its 3rd and 2nd arguments as an entry-proper
+;; formatted: NAME on CLASS
+;;
+;; @defivar uses its 2nd and 1st arguments as an entry-proper
+;; formatted: NAME of CLASS
+;;
+;; @defcv uses its 3rd and 2nd argument as an entry-proper
+;; formatted: NAME of CLASS
+
+(put 'defun 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(put 'defunx 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(put 'defmac 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(put 'defmacx 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(put 'defspec 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(put 'defspecx 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(put 'defvar 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(put 'defvarx 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(put 'defopt 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(put 'defoptx 'texinfo-defun-indexing-property 'texinfo-index-defun)
+(defun texinfo-index-defun (parsed-args)
+ ;; use 1st parsed-arg as entry-proper
+ ;; `index-list' will be texinfo-findex or the like
+ (let ((index-list (get texinfo-command-name 'texinfo-defun-index)))
+ (set index-list
+ (cons
+ ;; Three elements: entry-proper, node-name, line-number
+ (list
+ (car parsed-args)
+ texinfo-last-node
+ ;; Region formatting may not provide last node position.
+ (if texinfo-last-node-pos
+ (1+ (count-lines texinfo-last-node-pos (point)))
+ 1))
+ (symbol-value index-list)))))
+
+(put 'deffn 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(put 'deffnx 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(put 'defvr 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(put 'defvrx 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(put 'deftp 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(put 'deftpx 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(put 'deftypefun 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(put 'deftypefunx 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(put 'deftypevar 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(put 'deftypevarx 'texinfo-defun-indexing-property 'texinfo-index-deffn)
+(defun texinfo-index-deffn (parsed-args)
+ ;; use 2nd parsed-arg as entry-proper
+ ;; `index-list' will be texinfo-findex or the like
+ (let ((index-list (get texinfo-command-name 'texinfo-defun-index)))
+ (set index-list
+ (cons
+ ;; Three elements: entry-proper, node-name, line-number
+ (list
+ (car (cdr parsed-args))
+ texinfo-last-node
+ ;; Region formatting may not provide last node position.
+ (if texinfo-last-node-pos
+ (1+ (count-lines texinfo-last-node-pos (point)))
+ 1))
+ (symbol-value index-list)))))
+
+(put 'deftypefn 'texinfo-defun-indexing-property 'texinfo-index-deftypefn)
+(put 'deftypefnx 'texinfo-defun-indexing-property 'texinfo-index-deftypefn)
+(put 'deftypevr 'texinfo-defun-indexing-property 'texinfo-index-deftypefn)
+(put 'deftypevrx 'texinfo-defun-indexing-property 'texinfo-index-deftypefn)
+(defun texinfo-index-deftypefn (parsed-args)
+ ;; use 3rd parsed-arg as entry-proper
+ ;; `index-list' will be texinfo-findex or the like
+ (let ((index-list (get texinfo-command-name 'texinfo-defun-index)))
+ (set index-list
+ (cons
+ ;; Three elements: entry-proper, node-name, line-number
+ (list
+ (car (cdr (cdr parsed-args)))
+ texinfo-last-node
+ ;; Region formatting may not provide last node position.
+ (if texinfo-last-node-pos
+ (1+ (count-lines texinfo-last-node-pos (point)))
+ 1))
+ (symbol-value index-list)))))
+
+(put 'defmethod 'texinfo-defun-indexing-property 'texinfo-index-defmethod)
+(put 'defmethodx 'texinfo-defun-indexing-property 'texinfo-index-defmethod)
+(defun texinfo-index-defmethod (parsed-args)
+ ;; use 2nd on 1st parsed-arg as entry-proper
+ ;; `index-list' will be texinfo-findex or the like
+ (let ((index-list (get texinfo-command-name 'texinfo-defun-index)))
+ (set index-list
+ (cons
+ ;; Three elements: entry-proper, node-name, line-number
+ (list
+ (format "%s on %s"
+ (car (cdr parsed-args))
+ (car parsed-args))
+ texinfo-last-node
+ ;; Region formatting may not provide last node position.
+ (if texinfo-last-node-pos
+ (1+ (count-lines texinfo-last-node-pos (point)))
+ 1))
+ (symbol-value index-list)))))
+
+(put 'defop 'texinfo-defun-indexing-property 'texinfo-index-defop)
+(put 'defopx 'texinfo-defun-indexing-property 'texinfo-index-defop)
+(defun texinfo-index-defop (parsed-args)
+ ;; use 3rd on 2nd parsed-arg as entry-proper
+ ;; `index-list' will be texinfo-findex or the like
+ (let ((index-list (get texinfo-command-name 'texinfo-defun-index)))
+ (set index-list
+ (cons
+ ;; Three elements: entry-proper, node-name, line-number
+ (list
+ (format "%s on %s"
+ (car (cdr (cdr parsed-args)))
+ (car (cdr parsed-args)))
+ texinfo-last-node
+ ;; Region formatting may not provide last node position.
+ (if texinfo-last-node-pos
+ (1+ (count-lines texinfo-last-node-pos (point)))
+ 1))
+ (symbol-value index-list)))))
+
+(put 'defivar 'texinfo-defun-indexing-property 'texinfo-index-defivar)
+(put 'defivarx 'texinfo-defun-indexing-property 'texinfo-index-defivar)
+(defun texinfo-index-defivar (parsed-args)
+ ;; use 2nd of 1st parsed-arg as entry-proper
+ ;; `index-list' will be texinfo-findex or the like
+ (let ((index-list (get texinfo-command-name 'texinfo-defun-index)))
+ (set index-list
+ (cons
+ ;; Three elements: entry-proper, node-name, line-number
+ (list
+ (format "%s of %s"
+ (car (cdr parsed-args))
+ (car parsed-args))
+ texinfo-last-node
+ ;; Region formatting may not provide last node position.
+ (if texinfo-last-node-pos
+ (1+ (count-lines texinfo-last-node-pos (point)))
+ 1))
+ (symbol-value index-list)))))
+
+(put 'defcv 'texinfo-defun-indexing-property 'texinfo-index-defcv)
+(put 'defcvx 'texinfo-defun-indexing-property 'texinfo-index-defcv)
+(defun texinfo-index-defcv (parsed-args)
+ ;; use 3rd of 2nd parsed-arg as entry-proper
+ ;; `index-list' will be texinfo-findex or the like
+ (let ((index-list (get texinfo-command-name 'texinfo-defun-index)))
+ (set index-list
+ (cons
+ ;; Three elements: entry-proper, node-name, line-number
+ (list
+ (format "%s of %s"
+ (car (cdr (cdr parsed-args)))
+ (car (cdr parsed-args)))
+ texinfo-last-node
+ ;; Region formatting may not provide last node position.
+ (if texinfo-last-node-pos
+ (1+ (count-lines texinfo-last-node-pos (point)))
+ 1))
+ (symbol-value index-list)))))
+
+
+;;; Properties for definitions
+
+;; Each definition command has six properties:
+;;
+;; 1. texinfo-deffn-formatting-property to format definition line
+;; 2. texinfo-defun-indexing-property to create index entry
+;; 3. texinfo-format formatting command
+;; 4. texinfo-end end formatting command
+;; 5. texinfo-defun-type type of deffn to format
+;; 6. texinfo-defun-index type of index to use
+;;
+;; The `x' forms of each definition command are used for the second
+;; and subsequent header lines.
+
+;; The texinfo-deffn-formatting-property and texinfo-defun-indexing-property
+;; are listed just before the appropriate formatting and indexing commands.
+
+(put 'deffn 'texinfo-format 'texinfo-format-defun)
+(put 'deffnx 'texinfo-format 'texinfo-format-defunx)
+(put 'deffn 'texinfo-end 'texinfo-end-defun)
+(put 'deffn 'texinfo-defun-type '('deffn-type nil))
+(put 'deffnx 'texinfo-defun-type '('deffn-type nil))
+(put 'deffn 'texinfo-defun-index 'texinfo-findex)
+(put 'deffnx 'texinfo-defun-index 'texinfo-findex)
+
+(put 'defun 'texinfo-format 'texinfo-format-defun)
+(put 'defunx 'texinfo-format 'texinfo-format-defunx)
+(put 'defun 'texinfo-end 'texinfo-end-defun)
+(put 'defun 'texinfo-defun-type '('defun-type "Function"))
+(put 'defunx 'texinfo-defun-type '('defun-type "Function"))
+(put 'defun 'texinfo-defun-index 'texinfo-findex)
+(put 'defunx 'texinfo-defun-index 'texinfo-findex)
+
+(put 'defmac 'texinfo-format 'texinfo-format-defun)
+(put 'defmacx 'texinfo-format 'texinfo-format-defunx)
+(put 'defmac 'texinfo-end 'texinfo-end-defun)
+(put 'defmac 'texinfo-defun-type '('defun-type "Macro"))
+(put 'defmacx 'texinfo-defun-type '('defun-type "Macro"))
+(put 'defmac 'texinfo-defun-index 'texinfo-findex)
+(put 'defmacx 'texinfo-defun-index 'texinfo-findex)
+
+(put 'defspec 'texinfo-format 'texinfo-format-defun)
+(put 'defspecx 'texinfo-format 'texinfo-format-defunx)
+(put 'defspec 'texinfo-end 'texinfo-end-defun)
+(put 'defspec 'texinfo-defun-type '('defun-type "Special form"))
+(put 'defspecx 'texinfo-defun-type '('defun-type "Special form"))
+(put 'defspec 'texinfo-defun-index 'texinfo-findex)
+(put 'defspecx 'texinfo-defun-index 'texinfo-findex)
+
+(put 'defvr 'texinfo-format 'texinfo-format-defun)
+(put 'defvrx 'texinfo-format 'texinfo-format-defunx)
+(put 'defvr 'texinfo-end 'texinfo-end-defun)
+(put 'defvr 'texinfo-defun-type '('deffn-type nil))
+(put 'defvrx 'texinfo-defun-type '('deffn-type nil))
+(put 'defvr 'texinfo-defun-index 'texinfo-vindex)
+(put 'defvrx 'texinfo-defun-index 'texinfo-vindex)
+
+(put 'defvar 'texinfo-format 'texinfo-format-defun)
+(put 'defvarx 'texinfo-format 'texinfo-format-defunx)
+(put 'defvar 'texinfo-end 'texinfo-end-defun)
+(put 'defvar 'texinfo-defun-type '('defun-type "Variable"))
+(put 'defvarx 'texinfo-defun-type '('defun-type "Variable"))
+(put 'defvar 'texinfo-defun-index 'texinfo-vindex)
+(put 'defvarx 'texinfo-defun-index 'texinfo-vindex)
+
+(put 'defconst 'texinfo-format 'texinfo-format-defun)
+(put 'defconstx 'texinfo-format 'texinfo-format-defunx)
+(put 'defconst 'texinfo-end 'texinfo-end-defun)
+(put 'defconst 'texinfo-defun-type '('defun-type "Constant"))
+(put 'defconstx 'texinfo-defun-type '('defun-type "Constant"))
+(put 'defconst 'texinfo-defun-index 'texinfo-vindex)
+(put 'defconstx 'texinfo-defun-index 'texinfo-vindex)
+
+(put 'defcmd 'texinfo-format 'texinfo-format-defun)
+(put 'defcmdx 'texinfo-format 'texinfo-format-defunx)
+(put 'defcmd 'texinfo-end 'texinfo-end-defun)
+(put 'defcmd 'texinfo-defun-type '('defun-type "Command"))
+(put 'defcmdx 'texinfo-defun-type '('defun-type "Command"))
+(put 'defcmd 'texinfo-defun-index 'texinfo-findex)
+(put 'defcmdx 'texinfo-defun-index 'texinfo-findex)
+
+(put 'defopt 'texinfo-format 'texinfo-format-defun)
+(put 'defoptx 'texinfo-format 'texinfo-format-defunx)
+(put 'defopt 'texinfo-end 'texinfo-end-defun)
+(put 'defopt 'texinfo-defun-type '('defun-type "User Option"))
+(put 'defoptx 'texinfo-defun-type '('defun-type "User Option"))
+(put 'defopt 'texinfo-defun-index 'texinfo-vindex)
+(put 'defoptx 'texinfo-defun-index 'texinfo-vindex)
+
+(put 'deftp 'texinfo-format 'texinfo-format-defun)
+(put 'deftpx 'texinfo-format 'texinfo-format-defunx)
+(put 'deftp 'texinfo-end 'texinfo-end-defun)
+(put 'deftp 'texinfo-defun-type '('deftp-type nil))
+(put 'deftpx 'texinfo-defun-type '('deftp-type nil))
+(put 'deftp 'texinfo-defun-index 'texinfo-tindex)
+(put 'deftpx 'texinfo-defun-index 'texinfo-tindex)
+
+;;; Object-oriented stuff is a little hairier.
+
+(put 'defop 'texinfo-format 'texinfo-format-defun)
+(put 'defopx 'texinfo-format 'texinfo-format-defunx)
+(put 'defop 'texinfo-end 'texinfo-end-defun)
+(put 'defop 'texinfo-defun-type '('defop-type nil))
+(put 'defopx 'texinfo-defun-type '('defop-type nil))
+(put 'defop 'texinfo-defun-index 'texinfo-findex)
+(put 'defopx 'texinfo-defun-index 'texinfo-findex)
+
+(put 'defmethod 'texinfo-format 'texinfo-format-defun)
+(put 'defmethodx 'texinfo-format 'texinfo-format-defunx)
+(put 'defmethod 'texinfo-end 'texinfo-end-defun)
+(put 'defmethod 'texinfo-defun-type '('defmethod-type "Method"))
+(put 'defmethodx 'texinfo-defun-type '('defmethod-type "Method"))
+(put 'defmethod 'texinfo-defun-index 'texinfo-findex)
+(put 'defmethodx 'texinfo-defun-index 'texinfo-findex)
+
+(put 'defcv 'texinfo-format 'texinfo-format-defun)
+(put 'defcvx 'texinfo-format 'texinfo-format-defunx)
+(put 'defcv 'texinfo-end 'texinfo-end-defun)
+(put 'defcv 'texinfo-defun-type '('defop-type nil))
+(put 'defcvx 'texinfo-defun-type '('defop-type nil))
+(put 'defcv 'texinfo-defun-index 'texinfo-vindex)
+(put 'defcvx 'texinfo-defun-index 'texinfo-vindex)
+
+(put 'defivar 'texinfo-format 'texinfo-format-defun)
+(put 'defivarx 'texinfo-format 'texinfo-format-defunx)
+(put 'defivar 'texinfo-end 'texinfo-end-defun)
+(put 'defivar 'texinfo-defun-type '('defmethod-type "Instance variable"))
+(put 'defivarx 'texinfo-defun-type '('defmethod-type "Instance variable"))
+(put 'defivar 'texinfo-defun-index 'texinfo-vindex)
+(put 'defivarx 'texinfo-defun-index 'texinfo-vindex)
+
+;;; Typed functions and variables
+
+(put 'deftypefn 'texinfo-format 'texinfo-format-defun)
+(put 'deftypefnx 'texinfo-format 'texinfo-format-defunx)
+(put 'deftypefn 'texinfo-end 'texinfo-end-defun)
+(put 'deftypefn 'texinfo-defun-type '('deftypefn-type nil))
+(put 'deftypefnx 'texinfo-defun-type '('deftypefn-type nil))
+(put 'deftypefn 'texinfo-defun-index 'texinfo-findex)
+(put 'deftypefnx 'texinfo-defun-index 'texinfo-findex)
+
+(put 'deftypefun 'texinfo-format 'texinfo-format-defun)
+(put 'deftypefunx 'texinfo-format 'texinfo-format-defunx)
+(put 'deftypefun 'texinfo-end 'texinfo-end-defun)
+(put 'deftypefun 'texinfo-defun-type '('deftypefun-type "Function"))
+(put 'deftypefunx 'texinfo-defun-type '('deftypefun-type "Function"))
+(put 'deftypefun 'texinfo-defun-index 'texinfo-findex)
+(put 'deftypefunx 'texinfo-defun-index 'texinfo-findex)
+
+(put 'deftypevr 'texinfo-format 'texinfo-format-defun)
+(put 'deftypevrx 'texinfo-format 'texinfo-format-defunx)
+(put 'deftypevr 'texinfo-end 'texinfo-end-defun)
+(put 'deftypevr 'texinfo-defun-type '('deftypefn-type nil))
+(put 'deftypevrx 'texinfo-defun-type '('deftypefn-type nil))
+(put 'deftypevr 'texinfo-defun-index 'texinfo-vindex)
+(put 'deftypevrx 'texinfo-defun-index 'texinfo-vindex)
+
+(put 'deftypevar 'texinfo-format 'texinfo-format-defun)
+(put 'deftypevarx 'texinfo-format 'texinfo-format-defunx)
+(put 'deftypevar 'texinfo-end 'texinfo-end-defun)
+(put 'deftypevar 'texinfo-defun-type '('deftypevar-type "Variable"))
+(put 'deftypevarx 'texinfo-defun-type '('deftypevar-type "Variable"))
+(put 'deftypevar 'texinfo-defun-index 'texinfo-vindex)
+(put 'deftypevarx 'texinfo-defun-index 'texinfo-vindex)
+
+
+;;; @set, @clear, @ifset, @ifclear
+
+;; If a flag is set with @set FLAG, then text between @ifset and @end
+;; ifset is formatted normally, but if the flag is is cleared with
+;; @clear FLAG, then the text is not formatted; it is ignored.
+
+;; If a flag is cleared with @clear FLAG, then text between @ifclear
+;; and @end ifclear is formatted normally, but if the flag is is set with
+;; @set FLAG, then the text is not formatted; it is ignored. @ifclear
+;; is the opposite of @ifset.
+
+;; If a flag is set to a string with @set FLAG,
+;; replace @value{FLAG} with the string.
+;; If a flag with a value is cleared,
+;; @value{FLAG} is invalid,
+;; as if there had never been any @set FLAG previously.
+
+(put 'clear 'texinfo-format 'texinfo-clear)
+(defun texinfo-clear ()
+ "Clear the value of the flag."
+ (let* ((arg (texinfo-parse-arg-discard))
+ (flag (car (read-from-string arg)))
+ (value (substring arg (cdr (read-from-string arg)))))
+ (put flag 'texinfo-whether-setp 'flag-cleared)
+ (put flag 'texinfo-set-value "")))
+
+(put 'set 'texinfo-format 'texinfo-set)
+(defun texinfo-set ()
+ "Set the value of the flag, optionally to a string.
+The command `@set foo This is a string.'
+sets flag foo to the value: `This is a string.'
+The command `@value{foo}' expands to the value."
+ (let* ((arg (texinfo-parse-arg-discard))
+ (flag (car (read-from-string arg)))
+ (value (substring arg (cdr (read-from-string arg)))))
+ (put flag 'texinfo-whether-setp 'flag-set)
+ (put flag 'texinfo-set-value value)))
+
+(put 'value 'texinfo-format 'texinfo-value)
+(defun texinfo-value ()
+ "Insert the string to which the flag is set.
+The command `@set foo This is a string.'
+sets flag foo to the value: `This is a string.'
+The command `@value{foo}' expands to the value."
+ (let ((arg (texinfo-parse-arg-discard)))
+ (cond ((and
+ (eq (get (car (read-from-string arg)) 'texinfo-whether-setp)
+ 'flag-set)
+ (get (car (read-from-string arg)) 'texinfo-set-value))
+ (insert (get (car (read-from-string arg)) 'texinfo-set-value)))
+ ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp)
+ 'flag-cleared)
+ (insert (format "{No value for \"%s\"}" arg)))
+ ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp) nil)
+ (insert (format "{No value for \"%s\"}" arg))))))
+
+(put 'ifset 'texinfo-end 'texinfo-discard-command)
+(put 'ifset 'texinfo-format 'texinfo-if-set)
+(defun texinfo-if-set ()
+ "If set, continue formatting; else do not format region up to @end ifset"
+ (let ((arg (texinfo-parse-arg-discard)))
+ (cond
+ ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp)
+ 'flag-set)
+ ;; Format the text (i.e., do not remove it); do nothing here.
+ ())
+ ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp)
+ 'flag-cleared)
+ ;; Clear region (i.e., cause the text to be ignored).
+ (delete-region texinfo-command-start
+ (progn (re-search-forward "@end ifset[ \t]*\n")
+ (point))))
+ ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp)
+ nil)
+ ;; In this case flag is neither set nor cleared.
+ ;; Act as if set, i.e. do nothing.
+ ()))))
+
+(put 'ifclear 'texinfo-end 'texinfo-discard-command)
+(put 'ifclear 'texinfo-format 'texinfo-if-clear)
+(defun texinfo-if-clear ()
+ "If clear, continue formatting; if set, do not format up to @end ifset"
+ (let ((arg (texinfo-parse-arg-discard)))
+ (cond
+ ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp)
+ 'flag-set)
+ ;; Clear region (i.e., cause the text to be ignored).
+ (delete-region texinfo-command-start
+ (progn (re-search-forward "@end ifclear[ \t]*\n")
+ (point))))
+ ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp)
+ 'flag-cleared)
+ ;; Format the text (i.e., do not remove it); do nothing here.
+ ())
+ ((eq (get (car (read-from-string arg)) 'texinfo-whether-setp)
+ nil)
+ ;; In this case flag is neither set nor cleared.
+ ;; Act as if clear, i.e. do nothing.
+ ()))))
+
+
+;;; @ifeq
+
+(put 'ifeq 'texinfo-format 'texinfo-format-ifeq)
+(defun texinfo-format-ifeq ()
+ "If ARG1 and ARG2 caselessly string compare to same string, performs COMMAND.
+Otherwise produces no output.
+
+Thus:
+ @ifeq{ arg1 , arg1 , @code{foo}} bar
+
+ ==> `foo' bar.
+but
+ @ifeq{ arg1 , arg2 , @code{foo}} bar
+
+ ==> bar
+
+Note that the Texinfo command and its arguments must be arguments to
+the @ifeq command."
+ ;; compare-buffer-substrings does not exist in version 18; don't use
+ (goto-char texinfo-command-end)
+ (let* ((case-fold-search t)
+ (stop (save-excursion (forward-sexp 1) (point)))
+ start end
+ ;; @ifeq{arg1, arg2, @command{optional-args}}
+ (arg1
+ (progn
+ (forward-char 1)
+ (skip-chars-forward " ")
+ (setq start (point))
+ (search-forward "," stop t)
+ (skip-chars-backward ", ")
+ (buffer-substring start (point))))
+ (arg2
+ (progn
+ (search-forward "," stop t)
+ (skip-chars-forward " ")
+ (setq start (point))
+ (search-forward "," stop t)
+ (skip-chars-backward ", ")
+ (buffer-substring start (point))))
+ (texinfo-command
+ (progn
+ (search-forward "," stop t)
+ (skip-chars-forward " ")
+ (setq start (point))
+ (goto-char (1- stop))
+ (skip-chars-backward " ")
+ (buffer-substring start (point)))))
+ (delete-region texinfo-command-start stop)
+ (if (equal arg1 arg2)
+ (insert texinfo-command))
+ (goto-char texinfo-command-start)))
+
+
+;;; Process included files: `@include' command
+
+;; Updated 19 October 1990
+;; In the original version, include files were ignored by Info but
+;; incorporated in to the printed manual. To make references to the
+;; included file, the Texinfo source file has to refer to the included
+;; files using the `(filename)nodename' format for referring to other
+;; Info files. Also, the included files had to be formatted on their
+;; own. It was just like they were another file.
+
+;; Currently, include files are inserted into the buffer that is
+;; formatted for Info. If large, the resulting info file is split and
+;; tagified. For current include files to work, the master menu must
+;; refer to all the nodes, and the highest level nodes in the include
+;; files must have the correct next, prev, and up pointers.
+
+;; The included file may have an @setfilename and even an @settitle,
+;; but not an `\input texinfo' line.
+
+;; Updated 24 March 1993
+;; In order for @raisesections and @lowersections to work, included
+;; files must be inserted into the buffer holding the outer file
+;; before other Info formatting takes place. So @include is no longer
+;; is treated like other @-commands.
+(put 'include 'texinfo-format 'texinfo-format-noop)
+
+;; Original definition:
+;; (defun texinfo-format-include ()
+;; (let ((filename (texinfo-parse-arg-discard))
+;; (default-directory input-directory)
+;; subindex)
+;; (setq subindex
+;; (save-excursion
+;; (progn (find-file
+;; (cond ((file-readable-p (concat filename ".texinfo"))
+;; (concat filename ".texinfo"))
+;; ((file-readable-p (concat filename ".texi"))
+;; (concat filename ".texi"))
+;; ((file-readable-p (concat filename ".tex"))
+;; (concat filename ".tex"))
+;; ((file-readable-p filename)
+;; filename)
+;; (t (error "@include'd file %s not found"
+;; filename))))
+;; (texinfo-format-buffer-1))))
+;; (texinfo-subindex 'texinfo-vindex (car subindex) (nth 1 subindex))
+;; (texinfo-subindex 'texinfo-findex (car subindex) (nth 2 subindex))
+;; (texinfo-subindex 'texinfo-cindex (car subindex) (nth 3 subindex))
+;; (texinfo-subindex 'texinfo-pindex (car subindex) (nth 4 subindex))
+;; (texinfo-subindex 'texinfo-tindex (car subindex) (nth 5 subindex))
+;; (texinfo-subindex 'texinfo-kindex (car subindex) (nth 6 subindex))))
+;;
+;;(defun texinfo-subindex (indexvar file content)
+;; (set indexvar (cons (list 'recurse file content)
+;; (symbol-value indexvar))))
+
+;; Second definition:
+;; (put 'include 'texinfo-format 'texinfo-format-include)
+;; (defun texinfo-format-include ()
+;; (let ((filename (concat input-directory
+;; (texinfo-parse-arg-discard)))
+;; (default-directory input-directory))
+;; (message "Reading: %s" filename)
+;; (save-excursion
+;; (save-restriction
+;; (narrow-to-region
+;; (point)
+;; (+ (point) (car (cdr (insert-file-contents filename)))))
+;; (goto-char (point-min))
+;; (texinfo-append-refill)
+;; (texinfo-format-convert (point-min) (point-max))))
+;; (setq last-input-buffer input-buffer) ; to bypass setfilename
+;; ))
+
+
+;;; Numerous commands do nothing in Info
+;; These commands are defined in texinfo.tex for printed output.
+
+
+;;; various noops, such as @b{foo}, that take arguments in braces
+
+(put 'b 'texinfo-format 'texinfo-format-noop)
+(put 'i 'texinfo-format 'texinfo-format-noop)
+(put 'r 'texinfo-format 'texinfo-format-noop)
+(put 't 'texinfo-format 'texinfo-format-noop)
+(put 'w 'texinfo-format 'texinfo-format-noop)
+(put 'asis 'texinfo-format 'texinfo-format-noop)
+(put 'dmn 'texinfo-format 'texinfo-format-noop)
+(put 'math 'texinfo-format 'texinfo-format-noop)
+(put 'titlefont 'texinfo-format 'texinfo-format-noop)
+(defun texinfo-format-noop ()
+ (insert (texinfo-parse-arg-discard))
+ (goto-char texinfo-command-start))
+
+;; @hyphenation command discards an argument within braces
+(put 'hyphenation 'texinfo-format 'texinfo-discard-command-and-arg)
+(defun texinfo-discard-command-and-arg ()
+ "Discard both @-command and its argument in braces."
+ (goto-char texinfo-command-end)
+ (forward-list 1)
+ (setq texinfo-command-end (point))
+ (delete-region texinfo-command-start texinfo-command-end))
+
+
+;;; Do nothing commands, such as @smallbook, that have no args and no braces
+;; These must appear on a line of their own
+
+(put 'bye 'texinfo-format 'texinfo-discard-line)
+(put 'smallbook 'texinfo-format 'texinfo-discard-line)
+(put 'finalout 'texinfo-format 'texinfo-discard-line)
+(put 'overfullrule 'texinfo-format 'texinfo-discard-line)
+(put 'smallbreak 'texinfo-format 'texinfo-discard-line)
+(put 'medbreak 'texinfo-format 'texinfo-discard-line)
+(put 'bigbreak 'texinfo-format 'texinfo-discard-line)
+
+
+;;; These noop commands discard the rest of the line.
+
+(put 'c 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'comment 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'contents 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'group 'texinfo-end 'texinfo-discard-line-with-args)
+(put 'group 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'headings 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'setchapterstyle 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'hsize 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'itemindent 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'lispnarrowing 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'need 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'nopara 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'page 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'parindent 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'setchapternewpage 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'setq 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'settitle 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'setx 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'shortcontents 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'shorttitlepage 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'summarycontents 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'input 'texinfo-format 'texinfo-discard-line-with-args)
+(put 'dircategory 'texinfo-format 'texinfo-discard-line-with-args)
+
+
+;;; Some commands cannot be handled
+
+(defun texinfo-unsupported ()
+ (error "%s is not handled by texinfo"
+ (buffer-substring texinfo-command-start texinfo-command-end)))
+
+;;; Batch formatting
+
+(defun batch-texinfo-format ()
+ "Runs texinfo-format-buffer on the files remaining on the command line.
+Must be used only with -batch, and kills emacs on completion.
+Each file will be processed even if an error occurred previously.
+For example, invoke
+ \"emacs -batch -funcall batch-texinfo-format $docs/ ~/*.texinfo\"."
+ (if (not noninteractive)
+ (error "batch-texinfo-format may only be used -batch."))
+ (let ((version-control t)
+ (auto-save-default nil)
+ (find-file-run-dired nil)
+ (kept-old-versions 259259)
+ (kept-new-versions 259259))
+ (let ((error 0)
+ file
+ (files ()))
+ (while command-line-args-left
+ (setq file (expand-file-name (car command-line-args-left)))
+ (cond ((not (file-exists-p file))
+ (message ">> %s does not exist!" file)
+ (setq error 1
+ command-line-args-left (cdr command-line-args-left)))
+ ((file-directory-p file)
+ (setq command-line-args-left
+ (nconc (directory-files file)
+ (cdr command-line-args-left))))
+ (t
+ (setq files (cons file files)
+ command-line-args-left (cdr command-line-args-left)))))
+ (while files
+ (setq file (car files)
+ files (cdr files))
+ (condition-case err
+ (progn
+ (if buffer-file-name (kill-buffer (current-buffer)))
+ (find-file file)
+ (buffer-disable-undo (current-buffer))
+ (set-buffer-modified-p nil)
+ (texinfo-mode)
+ (message "texinfo formatting %s..." file)
+ (texinfo-format-buffer nil)
+ (if (buffer-modified-p)
+ (progn (message "Saving modified %s" (buffer-file-name))
+ (save-buffer))))
+ (error
+ (message ">> Error: %s" (prin1-to-string err))
+ (message ">> point at")
+ (let ((s (buffer-substring (point)
+ (min (+ (point) 100)
+ (point-max))))
+ (tem 0))
+ (while (setq tem (string-match "\n+" s tem))
+ (setq s (concat (substring s 0 (match-beginning 0))
+ "\n>> "
+ (substring s (match-end 0)))
+ tem (1+ tem)))
+ (message ">> %s" s))
+ (setq error 1))))
+ (kill-emacs error))))
+
+
+;;; Place `provide' at end of file.
+(provide 'texinfmt)
+
+;;; texinfmt.el ends here.
diff --git a/contrib/texinfo/emacs/texinfo.el b/contrib/texinfo/emacs/texinfo.el
new file mode 100644
index 0000000..0a1ab13
--- /dev/null
+++ b/contrib/texinfo/emacs/texinfo.el
@@ -0,0 +1,932 @@
+;;; texinfo.el--major mode for editing Texinfo files.
+
+;; Copyright (C) 1985, '88, '89, '90, '91,
+;; '92, '93, '96 Free Software Foundation, Inc.
+
+;; Author: Robert J. Chassell
+;; Date: 6 Sep 1996
+;; Maintainer: bug-texinfo@prep.ai.mit.edu
+;; Keywords: maint, tex, docs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+
+;;; Autoloads:
+
+(autoload 'makeinfo-region
+ "makeinfo"
+ "Make Info file from region of current Texinfo file, and switch to it.
+
+This command does not offer the `next-error' feature since it would
+apply to a temporary file, not the original; use the `makeinfo-buffer'
+command to gain use of `next-error'."
+ t nil)
+
+(autoload 'makeinfo-buffer
+ "makeinfo"
+ "Make Info file from current buffer.
+
+Use the \\[next-error] command to move to the next error
+\(if there are errors\)."
+ t nil)
+
+(autoload 'kill-compilation
+ "compile"
+ "Kill the process made by the \\[compile] command."
+ t nil)
+
+(autoload 'makeinfo-recenter-compilation-buffer
+ "makeinfo"
+ "Redisplay `*compilation*' buffer so most recent output can be seen.
+The last line of the buffer is displayed on
+line LINE of the window, or centered if LINE is nil."
+ t nil)
+
+(autoload 'texinfo-update-node
+ "texnfo-upd"
+ "Without any prefix argument, update the node in which point is located.
+Non-nil argument (prefix, if interactive) means update the nodes in the
+marked region.
+
+The functions for creating or updating nodes and menus, and their
+keybindings, are:
+
+ texinfo-update-node (&optional region-p) \\[texinfo-update-node]
+ texinfo-every-node-update () \\[texinfo-every-node-update]
+ texinfo-sequential-node-update (&optional region-p)
+
+ texinfo-make-menu (&optional region-p) \\[texinfo-make-menu]
+ texinfo-all-menus-update () \\[texinfo-all-menus-update]
+ texinfo-master-menu ()
+
+ texinfo-indent-menu-description (column &optional region-p)
+
+The `texinfo-column-for-description' variable specifies the column to
+which menu descriptions are indented. Its default value is 32."
+ t nil)
+
+(autoload 'texinfo-every-node-update
+ "texnfo-upd"
+ "Update every node in a Texinfo file."
+ t nil)
+
+(autoload 'texinfo-sequential-node-update
+ "texnfo-upd"
+ "Update one node (or many) in a Texinfo file with sequential pointers.
+
+This function causes the `Next' or `Previous' pointer to point to the
+immediately preceding or following node, even if it is at a higher or
+lower hierarchical level in the document. Continually pressing `n' or
+`p' takes you straight through the file.
+
+Without any prefix argument, update the node in which point is located.
+Non-nil argument (prefix, if interactive) means update the nodes in the
+marked region.
+
+This command makes it awkward to navigate among sections and
+subsections; it should be used only for those documents that are meant
+to be read like a novel rather than a reference, and for which the
+Info `g*' command is inadequate."
+ t nil)
+
+(autoload 'texinfo-make-menu
+ "texnfo-upd"
+ "Without any prefix argument, make or update a menu.
+Make the menu for the section enclosing the node found following point.
+
+Non-nil argument (prefix, if interactive) means make or update menus
+for nodes within or part of the marked region.
+
+Whenever a menu exists, and is being updated, the descriptions that
+are associated with node names in the pre-existing menu are
+incorporated into the new menu. Otherwise, the nodes' section titles
+are inserted as descriptions."
+ t nil)
+
+(autoload 'texinfo-all-menus-update
+ "texnfo-upd"
+ "Update every regular menu in a Texinfo file.
+Remove pre-existing master menu, if there is one.
+
+If called with a non-nil argument, this function first updates all the
+nodes in the buffer before updating the menus."
+ t nil)
+
+(autoload 'texinfo-master-menu
+ "texnfo-upd"
+ "Make a master menu for a whole Texinfo file.
+Non-nil argument (prefix, if interactive) means first update all
+existing nodes and menus. Remove pre-existing master menu, if there is one.
+
+This function creates a master menu that follows the top node. The
+master menu includes every entry from all the other menus. It
+replaces any existing ordinary menu that follows the top node.
+
+If called with a non-nil argument, this function first updates all the
+menus in the buffer (incorporating descriptions from pre-existing
+menus) before it constructs the master menu.
+
+The function removes the detailed part of an already existing master
+menu. This action depends on the pre-existing master menu using the
+standard `texinfo-master-menu-header'.
+
+The master menu has the following format, which is adapted from the
+recommendation in the Texinfo Manual:
+
+ * The first part contains the major nodes in the Texinfo file: the
+ nodes for the chapters, chapter-like sections, and the major
+ appendices. This includes the indices, so long as they are in
+ chapter-like sections, such as unnumbered sections.
+
+ * The second and subsequent parts contain a listing of the other,
+ lower level menus, in order. This way, an inquirer can go
+ directly to a particular node if he or she is searching for
+ specific information.
+
+Each of the menus in the detailed node listing is introduced by the
+title of the section containing the menu."
+ t nil)
+
+(autoload 'texinfo-indent-menu-description
+ "texnfo-upd"
+ "Indent every description in menu following point to COLUMN.
+Non-nil argument (prefix, if interactive) means indent every
+description in every menu in the region. Does not indent second and
+subsequent lines of a multi-line description."
+ t nil)
+
+(autoload 'texinfo-insert-node-lines
+ "texnfo-upd"
+ "Insert missing `@node' lines in region of Texinfo file.
+Non-nil argument (prefix, if interactive) means also to insert the
+section titles as node names; and also to insert the section titles as
+node names in pre-existing @node lines that lack names."
+ t nil)
+
+(autoload 'texinfo-start-menu-description
+ "texnfo-upd"
+ "In this menu entry, insert the node's section title as a description.
+Position point at beginning of description ready for editing.
+Do not insert a title if the line contains an existing description.
+
+You will need to edit the inserted text since a useful description
+complements the node name rather than repeats it as a title does."
+ t nil)
+
+(autoload 'texinfo-multiple-files-update
+ "texnfo-upd"
+ "Update first node pointers in each file included in OUTER-FILE;
+create or update main menu in the outer file that refers to such nodes.
+This does not create or update menus or pointers within the included files.
+
+With optional MAKE-MASTER-MENU argument (prefix arg, if interactive),
+insert a master menu in OUTER-FILE. This does not create or update
+menus or pointers within the included files.
+
+With optional UPDATE-EVERYTHING argument (numeric prefix arg, if
+interactive), update all the menus and all the `Next', `Previous', and
+`Up' pointers of all the files included in OUTER-FILE before inserting
+a master menu in OUTER-FILE.
+
+The command also updates the `Top' level node pointers of OUTER-FILE.
+
+Notes:
+
+ * this command does NOT save any files--you must save the
+ outer file and any modified, included files.
+
+ * except for the `Top' node, this command does NOT handle any
+ pre-existing nodes in the outer file; hence, indices must be
+ enclosed in an included file.
+
+Requirements:
+
+ * each of the included files must contain exactly one highest
+ hierarchical level node,
+ * this highest node must be the first node in the included file,
+ * each highest hierarchical level node must be of the same type.
+
+Thus, normally, each included file contains one, and only one,
+chapter."
+ t nil)
+
+
+;;; Code:
+
+;;; Don't you dare insert any `require' calls at top level in this file--rms.
+
+;;; Syntax table
+
+(defvar texinfo-mode-syntax-table nil)
+
+(if texinfo-mode-syntax-table
+ nil
+ (setq texinfo-mode-syntax-table (make-syntax-table))
+ (modify-syntax-entry ?\" " " texinfo-mode-syntax-table)
+ (modify-syntax-entry ?\\ " " texinfo-mode-syntax-table)
+ (modify-syntax-entry ?@ "\\" texinfo-mode-syntax-table)
+ (modify-syntax-entry ?\^q "\\" texinfo-mode-syntax-table)
+ (modify-syntax-entry ?\[ "(]" texinfo-mode-syntax-table)
+ (modify-syntax-entry ?\] ")[" texinfo-mode-syntax-table)
+ (modify-syntax-entry ?{ "(}" texinfo-mode-syntax-table)
+ (modify-syntax-entry ?} "){" texinfo-mode-syntax-table)
+ (modify-syntax-entry ?\' "w" texinfo-mode-syntax-table))
+
+;; Written by Wolfgang Bangerth <zcg51122@rpool1.rus.uni-stuttgart.de>
+;; To override this example, set either `imenu-generic-expression'
+;; or `imenu-create-index-function'.
+(defvar texinfo-imenu-generic-expression
+ '((nil "^@node[ \t]+\\([^,\n]*\\)" 1)
+ ("Chapters" "^@chapter[ \t]+\\(.*\\)$" 1))
+
+ "Imenu generic expression for TexInfo mode. See `imenu-generic-expression'.")
+
+(defvar texinfo-font-lock-keywords
+ '(;; All but the first 2 had an OVERRIDE of t.
+ ;; It didn't seem to be any better, and it's slower--simon.
+ ("^\\(@c\\|@comment\\)\\>.*" . font-lock-comment-face) ;comments
+ ;; Robert J. Chassell <bob@gnu.ai.mit.edu> says remove this line.
+ ;("\\$\\([^$]*\\)\\$" 1 font-lock-string-face t)
+ ("@\\([a-zA-Z]+\\|[^ \t\n]\\)" 1 font-lock-keyword-face) ;commands
+ ("^\\*\\(.*\\)[\t ]*$" 1 font-lock-function-name-face t) ;menu items
+ ("@\\(emph\\|strong\\|b\\|i\\){\\([^}]+\\)" 2 font-lock-comment-face)
+ ("@\\(file\\|kbd\\|key\\){\\([^}]+\\)" 2 font-lock-string-face)
+ ("@\\(samp\\|code\\|var\\|math\\){\\([^}]+\\)"
+ 2 font-lock-variable-name-face)
+ ("@\\(cite\\|xref\\|pxref\\){\\([^}]+\\)" 2 font-lock-reference-face)
+ ("@\\(end\\|itemx?\\) +\\(.+\\)" 2 font-lock-function-name-face keep)
+ )
+ "Additional expressions to highlight in TeXinfo mode.")
+
+(defvar texinfo-section-list
+ '(("top" 1)
+ ("majorheading" 1)
+ ("chapter" 2)
+ ("unnumbered" 2)
+ ("appendix" 2)
+ ("chapheading" 2)
+ ("section" 3)
+ ("unnumberedsec" 3)
+ ("appendixsec" 3)
+ ("heading" 3)
+ ("subsection" 4)
+ ("unnumberedsubsec" 4)
+ ("appendixsubsec" 4)
+ ("subheading" 4)
+ ("subsubsection" 5)
+ ("unnumberedsubsubsec" 5)
+ ("appendixsubsubsec" 5)
+ ("subsubheading" 5))
+ "Alist of sectioning commands and their relative level.")
+
+(defun texinfo-outline-level ()
+ ;; Calculate level of current texinfo outline heading.
+ (save-excursion
+ (if (bobp)
+ 0
+ (forward-char 1)
+ (let* ((word (buffer-substring-no-properties
+ (point) (progn (forward-word 1) (point))))
+ (entry (assoc word texinfo-section-list)))
+ (if entry
+ (nth 1 entry)
+ 5)))))
+
+
+;;; Keybindings
+(defvar texinfo-mode-map nil)
+
+;;; Keys common both to Texinfo mode and to TeX shell.
+
+(defun texinfo-define-common-keys (keymap)
+ "Define the keys both in Texinfo mode and in the texinfo-tex-shell."
+ (define-key keymap "\C-c\C-t\C-k" 'tex-kill-job)
+ (define-key keymap "\C-c\C-t\C-x" 'texinfo-quit-job)
+ (define-key keymap "\C-c\C-t\C-l" 'tex-recenter-output-buffer)
+ (define-key keymap "\C-c\C-t\C-d" 'texinfo-delete-from-print-queue)
+ (define-key keymap "\C-c\C-t\C-q" 'tex-show-print-queue)
+ (define-key keymap "\C-c\C-t\C-p" 'texinfo-tex-print)
+ (define-key keymap "\C-c\C-t\C-i" 'texinfo-texindex)
+
+ (define-key keymap "\C-c\C-t\C-r" 'texinfo-tex-region)
+ (define-key keymap "\C-c\C-t\C-b" 'texinfo-tex-buffer))
+
+;; Mode documentation displays commands in reverse order
+;; from how they are listed in the texinfo-mode-map.
+
+(if texinfo-mode-map
+ nil
+ (setq texinfo-mode-map (make-sparse-keymap))
+
+ ;; bindings for `texnfo-tex.el'
+ (texinfo-define-common-keys texinfo-mode-map)
+
+ ;; bindings for `makeinfo.el'
+ (define-key texinfo-mode-map "\C-c\C-m\C-k" 'kill-compilation)
+ (define-key texinfo-mode-map "\C-c\C-m\C-l"
+ 'makeinfo-recenter-compilation-buffer)
+ (define-key texinfo-mode-map "\C-c\C-m\C-r" 'makeinfo-region)
+ (define-key texinfo-mode-map "\C-c\C-m\C-b" 'makeinfo-buffer)
+
+ ;; bindings for `texinfmt.el'
+ (define-key texinfo-mode-map "\C-c\C-e\C-r" 'texinfo-format-region)
+ (define-key texinfo-mode-map "\C-c\C-e\C-b" 'texinfo-format-buffer)
+
+ ;; bindings for updating nodes and menus
+
+ (define-key texinfo-mode-map "\C-c\C-um" 'texinfo-master-menu)
+
+ (define-key texinfo-mode-map "\C-c\C-u\C-m" 'texinfo-make-menu)
+ (define-key texinfo-mode-map "\C-c\C-u\C-n" 'texinfo-update-node)
+ (define-key texinfo-mode-map "\C-c\C-u\C-e" 'texinfo-every-node-update)
+ (define-key texinfo-mode-map "\C-c\C-u\C-a" 'texinfo-all-menus-update)
+
+ (define-key texinfo-mode-map "\C-c\C-s" 'texinfo-show-structure)
+
+ (define-key texinfo-mode-map "\C-c}" 'up-list)
+ (define-key texinfo-mode-map "\C-c]" 'up-list)
+ (define-key texinfo-mode-map "\C-c{" 'texinfo-insert-braces)
+
+ ;; bindings for inserting strings
+
+ (define-key texinfo-mode-map "\C-c\C-c\C-d" 'texinfo-start-menu-description)
+
+ (define-key texinfo-mode-map "\C-c\C-cv" 'texinfo-insert-@var)
+ (define-key texinfo-mode-map "\C-c\C-ct" 'texinfo-insert-@table)
+ (define-key texinfo-mode-map "\C-c\C-cs" 'texinfo-insert-@samp)
+ (define-key texinfo-mode-map "\C-c\C-co" 'texinfo-insert-@noindent)
+ (define-key texinfo-mode-map "\C-c\C-cn" 'texinfo-insert-@node)
+ (define-key texinfo-mode-map "\C-c\C-ck" 'texinfo-insert-@kbd)
+ (define-key texinfo-mode-map "\C-c\C-ci" 'texinfo-insert-@item)
+ (define-key texinfo-mode-map "\C-c\C-cf" 'texinfo-insert-@file)
+ (define-key texinfo-mode-map "\C-c\C-cx" 'texinfo-insert-@example)
+ (define-key texinfo-mode-map "\C-c\C-ce" 'texinfo-insert-@end)
+ (define-key texinfo-mode-map "\C-c\C-cd" 'texinfo-insert-@dfn)
+ (define-key texinfo-mode-map "\C-c\C-cc" 'texinfo-insert-@code))
+
+
+;;; Texinfo mode
+
+(defvar texinfo-chapter-level-regexp
+ "chapter\\|unnumbered \\|appendix \\|majorheading\\|chapheading"
+ "Regular expression matching Texinfo chapter-level headings.
+This does not match `@node' and does not match the `@top' command.")
+
+;;;###autoload
+(defun texinfo-mode ()
+ "Major mode for editing Texinfo files.
+
+ It has these extra commands:
+\\{texinfo-mode-map}
+
+ These are files that are used as input for TeX to make printed manuals
+and also to be turned into Info files with \\[makeinfo-buffer] or
+the `makeinfo' program. These files must be written in a very restricted and
+modified version of TeX input format.
+
+ Editing commands are like text-mode except that the syntax table is
+set up so expression commands skip Texinfo bracket groups. To see
+what the Info version of a region of the Texinfo file will look like,
+use \\[makeinfo-region], which runs `makeinfo' on the current region.
+
+ You can show the structure of a Texinfo file with \\[texinfo-show-structure].
+This command shows the structure of a Texinfo file by listing the
+lines with the @-sign commands for @chapter, @section, and the like.
+These lines are displayed in another window called the *Occur* window.
+In that window, you can position the cursor over one of the lines and
+use \\[occur-mode-goto-occurrence], to jump to the corresponding spot
+in the Texinfo file.
+
+ In addition, Texinfo mode provides commands that insert various
+frequently used @-sign commands into the buffer. You can use these
+commands to save keystrokes. And you can insert balanced braces with
+\\[texinfo-insert-braces] and later use the command \\[up-list] to
+move forward past the closing brace.
+
+Also, Texinfo mode provides functions for automatically creating or
+updating menus and node pointers. These functions
+
+ * insert the `Next', `Previous' and `Up' pointers of a node,
+ * insert or update the menu for a section, and
+ * create a master menu for a Texinfo source file.
+
+Here are the functions:
+
+ texinfo-update-node \\[texinfo-update-node]
+ texinfo-every-node-update \\[texinfo-every-node-update]
+ texinfo-sequential-node-update
+
+ texinfo-make-menu \\[texinfo-make-menu]
+ texinfo-all-menus-update \\[texinfo-all-menus-update]
+ texinfo-master-menu
+
+ texinfo-indent-menu-description (column &optional region-p)
+
+The `texinfo-column-for-description' variable specifies the column to
+which menu descriptions are indented.
+
+Passed an argument (a prefix argument, if interactive), the
+`texinfo-update-node' and `texinfo-make-menu' functions do their jobs
+in the region.
+
+To use the updating commands, you must structure your Texinfo file
+hierarchically, such that each `@node' line, with the exception of the
+Top node, is accompanied by some kind of section line, such as an
+`@chapter' or `@section' line.
+
+If the file has a `top' node, it must be called `top' or `Top' and
+be the first node in the file.
+
+Entering Texinfo mode calls the value of text-mode-hook, and then the
+value of texinfo-mode-hook."
+ (interactive)
+ (text-mode)
+ (setq mode-name "Texinfo")
+ (setq major-mode 'texinfo-mode)
+ (use-local-map texinfo-mode-map)
+ (set-syntax-table texinfo-mode-syntax-table)
+ (make-local-variable 'page-delimiter)
+ (setq page-delimiter
+ (concat
+ "^@node [ \t]*[Tt]op\\|^@\\("
+ texinfo-chapter-level-regexp
+ "\\)"))
+ (make-local-variable 'require-final-newline)
+ (setq require-final-newline t)
+ (make-local-variable 'indent-tabs-mode)
+ (setq indent-tabs-mode nil)
+ (make-local-variable 'paragraph-separate)
+ (setq paragraph-separate (concat "^\b\\|^@[a-zA-Z]*[ \n]\\|" paragraph-separate))
+ (make-local-variable 'paragraph-start)
+ (setq paragraph-start (concat "^\b\\|^@[a-zA-Z]*[ \n]\\|" paragraph-start))
+ (make-local-variable 'fill-column)
+ (setq fill-column 72)
+ (make-local-variable 'comment-start)
+ (setq comment-start "@c ")
+ (make-local-variable 'comment-start-skip)
+ (setq comment-start-skip "@c +")
+ (make-local-variable 'words-include-escapes)
+ (setq words-include-escapes t)
+ (make-local-variable 'imenu-generic-expression)
+ (setq imenu-generic-expression texinfo-imenu-generic-expression)
+ (make-local-variable 'font-lock-defaults)
+ (setq font-lock-defaults '(texinfo-font-lock-keywords t))
+ (make-local-variable 'outline-regexp)
+ (setq outline-regexp
+ (concat "@\\("
+ (mapconcat 'car texinfo-section-list "\\>\\|")
+ "\\>\\)"))
+ (make-local-variable 'outline-level)
+ (setq outline-level 'texinfo-outline-level)
+ (make-local-variable 'tex-start-of-header)
+ (setq tex-start-of-header "%**start")
+ (make-local-variable 'tex-end-of-header)
+ (setq tex-end-of-header "%**end")
+ (run-hooks 'text-mode-hook 'texinfo-mode-hook))
+
+
+;;; Insert string commands
+
+;; Keep as concatinated lists for ease of maintenance
+(defconst texinfo-environment-regexp
+ (concat
+ "^@"
+ "\\("
+ "cartouche\\|"
+ "display\\|"
+ "end\\|"
+ "enumerate\\|"
+ "example\\|"
+ "f?table\\|"
+ "flushleft\\|"
+ "flushright\\|"
+ "format\\|"
+ "group\\|"
+ "ifhtml\\|"
+ "ifinfo\\|"
+ "iftex\\|"
+ "ignore\\|"
+ "itemize\\|"
+ "lisp\\|"
+ "macro\\|"
+ "multitable\\|"
+ "quotation\\|"
+ "smallexample\\|"
+ "smalllisp\\|"
+ "tex"
+ "\\)")
+ "Regexp for environment-like TexInfo list commands.
+ Subexpression 1 is what goes into the corresponding `@end' statement.")
+
+;; The following texinfo-insert-@end command not only inserts a SPC
+;; after the @end, but tries to find out what belongs there. It is
+;; not very smart: it does not understand nested lists.
+
+(defun texinfo-insert-@end ()
+ "Insert the matching `@end' for the last Texinfo command that needs one."
+ (interactive)
+ (let ((depth 1) string)
+ (save-excursion
+ (while (and (> depth 0)
+ (re-search-backward texinfo-environment-regexp nil t)
+ (if (looking-at "@end")
+ (setq depth (1+ depth))
+ (setq depth (1- depth)))))
+ (looking-at texinfo-environment-regexp)
+ (if (zerop depth)
+ (setq string
+ (buffer-substring (match-beginning 1)
+ (match-end 1)))))
+ (insert "@end ")
+ (if string (insert string "\n"))))
+
+;; The following insert commands accept a prefix arg N, which is the
+;; number of words (actually s-exprs) that should be surrounded by
+;; braces. Thus you can first paste a variable name into a .texinfo
+;; buffer, then say C-u 1 C-c C-c v at the beginning of the just
+;; pasted variable name to put @var{...} *around* the variable name.
+;; Operate on previous word or words with negative arg.
+
+;; These commands use texinfo-insert-@-with-arg
+(defun texinfo-insert-@-with-arg (string &optional arg)
+ (if arg
+ (progn
+ (setq arg (prefix-numeric-value arg))
+ (if (< arg 0)
+ (progn
+ (skip-chars-backward " \t\n\r\f")
+ (save-excursion
+ (forward-sexp arg)
+ (insert "@" string "{"))
+ (insert "}"))
+ (skip-chars-forward " \t\n\r\f")
+ (insert "@" string "{")
+ (forward-sexp arg)
+ (insert "}")))
+ (insert "@" string "{}")
+ (backward-char)))
+
+(defun texinfo-insert-braces ()
+ "Make a pair of braces and be poised to type inside of them.
+Use \\[up-list] to move forward out of the braces."
+ (interactive)
+ (insert "{}")
+ (backward-char))
+
+(defun texinfo-insert-@code (&optional arg)
+ "Insert a `@code{...}' command in a Texinfo buffer.
+A numeric argument says how many words the braces should surround.
+The default is not to surround any existing words with the braces."
+ (interactive "P")
+ (texinfo-insert-@-with-arg "code" arg))
+
+(defun texinfo-insert-@dfn (&optional arg)
+ "Insert a `@dfn{...}' command in a Texinfo buffer.
+A numeric argument says how many words the braces should surround.
+The default is not to surround any existing words with the braces."
+ (interactive "P")
+ (texinfo-insert-@-with-arg "dfn" arg))
+
+(defun texinfo-insert-@example ()
+ "Insert the string `@example' in a Texinfo buffer."
+ (interactive)
+ (insert "@example\n"))
+
+(defun texinfo-insert-@file (&optional arg)
+ "Insert a `@file{...}' command in a Texinfo buffer.
+A numeric argument says how many words the braces should surround.
+The default is not to surround any existing words with the braces."
+ (interactive "P")
+ (texinfo-insert-@-with-arg "file" arg))
+
+(defun texinfo-insert-@item ()
+ "Insert the string `@item' in a Texinfo buffer."
+ (interactive)
+ (insert "@item")
+ (newline))
+
+(defun texinfo-insert-@kbd (&optional arg)
+ "Insert a `@kbd{...}' command in a Texinfo buffer.
+A numeric argument says how many words the braces should surround.
+The default is not to surround any existing words with the braces."
+ (interactive "P")
+ (texinfo-insert-@-with-arg "kbd" arg))
+
+(defun texinfo-insert-@node ()
+ "Insert the string `@node' in a Texinfo buffer.
+This also inserts on the following line a comment indicating
+the order of arguments to @node."
+ (interactive)
+ (insert "@node \n@comment node-name, next, previous, up")
+ (forward-line -1)
+ (forward-char 6))
+
+(defun texinfo-insert-@noindent ()
+ "Insert the string `@noindent' in a Texinfo buffer."
+ (interactive)
+ (insert "@noindent\n"))
+
+(defun texinfo-insert-@samp (&optional arg)
+ "Insert a `@samp{...}' command in a Texinfo buffer.
+A numeric argument says how many words the braces should surround.
+The default is not to surround any existing words with the braces."
+ (interactive "P")
+ (texinfo-insert-@-with-arg "samp" arg))
+
+(defun texinfo-insert-@table (&optional arg)
+ "Insert the string `@table' in a Texinfo buffer."
+ (interactive "P")
+ (insert "@table "))
+
+(defun texinfo-insert-@var (&optional arg)
+ "Insert a `@var{}' command in a Texinfo buffer.
+A numeric argument says how many words the braces should surround.
+The default is not to surround any existing words with the braces."
+ (interactive "P")
+ (texinfo-insert-@-with-arg "var" arg))
+
+;;; Texinfo file structure
+
+;; These are defined in texnfo-upd.el.
+;; texinfo-section-types-regexp
+;; texinfo-section-level-regexp
+;; texinfo-subsection-level-regexp
+;; texinfo-subsubsection-level-regexp
+
+;; `texinfo-show-structure' requires texnfo-upd.el
+(defun texinfo-show-structure (&optional nodes-too)
+ "Show the structure of a Texinfo file.
+List the lines in the file that begin with the @-sign commands for
+@chapter, @section, and the like.
+
+With optional argument (prefix if interactive), list both the lines
+with @-sign commands for @chapter, @section, and the like, and list
+@node lines.
+
+Lines with structuring commands beginning in them are displayed in
+another buffer named `*Occur*'. In that buffer, you can move point to
+one of those lines and then use \\<occur-mode-map>\\[occur-mode-goto-occurrence],
+to jump to the corresponding spot in the Texinfo source file."
+
+ (interactive "P")
+ (require 'texnfo-upd)
+ (save-excursion
+ (goto-char (point-min))
+ (if nodes-too
+ (occur (concat "\\(^@node\\)\\|" texinfo-section-types-regexp))
+ (occur texinfo-section-types-regexp)))
+ (pop-to-buffer "*Occur*")
+ (goto-char (point-min))
+ (flush-lines "-----")
+ ;; Now format the "*Occur*" buffer to show the structure.
+ ;; Thanks to ceder@signum.se (Per Cederqvist)
+ (goto-char (point-max))
+ (let ((margin 5))
+ (while (re-search-backward "^ *[0-9]*:" nil 0)
+ (re-search-forward ":")
+ (setq margin
+ (cond
+ ((looking-at
+ (concat "@\\(" texinfo-chapter-level-regexp "\\)")) 5)
+ ;; ((looking-at "@chapter ") 5)
+ ;; ((looking-at "@unnumbered ") 5)
+ ;; ((looking-at "@appendix ") 5)
+ ;; ((looking-at "@majorheading ") 5)
+ ;; ((looking-at "@chapheading ") 5)
+
+ ((looking-at
+ (concat "@\\(" texinfo-section-level-regexp "\\)")) 9)
+ ;; ((looking-at "@section ") 9)
+ ;; ((looking-at "@unnumberedsec ") 9)
+ ;; ((looking-at "@appendixsec ") 9)
+ ;; ((looking-at "@heading ") 9)
+
+ ((looking-at
+ (concat "@\\(" texinfo-subsection-level-regexp "\\)")) 13)
+ ;; ((looking-at "@subsection ") 13)
+ ;; ((looking-at "@unnumberedsubsec ") 13)
+ ;; ((looking-at "@appendixsubsec ") 13)
+ ;; ((looking-at "@subheading ") 13)
+
+ ((looking-at
+ (concat "@\\(" texinfo-subsubsection-level-regexp "\\)")) 17)
+ ;; ((looking-at "@subsubsection ") 17)
+ ;; ((looking-at "@unnumberedsubsubsec ") 17)
+ ;; ((looking-at "@appendixsubsubsec ") 17)
+ ;; ((looking-at "@subsubheading ") 17)
+ (t margin)))
+ (indent-to-column margin)
+ (beginning-of-line))))
+
+;;; The tex and print function definitions:
+
+(defvar texinfo-texi2dvi-command "texi2dvi"
+ "*Command used by `texinfo-tex-buffer' to run TeX and texindex on a buffer.")
+
+(defvar texinfo-tex-command "tex"
+ "*Command used by `texinfo-tex-region' to run TeX on a region.")
+
+(defvar texinfo-texindex-command "texindex"
+ "*Command used by `texinfo-texindex' to sort unsorted index files.")
+
+(defvar texinfo-delete-from-print-queue-command "lprm"
+ "*Command string used to delete a job from the line printer queue.
+Command is used by \\[texinfo-delete-from-print-queue] based on
+number provided by a previous \\[tex-show-print-queue]
+command.")
+
+(defvar texinfo-tex-trailer "@bye"
+ "String appended after a region sent to TeX by `texinfo-tex-region'.")
+
+(defun texinfo-tex-region (beg end)
+ "Run TeX on the current region.
+This works by writing a temporary file (`tex-zap-file') in the directory
+that is the value of `tex-directory', then running TeX on that file.
+
+The first line of the buffer is copied to the
+temporary file; and if the buffer has a header, it is written to the
+temporary file before the region itself. The buffer's header is all lines
+between the strings defined by `tex-start-of-header' and `tex-end-of-header'
+inclusive. The header must start in the first 100 lines.
+
+The value of `texinfo-tex-trailer' is appended to the temporary file after the region."
+ (interactive "r")
+ (require 'tex-mode)
+ (if (get-buffer "*tex-shell*")
+ (tex-kill-job)
+ (tex-start-shell))
+ (or tex-zap-file (setq tex-zap-file (make-temp-name "#tz")))
+ (let ((tex-out-file (concat tex-zap-file ".tex"))
+ (temp-buffer (get-buffer-create " tex-Output-Buffer"))
+ (zap-directory
+ (file-name-as-directory (expand-file-name tex-directory))))
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (forward-line 100)
+ (let ((search-end (point))
+ (hbeg (point-min)) (hend (point-min))
+ (default-directory zap-directory))
+ (goto-char (point-min))
+
+ ;; Copy first line, the `\input texinfo' line, to temp file
+ (write-region (point)
+ (save-excursion (end-of-line) (point))
+ tex-out-file nil nil)
+
+ ;; Don't copy first line twice if region includes it.
+ (forward-line 1)
+ (if (< beg (point)) (setq beg (point)))
+
+ ;; Initialize the temp file with either the header or nothing
+ (if (search-forward tex-start-of-header search-end t)
+ (progn
+ (beginning-of-line)
+ (setq hbeg (point)) ; Mark beginning of header.
+ (if (search-forward tex-end-of-header nil t)
+ (progn (beginning-of-line)
+ (setq hend (point))) ; Mark end of header.
+ (setq hbeg (point-min))))) ; Else no header.
+
+ ;; Copy header to temp file.
+ (write-region (min hbeg beg) hend tex-out-file t nil)
+
+ ;; Copy region to temp file.
+ (write-region (max beg hend) end tex-out-file t nil))
+
+ ;; This is a kludge to insert the tex-trailer into the tex-out-file.
+ ;; We have to create a special buffer in which to insert
+ ;; the tex-trailer first because there is no function with
+ ;; which to append a literal string directly to a file.
+ (let ((local-tex-trailer texinfo-tex-trailer))
+ (set-buffer temp-buffer)
+ (erase-buffer)
+ ;; make sure trailer isn't hidden by a comment
+ (insert-string "\n")
+ (if local-tex-trailer (insert-string local-tex-trailer))
+ (tex-set-buffer-directory temp-buffer zap-directory)
+ (write-region (point-min) (point-max) tex-out-file t nil))
+
+;;; The following is sufficient in Emacs 19.
+;;; (write-region (concat "\n" texinfo-tex-trailer) nil
+;;; tex-out-file t nil)
+ ))
+
+ (tex-set-buffer-directory "*tex-shell*" zap-directory)
+ (tex-send-command tex-shell-cd-command zap-directory)
+ (tex-send-command texinfo-tex-command tex-out-file)
+ ;; alternatively:
+ ;; (send-string "tex-shell" (concat tex-shell-cd-command " "
+ ;; zap-directory "\n"))
+ ;; (send-string "tex-shell" (concat texinfo-tex-command " "
+ ;; tex-out-file "\n"))
+ (tex-recenter-output-buffer 0)))
+
+(defun texinfo-tex-buffer ()
+ "Run TeX on visited file, once or twice, to make a correct `.dvi' file."
+ (interactive)
+
+ ;; Make sure TeX shell is running.
+ (require 'tex-mode)
+ (if (get-buffer "*tex-shell*")
+ (quit-process (get-process "tex-shell") t)
+ (tex-start-shell))
+
+ (cond ((null buffer-file-name)
+ (error "Buffer not visiting any file!"))
+ ((buffer-modified-p)
+ (error "Buffer has been modified since last saved!")))
+
+ (setq tex-zap-file buffer-file-name)
+
+ (tex-send-command tex-shell-cd-command (file-name-directory tex-zap-file))
+
+ (tex-send-command texinfo-texi2dvi-command tex-zap-file)
+
+ ;; alternatively:
+ ;; (send-string "tex-shell"
+ ;; (concat tex-shell-cd-command
+ ;; " " (file-name-directory tex-zap-file) "\n"))
+ ;; )
+ ;;
+ ;; (send-string "tex-shell"
+ ;; (concat texinfo-texi2dvi-command " " tex-zap-file "\n"))
+
+
+ (tex-recenter-output-buffer 0))
+
+(defun texinfo-texindex ()
+ "Run `texindex' on unsorted index files.
+The index files are made by \\[texinfo-tex-region] or \\[texinfo-tex-buffer].
+This runs the shell command defined by `texinfo-texindex-command'."
+ (interactive)
+ (require 'tex-mode)
+ (tex-send-command texinfo-texindex-command (concat tex-zap-file ".??"))
+ ;; alternatively
+ ;; (send-string "tex-shell"
+ ;; (concat texinfo-texindex-command
+ ;; " " tex-zap-file ".??" "\n"))
+ (tex-recenter-output-buffer nil))
+
+(defun texinfo-tex-print ()
+ "Print `.dvi' file made by \\[texinfo-tex-region] or \\[texinfo-tex-buffer].
+This runs the shell command defined by `tex-dvi-print-command'."
+ (interactive)
+ (require 'tex-mode)
+ (tex-send-command tex-dvi-print-command (concat tex-zap-file ".dvi"))
+ ;; alternatively:
+ ;; (send-string "tex-shell"
+ ;; (concat tex-dvi-print-command
+ ;; " " tex-zap-file ".dvi" "\n"))
+ (tex-recenter-output-buffer nil))
+
+(defun texinfo-quit-job ()
+ "Quit currently running TeX job, by sending an `x' to it."
+ (interactive)
+ (if (not (get-process "tex-shell"))
+ (error "No TeX shell running"))
+ (tex-send-command "x"))
+;; alternatively:
+;; save-excursion
+;; (set-buffer (get-buffer "*tex-shell*"))
+;; (goto-char (point-max))
+;; (insert "x")
+;; (comint-send-input)
+
+(defun texinfo-delete-from-print-queue (job-number)
+ "Delete job from the line printer spooling queue.
+You are prompted for the job number (use a number shown by a previous
+\\[tex-show-print-queue] command)."
+ (interactive "nPrinter job number for deletion: ")
+ (require 'tex-mode)
+ (if (tex-shell-running)
+ (tex-kill-job)
+ (tex-start-shell))
+ (tex-send-command texinfo-delete-from-print-queue-command job-number)
+ ;; alternatively
+ ;; (send-string "tex-shell"
+ ;; (concat
+ ;; texinfo-delete-from-print-queue-command
+ ;; " "
+ ;; job-number"\n"))
+ (tex-recenter-output-buffer nil))
+
+(provide 'texinfo)
+
+;;; texinfo.el ends here
diff --git a/contrib/texinfo/emacs/texnfo-tex.el b/contrib/texinfo/emacs/texnfo-tex.el
new file mode 100644
index 0000000..225ea68
--- /dev/null
+++ b/contrib/texinfo/emacs/texnfo-tex.el
@@ -0,0 +1,346 @@
+;;;; texnfo-tex.el
+
+;;; Texinfo mode TeX and hardcopy printing commands.
+
+;; These commands are for running TeX on a region of a Texinfo file in
+;; GNU Emacs, or on the whole buffer, and for printing the resulting
+;; DVI file.
+
+;;; Version 2.07 22 October 1991
+;;; Robert J. Chassell
+;;; Please send bug reports to: bug-texinfo@prep.ai.mit.edu
+
+;;; Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+
+
+;;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+
+
+;;; The Texinfo mode TeX related commands are:
+
+; texinfo-tex-region to run tex on the current region.
+; texinfo-tex-buffer to run tex on the current buffer.
+; texinfo-texindex to sort unsorted index files.
+; texinfo-tex-print to print the .dvi file made by tex.
+; texinfo-kill-tex-job to kill the currently running tex job.
+; texinfo-recenter-tex-output-buffer to redisplay tex output buffer.
+; texinfo-show-tex-print-queue to show the print queue.
+
+
+;;; Keys common both to Texinfo mode and to TeX shell.
+
+;; Defined in `texinfo.el'
+; (defun texinfo-define-common-keys (keymap)
+; "Define the keys both in Texinfo mode and in the texinfo-tex-shell."
+; (define-key keymap "\C-c\C-t\C-k" 'texinfo-kill-tex-job)
+; (define-key keymap "\C-c\C-t\C-x" 'texinfo-quit-tex-job)
+; (define-key keymap "\C-c\C-t\C-l" 'texinfo-recenter-tex-output-buffer)
+; (define-key keymap "\C-c\C-t\C-d" 'texinfo-delete-from-tex-print-queue)
+; (define-key keymap "\C-c\C-t\C-q" 'texinfo-show-tex-print-queue)
+; (define-key keymap "\C-c\C-t\C-p" 'texinfo-tex-print)
+; (define-key keymap "\C-c\C-t\C-i" 'texinfo-texindex)
+; (define-key keymap "\C-c\C-t\C-r" 'texinfo-tex-region)
+; (define-key keymap "\C-c\C-t\C-b" 'texinfo-tex-buffer))
+
+;; See also texinfo-tex-start-shell.
+;; The following is executed in the `texinfo.el' file
+;(texinfo-define-common-keys texinfo-mode-map)
+
+
+;;; Variable definitions:
+
+(require 'shell)
+
+(defvar texinfo-tex-shell-cd-command "cd"
+ "Command to give to shell running TeX to change directory.")
+
+(defvar texinfo-tex-command "tex"
+ "*Command used by texinfo-tex-region to run tex on a region.")
+
+(defvar texinfo-texindex-command "texindex"
+ "*Command used by texinfo-texindex to sort unsorted index files.")
+
+(defvar texinfo-tex-dvi-print-command "lpr -d"
+ "*Command string used by \\[tex-print] to print a .dvi file.")
+
+(defvar texinfo-show-tex-queue-command "lpq"
+ "*Command string used to show the Texinfo TeX print queue.
+Command is used by \\[texinfo-show-tex-print-queue] and it
+should show the queue that \\[texinfo-tex-print] puts jobs on.")
+
+(defvar texinfo-delete-from-print-queue-command "lprm"
+ "*Command string used to delete a job from the line printer queue.
+Command is used by \\[texinfo-delete-from-tex-print-queue] based on
+number provided by a previous \\[texinfo-show-tex-print-queue]
+command.")
+
+(defvar texinfo-tex-trailer "@bye"
+ "String appended after a region sent to TeX by texinfo-tex-region.")
+
+(defvar texinfo-tex-original-file ""
+ "Original name of file on which to run TeX.")
+
+(defvar texinfo-tex-temp-file nil
+ "Temporary file name used for text being sent as input to TeX.")
+
+(defvar texinfo-tex-root-temp-file nil
+ "Temporary file name used for text being sent as input to TeX.")
+
+
+;;; Texinfo TeX main functions
+
+(defun texinfo-tex-region (beginning end)
+ "Run tex on the current region.
+
+A temporary file is written in the default directory, and tex is run
+in that directory. The first line of the file is copied to the
+temporary file; and if the buffer has a header, it is written to the
+temporary file before the region itself. The buffer's header is all
+lines between the strings defined by texinfo-start-of-header and
+texinfo-end-of-header inclusive. The header must start in the first 100
+lines. The value of texinfo-tex-trailer is appended to the temporary file
+after the region."
+
+ (interactive "r")
+ (if (get-buffer "*texinfo-tex-shell*")
+ (quit-process (get-process "texinfo-tex-shell") t)
+ (texinfo-tex-start-shell))
+
+ (setq texinfo-tex-root-temp-file
+ (expand-file-name
+ (make-temp-name
+ (prin1-to-string (read (buffer-name))))))
+
+ (let ((texinfo-tex-temp-file (concat texinfo-tex-root-temp-file ".tex")))
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (forward-line 100)
+ (let ((search-end (point))
+ (header-beginning (point-min)) (header-end (point-min)))
+ (goto-char (point-min))
+ ;; Copy first line, the `\input texinfo' line, to temp file
+ (write-region (point)
+ (save-excursion (forward-line 1) (point))
+ texinfo-tex-temp-file nil nil)
+ ;; Don't copy first line twice if region includes it.
+ (forward-line 1)
+ (if (< beginning (point)) (setq beginning (point)))
+ ;; Initialize the temp file with either the header or nothing
+ (if (search-forward texinfo-start-of-header search-end t)
+ (progn
+ (beginning-of-line)
+ (setq header-beginning (point)) ; Mark beginning of header.
+ (if (search-forward texinfo-end-of-header nil t)
+ (progn (beginning-of-line)
+ (setq header-end (point))) ; Mark end of header.
+ (setq header-beginning (point-min))))) ; Else no header.
+ ;; Copy header to temp file.
+ (write-region
+ (min header-beginning beginning )
+ header-end
+ texinfo-tex-temp-file t nil)
+ ;; Copy region to temp file.
+ (write-region
+ (max beginning header-end)
+ end
+ texinfo-tex-temp-file t nil)
+ ;; This is a kludge to insert the texinfo-tex-trailer into the
+ ;; texinfo-tex-temp-file. We have to create a special buffer
+ ;; in which to insert the texinfo-tex-trailer first because there is
+ ;; no function with which to append a literal string directly
+ ;; to a file.
+ (let ((local-tex-trailer texinfo-tex-trailer)
+ (temp-buffer (get-buffer-create " texinfo-trailer-buffer")))
+ (set-buffer temp-buffer)
+ (erase-buffer)
+ ;; make sure trailer isn't hidden by a comment
+ (insert-string "\n")
+ (if local-tex-trailer (insert local-tex-trailer))
+ (write-region (point-min) (point-max)
+ texinfo-tex-temp-file t nil)))
+ (set-process-sentinel (get-process "texinfo-tex-shell")
+ 'texinfo-tex-shell-sentinel)
+ (send-string "texinfo-tex-shell"
+ (concat texinfo-tex-shell-cd-command " "
+ default-directory "\n"))
+ (send-string "texinfo-tex-shell"
+ (concat texinfo-tex-command " "
+ texinfo-tex-temp-file "\n "))
+ (texinfo-recenter-tex-output-buffer 0)))))
+
+(defun texinfo-tex-buffer (buffer)
+ "Run TeX on current buffer.
+After running TeX the first time, you may have to run \\[texinfo-texindex]
+and then \\[texinfo-tex-buffer] again."
+ (interactive
+ (list
+ ;; Sometimes you put point into *texinfo-tex-shell*; this prompts
+ ;; you for the correct file regardless.
+ (if (and
+ (string= (buffer-name (current-buffer)) "*texinfo-tex-shell*")
+ texinfo-tex-root-temp-file)
+ (read-string (format "Run TeX on: ")
+ texinfo-tex-original-file)
+ (read-string (format "Run TeX on: ") (buffer-name (current-buffer))))))
+
+ ;; Set to original buffer if in *texinfo-tex-shell*; otherwise,
+ ;; record name of current buffer.
+ (if (string= (buffer-name (current-buffer)) "*texinfo-tex-shell*")
+ (set-buffer buffer)
+ (setq texinfo-tex-original-file
+ (buffer-name (current-buffer))))
+
+ (if (get-buffer "*texinfo-tex-shell*")
+ (quit-process (get-process "texinfo-tex-shell") t)
+ (texinfo-tex-start-shell))
+ (cond ((null buffer-file-name)
+ (error "Buffer not visiting any file!"))
+ ((buffer-modified-p)
+ (error "Buffer has been modified since last saved!"))
+ (t (set-process-sentinel (get-process "texinfo-tex-shell")
+ 'texinfo-tex-shell-sentinel)
+ (send-string "texinfo-tex-shell"
+ (concat texinfo-tex-shell-cd-command
+ " "
+ (file-name-directory
+ (buffer-file-name
+ (get-buffer buffer)))
+ "\n"))
+ (send-string "texinfo-tex-shell"
+ (concat texinfo-tex-command " " buffer "\n "))
+
+ ;; so the texinfo-tex-print command works
+ (setq texinfo-tex-root-temp-file
+ (substring buffer 0
+ (or (string-match "\\.tex" buffer)
+ (length buffer))))
+
+ (texinfo-recenter-tex-output-buffer 0))))
+
+(defun texinfo-texindex ()
+ "Run texindex on unsorted index files.
+The index files are made by \\[texinfo-tex-region] or \\[texinfo-tex-buffer].
+Runs the shell command defined by texinfo-texindex-command."
+ (interactive)
+ (send-string "texinfo-tex-shell"
+ (concat texinfo-texindex-command
+ " " texinfo-tex-root-temp-file ".??" "\n"))
+ (texinfo-recenter-tex-output-buffer nil))
+
+(defun texinfo-tex-print ()
+ "Print .dvi file made by \\[texinfo-tex-region] or \\[texinfo-tex-buffer].
+Runs the shell command defined by texinfo-tex-dvi-print-command."
+ (interactive)
+ (send-string "texinfo-tex-shell"
+ (concat texinfo-tex-dvi-print-command
+ " " texinfo-tex-root-temp-file ".dvi" "\n"))
+ (texinfo-recenter-tex-output-buffer nil))
+
+
+;;; Texinfo TeX utility functions
+
+(defun texinfo-tex-start-shell ()
+ (save-excursion
+ (require 'texinfo)
+ (set-buffer (make-shell "texinfo-tex-shell" "/bin/sh" nil "-v"))
+ (setq texinfo-tex-shell-map (copy-keymap shell-mode-map))
+ (texinfo-define-common-keys texinfo-tex-shell-map)
+ (use-local-map texinfo-tex-shell-map)
+ (run-hooks 'texinfo-tex-shell-hook)
+ (if (zerop (buffer-size))
+ (sleep-for 1))))
+
+(defun texinfo-quit-tex-job ()
+ "Quit currently running TeX job, by sending an `x' to it."
+ (interactive)
+ (if (not (get-process "texinfo-tex-shell"))
+ (error "No TeX shell running."))
+ (save-excursion
+ (set-buffer (get-buffer "*texinfo-tex-shell*"))
+ (goto-char (point-max))
+ (insert "x")
+ (shell-send-input)))
+
+(defun texinfo-kill-tex-job ()
+ "Kill the currently running TeX job."
+ (interactive)
+ (if (get-process "texinfo-tex-shell")
+ ;; Use `texinfo-tex-shell-sentinel' to restart
+ ;; texinfo-tex-shell after it is killed.
+ (kill-process (get-process "texinfo-tex-shell"))))
+
+(defun texinfo-tex-shell-sentinel (process event)
+ "Restart texinfo-tex-shell after it is killed."
+ (if (equal event "killed\n")
+ (save-excursion
+ (set-buffer "*texinfo-tex-shell*")
+ (insert "\n")
+ (texinfo-tex-start-shell))))
+
+(defun texinfo-recenter-tex-output-buffer (linenum)
+ "Redisplay buffer of TeX job output so that most recent output can be seen.
+The last line of the buffer is displayed on
+line LINE of the window, or centered if LINE is nil."
+ (interactive "P")
+ (let ((texinfo-tex-shell (get-buffer "*texinfo-tex-shell*"))
+ (old-buffer (current-buffer)))
+ (if (null texinfo-tex-shell)
+ (message "No TeX output buffer")
+ (pop-to-buffer texinfo-tex-shell)
+ (bury-buffer texinfo-tex-shell)
+ (goto-char (point-max))
+ (recenter (if linenum
+ (prefix-numeric-value linenum)
+ (/ (window-height) 2)))
+ (pop-to-buffer old-buffer)
+ )))
+
+(defun texinfo-show-tex-print-queue ()
+ "Show the print queue that \\[texinfo-tex-print] put your job on.
+Runs the shell command defined by texinfo-show-tex-queue-command."
+ (interactive)
+ (if (not (texinfo-tex-shell-running-p))
+ (texinfo-tex-start-shell))
+ (send-string "texinfo-tex-shell"
+ (concat texinfo-show-tex-queue-command "\n"))
+ (texinfo-recenter-tex-output-buffer nil))
+
+(defun texinfo-delete-from-tex-print-queue (job-number)
+ "Delete job from the line printer spooling queue.
+You are prompted for the job number (shown by a previous
+\\[texinfo-show-tex-print-queue] command."
+ (interactive "nPrinter job number for deletion: ")
+ (if (texinfo-tex-shell-running-p)
+ (texinfo-kill-tex-job)
+ (texinfo-tex-start-shell))
+ (send-string "texinfo-tex-shell"
+ (concat
+ texinfo-delete-from-print-queue-command
+ " "
+ job-number"\n"))
+ (texinfo-recenter-tex-output-buffer nil))
+
+(defun texinfo-tex-shell-running-p ()
+ (and (get-process "texinfo-tex-shell")
+ (eq (process-status (get-process "texinfo-tex-shell")) 'run)))
+
+
+;;; Place `provide' at end of file.
+(provide 'texnfo-tex)
+;;;;;;;;;;;;;;;; end texnfo-tex.el ;;;;;;;;;;;;;;;;
diff --git a/contrib/texinfo/emacs/texnfo-upd.el b/contrib/texinfo/emacs/texnfo-upd.el
new file mode 100644
index 0000000..4827fe5
--- /dev/null
+++ b/contrib/texinfo/emacs/texnfo-upd.el
@@ -0,0 +1,2058 @@
+;;; texnfo-upd.el --- utilities for updating nodes and menus in Texinfo files
+
+;; Copyright 1989, 1990, 1991, 1992, 1996 Free Software Foundation, Inc.
+
+;; Author: Robert J. Chassell
+;; Date: 12 Sep 1996
+;; Maintainer: Robert J. Chassell <bug-texinfo@prep.ai.mit.edu>
+;; Keywords: maint, tex, docs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Known bug: update commands fail to ignore @ignore.
+
+;; Summary: how to use the updating commands
+
+;; The node and menu updating functions automatically
+
+;; * insert missing `@node' lines,
+;; * insert the `Next', `Previous' and `Up' pointers of a node,
+;; * insert or update the menu for a section,
+;; * create a master menu for a Texinfo source file.
+;;
+;; Passed an argument, the `texinfo-update-node' and
+;; `texinfo-make-menu' functions do their jobs in the region.
+;;
+;; In brief, the functions for creating or updating nodes and menus, are:
+;;
+;; texinfo-update-node (&optional region-p)
+;; texinfo-every-node-update ()
+;; texinfo-sequential-node-update (&optional region-p)
+;;
+;; texinfo-make-menu (&optional region-p)
+;; texinfo-all-menus-update ()
+;; texinfo-master-menu ()
+;;
+;; texinfo-insert-node-lines (&optional title-p)
+;;
+;; texinfo-indent-menu-description (column &optional region-p)
+
+;; The `texinfo-column-for-description' variable specifies the column to
+;; which menu descriptions are indented.
+
+;; Texinfo file structure
+;; ----------------------
+
+;; To use the updating commands, you must structure your Texinfo file
+;; hierarchically. Each `@node' line, with the exception of the top
+;; node, must be accompanied by some kind of section line, such as an
+;; `@chapter' or `@section' line. Each node-line/section-line
+;; combination must look like this:
+
+;; @node Lists and Tables, Cross References, Structuring, Top
+;; @comment node-name, next, previous, up
+;; @chapter Making Lists and Tables
+
+;; or like this (without the `@comment' line):
+
+;; @node Lists and Tables, Cross References, Structuring, Top
+;; @chapter Making Lists and Tables
+
+;; If the file has a `top' node, it must be called `top' or `Top' and
+;; be the first node in the file.
+
+
+;;; The update node functions described in detail
+
+;; The `texinfo-update-node' function without an argument inserts
+;; the correct next, previous and up pointers for the node in which
+;; point is located (i.e., for the node preceding point).
+
+;; With an argument, the `texinfo-update-node' function inserts the
+;; correct next, previous and up pointers for the nodes inside the
+;; region.
+
+;; It does not matter whether the `@node' line has pre-existing
+;; `Next', `Previous', or `Up' pointers in it. They are removed.
+
+;; The `texinfo-every-node-update' function runs `texinfo-update-node'
+;; on the whole buffer.
+
+;; The `texinfo-sequential-node-update' function inserts the
+;; immediately following and preceding node into the `Next' or
+;; `Previous' pointers regardless of their hierarchical level. This is
+;; only useful for certain kinds of text, like a novel, which you go
+;; through sequentially.
+
+
+;;; The menu making functions described in detail
+
+;; The `texinfo-make-menu' function without an argument creates or
+;; updates a menu for the section encompassing the node that follows
+;; point. With an argument, it makes or updates menus for the nodes
+;; within or part of the marked region.
+
+;; Whenever an existing menu is updated, the descriptions from
+;; that menu are incorporated into the new menu. This is done by copying
+;; descriptions from the existing menu to the entries in the new menu
+;; that have the same node names. If the node names are different, the
+;; descriptions are not copied to the new menu.
+
+;; Menu entries that refer to other Info files are removed since they
+;; are not a node within current buffer. This is a deficiency.
+
+;; The `texinfo-all-menus-update' function runs `texinfo-make-menu'
+;; on the whole buffer.
+
+;; The `texinfo-master-menu' function creates an extended menu located
+;; after the top node. (The file must have a top node.) The function
+;; first updates all the regular menus in the buffer (incorporating the
+;; descriptions from pre-existing menus), and then constructs a master
+;; menu that includes every entry from every other menu. (However, the
+;; function cannot update an already existing master menu; if one
+;; exists, it must be removed before calling the function.)
+
+;; The `texinfo-indent-menu-description' function indents every
+;; description in the menu following point, to the specified column.
+;; Non-nil argument (prefix, if interactive) means indent every
+;; description in every menu in the region. This function does not
+;; indent second and subsequent lines of a multi-line description.
+
+;; The `texinfo-insert-node-lines' function inserts `@node' before the
+;; `@chapter', `@section', and such like lines of a region in a Texinfo
+;; file where the `@node' lines are missing.
+;;
+;; With a non-nil argument (prefix, if interactive), the function not
+;; only inserts `@node' lines but also inserts the chapter or section
+;; titles as the names of the corresponding nodes; and inserts titles
+;; as node names in pre-existing `@node' lines that lack names.
+;;
+;; Since node names should be more concise than section or chapter
+;; titles, node names so inserted will need to be edited manually.
+
+
+;;; Code:
+
+;;; The menu making functions
+
+(defun texinfo-make-menu (&optional region-p)
+ "Without any prefix argument, make or update a menu.
+Make the menu for the section enclosing the node found following point.
+
+Non-nil argument (prefix, if interactive) means make or update menus
+for nodes within or part of the marked region.
+
+Whenever a menu exists, and is being updated, the descriptions that
+are associated with node names in the pre-existing menu are
+incorporated into the new menu. Otherwise, the nodes' section titles
+are inserted as descriptions."
+
+ (interactive "P")
+ (if (not region-p)
+ (let ((level (texinfo-hierarchic-level)))
+ (texinfo-make-one-menu level)
+ (message "Done...updated the menu. You may save the buffer."))
+ ;; else
+ (message "Making or updating menus in %s... " (buffer-name))
+ (let ((beginning (region-beginning))
+ (region-end (region-end))
+ (level (progn ; find section type following point
+ (goto-char (region-beginning))
+ (texinfo-hierarchic-level))))
+ (if (= region-end beginning)
+ (error "Please mark a region!"))
+ (save-excursion
+ (save-restriction
+ (widen)
+
+ (while (texinfo-find-lower-level-node level region-end)
+ (setq level (texinfo-hierarchic-level)) ; new, lower level
+ (texinfo-make-one-menu level))
+
+ (while (and (< (point) region-end)
+ (texinfo-find-higher-level-node level region-end))
+ (setq level (texinfo-hierarchic-level))
+ (while (texinfo-find-lower-level-node level region-end)
+ (setq level (texinfo-hierarchic-level)) ; new, lower level
+ (texinfo-make-one-menu level))))))
+ (message "Done...updated menus. You may save the buffer.")))
+
+(defun texinfo-make-one-menu (level)
+ "Make a menu of all the appropriate nodes in this section.
+`Appropriate nodes' are those associated with sections that are
+at the level specified by LEVEL. Point is left at the end of menu."
+ (let*
+ ((case-fold-search t)
+ (beginning
+ (save-excursion
+ (goto-char (texinfo-update-menu-region-beginning level))
+ (end-of-line)
+ (point)))
+ (end (texinfo-update-menu-region-end level))
+ (first (texinfo-menu-first-node beginning end))
+ (node-name (progn
+ (goto-char beginning)
+ (beginning-of-line)
+ (texinfo-copy-node-name)))
+ (new-menu-list (texinfo-make-menu-list beginning end level)))
+ (if (texinfo-old-menu-p beginning first)
+ (progn
+ (texinfo-incorporate-descriptions new-menu-list)
+ (texinfo-incorporate-menu-entry-names new-menu-list)
+ (texinfo-delete-old-menu beginning first)))
+ (texinfo-insert-menu new-menu-list node-name)))
+
+(defun texinfo-all-menus-update (&optional update-all-nodes-p)
+ "Update every regular menu in a Texinfo file.
+Update pre-existing master menu, if there is one.
+
+If called with a non-nil argument, this function first updates all the
+nodes in the buffer before updating the menus."
+ (interactive "P")
+ (let ((case-fold-search t)
+ master-menu-p)
+ (save-excursion
+ (push-mark (point-max) t)
+ (goto-char (point-min))
+ (message "Checking for a master menu in %s ... "(buffer-name))
+ (save-excursion
+ (if (re-search-forward texinfo-master-menu-header nil t)
+ ;; Remove detailed master menu listing
+ (progn
+ (setq master-menu-p t)
+ (goto-char (match-beginning 0))
+ (let ((end-of-detailed-menu-descriptions
+ (save-excursion ; beginning of end menu line
+ (goto-char (texinfo-menu-end))
+ (beginning-of-line) (forward-char -1)
+ (point))))
+ (delete-region (point) end-of-detailed-menu-descriptions)))))
+
+ (if update-all-nodes-p
+ (progn
+ (message "Updating all nodes in %s ... " (buffer-name))
+ (sleep-for 2)
+ (push-mark (point-max) t)
+ (goto-char (point-min))
+ ;; Using the mark to pass bounds this way
+ ;; is kludgy, but it's not worth fixing. -- rms.
+ (let ((mark-active t))
+ (texinfo-update-node t))))
+
+ (message "Updating all menus in %s ... " (buffer-name))
+ (sleep-for 2)
+ (push-mark (point-max) t)
+ (goto-char (point-min))
+ ;; Using the mark to pass bounds this way
+ ;; is kludgy, but it's not worth fixing. -- rms.
+ (let ((mark-active t))
+ (texinfo-make-menu t))
+
+ (if master-menu-p
+ (progn
+ (message "Updating the master menu in %s... " (buffer-name))
+ (sleep-for 2)
+ (texinfo-master-menu nil))))
+
+ (message "Done...updated all the menus. You may save the buffer.")))
+
+(defun texinfo-find-lower-level-node (level region-end)
+ "Search forward from point for node at any level lower than LEVEL.
+Search is limited to the end of the marked region, REGION-END,
+and to the end of the menu region for the level.
+
+Return t if the node is found, else nil. Leave point at the beginning
+of the node if one is found; else do not move point."
+ (let ((case-fold-search t))
+ (if (and (< (point) region-end)
+ (re-search-forward
+ (concat
+ "\\(^@node\\).*\n" ; match node line
+ "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any
+ "\\|" ; or
+ "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any
+ (eval (cdr (assoc level texinfo-update-menu-lower-regexps))))
+ ;; the next higher level node marks the end of this
+ ;; section, and no lower level node will be found beyond
+ ;; this position even if region-end is farther off
+ (texinfo-update-menu-region-end level)
+ t))
+ (goto-char (match-beginning 1)))))
+
+(defun texinfo-find-higher-level-node (level region-end)
+ "Search forward from point for node at any higher level than argument LEVEL.
+Search is limited to the end of the marked region, REGION-END.
+
+Return t if the node is found, else nil. Leave point at the beginning
+of the node if one is found; else do not move point."
+ (let ((case-fold-search t))
+ (cond
+ ((or (string-equal "top" level) (string-equal "chapter" level))
+ (if (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" region-end t)
+ (progn (beginning-of-line) t)))
+ (t
+ (if (re-search-forward
+ (concat
+ "\\(^@node\\).*\n" ; match node line
+ "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any
+ "\\|" ; or
+ "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any
+ (eval (cdr (assoc level texinfo-update-menu-higher-regexps))))
+ region-end t)
+ (progn (beginning-of-line) t))))))
+
+
+;;; Making the list of new menu entries
+
+(defun texinfo-make-menu-list (beginning end level)
+ "Make a list of node names and their descriptions.
+Point is left at the end of the menu region, but the menu is not inserted.
+
+First argument is position from which to start making menu list;
+second argument is end of region in which to try to locate entries;
+third argument is the level of the nodes that are the entries.
+
+Node names and descriptions are dotted pairs of strings. Each pair is
+an element of the list. If the description does not exist, the
+element consists only of the node name."
+ (goto-char beginning)
+ (let (new-menu-list)
+ (while (texinfo-menu-locate-entry-p level end)
+ (setq new-menu-list
+ (cons (cons
+ (texinfo-copy-node-name)
+ (prog1 "" (forward-line 1)))
+ ;; Use following to insert section titles automatically.
+ ;; (texinfo-copy-section-title))
+ new-menu-list)))
+ (reverse new-menu-list)))
+
+(defun texinfo-menu-locate-entry-p (level search-end)
+ "Find a node that will be part of menu for this section.
+First argument is a string such as \"section\" specifying the general
+hierarchical level of the menu; second argument is a position
+specifying the end of the search.
+
+The function returns t if the node is found, else nil. It searches
+forward from point, and leaves point at the beginning of the node.
+
+The function finds entries of the same type. Thus `subsections' and
+`unnumberedsubsecs' will appear in the same menu."
+ (let ((case-fold-search t))
+ (if (re-search-forward
+ (concat
+ "\\(^@node\\).*\n" ; match node line
+ "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any
+ "\\|" ; or
+ "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any
+ (eval
+ (cdr (assoc level texinfo-update-menu-same-level-regexps))))
+ search-end
+ t)
+ (goto-char (match-beginning 1)))))
+
+(defun texinfo-copy-node-name ()
+ "Return the node name as a string.
+
+Start with point at the beginning of the node line; copy the text
+after the node command up to the first comma on the line, if any, and
+return the text as a string. Leaves point at the beginning of the
+line. If there is no node name, returns an empty string."
+
+ (save-excursion
+ (buffer-substring
+ (progn (forward-word 1) ; skip over node command
+ (skip-chars-forward " \t") ; and over spaces
+ (point))
+ (if (search-forward
+ ","
+ (save-excursion (end-of-line) (point)) t) ; bound search
+ (1- (point))
+ (end-of-line) (point)))))
+
+(defun texinfo-copy-section-title ()
+ "Return the title of the section as a string.
+The title is used as a description line in the menu when one does not
+already exist.
+
+Move point to the beginning of the appropriate section line by going
+to the start of the text matched by last regexp searched for, which
+must have been done by `texinfo-menu-locate-entry-p'."
+
+ ;; could use the same re-search as in `texinfo-menu-locate-entry-p'
+ ;; instead of using `match-beginning'; such a variation would be
+ ;; more general, but would waste information already collected
+
+ (goto-char (match-beginning 7)) ; match section name
+
+ (buffer-substring
+ (progn (forward-word 1) ; skip over section type
+ (skip-chars-forward " \t") ; and over spaces
+ (point))
+ (progn (end-of-line) (point))))
+
+
+;;; Handling the old menu
+
+(defun texinfo-old-menu-p (beginning first)
+ "Move point to the beginning of the menu for this section, if any.
+Otherwise move point to the end of the first node of this section.
+Return t if a menu is found, nil otherwise.
+
+First argument is the position of the beginning of the section in which
+the menu will be located; second argument is the position of the first
+node within the section.
+
+If no menu is found, the function inserts two newlines just before the
+end of the section, and leaves point there where a menu ought to be."
+ (goto-char beginning)
+ (if (not (re-search-forward "^@menu" first 'goto-end))
+ (progn (insert "\n\n") (forward-line -2) nil)
+ t))
+
+(defun texinfo-incorporate-descriptions (new-menu-list)
+ "Copy the old menu line descriptions that exist to the new menu.
+
+Point must be at beginning of old menu.
+
+If the node-name of the new menu is found in the old menu, insert the
+old description into the new entry.
+
+For this function, the new menu is a list made up of lists of dotted
+pairs in which the first element of the pair is the node name and the
+second element the description. The new menu is changed destructively.
+The old menu is the menu as it appears in the texinfo file."
+
+ (let ((new-menu-list-pointer new-menu-list)
+ (end-of-menu (texinfo-menu-end)))
+ (while new-menu-list
+ (save-excursion ; keep point at beginning of menu
+ (if (re-search-forward
+ ;; Existing nodes can have the form
+ ;; * NODE NAME:: DESCRIPTION
+ ;; or
+ ;; * MENU ITEM: NODE NAME. DESCRIPTION.
+ ;;
+ ;; Recognize both when looking for the description.
+ (concat "\\* \\(" ; so only menu entries are found
+ (car (car new-menu-list)) "::"
+ "\\|"
+ ".*: " (car (car new-menu-list)) "[.,\t\n]"
+ "\\)"
+ ) ; so only complete entries are found
+ end-of-menu
+ t)
+ (setcdr (car new-menu-list)
+ (texinfo-menu-copy-old-description end-of-menu))))
+ (setq new-menu-list (cdr new-menu-list)))
+ (setq new-menu-list new-menu-list-pointer)))
+
+(defun texinfo-incorporate-menu-entry-names (new-menu-list)
+ "Copy any old menu entry names to the new menu.
+
+Point must be at beginning of old menu.
+
+If the node-name of the new menu entry cannot be found in the old
+menu, do nothing.
+
+For this function, the new menu is a list made up of lists of dotted
+pairs in which the first element of the pair is the node name and the
+second element is the description (or nil).
+
+If we find an existing menu entry name, we change the first element of
+the pair to be another dotted pair in which the car is the menu entry
+name and the cdr is the node name.
+
+NEW-MENU-LIST is changed destructively. The old menu is the menu as it
+appears in the texinfo file."
+
+ (let ((new-menu-list-pointer new-menu-list)
+ (end-of-menu (texinfo-menu-end)))
+ (while new-menu-list
+ (save-excursion ; keep point at beginning of menu
+ (if (re-search-forward
+ ;; Existing nodes can have the form
+ ;; * NODE NAME:: DESCRIPTION
+ ;; or
+ ;; * MENU ITEM: NODE NAME. DESCRIPTION.
+ ;;
+ ;; We're interested in the second case.
+ (concat "\\* " ; so only menu entries are found
+ "\\(.*\\): " (car (car new-menu-list)) "[.,\t\n]")
+ end-of-menu
+ t)
+ (setcar
+ (car new-menu-list) ; replace the node name
+ (cons (buffer-substring (match-beginning 1) (match-end 1))
+ (car (car new-menu-list)))))
+ (setq new-menu-list (cdr new-menu-list))))
+ (setq new-menu-list new-menu-list-pointer)))
+
+(defun texinfo-menu-copy-old-description (end-of-menu)
+ "Return description field of old menu line as string.
+Point must be located just after the node name. Point left before description.
+Single argument, END-OF-MENU, is position limiting search."
+ (skip-chars-forward "[:.,\t\n ]+")
+ ;; don't copy a carriage return at line beginning with asterisk!
+ ;; do copy a description that begins with an `@'!
+ ;; !! Known bug: does not copy descriptions starting with ^|\{?* etc.
+ (if (and (looking-at "\\(\\w+\\|@\\)")
+ (not (looking-at "\\(^\\* \\|^@end menu\\)")))
+ (buffer-substring
+ (point)
+ (save-excursion
+ (re-search-forward "\\(^\\* \\|^@end menu\\)" end-of-menu t)
+ (forward-line -1)
+ (end-of-line) ; go to end of last description line
+ (point)))
+ ""))
+
+(defun texinfo-menu-end ()
+ "Return position of end of menu. Does not change location of point.
+Signal an error if not end of menu."
+ (save-excursion
+ (if (re-search-forward "^@end menu" nil t)
+ (point)
+ (error "Menu does not have an end."))))
+
+(defun texinfo-delete-old-menu (beginning first)
+ "Delete the old menu. Point must be in or after menu.
+First argument is position of the beginning of the section in which
+the menu will be located; second argument is the position of the first
+node within the section."
+ ;; No third arg to search, so error if search fails.
+ (re-search-backward "^@menu" beginning)
+ (delete-region (point)
+ (save-excursion
+ (re-search-forward "^@end menu" first)
+ (point))))
+
+
+;;; Inserting new menu
+
+;; try 32, but perhaps 24 is better
+(defvar texinfo-column-for-description 32
+ "*Column at which descriptions start in a Texinfo menu.")
+
+(defun texinfo-insert-menu (menu-list node-name)
+ "Insert formatted menu at point.
+Indents the first line of the description, if any, to the value of
+texinfo-column-for-description.
+
+MENU-LIST has form:
+
+ \(\(\"node-name1\" . \"description\"\)
+ \(\"node-name2\" . \"description\"\) ... \)
+
+However, the description field might be nil.
+
+Also, the node-name field might itself be a dotted pair (call it P) of
+strings instead of just a string. In that case, the car of P
+is the menu entry name, and the cdr of P is the node name."
+
+ (insert "@menu\n")
+ (while menu-list
+ ;; Every menu entry starts with a star and a space.
+ (insert "* ")
+
+ ;; Insert the node name (and menu entry name, if present).
+ (let ((node-part (car (car menu-list))))
+ (if (stringp node-part)
+ ;; "Double colon" entry line; menu entry and node name are the same,
+ (insert (format "%s::" node-part))
+ ;; "Single colon" entry line; menu entry and node name are different.
+ (insert (format "%s: %s." (car node-part) (cdr node-part)))))
+
+ ;; Insert the description, if present.
+ (if (cdr (car menu-list))
+ (progn
+ ;; Move to right place.
+ (indent-to texinfo-column-for-description 2)
+ ;; Insert description.
+ (insert (format "%s" (cdr (car menu-list))))))
+
+ (insert "\n") ; end this menu entry
+ (setq menu-list (cdr menu-list)))
+ (insert "@end menu")
+ (message
+ "Updated \"%s\" level menu following node: %s ... " level node-name))
+
+
+;;; Starting menu descriptions by inserting titles
+
+(defun texinfo-start-menu-description ()
+ "In this menu entry, insert the node's section title as a description.
+Position point at beginning of description ready for editing.
+Do not insert a title if the line contains an existing description.
+
+You will need to edit the inserted text since a useful description
+complements the node name rather than repeats it as a title does."
+
+ (interactive)
+ (let (beginning end node-name title)
+ (save-excursion
+ (beginning-of-line)
+ (if (search-forward "* " (save-excursion (end-of-line) (point)) t)
+ (progn (skip-chars-forward " \t")
+ (setq beginning (point)))
+ (error "This is not a line in a menu!"))
+
+ (cond
+ ;; "Double colon" entry line; menu entry and node name are the same,
+ ((search-forward "::" (save-excursion (end-of-line) (point)) t)
+ (if (looking-at "[ \t]*[^ \t\n]+")
+ (error "Descriptive text already exists."))
+ (skip-chars-backward ": \t")
+ (setq node-name (buffer-substring beginning (point))))
+
+ ;; "Single colon" entry line; menu entry and node name are different.
+ ((search-forward ":" (save-excursion (end-of-line) (point)) t)
+ (skip-chars-forward " \t")
+ (setq beginning (point))
+ ;; Menu entry line ends in a period, comma, or tab.
+ (if (re-search-forward "[.,\t]"
+ (save-excursion (forward-line 1) (point)) t)
+ (progn
+ (if (looking-at "[ \t]*[^ \t\n]+")
+ (error "Descriptive text already exists."))
+ (skip-chars-backward "., \t")
+ (setq node-name (buffer-substring beginning (point))))
+ ;; Menu entry line ends in a return.
+ (re-search-forward ".*\n"
+ (save-excursion (forward-line 1) (point)) t)
+ (skip-chars-backward " \t\n")
+ (setq node-name (buffer-substring beginning (point)))
+ (if (= 0 (length node-name))
+ (error "No node name on this line.")
+ (insert "."))))
+ (t (error "No node name on this line.")))
+ ;; Search for node that matches node name, and copy the section title.
+ (if (re-search-forward
+ (concat
+ "^@node[ \t]+"
+ node-name
+ ".*\n" ; match node line
+ "\\("
+ "\\(\\(^@c \\|^@comment\\).*\n\\)" ; match comment line, if any
+ "\\|" ; or
+ "\\(^@ifinfo[ ]*\n\\)" ; ifinfo line, if any
+ "\\)?")
+ nil t)
+ (progn
+ (setq title
+ (buffer-substring
+ ;; skip over section type
+ (progn (forward-word 1)
+ ;; and over spaces
+ (skip-chars-forward " \t")
+ (point))
+ (progn (end-of-line)
+ (skip-chars-backward " \t")
+ (point)))))
+ (error "Cannot find node to match node name in menu entry.")))
+ ;; Return point to the menu and insert the title.
+ (end-of-line)
+ (delete-region
+ (point)
+ (save-excursion (skip-chars-backward " \t") (point)))
+ (indent-to texinfo-column-for-description 2)
+ (save-excursion (insert title))))
+
+
+;;; Handling description indentation
+
+;; Since the make-menu functions indent descriptions, these functions
+;; are useful primarily for indenting a single menu specially.
+
+(defun texinfo-indent-menu-description (column &optional region-p)
+ "Indent every description in menu following point to COLUMN.
+Non-nil argument (prefix, if interactive) means indent every
+description in every menu in the region. Does not indent second and
+subsequent lines of a multi-line description."
+
+ (interactive
+ "nIndent menu descriptions to (column number): \nP")
+ (save-excursion
+ (save-restriction
+ (widen)
+ (if (not region-p)
+ (progn
+ (re-search-forward "^@menu")
+ (texinfo-menu-indent-description column)
+ (message
+ "Indented descriptions in menu. You may save the buffer."))
+ ;;else
+ (message "Indenting every menu description in region... ")
+ (goto-char (region-beginning))
+ (while (and (< (point) (region-end))
+ (texinfo-locate-menu-p))
+ (forward-line 1)
+ (texinfo-menu-indent-description column))
+ (message "Indenting done. You may save the buffer.")))))
+
+(defun texinfo-menu-indent-description (to-column-number)
+ "Indent the Texinfo file menu description to TO-COLUMN-NUMBER.
+Start with point just after the word `menu' in the `@menu' line and
+leave point on the line before the `@end menu' line. Does not indent
+second and subsequent lines of a multi-line description."
+ (let* ((beginning-of-next-line (point)))
+ (while (< beginning-of-next-line
+ (save-excursion ; beginning of end menu line
+ (goto-char (texinfo-menu-end))
+ (beginning-of-line)
+ (point)))
+
+ (if (re-search-forward "\\* \\(.*::\\|.*: [^.,\t\n]+[.,\t]\\)"
+ (texinfo-menu-end)
+ t)
+ (progn
+ (let ((beginning-white-space (point)))
+ (skip-chars-forward " \t") ; skip over spaces
+ (if (looking-at "\\(@\\|\\w\\)+") ; if there is text
+ (progn
+ ;; remove pre-existing indentation
+ (delete-region beginning-white-space (point))
+ (indent-to-column to-column-number))))))
+ ;; position point at beginning of next line
+ (forward-line 1)
+ (setq beginning-of-next-line (point)))))
+
+
+;;; Making the master menu
+
+(defun texinfo-master-menu (update-all-nodes-menus-p)
+ "Make a master menu for a whole Texinfo file.
+Non-nil argument (prefix, if interactive) means first update all
+existing nodes and menus. Remove pre-existing master menu, if there is one.
+
+This function creates a master menu that follows the top node. The
+master menu includes every entry from all the other menus. It
+replaces any existing ordinary menu that follows the top node.
+
+If called with a non-nil argument, this function first updates all the
+menus in the buffer (incorporating descriptions from pre-existing
+menus) before it constructs the master menu.
+
+The function removes the detailed part of an already existing master
+menu. This action depends on the pre-existing master menu using the
+standard `texinfo-master-menu-header'.
+
+The master menu has the following format, which is adapted from the
+recommendation in the Texinfo Manual:
+
+ * The first part contains the major nodes in the Texinfo file: the
+ nodes for the chapters, chapter-like sections, and the major
+ appendices. This includes the indices, so long as they are in
+ chapter-like sections, such as unnumbered sections.
+
+ * The second and subsequent parts contain a listing of the other,
+ lower level menus, in order. This way, an inquirer can go
+ directly to a particular node if he or she is searching for
+ specific information.
+
+Each of the menus in the detailed node listing is introduced by the
+title of the section containing the menu."
+
+ (interactive "P")
+ (let ((case-fold-search t))
+ (widen)
+ (goto-char (point-min))
+
+ ;; Move point to location after `top'.
+ (if (not (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" nil t))
+ (error "This buffer needs a Top node!"))
+
+ (let ((first-chapter
+ (save-excursion
+ (or (re-search-forward "^@node" nil t)
+ (error "Too few nodes for a master menu!"))
+ (point))))
+ (if (re-search-forward texinfo-master-menu-header first-chapter t)
+ ;; Remove detailed master menu listing
+ (progn
+ (goto-char (match-beginning 0))
+ (let ((end-of-detailed-menu-descriptions
+ (save-excursion ; beginning of end menu line
+ (goto-char (texinfo-menu-end))
+ (beginning-of-line) (forward-char -1)
+ (point))))
+ (delete-region (point) end-of-detailed-menu-descriptions)))))
+
+ (if update-all-nodes-menus-p
+ (progn
+ (message "Making a master menu in %s ...first updating all nodes... "
+ (buffer-name))
+ (sleep-for 2)
+ (push-mark (point-max) t)
+ (goto-char (point-min))
+ (texinfo-update-node t)
+
+ (message "Updating all menus in %s ... " (buffer-name))
+ (sleep-for 2)
+ (push-mark (point-max) t)
+ (goto-char (point-min))
+ (texinfo-make-menu t)))
+
+ (message "Now making the master menu in %s... " (buffer-name))
+ (sleep-for 2)
+ (goto-char (point-min))
+ (texinfo-insert-master-menu-list
+ (texinfo-master-menu-list))
+
+ ;; Remove extra newlines that texinfo-insert-master-menu-list
+ ;; may have inserted.
+
+ (save-excursion
+ (goto-char (point-min))
+
+ (if (re-search-forward texinfo-master-menu-header nil t)
+ (progn
+ (goto-char (match-beginning 0))
+ (insert "\n")
+ (delete-blank-lines)
+ (goto-char (point-min))))
+
+ (re-search-forward "^@menu")
+ (forward-line -1)
+ (delete-blank-lines)
+
+ (re-search-forward "^@end menu")
+ (forward-line 1)
+ (delete-blank-lines))
+
+ (message
+ "Done...completed making master menu. You may save the buffer.")))
+
+(defun texinfo-master-menu-list ()
+ "Return a list of menu entries and header lines for the master menu.
+
+Start with the menu for chapters and indices and then find each
+following menu and the title of the node preceding that menu.
+
+The master menu list has this form:
+
+ \(\(\(... \"entry-1-2\" \"entry-1\"\) \"title-1\"\)
+ \(\(... \"entry-2-2\" \"entry-2-1\"\) \"title-2\"\)
+ ...\)
+
+However, there does not need to be a title field."
+
+ (let (master-menu-list)
+ (while (texinfo-locate-menu-p)
+ (setq master-menu-list
+ (cons (list
+ (texinfo-copy-menu)
+ (texinfo-copy-menu-title))
+ master-menu-list)))
+ (reverse master-menu-list)))
+
+(defun texinfo-insert-master-menu-list (master-menu-list)
+ "Format and insert the master menu in the current buffer."
+ (goto-char (point-min))
+ ;; Insert a master menu only after `Top' node and before next node
+ ;; \(or include file if there is no next node\).
+ (if (not (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" nil t))
+ (error "This buffer needs a Top node!"))
+ (let ((first-chapter
+ (save-excursion (re-search-forward "^@node\\|^@include") (point))))
+ (if (not (re-search-forward "^@menu" first-chapter t))
+ (error
+ "Buffer lacks ordinary `Top' menu in which to insert master.")))
+ (beginning-of-line)
+ (delete-region ; buffer must have ordinary top menu
+ (point)
+ (save-excursion (re-search-forward "^@end menu") (point)))
+
+ (save-excursion ; leave point at beginning of menu
+ ;; Handle top of menu
+ (insert "\n@menu\n")
+ ;; Insert chapter menu entries
+ (setq this-very-menu-list (reverse (car (car master-menu-list))))
+ ;; Tell user what is going on.
+ (message "Inserting chapter menu entry: %s ... " this-very-menu-list)
+ (while this-very-menu-list
+ (insert "* " (car this-very-menu-list) "\n")
+ (setq this-very-menu-list (cdr this-very-menu-list)))
+
+ (setq master-menu-list (cdr master-menu-list))
+
+ ;; Only insert detailed master menu if there is one....
+ (if (car (car master-menu-list))
+;; @detailmenu added 5 Sept 1996 at Karl Berry's request to avert a
+;; bug in `makeinfo'; all agree this is a bad kluge and should
+;; eventually be removed. @detailmenu ... @end detailmenu is a noop
+;; in `texinfmt.el' See @end detailmenu below
+;; also see `texinfo-all-menus-update' above, `texinfo-master-menu',
+;; `texinfo-multiple-files-update'
+ (insert texinfo-master-menu-header))
+
+ ;; Now, insert all the other menus
+
+ ;; The menu master-menu-list has a form like this:
+ ;; ((("beta" "alpha") "title-A")
+ ;; (("delta" "gamma") "title-B"))
+
+ (while master-menu-list
+
+ (message
+ "Inserting menu for %s .... " (car (cdr (car master-menu-list))))
+ ;; insert title of menu section
+ (insert "\n" (car (cdr (car master-menu-list))) "\n\n")
+
+ ;; insert each menu entry
+ (setq this-very-menu-list (reverse (car (car master-menu-list))))
+ (while this-very-menu-list
+ (insert "* " (car this-very-menu-list) "\n")
+ (setq this-very-menu-list (cdr this-very-menu-list)))
+
+ (setq master-menu-list (cdr master-menu-list)))
+
+ ;; Finish menu
+;; @detailmenu (see note above)
+ (insert "\n@end detailmenu")
+ (insert "\n@end menu\n\n")))
+
+(defvar texinfo-master-menu-header
+ "\n@detailmenu\n --- The Detailed Node Listing ---\n"
+ "String inserted before lower level entries in Texinfo master menu.
+It comes after the chapter-level menu entries.")
+
+(defun texinfo-locate-menu-p ()
+ "Find the next menu in the texinfo file.
+If found, leave point after word `menu' on the `@menu' line, and return t.
+If a menu is not found, do not move point and return nil."
+ (re-search-forward "\\(^@menu\\)" nil t))
+
+(defun texinfo-copy-menu-title ()
+ "Return the title of the section preceding the menu as a string.
+If such a title cannot be found, return an empty string. Do not move
+point."
+ (let ((case-fold-search t))
+ (save-excursion
+ (if (re-search-backward
+ (concat
+ "\\(^@top"
+ "\\|" ; or
+ texinfo-section-types-regexp ; all other section types
+ "\\)")
+ nil
+ t)
+ (progn
+ (beginning-of-line)
+ (forward-word 1) ; skip over section type
+ (skip-chars-forward " \t") ; and over spaces
+ (buffer-substring
+ (point)
+ (progn (end-of-line) (point))))
+ ""))))
+
+(defun texinfo-copy-menu ()
+ "Return the entries of an existing menu as a list.
+Start with point just after the word `menu' in the `@menu' line
+and leave point on the line before the `@end menu' line."
+ (let* (this-menu-list
+ (end-of-menu (texinfo-menu-end)) ; position of end of `@end menu'
+ (last-entry (save-excursion ; position of beginning of
+ ; last `* ' entry
+ (goto-char end-of-menu)
+ ;; handle multi-line description
+ (if (not (re-search-backward "^\\* " nil t))
+ (error "No entries in menu."))
+ (point))))
+ (while (< (point) last-entry)
+ (if (re-search-forward "^\\* " end-of-menu t)
+ (progn
+ (setq this-menu-list
+ (cons
+ (buffer-substring
+ (point)
+ ;; copy multi-line descriptions
+ (save-excursion
+ (re-search-forward "\\(^\\* \\|^@e\\)" nil t)
+ (- (point) 3)))
+ this-menu-list)))))
+ this-menu-list))
+
+
+;;; Determining the hierarchical level in the texinfo file
+
+(defun texinfo-specific-section-type ()
+ "Return the specific type of next section, as a string.
+For example, \"unnumberedsubsec\". Return \"top\" for top node.
+
+Searches forward for a section. Hence, point must be before the
+section whose type will be found. Does not move point. Signal an
+error if the node is not the top node and a section is not found."
+ (let ((case-fold-search t))
+ (save-excursion
+ (cond
+ ((re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)"
+;;; Following search limit by cph but causes a bug
+;;; (save-excursion
+;;; (end-of-line)
+;;; (point))
+ nil
+ t)
+ "top")
+ ((re-search-forward texinfo-section-types-regexp nil t)
+ (buffer-substring-no-properties
+ (progn (beginning-of-line) ; copy its name
+ (1+ (point)))
+ (progn (forward-word 1)
+ (point))))
+ (t
+ (error
+ "texinfo-specific-section-type: Chapter or section not found."))))))
+
+(defun texinfo-hierarchic-level ()
+ "Return the general hierarchal level of the next node in a texinfo file.
+Thus, a subheading or appendixsubsec is of type subsection."
+ (let ((case-fold-search t))
+ (cdr (assoc
+ (texinfo-specific-section-type)
+ texinfo-section-to-generic-alist))))
+
+
+;;; Locating the major positions
+
+(defun texinfo-update-menu-region-beginning (level)
+ "Locate beginning of higher level section this section is within.
+Return position of the beginning of the node line; do not move point.
+Thus, if this level is subsection, searches backwards for section node.
+Only argument is a string of the general type of section."
+ (let ((case-fold-search t))
+ ;; !! Known bug: if section immediately follows top node, this
+ ;; returns the beginning of the buffer as the beginning of the
+ ;; higher level section.
+ (cond
+ ((or (string-equal "top" level)
+ (string-equal "chapter" level))
+ (save-excursion
+ (goto-char (point-min))
+ (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" nil t)
+ (beginning-of-line)
+ (point)))
+ (t
+ (save-excursion
+ (re-search-backward
+ (concat
+ "\\(^@node\\).*\n" ; match node line
+ "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any
+ "\\|" ; or
+ "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any
+ (eval
+ (cdr (assoc level texinfo-update-menu-higher-regexps))))
+ nil
+ 'goto-beginning)
+ (point))))))
+
+(defun texinfo-update-menu-region-end (level)
+ "Locate end of higher level section this section is within.
+Return position; do not move point. Thus, if this level is a
+subsection, find the node for the section this subsection is within.
+If level is top or chapter, returns end of file. Only argument is a
+string of the general type of section."
+ (let ((case-fold-search t))
+ (save-excursion
+ (if (re-search-forward
+ (concat
+ "\\(^@node\\).*\n" ; match node line
+ "\\(\\(\\(^@c\\).*\n\\)" ; match comment line, if any
+ "\\|" ; or
+ "\\(^@ifinfo[ ]*\n\\)\\)?" ; ifinfo line, if any
+ (eval
+ ;; Never finds end of level above chapter so goes to end.
+ (cdr (assoc level texinfo-update-menu-higher-regexps))))
+ nil
+ 'goto-end)
+ (match-beginning 1)
+ (point-max)))))
+
+(defun texinfo-menu-first-node (beginning end)
+ "Locate first node of the section the menu will be placed in.
+Return position; do not move point.
+The menu will be located just before this position.
+
+First argument is the position of the beginning of the section in
+which the menu will be located; second argument is the position of the
+end of that region; it limits the search."
+
+ (save-excursion
+ (goto-char beginning)
+ (forward-line 1)
+ (re-search-forward "^@node" end t)
+ (beginning-of-line)
+ (point)))
+
+
+;;; Alists and regular expressions for defining hierarchical levels
+
+(defvar texinfo-section-to-generic-alist
+ '(("top" . "top")
+
+ ("chapter" . "chapter")
+ ("unnumbered" . "chapter")
+ ("majorheading" . "chapter")
+ ("chapheading" . "chapter")
+ ("appendix" . "chapter")
+
+ ("section" . "section")
+ ("unnumberedsec" . "section")
+ ("heading" . "section")
+ ("appendixsec" . "section")
+
+ ("subsection" . "subsection")
+ ("unnumberedsubsec" . "subsection")
+ ("subheading" . "subsection")
+ ("appendixsubsec" . "subsection")
+
+ ("subsubsection" . "subsubsection")
+ ("unnumberedsubsubsec" . "subsubsection")
+ ("subsubheading" . "subsubsection")
+ ("appendixsubsubsec" . "subsubsection"))
+ "*An alist of specific and corresponding generic Texinfo section types.
+The keys are strings specifying specific types of section; the values
+are strings of their corresponding general types.")
+
+;; We used to look for just sub, but that found @subtitle.
+(defvar texinfo-section-types-regexp
+ "^@\\(chapter \\|sect\\|subs\\|subh\\|unnum\\|major\\|chapheading \\|heading \\|appendix\\)"
+ "Regexp matching chapter, section, other headings (but not the top node).")
+
+(defvar texinfo-chapter-level-regexp
+ "chapter\\|unnumbered \\|appendix \\|majorheading\\|chapheading"
+ "Regular expression matching just the Texinfo chapter level headings.")
+
+(defvar texinfo-section-level-regexp
+ "section\\|unnumberedsec\\|heading \\|appendixsec"
+ "Regular expression matching just the Texinfo section level headings.")
+
+(defvar texinfo-subsection-level-regexp
+ "subsection\\|unnumberedsubsec\\|subheading\\|appendixsubsec"
+ "Regular expression matching just the Texinfo subsection level headings.")
+
+(defvar texinfo-subsubsection-level-regexp
+ "subsubsection\\|unnumberedsubsubsec\\|subsubheading\\|appendixsubsubsec"
+ "Regular expression matching just the Texinfo subsubsection level headings.")
+
+(defvar texinfo-update-menu-same-level-regexps
+ '(("top" . "top[ \t]+")
+ ("chapter" .
+ (concat "\\(^@\\)\\(" texinfo-chapter-level-regexp "\\)[ \t]*"))
+ ("section" .
+ (concat "\\(^@\\)\\(" texinfo-section-level-regexp "\\)[ \t]*"))
+ ("subsection" .
+ (concat "\\(^@\\)\\(" texinfo-subsection-level-regexp "\\)[ \t]+"))
+ ("subsubsection" .
+ (concat "\\(^@\\)\\(" texinfo-subsubsection-level-regexp "\\)[ \t]+")))
+ "*Regexps for searching for same level sections in a Texinfo file.
+The keys are strings specifying the general hierarchical level in the
+document; the values are regular expressions.")
+
+(defvar texinfo-update-menu-higher-regexps
+ '(("top" . "^@node [ \t]*DIR")
+ ("chapter" . "^@node [ \t]*top[ \t]*\\(,\\|$\\)")
+ ("section" .
+ (concat
+ "\\(^@\\("
+ texinfo-chapter-level-regexp
+ "\\)[ \t]*\\)"))
+ ("subsection" .
+ (concat
+ "\\(^@\\("
+ texinfo-section-level-regexp
+ "\\|"
+ texinfo-chapter-level-regexp
+ "\\)[ \t]*\\)"))
+ ("subsubsection" .
+ (concat
+ "\\(^@\\("
+ texinfo-subsection-level-regexp
+ "\\|"
+ texinfo-section-level-regexp
+ "\\|"
+ texinfo-chapter-level-regexp
+ "\\)[ \t]*\\)")))
+ "*Regexps for searching for higher level sections in a Texinfo file.
+The keys are strings specifying the general hierarchical level in the
+document; the values are regular expressions.")
+
+(defvar texinfo-update-menu-lower-regexps
+ '(("top" .
+ (concat
+ "\\(^@\\("
+ texinfo-chapter-level-regexp
+ "\\|"
+ texinfo-section-level-regexp
+ "\\|"
+ texinfo-subsection-level-regexp
+ "\\|"
+ texinfo-subsubsection-level-regexp
+ "\\)[ \t]*\\)"))
+ ("chapter" .
+ (concat
+ "\\(^@\\("
+ texinfo-section-level-regexp
+ "\\|"
+ texinfo-subsection-level-regexp
+ "\\|"
+ texinfo-subsubsection-level-regexp
+ "\\)[ \t]*\\)"))
+ ("section" .
+ (concat
+ "\\(^@\\("
+ texinfo-subsection-level-regexp
+ "\\|"
+ texinfo-subsubsection-level-regexp
+ "\\)[ \t]+\\)"))
+ ("subsection" .
+ (concat
+ "\\(^@\\("
+ texinfo-subsubsection-level-regexp
+ "\\)[ \t]+\\)"))
+ ("subsubsection" . "nothing lower"))
+ "*Regexps for searching for lower level sections in a Texinfo file.
+The keys are strings specifying the general hierarchical level in the
+document; the values are regular expressions.")
+
+
+;;; Updating a node
+
+;;;###autoload
+(defun texinfo-update-node (&optional region-p)
+ "Without any prefix argument, update the node in which point is located.
+Non-nil argument (prefix, if interactive) means update the nodes in the
+marked region.
+
+The functions for creating or updating nodes and menus, and their
+keybindings, are:
+
+ texinfo-update-node (&optional region-p) \\[texinfo-update-node]
+ texinfo-every-node-update () \\[texinfo-every-node-update]
+ texinfo-sequential-node-update (&optional region-p)
+
+ texinfo-make-menu (&optional region-p) \\[texinfo-make-menu]
+ texinfo-all-menus-update () \\[texinfo-all-menus-update]
+ texinfo-master-menu ()
+
+ texinfo-indent-menu-description (column &optional region-p)
+
+The `texinfo-column-for-description' variable specifies the column to
+which menu descriptions are indented. Its default value is 32."
+
+ (interactive "P")
+ (if (not region-p)
+ ;; update a single node
+ (let ((auto-fill-function nil) (auto-fill-hook nil))
+ (if (not (re-search-backward "^@node" (point-min) t))
+ (error "Node line not found before this position."))
+ (texinfo-update-the-node)
+ (message "Done...updated the node. You may save the buffer."))
+ ;; else
+ (let ((auto-fill-function nil)
+ (auto-fill-hook nil)
+ (beginning (region-beginning))
+ (end (region-end)))
+ (if (= end beginning)
+ (error "Please mark a region!"))
+ (save-restriction
+ (narrow-to-region beginning end)
+ (goto-char beginning)
+ (push-mark (point) t)
+ (while (re-search-forward "^@node" (point-max) t)
+ (beginning-of-line)
+ (texinfo-update-the-node))
+ (message "Done...updated nodes in region. You may save the buffer.")))))
+
+;;;###autoload
+(defun texinfo-every-node-update ()
+ "Update every node in a Texinfo file."
+ (interactive)
+ (save-excursion
+ (push-mark (point-max) t)
+ (goto-char (point-min))
+ ;; Using the mark to pass bounds this way
+ ;; is kludgy, but it's not worth fixing. -- rms.
+ (let ((mark-active t))
+ (texinfo-update-node t))
+ (message "Done...updated every node. You may save the buffer.")))
+
+(defun texinfo-update-the-node ()
+ "Update one node. Point must be at the beginning of node line.
+Leave point at the end of the node line."
+ (texinfo-check-for-node-name)
+ (texinfo-delete-existing-pointers)
+ (message "Updating node: %s ... " (texinfo-copy-node-name))
+ (save-restriction
+ (widen)
+ (let*
+ ((case-fold-search t)
+ (level (texinfo-hierarchic-level))
+ (beginning (texinfo-update-menu-region-beginning level))
+ (end (texinfo-update-menu-region-end level)))
+ (if (string-equal level "top")
+ (texinfo-top-pointer-case)
+ ;; else
+ (texinfo-insert-pointer beginning end level 'next)
+ (texinfo-insert-pointer beginning end level 'previous)
+ (texinfo-insert-pointer beginning end level 'up)
+ (texinfo-clean-up-node-line)))))
+
+(defun texinfo-top-pointer-case ()
+ "Insert pointers in the Top node. This is a special case.
+
+The `Next' pointer is a pointer to a chapter or section at a lower
+hierarchical level in the file. The `Previous' and `Up' pointers are
+to `(dir)'. Point must be at the beginning of the node line, and is
+left at the end of the node line."
+
+ (texinfo-clean-up-node-line)
+ (insert ", "
+ (save-excursion
+ ;; There may be an @chapter or other such command between
+ ;; the top node line and the next node line, as a title
+ ;; for an `ifinfo' section. This @chapter command must
+ ;; must be skipped. So the procedure is to search for
+ ;; the next `@node' line, and then copy its name.
+ (if (re-search-forward "^@node" nil t)
+ (progn
+ (beginning-of-line)
+ (texinfo-copy-node-name))
+ " "))
+ ", (dir), (dir)"))
+
+(defun texinfo-check-for-node-name ()
+ "Determine whether the node has a node name. Prompt for one if not.
+Point must be at beginning of node line. Does not move point."
+ (save-excursion
+ (let ((initial (texinfo-copy-next-section-title)))
+ ;; This is not clean. Use `interactive' to read the arg.
+ (forward-word 1) ; skip over node command
+ (skip-chars-forward " \t") ; and over spaces
+ (if (not (looking-at "[^,\t\n ]+")) ; regexp based on what Info looks for
+ ; alternatively, use "[a-zA-Z]+"
+ (let ((node-name
+ (read-from-minibuffer
+ "Node name (use no @, commas, colons, or apostrophes): "
+ initial)))
+ (insert " " node-name))))))
+
+(defun texinfo-delete-existing-pointers ()
+ "Delete `Next', `Previous', and `Up' pointers.
+Starts from the current position of the cursor, and searches forward
+on the line for a comma and if one is found, deletes the rest of the
+line, including the comma. Leaves point at beginning of line."
+ (let ((eol-point (save-excursion (end-of-line) (point))))
+ (if (search-forward "," eol-point t)
+ (delete-region (1- (point)) eol-point)))
+ (beginning-of-line))
+
+(defun texinfo-find-pointer (beginning end level direction)
+ "Move point to section associated with next, previous, or up pointer.
+Return type of pointer (either 'normal or 'no-pointer).
+
+The first and second arguments bound the search for a pointer to the
+beginning and end, respectively, of the enclosing higher level
+section. The third argument is a string specifying the general kind
+of section such as \"chapter\" or \"section\". When looking for the
+`Next' pointer, the section found will be at the same hierarchical
+level in the Texinfo file; when looking for the `Previous' pointer,
+the section found will be at the same or higher hierarchical level in
+the Texinfo file; when looking for the `Up' pointer, the section found
+will be at some level higher in the Texinfo file. The fourth argument
+\(one of 'next, 'previous, or 'up\) specifies whether to find the
+`Next', `Previous', or `Up' pointer."
+ (let ((case-fold-search t))
+ (cond ((eq direction 'next)
+ (forward-line 3) ; skip over current node
+ ;; Search for section commands accompanied by node lines;
+ ;; ignore section commands in the middle of nodes.
+ (if (re-search-forward
+ ;; A `Top' node is never a next pointer, so won't find it.
+ (concat
+ ;; Match node line.
+ "\\(^@node\\).*\n"
+ ;; Match comment or ifinfo line, if any
+ "\\(\\(\\(^@c\\).*\n\\)\\|\\(^@ifinfo[ ]*\n\\)\\)?"
+ (eval
+ (cdr (assoc level texinfo-update-menu-same-level-regexps))))
+ end
+ t)
+ 'normal
+ 'no-pointer))
+ ((eq direction 'previous)
+ (if (re-search-backward
+ (concat
+ "\\("
+ ;; Match node line.
+ "\\(^@node\\).*\n"
+ ;; Match comment or ifinfo line, if any
+ "\\(\\(\\(^@c\\).*\n\\)\\|\\(^@ifinfo[ ]*\n\\)\\)?"
+ (eval
+ (cdr (assoc level texinfo-update-menu-same-level-regexps)))
+ "\\|"
+ ;; Match node line.
+ "\\(^@node\\).*\n"
+ ;; Match comment or ifinfo line, if any
+ "\\(\\(\\(^@c\\).*\n\\)\\|\\(^@ifinfo[ ]*\n\\)\\)?"
+ (eval
+ (cdr (assoc level texinfo-update-menu-higher-regexps)))
+ "\\|"
+ ;; Handle `Top' node specially.
+ "^@node [ \t]*top[ \t]*\\(,\\|$\\)"
+ "\\)")
+ beginning
+ t)
+ 'normal
+ 'no-pointer))
+ ((eq direction 'up)
+ (if (re-search-backward
+ (concat
+ "\\("
+ ;; Match node line.
+ "\\(^@node\\).*\n"
+ ;; Match comment or ifinfo line, if any
+ "\\(\\(\\(^@c\\).*\n\\)\\|\\(^@ifinfo[ ]*\n\\)\\)?"
+ (eval (cdr (assoc level texinfo-update-menu-higher-regexps)))
+ "\\|"
+ ;; Handle `Top' node specially.
+ "^@node [ \t]*top[ \t]*\\(,\\|$\\)"
+ "\\)")
+ (save-excursion
+ (goto-char beginning)
+ (beginning-of-line)
+ (point))
+ t)
+ 'normal
+ 'no-pointer))
+ (t
+ (error "texinfo-find-pointer: lack proper arguments")))))
+
+(defun texinfo-pointer-name (kind)
+ "Return the node name preceding the section command.
+The argument is the kind of section, either normal or no-pointer."
+ (let (name)
+ (cond ((eq kind 'normal)
+ (end-of-line) ; this handles prev node top case
+ (re-search-backward ; when point is already
+ "^@node" ; at the beginning of @node line
+ (save-excursion (forward-line -3))
+ t)
+ (setq name (texinfo-copy-node-name)))
+ ((eq kind 'no-pointer)
+ (setq name " "))) ; put a blank in the pointer slot
+ name))
+
+(defun texinfo-insert-pointer (beginning end level direction)
+ "Insert the `Next', `Previous' or `Up' node name at point.
+Move point forward.
+
+The first and second arguments bound the search for a pointer to the
+beginning and end, respectively, of the enclosing higher level
+section. The third argument is the hierarchical level of the Texinfo
+file, a string such as \"section\". The fourth argument is direction
+towards which the pointer is directed, one of `next, `previous, or
+'up."
+
+ (end-of-line)
+ (insert
+ ", "
+ (save-excursion
+ (texinfo-pointer-name
+ (texinfo-find-pointer beginning end level direction)))))
+
+(defun texinfo-clean-up-node-line ()
+ "Remove extra commas, if any, at end of node line."
+ (end-of-line)
+ (skip-chars-backward ", ")
+ (delete-region (point) (save-excursion (end-of-line) (point))))
+
+
+;;; Updating nodes sequentially
+;; These sequential update functions insert `Next' or `Previous'
+;; pointers that point to the following or preceding nodes even if they
+;; are at higher or lower hierarchical levels. This means that if a
+;; section contains one or more subsections, the section's `Next'
+;; pointer will point to the subsection and not the following section.
+;; (The subsection to which `Next' points will most likely be the first
+;; item on the section's menu.)
+
+;;;###autoload
+(defun texinfo-sequential-node-update (&optional region-p)
+ "Update one node (or many) in a Texinfo file with sequential pointers.
+
+This function causes the `Next' or `Previous' pointer to point to the
+immediately preceding or following node, even if it is at a higher or
+lower hierarchical level in the document. Continually pressing `n' or
+`p' takes you straight through the file.
+
+Without any prefix argument, update the node in which point is located.
+Non-nil argument (prefix, if interactive) means update the nodes in the
+marked region.
+
+This command makes it awkward to navigate among sections and
+subsections; it should be used only for those documents that are meant
+to be read like a novel rather than a reference, and for which the
+Info `g*' command is inadequate."
+
+ (interactive "P")
+ (if (not region-p)
+ ;; update a single node
+ (let ((auto-fill-function nil) (auto-fill-hook nil))
+ (if (not (re-search-backward "^@node" (point-min) t))
+ (error "Node line not found before this position."))
+ (texinfo-sequentially-update-the-node)
+ (message
+ "Done...sequentially updated the node . You may save the buffer."))
+ ;; else
+ (let ((auto-fill-function nil)
+ (auto-fill-hook nil)
+ (beginning (region-beginning))
+ (end (region-end)))
+ (if (= end beginning)
+ (error "Please mark a region!"))
+ (save-restriction
+ (narrow-to-region beginning end)
+ (goto-char beginning)
+ (push-mark (point) t)
+ (while (re-search-forward "^@node" (point-max) t)
+ (beginning-of-line)
+ (texinfo-sequentially-update-the-node))
+ (message
+ "Done...updated the nodes in sequence. You may save the buffer.")))))
+
+(defun texinfo-sequentially-update-the-node ()
+ "Update one node such that the pointers are sequential.
+A `Next' or `Previous' pointer points to any preceding or following node,
+regardless of its hierarchical level."
+
+ (texinfo-check-for-node-name)
+ (texinfo-delete-existing-pointers)
+ (message
+ "Sequentially updating node: %s ... " (texinfo-copy-node-name))
+ (save-restriction
+ (widen)
+ (let*
+ ((case-fold-search t)
+ (level (texinfo-hierarchic-level)))
+ (if (string-equal level "top")
+ (texinfo-top-pointer-case)
+ ;; else
+ (texinfo-sequentially-insert-pointer level 'next)
+ (texinfo-sequentially-insert-pointer level 'previous)
+ (texinfo-sequentially-insert-pointer level 'up)
+ (texinfo-clean-up-node-line)))))
+
+(defun texinfo-sequentially-find-pointer (level direction)
+ "Find next or previous pointer sequentially in Texinfo file, or up pointer.
+Move point to section associated with the pointer. Find point even if
+it is in a different section.
+
+Return type of pointer (either 'normal or 'no-pointer).
+
+The first argument is a string specifying the general kind of section
+such as \"chapter\" or \"section\". The section found will be at the
+same hierarchical level in the Texinfo file, or, in the case of the up
+pointer, some level higher. The second argument (one of 'next,
+'previous, or 'up) specifies whether to find the `Next', `Previous',
+or `Up' pointer."
+ (let ((case-fold-search t))
+ (cond ((eq direction 'next)
+ (forward-line 3) ; skip over current node
+ (if (re-search-forward
+ texinfo-section-types-regexp
+ (point-max)
+ t)
+ 'normal
+ 'no-pointer))
+ ((eq direction 'previous)
+ (if (re-search-backward
+ texinfo-section-types-regexp
+ (point-min)
+ t)
+ 'normal
+ 'no-pointer))
+ ((eq direction 'up)
+ (if (re-search-backward
+ (eval (cdr (assoc level texinfo-update-menu-higher-regexps)))
+ beginning
+ t)
+ 'normal
+ 'no-pointer))
+ (t
+ (error "texinfo-sequential-find-pointer: lack proper arguments")))))
+
+(defun texinfo-sequentially-insert-pointer (level direction)
+ "Insert the `Next', `Previous' or `Up' node name at point.
+Move point forward.
+
+The first argument is the hierarchical level of the Texinfo file, a
+string such as \"section\". The second argument is direction, one of
+`next, `previous, or 'up."
+
+ (end-of-line)
+ (insert
+ ", "
+ (save-excursion
+ (texinfo-pointer-name
+ (texinfo-sequentially-find-pointer level direction)))))
+
+
+;;; Inserting `@node' lines
+;; The `texinfo-insert-node-lines' function inserts `@node' lines as needed
+;; before the `@chapter', `@section', and such like lines of a region
+;; in a Texinfo file.
+
+(defun texinfo-insert-node-lines (beginning end &optional title-p)
+ "Insert missing `@node' lines in region of Texinfo file.
+Non-nil argument (prefix, if interactive) means also to insert the
+section titles as node names; and also to insert the section titles as
+node names in pre-existing @node lines that lack names."
+ (interactive "r\nP")
+
+ ;; Use marker; after inserting node lines, leave point at end of
+ ;; region and mark at beginning.
+
+ (let (beginning-marker end-marker title last-section-position)
+
+ ;; Save current position on mark ring and set mark to end.
+ (push-mark end t)
+ (setq end-marker (mark-marker))
+
+ (goto-char beginning)
+ (while (re-search-forward
+ texinfo-section-types-regexp
+ end-marker
+ 'end)
+ ;; Copy title if desired.
+ (if title-p
+ (progn
+ (beginning-of-line)
+ (forward-word 1)
+ (skip-chars-forward " \t")
+ (setq title (buffer-substring
+ (point)
+ (save-excursion (end-of-line) (point))))))
+ ;; Insert node line if necessary.
+ (if (re-search-backward
+ "^@node"
+ ;; Avoid finding previous node line if node lines are close.
+ (or last-section-position
+ (save-excursion (forward-line -2) (point))) t)
+ ;; @node is present, and point at beginning of that line
+ (forward-word 1) ; Leave point just after @node.
+ ;; Else @node missing; insert one.
+ (beginning-of-line) ; Beginning of `@section' line.
+ (insert "@node\n")
+ (backward-char 1)) ; Leave point just after `@node'.
+ ;; Insert title if desired.
+ (if title-p
+ (progn
+ (skip-chars-forward " \t")
+ ;; Use regexp based on what info looks for
+ ;; (alternatively, use "[a-zA-Z]+");
+ ;; this means we only insert a title if none exists.
+ (if (not (looking-at "[^,\t\n ]+"))
+ (progn
+ (beginning-of-line)
+ (forward-word 1)
+ (insert " " title)
+ (message "Inserted title %s ... " title)))))
+ ;; Go forward beyond current section title.
+ (re-search-forward texinfo-section-types-regexp
+ (save-excursion (forward-line 3) (point)) t)
+ (setq last-section-position (point))
+ (forward-line 1))
+
+ ;; Leave point at end of region, mark at beginning.
+ (set-mark beginning)
+
+ (if title-p
+ (message
+ "Done inserting node lines and titles. You may save the buffer.")
+ (message "Done inserting node lines. You may save the buffer."))))
+
+
+;;; Update and create menus for multi-file Texinfo sources
+
+;; 1. M-x texinfo-multiple-files-update
+;;
+;; Read the include file list of an outer Texinfo file and
+;; update all highest level nodes in the files listed and insert a
+;; main menu in the outer file after its top node.
+
+;; 2. C-u M-x texinfo-multiple-files-update
+;;
+;; Same as 1, but insert a master menu. (Saves reupdating lower
+;; level menus and nodes.) This command simply reads every menu,
+;; so if the menus are wrong, the master menu will be wrong.
+;; Similarly, if the lower level node pointers are wrong, they
+;; will stay wrong.
+
+;; 3. C-u 2 M-x texinfo-multiple-files-update
+;;
+;; Read the include file list of an outer Texinfo file and
+;; update all nodes and menus in the files listed and insert a
+;; master menu in the outer file after its top node.
+
+;;; Note: these functions:
+;;;
+;;; * Do not save or delete any buffers. You may fill up your memory.
+;;; * Do not handle any pre-existing nodes in outer file.
+;;; Hence, you may need a file for indices.
+
+
+;;; Auxiliary functions for multiple file updating
+
+(defun texinfo-multi-file-included-list (outer-file)
+ "Return a list of the included files in OUTER-FILE."
+ (let ((included-file-list (list outer-file))
+ start)
+ (save-excursion
+ (switch-to-buffer (find-file-noselect outer-file))
+ (widen)
+ (goto-char (point-min))
+ (while (re-search-forward "^@include" nil t)
+ (skip-chars-forward " \t")
+ (setq start (point))
+ (end-of-line)
+ (skip-chars-backward " \t")
+ (setq included-file-list
+ (cons (buffer-substring start (point))
+ included-file-list)))
+ (nreverse included-file-list))))
+
+(defun texinfo-copy-next-section-title ()
+ "Return the name of the immediately following section as a string.
+
+Start with point at the beginning of the node line. Leave point at the
+same place. If there is no title, returns an empty string."
+
+ (save-excursion
+ (end-of-line)
+ (let ((node-end (or
+ (save-excursion
+ (if (re-search-forward "\\(^@node\\)" nil t)
+ (match-beginning 0)))
+ (point-max))))
+ (if (re-search-forward texinfo-section-types-regexp node-end t)
+ (progn
+ (beginning-of-line)
+ ;; copy title
+ (let ((title
+ (buffer-substring
+ (progn (forward-word 1) ; skip over section type
+ (skip-chars-forward " \t") ; and over spaces
+ (point))
+ (progn (end-of-line) (point)))))
+ title))
+ ""))))
+
+(defun texinfo-multi-file-update (files &optional update-everything)
+ "Update first node pointers in each file in FILES.
+Return a list of the node names.
+
+The first file in the list is an outer file; the remaining are
+files included in the outer file with `@include' commands.
+
+If optional arg UPDATE-EVERYTHING non-nil, update every menu and
+pointer in each of the included files.
+
+Also update the `Top' level node pointers of the outer file.
+
+Requirements:
+
+ * the first file in the FILES list must be the outer file,
+ * each of the included files must contain exactly one highest
+ hierarchical level node,
+ * this node must be the first node in the included file,
+ * each highest hierarchical level node must be of the same type.
+
+Thus, normally, each included file contains one, and only one,
+chapter."
+
+;; The menu-list has the form:
+;;
+;; \(\(\"node-name1\" . \"title1\"\)
+;; \(\"node-name2\" . \"title2\"\) ... \)
+;;
+;; However, there does not need to be a title field and this function
+;; does not fill it; however a comment tells you how to do so.
+;; You would use the title field if you wanted to insert titles in the
+;; description slot of a menu as a description.
+
+ (let ((case-fold-search t)
+ menu-list)
+
+ ;; Find the name of the first node of the first included file.
+ (switch-to-buffer (find-file-noselect (car (cdr files))))
+ (widen)
+ (goto-char (point-min))
+ (if (not (re-search-forward "^@node" nil t))
+ (error "No `@node' line found in %s !" (buffer-name)))
+ (beginning-of-line)
+ (texinfo-check-for-node-name)
+ (setq next-node-name (texinfo-copy-node-name))
+
+ (setq menu-list
+ (cons (cons
+ next-node-name
+ (prog1 "" (forward-line 1)))
+ ;; Use following to insert section titles automatically.
+ ;; (texinfo-copy-next-section-title)
+ menu-list))
+
+ ;; Go to outer file
+ (switch-to-buffer (find-file-noselect (car files)))
+ (goto-char (point-min))
+ (if (not (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" nil t))
+ (error "This buffer needs a Top node!"))
+ (beginning-of-line)
+ (texinfo-delete-existing-pointers)
+ (end-of-line)
+ (insert ", " next-node-name ", (dir), (dir)")
+ (beginning-of-line)
+ (setq previous-node-name "Top")
+ (setq files (cdr files))
+
+ (while files
+
+ (if (not (cdr files))
+ ;; No next file
+ (setq next-node-name "")
+ ;; Else,
+ ;; find the name of the first node in the next file.
+ (switch-to-buffer (find-file-noselect (car (cdr files))))
+ (widen)
+ (goto-char (point-min))
+ (if (not (re-search-forward "^@node" nil t))
+ (error "No `@node' line found in %s !" (buffer-name)))
+ (beginning-of-line)
+ (texinfo-check-for-node-name)
+ (setq next-node-name (texinfo-copy-node-name))
+ (setq menu-list
+ (cons (cons
+ next-node-name
+ (prog1 "" (forward-line 1)))
+ ;; Use following to insert section titles automatically.
+ ;; (texinfo-copy-next-section-title)
+ menu-list)))
+
+ ;; Go to node to be updated.
+ (switch-to-buffer (find-file-noselect (car files)))
+ (goto-char (point-min))
+ (if (not (re-search-forward "^@node" nil t))
+ (error "No `@node' line found in %s !" (buffer-name)))
+ (beginning-of-line)
+
+ ;; Update other menus and nodes if requested.
+ (if update-everything (texinfo-all-menus-update t))
+
+ (beginning-of-line)
+ (texinfo-delete-existing-pointers)
+ (end-of-line)
+ (insert ", " next-node-name ", " previous-node-name ", " up-node-name)
+
+ (beginning-of-line)
+ (setq previous-node-name (texinfo-copy-node-name))
+
+ (setq files (cdr files)))
+ (nreverse menu-list)))
+
+(defun texinfo-multi-files-insert-main-menu (menu-list)
+ "Insert formatted main menu at point.
+Indents the first line of the description, if any, to the value of
+texinfo-column-for-description."
+
+ (insert "@menu\n")
+ (while menu-list
+ ;; Every menu entry starts with a star and a space.
+ (insert "* ")
+
+ ;; Insert the node name (and menu entry name, if present).
+ (let ((node-part (car (car menu-list))))
+ (if (stringp node-part)
+ ;; "Double colon" entry line; menu entry and node name are the same,
+ (insert (format "%s::" node-part))
+ ;; "Single colon" entry line; menu entry and node name are different.
+ (insert (format "%s: %s." (car node-part) (cdr node-part)))))
+
+ ;; Insert the description, if present.
+ (if (cdr (car menu-list))
+ (progn
+ ;; Move to right place.
+ (indent-to texinfo-column-for-description 2)
+ ;; Insert description.
+ (insert (format "%s" (cdr (car menu-list))))))
+
+ (insert "\n") ; end this menu entry
+ (setq menu-list (cdr menu-list)))
+ (insert "@end menu"))
+
+(defun texinfo-multi-file-master-menu-list (files-list)
+ "Return master menu list from files in FILES-LIST.
+Menu entries in each file collected using `texinfo-master-menu-list'.
+
+The first file in FILES-LIST must be the outer file; the others must
+be the files included within it. A main menu must already exist."
+ (save-excursion
+ (let (master-menu-list)
+ (while files-list
+ (switch-to-buffer (find-file-noselect (car files-list)))
+ (message "Working on: %s " (current-buffer))
+ (goto-char (point-min))
+ (setq master-menu-list
+ (append master-menu-list (texinfo-master-menu-list)))
+ (setq files-list (cdr files-list)))
+ master-menu-list)))
+
+
+;;; The multiple-file update function
+
+(defun texinfo-multiple-files-update
+ (outer-file &optional update-everything make-master-menu)
+ "Update first node pointers in each file included in OUTER-FILE;
+create or update the `Top' level node pointers and the main menu in
+the outer file that refers to such nodes. This does not create or
+update menus or pointers within the included files.
+
+With optional MAKE-MASTER-MENU argument (prefix arg, if interactive),
+insert a master menu in OUTER-FILE in addition to creating or updating
+pointers in the first @node line in each included file and creating or
+updating the `Top' level node pointers of the outer file. This does
+not create or update other menus and pointers within the included
+files.
+
+With optional UPDATE-EVERYTHING argument (numeric prefix arg, if
+interactive), update all the menus and all the `Next', `Previous', and
+`Up' pointers of all the files included in OUTER-FILE before inserting
+a master menu in OUTER-FILE. Also, update the `Top' level node
+pointers of OUTER-FILE.
+
+Notes:
+
+ * this command does NOT save any files--you must save the
+ outer file and any modified, included files.
+
+ * except for the `Top' node, this command does NOT handle any
+ pre-existing nodes in the outer file; hence, indices must be
+ enclosed in an included file.
+
+Requirements:
+
+ * each of the included files must contain exactly one highest
+ hierarchical level node,
+ * this highest node must be the first node in the included file,
+ * each highest hierarchical level node must be of the same type.
+
+Thus, normally, each included file contains one, and only one,
+chapter."
+
+ (interactive (cons
+ (read-string
+ "Name of outer `include' file: "
+ (buffer-file-name))
+ (cond ((not current-prefix-arg)
+ '(nil nil))
+ ((listp current-prefix-arg)
+ '(t nil)) ; make-master-menu
+ ((numberp current-prefix-arg)
+ '(t t)) ; update-everything
+ )))
+
+ (let* ((included-file-list (texinfo-multi-file-included-list outer-file))
+ (files included-file-list)
+ main-menu-list
+ next-node-name
+ previous-node-name
+ (up-node-name "Top"))
+
+;;; Update the pointers
+;;; and collect the names of the nodes and titles
+ (setq main-menu-list (texinfo-multi-file-update files update-everything))
+
+;;; Insert main menu
+
+ ;; Go to outer file
+ (switch-to-buffer (find-file-noselect (car included-file-list)))
+ (if (texinfo-old-menu-p
+ (point-min)
+ (save-excursion
+ (re-search-forward "^@include")
+ (beginning-of-line)
+ (point)))
+
+ ;; If found, leave point after word `menu' on the `@menu' line.
+ (progn
+ (texinfo-incorporate-descriptions main-menu-list)
+ ;; Delete existing menu.
+ (beginning-of-line)
+ (delete-region
+ (point)
+ (save-excursion (re-search-forward "^@end menu") (point)))
+ ;; Insert main menu
+ (texinfo-multi-files-insert-main-menu main-menu-list))
+
+ ;; Else no current menu; insert it before `@include'
+ (texinfo-multi-files-insert-main-menu main-menu-list))
+
+;;; Insert master menu
+
+ (if make-master-menu
+ (progn
+ ;; First, removing detailed part of any pre-existing master menu
+ (goto-char (point-min))
+ (if (re-search-forward texinfo-master-menu-header nil t)
+ ;; Remove detailed master menu listing
+ (progn
+ (goto-char (match-beginning 0))
+ (let ((end-of-detailed-menu-descriptions
+ (save-excursion ; beginning of end menu line
+ (goto-char (texinfo-menu-end))
+ (beginning-of-line) (forward-char -1)
+ (point))))
+ (delete-region (point) end-of-detailed-menu-descriptions))))
+
+ ;; Create a master menu and insert it
+ (texinfo-insert-master-menu-list
+ (texinfo-multi-file-master-menu-list
+ included-file-list)))))
+
+ ;; Remove unwanted extra lines.
+ (save-excursion
+ (goto-char (point-min))
+
+ (re-search-forward "^@menu")
+ (forward-line -1)
+ (insert "\n") ; Ensure at least one blank line.
+ (delete-blank-lines)
+
+ (re-search-forward "^@end menu")
+ (forward-line 1)
+ (insert "\n") ; Ensure at least one blank line.
+ (delete-blank-lines))
+
+ (message "Multiple files updated."))
+
+
+;;; Place `provide' at end of file.
+(provide 'texnfo-upd)
+
+;;; texnfo-upd.el ends here
OpenPOWER on IntegriCloud