changeset 107:0676c0c9346f

Initial revision
author Robert J. Chassell <bob@rattlesnake.com>
date Sat, 20 Oct 1990 02:38:04 +0000
parents 27d813d24ccc
children 8644955999f0
files lisp/textmodes/texnfo-upd.el
diffstat 1 files changed, 1722 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/textmodes/texnfo-upd.el	Sat Oct 20 02:38:04 1990 +0000
@@ -0,0 +1,1722 @@
+;;;; texnfo-upd.el
+;;;; A utility for updating nodes and menus in Texinfo files.
+
+;;;; Robert J. Chassell, 23 September 1989; 15 May 1990
+
+;;;; Copyright 1989, 1990 Free Software Foundation
+
+;; 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+(provide 'texnfo-upd)
+
+
+;;;; Summary
+
+; (Much of the following commentary ought eventually be incorporated
+; into the Texinfo Manual.)
+
+; 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.
+
+; These functions replace doing these jobs by hand.
+; You may find them helpful.
+
+; 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 updating functions 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-update-node' 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 `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.
+
+ 
+;;;; 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... ")
+    (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 (texinfo-update-menu-region-beginning level))
+       (end (texinfo-update-menu-region-end level))
+       (first (texinfo-menu-first-node beginning end))
+       (node-name (progn
+                    (goto-char beginning)
+                    (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-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.
+You must remove the detailed part of a pre-existing master menu before
+running this command, lest it be partly duplicated.
+
+If called with a non-nil argument, this function first updates all the
+nodes in the buffer before updating the menus."
+  (interactive "P")
+  (save-excursion
+    (mark-whole-buffer)
+    (message "Checking for a master menu... ")
+    (save-excursion
+      (if (re-search-forward texinfo-master-menu-header nil t)
+          (error
+           "Please remove existing master menu, lest it be partly duplicated!")))
+
+  (if update-all-nodes-p
+      (progn
+        (message "First updating all nodes... ")
+        (sleep-for 2)
+        (mark-whole-buffer)
+        (texinfo-update-node t)))
+
+    (message "Updating all menus... ")
+    (sleep-for 2)
+    (texinfo-make-menu t)
+    (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."
+
+  (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."
+
+  (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                      ; (won't ever find a `top' node)
+              (cdr (assoc level texinfo-update-menu-higher-regexps))))
+            nil
+            t))
+      (goto-char (match-beginning 1))))
+
+
+;;;; 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)
+                   (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 postion
+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."
+  (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 entry cannot be found in the old
+menu, use the new section title for the description, but if the
+node-name of the new menu is found in the old menu, replace the
+section title with the old description, whatever it may be.
+
+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 (search-forward
+             (concat "\* "              ; so only menu entries are found
+                     (car (car new-menu-list))
+                     ":")               ; 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-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 `@'!
+  (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-name\" . \"description\"\) ... \)
+
+However, there does not need to be a description field."
+  
+  (insert "@menu\n")
+  (while menu-list
+    (if (cdr (car menu-list))       ; menu-list has description entry
+        (progn
+          (insert 
+           (format "* %s::" (car (car menu-list)))) ; node-name entry
+          (indent-to texinfo-column-for-description 2)
+          (insert 
+           (format "%s\n" (cdr (car menu-list)))))  ; description entry
+        ;; else menu-list lacks description entry
+      (insert
+       (format "* %s::\n" (car (car menu-list)))))  ; node-name entry
+    (setq menu-list (cdr menu-list)))
+  (insert "@end menu")
+  (message
+   "Updated \"%s\" level menu following node: %s ... "
+   level node-name))
+
+
+;;;; 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 (search-forward "::" (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-exisitng 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")
+  (widen)
+  (goto-char (point-min))
+
+  ;; Move point to location after `top'.
+  (if (not (re-search-forward "^@node [ \t]*top" nil t))
+      (error "This buffer needs a Top node!"))
+
+  (let ((first-chapter                  
+         (save-excursion (re-search-forward "^@node") (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...first updating all nodes... ")
+        (sleep-for 2)
+        (mark-whole-buffer)
+        (texinfo-update-node t)
+
+        (message "Updating all menus... ")        
+        (sleep-for 2)
+        (mark-whole-buffer)
+        (texinfo-make-menu t)))
+
+  (message "Now making the master menu... ")
+  (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))
+    
+    (re-search-forward texinfo-master-menu-header)
+    (goto-char (match-beginning 0))
+    (insert "\n")
+    (delete-blank-lines)
+    
+    (re-search-backward "^@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))
+  (re-search-forward "^@menu")
+  (beginning-of-line)
+  (delete-region (point)        ; buffer must have ordinary top menu
+                 (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))
+
+  (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
+  (insert "@end menu\n\n")))
+
+(defvar texinfo-master-menu-header
+  "\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."
+  (save-excursion
+    (if (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 (texinfo-hierarchic-level)
+                   texinfo-update-menu-higher-regexps))))
+         nil
+         t)
+        (texinfo-copy-section-title)
+      " ")))
+
+(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)
+                      (re-search-backward "^\* ") ; handle multi-line desc.
+                      (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."
+  (save-excursion
+    (cond
+     ((re-search-forward "^@node [ \t]*top" nil t)
+      "top")
+     ((re-search-forward texinfo-section-types-regexp nil t)
+      (buffer-substring (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."
+  (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."
+  
+  (cond
+   ((string-equal "top" level)
+    (save-excursion
+      (re-search-forward "^@node [ \t]*top" nil t) (point)))
+   ((string-equal "chapter" level)
+    (save-excursion
+      (re-search-backward "^@node [ \t]*top" nil t)
+      ;; Leave point at end of line so texinfo-menu-locate-entry-p does not
+      ;; accidentally copy an info-only title for the top node into
+      ;; the main or master menu
+      (end-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."
+
+  (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
+           (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.")
+
+(defvar texinfo-section-types-regexp
+  "^@\\(chapter \\|sect\\|sub\\|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")
+    ("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
+
+(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 24."
+  
+  (interactive "P")
+  (if (not region-p)
+      (let ((auto-fill-hook nil)) ; update a single node
+        (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-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)
+	(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.")))))
+
+(defun texinfo-every-node-update ()
+  "Update every node in a Texinfo file."
+  (interactive)
+  (save-excursion
+    (mark-whole-buffer)
+    (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
+    (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: ")))
+          (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."
+  (if (search-forward "," (save-excursion (end-of-line) (point)) t)
+      (progn
+        (goto-char (1- (point)))
+        (kill-line nil)))
+  (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."
+
+  (cond ((eq direction 'next)
+         (forward-line 3)             ; skip over current node
+         (if (re-search-forward
+              (eval
+               (cdr (assoc level texinfo-update-menu-same-level-regexps)))
+              end
+              t)
+             'normal
+           'no-pointer))
+        ((eq direction 'previous)
+         (if (re-search-backward
+              (concat
+               "\\("
+               (eval
+                (cdr (assoc level texinfo-update-menu-same-level-regexps)))
+               "\\|"
+               (eval
+                (cdr (assoc level texinfo-update-menu-higher-regexps)))
+               "\\)")
+              beginning
+              t)
+             'normal
+           'no-pointer))
+        ((eq direction 'up)
+         (if (re-search-backward
+             (eval (cdr (assoc level texinfo-update-menu-higher-regexps)))
+              (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.)
+
+(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)
+      (let ((auto-fill-hook nil))   ; update a single node
+        (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-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)
+        (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."
+  
+  (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 (&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 "P")
+  (save-excursion
+    (let ((begin-region  (region-beginning))
+          (end-region (region-end)))
+      (goto-char begin-region)
+      (while (< (point) end-region)
+        (re-search-forward texinfo-section-types-regexp nil 'end)
+        ;; copy title, since most often, we will need it
+        (let ((title
+               (progn
+                 (beginning-of-line)
+                 (forward-word 1)
+                 (skip-chars-forward " \t")
+                 (buffer-substring
+                  (point)
+                  (save-excursion (end-of-line) (point))))))
+          ;; insert a node if necessary
+          (if (re-search-backward
+               "^@node"
+               (save-excursion
+                 (forward-line -3)
+                 (point))
+               t)
+              ;;  @node present, and point at beginning of that line
+              (forward-word 1)
+            ;; else @node missing, insert one
+            (progn
+              (beginning-of-line)       ; beginning of `@section' line
+              (insert "@node\n")
+              (backward-char 1)))       ; leave point just after `@node'
+          ;; insert a title if warranted
+          (if title-p
+              (progn
+                (skip-chars-forward " \t")
+                ;; use regexp based on what info looks for
+                ;; (alternatively, use "[a-zA-Z]+")
+                (if (not (looking-at "[^,\t\n ]+")) 
+                    (progn
+                      (beginning-of-line) 
+                      (forward-word 1)
+                      (insert " " title)
+                      (message "Inserted title %s ... " title)))))
+          ;; in any case, go forward beyond current section title
+          (forward-line 3)))))
+  (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 ((section-end (or 
+                        (save-excursion
+                          (re-search-forward "\\(^@node\\)" nil t)
+                          (match-beginning 0))
+                        (point-max))))
+      (if (re-search-forward texinfo-section-types-regexp section-end t)
+          ;; 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 and the title immediate following them.
+
+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."
+  
+  (let (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
+                 (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" 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
+                     (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)
+      (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))
+      
+      ;; Update other menus and nodes if requested.
+      (if update-everything (texinfo-all-menus-update t))
+      
+      (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
+    (if (cdr (car menu-list))       ; menu-list has description entry
+        (progn
+          (insert 
+           (format "* %s::" (car (car menu-list)))) ; node-name entry
+          (indent-to texinfo-column-for-description 2)
+          (insert 
+           (format "%s\n" (cdr (car menu-list)))))  ; description entry
+        ;; else menu-list lacks description entry
+      (insert
+       (format "* %s::\n" (car (car menu-list)))))  ; node-name 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 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."
+
+  (interactive "fName of outer `include' file: ")
+
+  (cond (current-prefix-arg
+         (setq make-master-menu (listp current-prefix-arg))
+         (setq update-everything (numberp current-prefix-arg))))
+
+  (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)))))
+  (message "Multiple files updated."))
+
+;;;;;;;;;;;;;;;; end texnfo-upd.el ;;;;;;;;;;;;;;;;