]> code.delx.au - gnu-emacs/blobdiff - lisp/org/ox-texinfo.el
Update copyright year to 2015
[gnu-emacs] / lisp / org / ox-texinfo.el
index a55f298ddc3e2ccd47c76da6ceef5cf21a1e547b..0c81b62061812e82f594e4076b77cbb6e6c86a2b 100644 (file)
@@ -1,6 +1,6 @@
 ;;; ox-texinfo.el --- Texinfo Back-End for Org Export Engine
 
 ;;; ox-texinfo.el --- Texinfo Back-End for Org Export Engine
 
-;; Copyright (C) 2012-2014 Free Software Foundation, Inc.
+;; Copyright (C) 2012-2015 Free Software Foundation, Inc.
 ;; Author: Jonathan Leech-Pepin <jonathan.leechpepin at gmail dot com>
 ;; Keywords: outlines, hypermedia, calendar, wp
 
 ;; Author: Jonathan Leech-Pepin <jonathan.leechpepin at gmail dot com>
 ;; Keywords: outlines, hypermedia, calendar, wp
 
 
 ;;; Commentary:
 ;;
 
 ;;; Commentary:
 ;;
-;; This library implements a Texinfo back-end for Org generic
-;; exporter.
-;;
-;; To test it, run
-;;
-;;   M-: (org-export-to-buffer 'texinfo "*Test Texinfo*") RET
-;;
-;; in an Org mode buffer then switch to the buffer to see the Texinfo
-;; export.  See ox.el for more details on how this exporter works.
-;;
-
-;; It introduces nine new buffer keywords: "TEXINFO_CLASS",
-;; "TEXINFO_FILENAME", "TEXINFO_HEADER", "TEXINFO_POST_HEADER",
-;; "TEXINFO_DIR_CATEGORY", "TEXINFO_DIR_TITLE", "TEXINFO_DIR_DESC"
-;; "SUBTITLE" and "SUBAUTHOR".
-
-;;
-;; It introduces 1 new headline property keywords:
-;; "TEXINFO_MENU_TITLE" for optional menu titles.
-;;
-;; To include inline code snippets (for example for generating @kbd{}
-;; and @key{} commands), the following export-snippet keys are
-;; accepted:
-;;
-;;     texinfo
-;;     info
-;;
-;; You can add them for export snippets via any of the below:
-;;
-;;    (add-to-list 'org-export-snippet-translation-alist
-;;                 '("info" . "texinfo"))
-;;
+;; See Org manual for details.
 
 ;;; Code:
 
 
 ;;; Code:
 
@@ -70,8 +39,8 @@
     (center-block . org-texinfo-center-block)
     (clock . org-texinfo-clock)
     (code . org-texinfo-code)
     (center-block . org-texinfo-center-block)
     (clock . org-texinfo-clock)
     (code . org-texinfo-code)
-    (comment . org-texinfo-comment)
-    (comment-block . org-texinfo-comment-block)
+    (comment . (lambda (&rest args) ""))
+    (comment-block . (lambda (&rest args) ""))
     (drawer . org-texinfo-drawer)
     (dynamic-block . org-texinfo-dynamic-block)
     (entity . org-texinfo-entity)
     (drawer . org-texinfo-drawer)
     (dynamic-block . org-texinfo-dynamic-block)
     (entity . org-texinfo-entity)
   :export-block "TEXINFO"
   :filters-alist
   '((:filter-headline . org-texinfo-filter-section-blank-lines)
   :export-block "TEXINFO"
   :filters-alist
   '((:filter-headline . org-texinfo-filter-section-blank-lines)
+    (:filter-parse-tree . org-texinfo--normalize-headlines)
     (:filter-section . org-texinfo-filter-section-blank-lines))
   :menu-entry
   '(?i "Export to Texinfo"
        ((?t "As TEXI file" org-texinfo-export-to-texinfo)
        (?i "As INFO file" org-texinfo-export-to-info)))
   :options-alist
     (:filter-section . org-texinfo-filter-section-blank-lines))
   :menu-entry
   '(?i "Export to Texinfo"
        ((?t "As TEXI file" org-texinfo-export-to-texinfo)
        (?i "As INFO file" org-texinfo-export-to-info)))
   :options-alist
-  '((:texinfo-filename "TEXINFO_FILENAME" nil org-texinfo-filename t)
+  '((:texinfo-filename "TEXINFO_FILENAME" nil nil t)
     (:texinfo-class "TEXINFO_CLASS" nil org-texinfo-default-class t)
     (:texinfo-header "TEXINFO_HEADER" nil nil newline)
     (:texinfo-post-header "TEXINFO_POST_HEADER" nil nil newline)
     (:texinfo-class "TEXINFO_CLASS" nil org-texinfo-default-class t)
     (:texinfo-header "TEXINFO_HEADER" nil nil newline)
     (:texinfo-post-header "TEXINFO_POST_HEADER" nil nil newline)
     (:subauthor "SUBAUTHOR" nil nil newline)
     (:texinfo-dircat "TEXINFO_DIR_CATEGORY" nil nil t)
     (:texinfo-dirtitle "TEXINFO_DIR_TITLE" nil nil t)
     (:subauthor "SUBAUTHOR" nil nil newline)
     (:texinfo-dircat "TEXINFO_DIR_CATEGORY" nil nil t)
     (:texinfo-dirtitle "TEXINFO_DIR_TITLE" nil nil t)
-    (:texinfo-dirdesc "TEXINFO_DIR_DESC" nil nil t)))
+    (:texinfo-dirdesc "TEXINFO_DIR_DESC" nil nil t)
+    (:texinfo-printed-title "TEXINFO_PRINTED_TITLE" nil nil t)))
 
 
 \f
 
 
 \f
   :package-version '(Org . "8.0")
   :group 'org-export)
 
   :package-version '(Org . "8.0")
   :group 'org-export)
 
-;;; Preamble
-
-(defcustom org-texinfo-filename ""
-  "Default filename for Texinfo output."
-  :group 'org-export-texinfo
-  :type '(string :tag "Export Filename"))
+;;;; Preamble
 
 (defcustom org-texinfo-coding-system nil
   "Default document encoding for Texinfo output.
 
 (defcustom org-texinfo-coding-system nil
   "Default document encoding for Texinfo output.
@@ -162,19 +128,42 @@ If `nil' it will default to `buffer-file-coding-system'."
 
 (defcustom org-texinfo-classes
   '(("info"
 
 (defcustom org-texinfo-classes
   '(("info"
-     "\\input texinfo    @c -*- texinfo -*-"
+     "@documentencoding AUTO\n@documentlanguage AUTO"
      ("@chapter %s" . "@unnumbered %s")
      ("@section %s" . "@unnumberedsec %s")
      ("@subsection %s" . "@unnumberedsubsec %s")
      ("@subsubsection %s" . "@unnumberedsubsubsec %s")))
   "Alist of Texinfo classes and associated header and structure.
      ("@chapter %s" . "@unnumbered %s")
      ("@section %s" . "@unnumberedsec %s")
      ("@subsection %s" . "@unnumberedsubsec %s")
      ("@subsubsection %s" . "@unnumberedsubsubsec %s")))
   "Alist of Texinfo classes and associated header and structure.
-If #+Texinfo_CLASS is set in the buffer, use its value and the
+If #+TEXINFO_CLASS is set in the buffer, use its value and the
 associated information.  Here is the structure of each cell:
 
   \(class-name
     header-string
 associated information.  Here is the structure of each cell:
 
   \(class-name
     header-string
-    \(numbered-section . unnumbered-section\)
-    ...\)
+    \(numbered-section . unnumbered-section)
+    ...)
+
+
+The header string
+-----------------
+
+The header string is inserted in the header of the generated
+document, right after \"@setfilename\" and \"@settitle\"
+commands.
+
+If it contains the special string
+
+  \"@documentencoding AUTO\"
+
+\"AUTO\" will be replaced with an appropriate coding system.  See
+`org-texinfo-coding-system' for more information.  Likewise, if
+the string contains the special string
+
+  \"@documentlanguage AUTO\"
+
+\"AUTO\" will be replaced with the language defined in the
+buffer, through #+LANGUAGE keyword, or globally, with
+`org-export-default-language', which see.
+
 
 The sectioning structure
 ------------------------
 
 The sectioning structure
 ------------------------
@@ -186,10 +175,12 @@ section string and will be replaced by the title of the section.
 
 Instead of a list of sectioning commands, you can also specify
 a function name.  That function will be called with two
 
 Instead of a list of sectioning commands, you can also specify
 a function name.  That function will be called with two
-parameters, the \(reduced) level of the headline, and a predicate
+parameters, the reduced) level of the headline, and a predicate
 non-nil when the headline should be numbered.  It must return
 a format string in which the section title will be added."
   :group 'org-export-texinfo
 non-nil when the headline should be numbered.  It must return
 a format string in which the section title will be added."
   :group 'org-export-texinfo
+  :version "24.4"
+  :package-version '(Org . "8.2")
   :type '(repeat
          (list (string :tag "Texinfo class")
                (string :tag "Texinfo header")
   :type '(repeat
          (list (string :tag "Texinfo class")
                (string :tag "Texinfo header")
@@ -200,7 +191,7 @@ a format string in which the section title will be added."
                               (string :tag "unnumbered"))
                         (function :tag "Hook computing sectioning"))))))
 
                               (string :tag "unnumbered"))
                         (function :tag "Hook computing sectioning"))))))
 
-;;; Headline
+;;;; Headline
 
 (defcustom org-texinfo-format-headline-function 'ignore
   "Function to format headline text.
 
 (defcustom org-texinfo-format-headline-function 'ignore
   "Function to format headline text.
@@ -230,22 +221,16 @@ order to reproduce the default set-up:
   :group 'org-export-texinfo
   :type 'function)
 
   :group 'org-export-texinfo
   :type 'function)
 
-;;; Node listing (menu)
+;;;; Node listing (menu)
 
 (defcustom org-texinfo-node-description-column 32
 
 (defcustom org-texinfo-node-description-column 32
-  "Column at which to start the description in the node
-  listings.
-
+  "Column at which to start the description in the node listings.
 If a node title is greater than this length, the description will
 be placed after the end of the title."
   :group 'org-export-texinfo
   :type 'integer)
 
 If a node title is greater than this length, the description will
 be placed after the end of the title."
   :group 'org-export-texinfo
   :type 'integer)
 
-;;; Footnotes
-;;
-;; Footnotes are inserted directly
-
-;;; Timestamps
+;;;; Timestamps
 
 (defcustom org-texinfo-active-timestamp-format "@emph{%s}"
   "A printf format string to be applied to active timestamps."
 
 (defcustom org-texinfo-active-timestamp-format "@emph{%s}"
   "A printf format string to be applied to active timestamps."
@@ -262,14 +247,14 @@ be placed after the end of the title."
   :group 'org-export-texinfo
   :type 'string)
 
   :group 'org-export-texinfo
   :type 'string)
 
-;;; Links
+;;;; Links
 
 (defcustom org-texinfo-link-with-unknown-path-format "@indicateurl{%s}"
   "Format string for links with unknown path type."
   :group 'org-export-texinfo
   :type 'string)
 
 
 (defcustom org-texinfo-link-with-unknown-path-format "@indicateurl{%s}"
   "Format string for links with unknown path type."
   :group 'org-export-texinfo
   :type 'string)
 
-;;; Tables
+;;;; Tables
 
 (defcustom org-texinfo-tables-verbatim nil
   "When non-nil, tables are exported verbatim."
 
 (defcustom org-texinfo-tables-verbatim nil
   "When non-nil, tables are exported verbatim."
@@ -285,12 +270,14 @@ When nil, no transformation is made."
   :group 'org-export-texinfo
   :type '(choice
          (string :tag "Format string")
   :group 'org-export-texinfo
   :type '(choice
          (string :tag "Format string")
-         (const :tag "No formatting")))
+         (const :tag "No formatting" nil)))
 
 (defcustom org-texinfo-def-table-markup "@samp"
 
 (defcustom org-texinfo-def-table-markup "@samp"
-  "Default setting for @table environments.")
+  "Default setting for @table environments."
+  :group 'org-export-texinfo
+  :type 'string)
 
 
-;;; Text markup
+;;;; Text markup
 
 (defcustom org-texinfo-text-markup-alist '((bold . "@strong{%s}")
                                           (code . code)
 
 (defcustom org-texinfo-text-markup-alist '((bold . "@strong{%s}")
                                           (code . code)
@@ -314,7 +301,7 @@ returned as-is."
   :type 'alist
   :options '(bold code italic verbatim comment))
 
   :type 'alist
   :options '(bold code italic verbatim comment))
 
-;;; Drawers
+;;;; Drawers
 
 (defcustom org-texinfo-format-drawer-function
   (lambda (name contents) contents)
 
 (defcustom org-texinfo-format-drawer-function
   (lambda (name contents) contents)
@@ -329,10 +316,10 @@ The function should return the string to be exported.
 The default function simply returns the value of CONTENTS."
   :group 'org-export-texinfo
   :version "24.4"
 The default function simply returns the value of CONTENTS."
   :group 'org-export-texinfo
   :version "24.4"
-  :package-version '(Org . "8.3")
+  :package-version '(Org . "8.2")
   :type 'function)
 
   :type 'function)
 
-;;; Inlinetasks
+;;;; Inlinetasks
 
 (defcustom org-texinfo-format-inlinetask-function 'ignore
   "Function called to format an inlinetask in Texinfo code.
 
 (defcustom org-texinfo-format-inlinetask-function 'ignore
   "Function called to format an inlinetask in Texinfo code.
@@ -348,7 +335,7 @@ The function must accept six parameters:
 The function should return the string to be exported.
 
 For example, the variable could be set to the following function
 The function should return the string to be exported.
 
 For example, the variable could be set to the following function
-in order to mimic default behaviour:
+in order to mimic default behavior:
 
 \(defun org-texinfo-format-inlinetask \(todo type priority name tags contents\)
 \"Format an inline task element for Texinfo export.\"
 
 \(defun org-texinfo-format-inlinetask \(todo type priority name tags contents\)
 \"Format an inline task element for Texinfo export.\"
@@ -368,18 +355,13 @@ in order to mimic default behaviour:
   :group 'org-export-texinfo
   :type 'function)
 
   :group 'org-export-texinfo
   :type 'function)
 
-;;; Src blocks
-;;
-;; Src Blocks are example blocks, except for LISP
+;;;; Compilation
 
 
-;;; Compilation
-
-(defcustom org-texinfo-info-process
-  '("makeinfo %f")
+(defcustom org-texinfo-info-process '("makeinfo %f")
   "Commands to process a Texinfo file to an INFO file.
 This is list of strings, each of them will be given to the shell
 as a command.  %f in the command will be replaced by the full
   "Commands to process a Texinfo file to an INFO file.
 This is list of strings, each of them will be given to the shell
 as a command.  %f in the command will be replaced by the full
-file name, %b by the file base name \(i.e without extension) and
+file name, %b by the file base name (i.e without extension) and
 %o by the base directory of the file."
   :group 'org-export-texinfo
   :type '(repeat :tag "Shell command sequence"
 %o by the base directory of the file."
   :group 'org-export-texinfo
   :type '(repeat :tag "Shell command sequence"
@@ -401,12 +383,13 @@ set `org-texinfo-logfiles-extensions'."
   :group 'org-export-latex
   :type 'boolean)
 
   :group 'org-export-latex
   :type 'boolean)
 
-
 ;;; Constants
 ;;; Constants
+
 (defconst org-texinfo-max-toc-depth 4
 (defconst org-texinfo-max-toc-depth 4
-  "Maximum depth for creation of detailed menu listings.  Beyond
-  this depth Texinfo will not recognize the nodes and will cause
-  errors.  Left as a constant in case this value ever changes.")
+  "Maximum depth for creation of detailed menu listings.
+Beyond this depth, Texinfo will not recognize the nodes and will
+cause errors.  Left as a constant in case this value ever
+changes.")
 
 (defconst org-texinfo-supported-coding-systems
   '("US-ASCII" "UTF-8" "ISO-8859-15" "ISO-8859-1" "ISO-8859-2" "koi8-r" "koi8-u")
 
 (defconst org-texinfo-supported-coding-systems
   '("US-ASCII" "UTF-8" "ISO-8859-15" "ISO-8859-1" "ISO-8859-2" "koi8-r" "koi8-u")
@@ -423,6 +406,31 @@ If two strings share the same prefix (e.g. \"ISO-8859-1\" and
   (let ((blanks (make-string 2 ?\n)))
     (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline)))
 
   (let ((blanks (make-string 2 ?\n)))
     (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline)))
 
+(defun org-texinfo--normalize-headlines (tree back-end info)
+  "Normalize headlines in TREE.
+
+BACK-END is the symbol specifying back-end used for export. INFO
+is a plist used as a communication channel.
+
+Make sure every headline in TREE contains a section, since those
+are required to install a menu.  Also put exactly one blank line
+at the end of each section.
+
+Return new tree."
+  (org-element-map tree 'headline
+    (lambda (hl)
+      (org-element-put-property hl :post-blank 1)
+      (let ((contents (org-element-contents hl)))
+       (when contents
+         (let ((first (org-element-map contents '(headline section)
+                        #'identity info t)))
+           (unless (eq (org-element-type first) 'section)
+             (apply #'org-element-set-contents
+                    hl
+                    (cons `(section (:parent ,hl)) contents)))))))
+    info)
+  tree)
+
 (defun org-texinfo--find-verb-separator (s)
   "Return a character not used in string S.
 This is used to choose a separator for constructs like \\verb."
 (defun org-texinfo--find-verb-separator (s)
   "Return a character not used in string S.
 This is used to choose a separator for constructs like \\verb."
@@ -431,18 +439,6 @@ This is used to choose a separator for constructs like \\verb."
          when (not (string-match (regexp-quote (char-to-string c)) s))
          return (char-to-string c))))
 
          when (not (string-match (regexp-quote (char-to-string c)) s))
          return (char-to-string c))))
 
-(defun org-texinfo--make-option-string (options)
-  "Return a comma separated string of keywords and values.
-OPTIONS is an alist where the key is the options keyword as
-a string, and the value a list containing the keyword value, or
-nil."
-  (mapconcat (lambda (pair)
-              (concat (first pair)
-                      (when (> (length (second pair)) 0)
-                        (concat "=" (second pair)))))
-            options
-            ","))
-
 (defun org-texinfo--text-markup (text markup)
   "Format TEXT depending on MARKUP text markup.
 See `org-texinfo-text-markup-alist' for details."
 (defun org-texinfo--text-markup (text markup)
   "Format TEXT depending on MARKUP text markup.
 See `org-texinfo-text-markup-alist' for details."
@@ -470,207 +466,44 @@ See `org-texinfo-text-markup-alist' for details."
      ;; Else use format string.
      (t (format fmt text)))))
 
      ;; Else use format string.
      (t (format fmt text)))))
 
-(defun org-texinfo--get-node (headline info)
-  "Return node entry associated to HEADLINE.
-INFO is a plist used as a communication channel."
-  (let ((menu-title (org-export-get-alt-title headline info)))
-    (org-texinfo--sanitize-menu
-     (replace-regexp-in-string
-      "%" "%%"
-      (if menu-title (org-export-data menu-title info)
-       (org-texinfo--sanitize-headline
-        (org-element-property :title headline) info))))))
-
-;;; Headline sanitizing
-
-(defun org-texinfo--sanitize-headline (headline info)
-  "Remove all formatting from the text of a headline for use in
-  node and menu listing."
-  (mapconcat 'identity
-            (org-texinfo--sanitize-headline-contents headline info) " "))
-
-(defun org-texinfo--sanitize-headline-contents (headline info)
-  "Retrieve the content of the headline.
-
-Any content that can contain further formatting is checked
-recursively, to ensure that nested content is also properly
-retrieved."
-  (loop for contents in headline append
-       (cond
-        ;; already a string
-        ((stringp contents)
-         (list (replace-regexp-in-string " $" "" contents)))
-        ;; Is exported as-is (value)
-        ((org-element-map contents '(verbatim code)
-           (lambda (value) (org-element-property :value value)) info))
-        ;; Has content and recurse into the content
-        ((org-element-contents contents)
-         (org-texinfo--sanitize-headline-contents
-          (org-element-contents contents) info)))))
-
-;;; Menu sanitizing
-
-(defun org-texinfo--sanitize-menu (title)
-  "Remove invalid characters from TITLE for use in menus and
-nodes.
-
-Based on Texinfo specifications, the following must be removed:
-@ { } ( ) : . ,"
-  (replace-regexp-in-string "[@{}():,.]" "" title))
-
-;;; Content sanitizing
+(defun org-texinfo--get-node (blob info)
+  "Return node or anchor associated to BLOB.
+BLOB is an element or object.  INFO is a plist used as
+a communication channel.  The function guarantees the node or
+anchor name is unique."
+  (let ((cache (plist-get info :texinfo-node-cache)))
+    (or (cdr (assq blob cache))
+       (let ((name
+              (org-texinfo--sanitize-node
+               (case (org-element-type blob)
+                 (headline
+                  (org-export-data (org-export-get-alt-title blob info) info))
+                 ((radio-target target) (org-element-property :value blob))
+                 (otherwise (or (org-element-property :name blob) ""))))))
+         ;; Ensure NAME is unique.
+         (while (rassoc name cache) (setq name (concat name "x")))
+         (plist-put info :texinfo-node-cache (cons (cons blob name) cache))
+         name))))
+
+;;;; Menu sanitizing
+
+(defun org-texinfo--sanitize-node (title)
+  "Bend string TITLE to node line requirements.
+Trim string and collapse multiple whitespace characters as they
+are not significant.  Also remove the following characters: @
+{ } ( ) : . ,"
+  (replace-regexp-in-string
+   "[:,.]" ""
+   (replace-regexp-in-string
+    "\\`(\\(.*)\\)" "[\\1"
+    (org-trim (replace-regexp-in-string "[ \t]\\{2,\\}" " " title)))))
+
+;;;; Content sanitizing
 
 (defun org-texinfo--sanitize-content (text)
 
 (defun org-texinfo--sanitize-content (text)
-  "Ensure characters are properly escaped when used in headlines or blocks.
-
-Escape characters are: @ { }"
-  (replace-regexp-in-string "\\\([@{}]\\\)" "@\\1" text))
-
-;;; Menu creation
-
-(defun org-texinfo--build-menu (tree level info &optional detailed)
-  "Create the @menu/@end menu information from TREE at headline
-level LEVEL.
-
-TREE contains the parse-tree to work with, either of the entire
-document or of a specific parent headline.  LEVEL indicates what
-level of headlines to look at when generating the menu.  INFO is
-a plist containing contextual information.
-
-Detailed determines whether to build a single level of menu, or
-recurse into all children as well."
-  (let ((menu (org-texinfo--generate-menu-list tree level info))
-       output text-menu)
-    (cond
-     (detailed
-      ;; Looping is done within the menu generation.
-      (setq text-menu (org-texinfo--generate-detailed menu level info)))
-     (t
-      (setq text-menu (org-texinfo--generate-menu-items menu info))))
-    (when text-menu
-      (setq output (org-texinfo--format-menu text-menu))
-      (mapconcat 'identity output "\n"))))
-
-(defun org-texinfo--generate-detailed (menu level info)
-  "Generate a detailed listing of all subheadings within MENU starting at LEVEL.
-
-MENU is the parse-tree to work with.  LEVEL is the starting level
-for the menu headlines and from which recursion occurs.  INFO is
-a plist containing contextual information."
-  (when level
-    (let ((max-depth (min org-texinfo-max-toc-depth
-                     (plist-get info :headline-levels))))
-      (when (> max-depth level)
-       (loop for headline in menu append
-             (let* ((title (org-texinfo--menu-headlines headline info))
-                    ;; Create list of menu entries for the next level
-                    (sublist (org-texinfo--generate-menu-list
-                              headline (1+ level) info))
-                    ;; Generate the menu items for that level.  If
-                    ;; there are none omit that heading completely,
-                    ;; otherwise join the title to it's related entries.
-                    (submenu (if (org-texinfo--generate-menu-items sublist info)
-                                 (append (list title)
-                                         (org-texinfo--generate-menu-items sublist info))
-                               'nil))
-                    ;; Start the process over the next level down.
-                    (recursion (org-texinfo--generate-detailed sublist (1+ level) info)))
-               (setq recursion (append submenu recursion))
-               recursion))))))
-
-(defun org-texinfo--generate-menu-list (tree level info)
-  "Generate the list of headlines that are within a given level
-of the tree for further formatting.
-
-TREE is the parse-tree containing the headlines.  LEVEL is the
-headline level to generate a list of.  INFO is a plist holding
-contextual information."
-  (org-element-map tree 'headline
-    (lambda (head)
-      (and (= (org-export-get-relative-level head info) level)
-          ;; Do not take note of footnotes or copying headlines.
-          (not (org-element-property :COPYING head))
-          (not (org-element-property :footnote-section-p head))
-          ;; Collect headline.
-          head))
-    info))
-
-(defun org-texinfo--generate-menu-items (items info)
-  "Generate a list of headline information from the listing ITEMS.
-
-ITEMS is a list of the headlines to be converted into entries.
-INFO is a plist containing contextual information.
-
-Returns a list containing the following information from each
-headline: length, title, description.  This is used to format the
-menu using `org-texinfo--format-menu'."
-  (loop for headline in items collect
-       (let* ((menu-title (org-texinfo--sanitize-menu
-                           (org-export-data
-                            (org-export-get-alt-title headline info)
-                            info)))
-              (title (org-texinfo--sanitize-menu
-                      (org-texinfo--sanitize-headline
-                       (org-element-property :title headline) info)))
-              (descr (org-export-data
-                      (org-element-property :DESCRIPTION headline)
-                      info))
-              (menu-entry (if (string= "" menu-title) title menu-title))
-              (len (length menu-entry))
-              (output (list len menu-entry descr)))
-         output)))
-
-(defun org-texinfo--menu-headlines (headline info)
-  "Retrieve the title from HEADLINE.
-
-INFO is a plist holding contextual information.
-
-Return the headline as a list of (length title description) with
-length of -1 and nil description.  This is used in
-`org-texinfo--format-menu' to identify headlines as opposed to
-entries."
-  (let ((title (org-export-data
-               (org-element-property :title headline) info)))
-    (list -1 title 'nil)))
-
-(defun org-texinfo--format-menu (text-menu)
-  "Format the TEXT-MENU items to be properly printed in the menu.
-
-Each entry in the menu should be provided as (length title
-description).
-
-Headlines in the detailed menu are given length -1 to ensure they
-are never confused with other entries.  They also have no
-description.
-
-Other menu items are output as:
-    Title::     description
-
-With the spacing between :: and description based on the length
-of the longest menu entry."
-
-  (let (output)
-    (setq output
-          (mapcar (lambda (name)
-                    (let* ((title   (nth 1 name))
-                           (desc    (nth 2 name))
-                           (length  (nth 0 name))
-                          (column  (max
-                                    ;;6 is "* " ":: " for inserted text
-                                    length
-                                    (-
-                                     org-texinfo-node-description-column
-                                     6)))
-                          (spacing (- column length)
-                                   ))
-                      (if (> length -1)
-                          (concat "* " title "::  "
-                                  (make-string spacing ?\s)
-                                  (if desc
-                                      (concat desc)))
-                        (concat "\n" title "\n"))))
-                 text-menu))
-    output))
+  "Escape special characters in string TEXT.
+Special characters are: @ { }"
+  (replace-regexp-in-string "[@{}]" "@\\&" text))
 
 ;;; Template
 
 
 ;;; Template
 
@@ -678,145 +511,127 @@ of the longest menu entry."
   "Return complete document string after Texinfo conversion.
 CONTENTS is the transcoded contents string.  INFO is a plist
 holding export options."
   "Return complete document string after Texinfo conversion.
 CONTENTS is the transcoded contents string.  INFO is a plist
 holding export options."
-  (let* ((title (org-export-data (plist-get info :title) info))
-        (info-filename (or (plist-get info :texinfo-filename)
-                           (file-name-nondirectory
-                            (org-export-output-file-name ".info"))))
-        (author (org-export-data (plist-get info :author) info))
-        (lang (org-export-data (plist-get info :language) info))
-        (texinfo-header (plist-get info :texinfo-header))
-        (texinfo-post-header (plist-get info :texinfo-post-header))
-        (subtitle (plist-get info :subtitle))
-        (subauthor (plist-get info :subauthor))
-        (class (plist-get info :texinfo-class))
-        (header (nth 1 (assoc class org-texinfo-classes)))
-        (copying
-         (org-element-map (plist-get info :parse-tree) 'headline
-           (lambda (hl) (and (org-element-property :COPYING hl) hl)) info t))
-        (dircat (plist-get info :texinfo-dircat))
-        (dirtitle (plist-get info :texinfo-dirtitle))
-        (dirdesc (plist-get info :texinfo-dirdesc))
-        ;; Spacing to align description (column 32 - 3 for `* ' and
-        ;; `.' in text.
-        (dirspacing (- 29 (length dirtitle)))
-        (menu (org-texinfo-make-menu info 'main))
-        (detail-menu (org-texinfo-make-menu info 'detailed)))
+  (let ((title (org-export-data (plist-get info :title) info))
+       ;; Copying data is the contents of the first headline in
+       ;; parse tree with a non-nil copying property.
+       (copying (org-element-map (plist-get info :parse-tree) 'headline
+                  (lambda (hl)
+                    (and (org-not-nil (org-element-property :COPYING hl))
+                         (org-element-contents hl)))
+                  info t)))
     (concat
     (concat
-     ;; Header
-     header "\n"
+     "\\input texinfo    @c -*- texinfo -*-\n"
      "@c %**start of header\n"
      "@c %**start of header\n"
-     ;; Filename and Title
-     "@setfilename " info-filename "\n"
-     "@settitle " title "\n"
-     ;; Coding system.
-     (format
-      "@documentencoding %s\n"
-      (catch 'coding-system
-       (let ((case-fold-search t)
-             (name (symbol-name (or org-texinfo-coding-system
-                                    buffer-file-coding-system))))
-         (dolist (system org-texinfo-supported-coding-systems "UTF-8")
-           (when (org-string-match-p (regexp-quote system) name)
-             (throw 'coding-system system))))))
-     "\n"
-     (format "@documentlanguage %s\n" lang)
-     "\n\n"
-     "@c Version and Contact Info\n"
-     "@set AUTHOR " author "\n"
-
-     ;; Additional Header Options set by `#+TEXINFO_HEADER
-     (if texinfo-header
-        (concat "\n"
-                texinfo-header
-                "\n"))
-
-     "@c %**end of header\n"
-     "@finalout\n"
-     "\n\n"
-
-     ;; Additional Header Options set by #+TEXINFO_POST_HEADER
-     (if texinfo-post-header
-        (concat "\n"
-                texinfo-post-header
-                "\n"))
-
-     ;; Copying
-     "@copying\n"
-     ;; Only export the content of the headline, do not need the
-     ;; initial headline.
-     (org-export-data (nth 2 copying) info)
-     "@end copying\n"
-     "\n\n"
-
-     ;; Info directory information
-     ;; Only supply if both title and category are provided
-     (if (and dircat dirtitle)
+     (let ((file (or (plist-get info :texinfo-filename)
+                    (let ((f (plist-get info :output-file)))
+                      (and f (concat (file-name-sans-extension f) ".info"))))))
+       (and file (format "@setfilename %s\n" file)))
+     (format "@settitle %s\n" title)
+     ;; Insert class-defined header.
+     (org-element-normalize-string
+      (let ((header (nth 1 (assoc (plist-get info :texinfo-class)
+                                 org-texinfo-classes)))
+           (coding
+            (catch 'coding-system
+              (let ((case-fold-search t)
+                    (name (symbol-name (or org-texinfo-coding-system
+                                           buffer-file-coding-system))))
+                (dolist (system org-texinfo-supported-coding-systems "UTF-8")
+                  (when (org-string-match-p (regexp-quote system) name)
+                    (throw 'coding-system system))))))
+           (language (plist-get info :language))
+           (case-fold-search nil))
+       ;; Auto coding system.
+       (replace-regexp-in-string
+        "^@documentencoding \\(AUTO\\)$"
+        coding
+        (replace-regexp-in-string
+         "^@documentlanguage \\(AUTO\\)$" language header t nil 1) t nil 1)))
+     ;; Additional header options set by #+TEXINFO_HEADER.
+     (let ((texinfo-header (plist-get info :texinfo-header)))
+       (and texinfo-header (org-element-normalize-string texinfo-header)))
+     "@c %**end of header\n\n"
+     ;; Additional options set by #+TEXINFO_POST_HEADER.
+     (let ((texinfo-post-header (plist-get info :texinfo-post-header)))
+       (and texinfo-post-header
+           (org-element-normalize-string texinfo-post-header)))
+     ;; Copying.
+     (and copying
+         (format "@copying\n%s@end copying\n\n"
+                 (org-element-normalize-string
+                  (org-export-data copying info))))
+     ;; Info directory information.  Only supply if both title and
+     ;; category are provided.
+     (let ((dircat (plist-get info :texinfo-dircat))
+          (dirtitle
+           (let ((title (plist-get info :texinfo-dirtitle)))
+             (and title
+                  (string-match "^\\(?:\\* \\)?\\(.*?\\)\\(\\.\\)?$" title)
+                  (format "* %s." (match-string 1 title))))))
+       (when (and dircat dirtitle)
         (concat "@dircategory " dircat "\n"
                 "@direntry\n"
         (concat "@dircategory " dircat "\n"
                 "@direntry\n"
-                "* " dirtitle "."
-                (make-string dirspacing ?\s)
-                dirdesc "\n"
-                "@end direntry\n"))
-     "\n\n"
-
+                (let ((dirdesc
+                       (let ((desc (plist-get info :texinfo-dirdesc)))
+                         (cond ((not desc) nil)
+                               ((org-string-match-p "\\.$" desc) desc)
+                               (t (concat desc "."))))))
+                  (if dirdesc (format "%-23s %s" dirtitle dirdesc) dirtitle))
+                "\n"
+                "@end direntry\n\n")))
      ;; Title
      ;; Title
+     "@finalout\n"
      "@titlepage\n"
      "@titlepage\n"
-     "@title " title "\n\n"
-     (if subtitle
-        (concat "@subtitle " subtitle "\n"))
-     "@author " author "\n"
-     (if subauthor
-        (concat subauthor "\n"))
-     "\n"
-     "@c The following two commands start the copyright page.\n"
-     "@page\n"
-     "@vskip 0pt plus 1filll\n"
-     "@insertcopying\n"
+     (format "@title %s\n" (or (plist-get info :texinfo-printed-title) title))
+     (let ((subtitle (plist-get info :subtitle)))
+       (and subtitle
+           (org-element-normalize-string
+            (replace-regexp-in-string "^" "@subtitle " subtitle))))
+     (when (plist-get info :with-author)
+       (concat
+       ;; Primary author.
+       (let ((author (org-string-nw-p
+                      (org-export-data (plist-get info :author) info)))
+             (email (and (plist-get info :with-email)
+                         (org-string-nw-p
+                          (org-export-data (plist-get info :email) info)))))
+         (cond ((and author email)
+                (format "@author %s (@email{%s})\n" author email))
+               (author (format "@author %s\n" author))
+               (email (format "@author @email{%s}\n" email))))
+       ;; Other authors.
+       (let ((subauthor (plist-get info :subauthor)))
+         (and subauthor
+              (org-element-normalize-string
+               (replace-regexp-in-string "^" "@author " subauthor))))))
+     (and copying "@page\n@vskip 0pt plus 1filll\n@insertcopying\n")
      "@end titlepage\n\n"
      "@end titlepage\n\n"
-     "@c Output the table of contents at the beginning.\n"
-     "@contents\n\n"
-
+     ;; Table of contents.
+     (and (plist-get info :with-toc) "@contents\n\n")
      ;; Configure Top Node when not for Tex
      "@ifnottex\n"
      "@node Top\n"
      ;; Configure Top Node when not for Tex
      "@ifnottex\n"
      "@node Top\n"
-     "@top " title " Manual\n"
-     "@insertcopying\n"
+     (format "@top %s\n" title)
+     (and copying "@insertcopying\n")
      "@end ifnottex\n\n"
      "@end ifnottex\n\n"
-
-     ;; Do not output menus if they are empty
-     (if menu
-        ;; Menu
-        (concat "@menu\n"
-                menu
-                "\n\n"
-                ;; Detailed Menu
-                (if detail-menu
-                    (concat "@detailmenu\n"
-                            " --- The Detailed Node Listing ---\n"
-                            detail-menu
-                            "\n\n"
-                            "@end detailmenu\n"))
-                "@end menu\n"))
-     "\n\n"
-
-     ;; Document's body.
-     contents
+     ;; Menu.
+     (org-texinfo-make-menu (plist-get info :parse-tree) info 'master)
      "\n"
      "\n"
+     ;; Document's body.
+     contents "\n"
      ;; Creator.
      ;; Creator.
-     (let ((creator-info (plist-get info :with-creator)))
-       (cond
-       ((not creator-info) "")
-       ((eq creator-info 'comment)
-        (format "@c %s\n" (plist-get info :creator)))
-       (t (concat (plist-get info :creator) "\n"))))
+     (case (plist-get info :with-creator)
+       ((nil) nil)
+       (comment (format "@c %s\n" (plist-get info :creator)))
+       (otherwise (concat (plist-get info :creator) "\n")))
      ;; Document end.
      ;; Document end.
-     "\n@bye")))
+     "@bye")))
 
 
 \f
 ;;; Transcode Functions
 
 
 
 \f
 ;;; Transcode Functions
 
-;;; Bold
+;;;; Bold
 
 (defun org-texinfo-bold (bold contents info)
   "Transcode BOLD from Org to Texinfo.
 
 (defun org-texinfo-bold (bold contents info)
   "Transcode BOLD from Org to Texinfo.
@@ -824,7 +639,7 @@ CONTENTS is the text with bold markup.  INFO is a plist holding
 contextual information."
   (org-texinfo--text-markup contents 'bold))
 
 contextual information."
   (org-texinfo--text-markup contents 'bold))
 
-;;; Center Block
+;;;; Center Block
 
 (defun org-texinfo-center-block (center-block contents info)
   "Transcode a CENTER-BLOCK element from Org to Texinfo.
 
 (defun org-texinfo-center-block (center-block contents info)
   "Transcode a CENTER-BLOCK element from Org to Texinfo.
@@ -832,7 +647,7 @@ CONTENTS holds the contents of the block.  INFO is a plist used
 as a communication channel."
   contents)
 
 as a communication channel."
   contents)
 
-;;; Clock
+;;;; Clock
 
 (defun org-texinfo-clock (clock contents info)
   "Transcode a CLOCK element from Org to Texinfo.
 
 (defun org-texinfo-clock (clock contents info)
   "Transcode a CLOCK element from Org to Texinfo.
@@ -849,7 +664,7 @@ information."
                     (and time (format " (%s)" time)))))
    "@*"))
 
                     (and time (format " (%s)" time)))))
    "@*"))
 
-;;; Code
+;;;; Code
 
 (defun org-texinfo-code (code contents info)
   "Transcode a CODE object from Org to Texinfo.
 
 (defun org-texinfo-code (code contents info)
   "Transcode a CODE object from Org to Texinfo.
@@ -857,23 +672,7 @@ CONTENTS is nil.  INFO is a plist used as a communication
 channel."
   (org-texinfo--text-markup (org-element-property :value code) 'code))
 
 channel."
   (org-texinfo--text-markup (org-element-property :value code) 'code))
 
-;;; Comment
-
-(defun org-texinfo-comment (comment contents info)
-  "Transcode a COMMENT object from Org to Texinfo.
-CONTENTS is the text in the comment.  INFO is a plist holding
-contextual information."
-  (org-texinfo--text-markup (org-element-property :value comment) 'comment))
-
-;;; Comment Block
-
-(defun org-texinfo-comment-block (comment-block contents info)
-  "Transcode a COMMENT-BLOCK object from Org to Texinfo.
-CONTENTS is the text within the block.  INFO is a plist holding
-contextual information."
-  (format "@ignore\n%s@end ignore" (org-element-property :value comment-block)))
-
-;;; Drawer
+;;;; Drawer
 
 (defun org-texinfo-drawer (drawer contents info)
   "Transcode a DRAWER element from Org to Texinfo.
 
 (defun org-texinfo-drawer (drawer contents info)
   "Transcode a DRAWER element from Org to Texinfo.
@@ -884,15 +683,15 @@ holding contextual information."
                          name contents)))
     output))
 
                          name contents)))
     output))
 
-;;; Dynamic Block
+;;;; Dynamic Block
 
 (defun org-texinfo-dynamic-block (dynamic-block contents info)
   "Transcode a DYNAMIC-BLOCK element from Org to Texinfo.
 CONTENTS holds the contents of the block.  INFO is a plist
 
 (defun org-texinfo-dynamic-block (dynamic-block contents info)
   "Transcode a DYNAMIC-BLOCK element from Org to Texinfo.
 CONTENTS holds the contents of the block.  INFO is a plist
-holding contextual information.  See `org-export-data'."
+holding contextual information."
   contents)
 
   contents)
 
-;;; Entity
+;;;; Entity
 
 (defun org-texinfo-entity (entity contents info)
   "Transcode an ENTITY object from Org to Texinfo.
 
 (defun org-texinfo-entity (entity contents info)
   "Transcode an ENTITY object from Org to Texinfo.
@@ -901,7 +700,7 @@ contextual information."
   (let ((ent (org-element-property :latex entity)))
     (if (org-element-property :latex-math-p entity) (format "@math{%s}" ent) ent)))
 
   (let ((ent (org-element-property :latex entity)))
     (if (org-element-property :latex-math-p entity) (format "@math{%s}" ent) ent)))
 
-;;; Example Block
+;;;; Example Block
 
 (defun org-texinfo-example-block (example-block contents info)
   "Transcode an EXAMPLE-BLOCK element from Org to Texinfo.
 
 (defun org-texinfo-example-block (example-block contents info)
   "Transcode an EXAMPLE-BLOCK element from Org to Texinfo.
@@ -910,7 +709,7 @@ information."
   (format "@verbatim\n%s@end verbatim"
          (org-export-format-code-default example-block info)))
 
   (format "@verbatim\n%s@end verbatim"
          (org-export-format-code-default example-block info)))
 
-;;; Export Block
+;;;; Export Block
 
 (defun org-texinfo-export-block (export-block contents info)
   "Transcode a EXPORT-BLOCK element from Org to Texinfo.
 
 (defun org-texinfo-export-block (export-block contents info)
   "Transcode a EXPORT-BLOCK element from Org to Texinfo.
@@ -918,7 +717,7 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
   (when (string= (org-element-property :type export-block) "TEXINFO")
     (org-remove-indentation (org-element-property :value export-block))))
 
   (when (string= (org-element-property :type export-block) "TEXINFO")
     (org-remove-indentation (org-element-property :value export-block))))
 
-;;; Export Snippet
+;;;; Export Snippet
 
 (defun org-texinfo-export-snippet (export-snippet contents info)
   "Transcode a EXPORT-SNIPPET object from Org to Texinfo.
 
 (defun org-texinfo-export-snippet (export-snippet contents info)
   "Transcode a EXPORT-SNIPPET object from Org to Texinfo.
@@ -926,7 +725,7 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
   (when (eq (org-export-snippet-backend export-snippet) 'texinfo)
     (org-element-property :value export-snippet)))
 
   (when (eq (org-export-snippet-backend export-snippet) 'texinfo)
     (org-element-property :value export-snippet)))
 
-;;; Fixed Width
+;;;; Fixed Width
 
 (defun org-texinfo-fixed-width (fixed-width contents info)
   "Transcode a FIXED-WIDTH element from Org to Texinfo.
 
 (defun org-texinfo-fixed-width (fixed-width contents info)
   "Transcode a FIXED-WIDTH element from Org to Texinfo.
@@ -936,8 +735,7 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
           (org-texinfo--sanitize-content
            (org-element-property :value fixed-width)))))
 
           (org-texinfo--sanitize-content
            (org-element-property :value fixed-width)))))
 
-;;; Footnote Reference
-;;
+;;;; Footnote Reference
 
 (defun org-texinfo-footnote-reference (footnote contents info)
   "Create a footnote reference for FOOTNOTE.
 
 (defun org-texinfo-footnote-reference (footnote contents info)
   "Create a footnote reference for FOOTNOTE.
@@ -948,7 +746,7 @@ plist holding contextual information."
     (format "@footnote{%s}"
            (org-trim (org-export-data def info)))))
 
     (format "@footnote{%s}"
            (org-trim (org-export-data def info)))))
 
-;;; Headline
+;;;; Headline
 
 (defun org-texinfo-headline (headline contents info)
   "Transcode a HEADLINE element from Org to Texinfo.
 
 (defun org-texinfo-headline (headline contents info)
   "Transcode a HEADLINE element from Org to Texinfo.
@@ -958,66 +756,29 @@ holding contextual information."
         (level (org-export-get-relative-level headline info))
         (numberedp (org-export-numbered-headline-p headline info))
         (class-sectioning (assoc class org-texinfo-classes))
         (level (org-export-get-relative-level headline info))
         (numberedp (org-export-numbered-headline-p headline info))
         (class-sectioning (assoc class org-texinfo-classes))
-        ;; Find the index type, if any
+        ;; Find the index type, if any.
         (index (org-element-property :INDEX headline))
         (index (org-element-property :INDEX headline))
-        ;; Check if it is an appendix
-        (appendix (org-element-property :APPENDIX headline))
-        ;; Retrieve headline text
-        (text (org-texinfo--sanitize-headline
-               (org-element-property :title headline) info))
         ;; Create node info, to insert it before section formatting.
         ;; Create node info, to insert it before section formatting.
-        ;; Use custom menu title if present
+        ;; Use custom menu title if present.
         (node (format "@node %s\n" (org-texinfo--get-node headline info)))
         (node (format "@node %s\n" (org-texinfo--get-node headline info)))
-        ;; Menus must be generated with first child, otherwise they
-        ;; will not nest properly
-        (menu (let* ((first (org-export-first-sibling-p headline info))
-                     (parent (org-export-get-parent-headline headline))
-                     (title (org-texinfo--sanitize-headline
-                             (org-element-property :title parent) info))
-                     heading listing
-                     (tree (plist-get info :parse-tree)))
-                (if first
-                    (org-element-map (plist-get info :parse-tree) 'headline
-                      (lambda (ref)
-                        (if (member title (org-element-property :title ref))
-                            (push ref heading)))
-                      info t))
-                (setq listing (org-texinfo--build-menu
-                               (car heading) level info))
-                (if listing
-                    (setq listing (replace-regexp-in-string
-                                   "%" "%%" listing)
-                          listing (format
-                                   "\n@menu\n%s\n@end menu\n\n" listing))
-                  'nil)))
         ;; Section formatting will set two placeholders: one for the
         ;; title and the other for the contents.
         (section-fmt
         ;; Section formatting will set two placeholders: one for the
         ;; title and the other for the contents.
         (section-fmt
-         (let ((sec (if (and (symbolp (nth 2 class-sectioning))
-                             (fboundp (nth 2 class-sectioning)))
-                        (funcall (nth 2 class-sectioning) level numberedp)
-                      (nth (1+ level) class-sectioning))))
-           (cond
-            ;; No section available for that LEVEL.
-            ((not sec) nil)
-            ;; Section format directly returned by a function.
-            ((stringp sec) sec)
-            ;; (numbered-section . unnumbered-section)
-            ((not (consp (cdr sec)))
+         (if (org-not-nil (org-element-property :APPENDIX headline))
+             "@appendix %s\n%s"
+           (let ((sec (if (and (symbolp (nth 2 class-sectioning))
+                               (fboundp (nth 2 class-sectioning)))
+                          (funcall (nth 2 class-sectioning) level numberedp)
+                        (nth (1+ level) class-sectioning))))
              (cond
              (cond
-              ;;If an index, always unnumbered
-              (index
-               (concat menu node (cdr sec) "\n%s"))
-              (appendix
-               (concat menu node (replace-regexp-in-string
-                                  "unnumbered"
-                                  "appendix"
-                                  (cdr sec)) "\n%s"))
-               ;; Otherwise number as needed.
-              (t
-               (concat menu node
-                       (funcall
-                        (if numberedp #'car #'cdr) sec) "\n%s")))))))
+              ;; No section available for that LEVEL.
+              ((not sec) nil)
+              ;; Section format directly returned by a function.
+              ((stringp sec) sec)
+              ;; (numbered-section . unnumbered-section)
+              ((not (consp (cdr sec)))
+               (concat (if (or index (not numberedp)) (cdr sec) (car sec))
+                       "\n%s"))))))
         (todo
          (and (plist-get info :with-todo-keywords)
               (let ((todo (org-element-property :todo-keyword headline)))
         (todo
          (and (plist-get info :with-todo-keywords)
               (let ((todo (org-element-property :todo-keyword headline)))
@@ -1027,100 +788,54 @@ holding contextual information."
                    (org-export-get-tags headline info)))
         (priority (and (plist-get info :with-priority)
                        (org-element-property :priority headline)))
                    (org-export-get-tags headline info)))
         (priority (and (plist-get info :with-priority)
                        (org-element-property :priority headline)))
-        ;; Create the headline text along with a no-tag version.  The
-        ;; latter is required to remove tags from table of contents.
-        (full-text (org-texinfo--sanitize-content
-                    (if (not (eq org-texinfo-format-headline-function 'ignore))
-                        ;; User-defined formatting function.
-                        (funcall org-texinfo-format-headline-function
-                                 todo todo-type priority text tags)
-                      ;; Default formatting.
-                      (concat
-                       (when todo
-                         (format "@strong{%s} " todo))
-                       (when priority (format "@emph{#%s} " priority))
-                       text
-                       (when tags
-                         (format " :%s:"
-                                 (mapconcat 'identity tags ":")))))))
-        (full-text-no-tag
-         (org-texinfo--sanitize-content
-          (if (not (eq org-texinfo-format-headline-function 'ignore))
-              ;; User-defined formatting function.
-              (funcall org-texinfo-format-headline-function
-                       todo todo-type priority text nil)
-            ;; Default formatting.
-            (concat
-             (when todo (format "@strong{%s} " todo))
-             (when priority (format "@emph{#%c} " priority))
-             text))))
-        (pre-blanks
-         (make-string (org-element-property :pre-blank headline) 10)))
+        (text (org-export-data (org-element-property :title headline) info))
+        (full-text (if (not (eq org-texinfo-format-headline-function 'ignore))
+                       ;; User-defined formatting function.
+                       (funcall org-texinfo-format-headline-function
+                                todo todo-type priority text tags)
+                     ;; Default formatting.
+                     (concat
+                      (when todo
+                        (format "@strong{%s} " todo))
+                      (when priority (format "@emph{#%s} " priority))
+                      text
+                      (when tags
+                        (format " :%s:"
+                                (mapconcat 'identity tags ":"))))))
+        (contents (if (org-string-nw-p contents) (concat "\n" contents) "")))
     (cond
      ;; Case 1: This is a footnote section: ignore it.
      ((org-element-property :footnote-section-p headline) nil)
      ;; Case 2: This is the `copying' section: ignore it
      ;;         This is used elsewhere.
     (cond
      ;; Case 1: This is a footnote section: ignore it.
      ((org-element-property :footnote-section-p headline) nil)
      ;; Case 2: This is the `copying' section: ignore it
      ;;         This is used elsewhere.
-     ((org-element-property :COPYING headline) nil)
+     ((org-not-nil (org-element-property :COPYING headline)) nil)
      ;; Case 3: An index.  If it matches one of the known indexes,
      ;;         print it as such following the contents, otherwise
      ;;         print the contents and leave the index up to the user.
      (index
      ;; Case 3: An index.  If it matches one of the known indexes,
      ;;         print it as such following the contents, otherwise
      ;;         print the contents and leave the index up to the user.
      (index
-      (format
-       section-fmt full-text
-       (concat pre-blanks contents "\n"
-              (if (member index '("cp" "fn" "ky" "pg" "tp" "vr"))
-                  (concat "@printindex " index)))))
+      (concat node
+             (format
+              section-fmt
+              full-text
+              (concat contents
+                      (and (member index '("cp" "fn" "ky" "pg" "tp" "vr"))
+                           (concat "\n@printindex " index))))))
      ;; Case 4: This is a deep sub-tree: export it as a list item.
      ;;         Also export as items headlines for which no section
      ;;         format has been found.
      ((or (not section-fmt) (org-export-low-level-p headline info))
       ;; Build the real contents of the sub-tree.
      ;; Case 4: This is a deep sub-tree: export it as a list item.
      ;;         Also export as items headlines for which no section
      ;;         format has been found.
      ((or (not section-fmt) (org-export-low-level-p headline info))
       ;; Build the real contents of the sub-tree.
-      (let ((low-level-body
-            (concat
-             ;; If the headline is the first sibling, start a list.
-             (when (org-export-first-sibling-p headline info)
-               (format "@%s\n" (if numberedp 'enumerate 'itemize)))
-             ;; Itemize headline
-             "@item\n" full-text "\n" pre-blanks contents)))
-       ;; If headline is not the last sibling simply return
-       ;; LOW-LEVEL-BODY.  Otherwise, also close the list, before any
-       ;; blank line.
-       (if (not (org-export-last-sibling-p headline info)) low-level-body
-         (replace-regexp-in-string
-          "[ \t\n]*\\'"
-          (format "\n@end %s" (if numberedp 'enumerate 'itemize))
-          low-level-body))))
+      (concat (and (org-export-first-sibling-p headline info)
+                  (format "@%s\n" (if numberedp 'enumerate 'itemize)))
+             "@item\n" full-text "\n"
+             contents
+             (if (org-export-last-sibling-p headline info)
+                 (format "@end %s" (if numberedp 'enumerate 'itemize))
+               "\n")))
      ;; Case 5: Standard headline.  Export it as a section.
      ;; Case 5: Standard headline.  Export it as a section.
-     (t
-      (cond
-       ((not (and tags (eq (plist-get info :with-tags) 'not-in-toc)))
-       ;; Regular section.  Use specified format string.
-       (format (replace-regexp-in-string "%]" "%%]" section-fmt) full-text
-               (concat pre-blanks contents)))
-       ((string-match "\\`@\\(.*?\\){" section-fmt)
-       ;; If tags should be removed from table of contents, insert
-       ;; title without tags as an alternative heading in sectioning
-       ;; command.
-       (format (replace-match (concat (match-string 1 section-fmt) "[%s]")
-                              nil nil section-fmt 1)
-               ;; Replace square brackets with parenthesis since
-               ;; square brackets are not supported in optional
-               ;; arguments.
-               (replace-regexp-in-string
-                "\\[" "("
-                (replace-regexp-in-string
-                 "\\]" ")"
-                 full-text-no-tag))
-               full-text
-               (concat pre-blanks contents)))
-       (t
-       ;; Impossible to add an alternative heading.  Fallback to
-       ;; regular sectioning format string.
-       (format (replace-regexp-in-string "%]" "%%]" section-fmt) full-text
-               (concat pre-blanks contents))))))))
-
-;;; Inline Src Block
+     (t (concat node (format section-fmt full-text contents))))))
+
+;;;; Inline Src Block
 
 (defun org-texinfo-inline-src-block (inline-src-block contents info)
   "Transcode an INLINE-SRC-BLOCK element from Org to Texinfo.
 
 (defun org-texinfo-inline-src-block (inline-src-block contents info)
   "Transcode an INLINE-SRC-BLOCK element from Org to Texinfo.
@@ -1130,7 +845,7 @@ contextual information."
         (separator (org-texinfo--find-verb-separator code)))
     (concat "@verb{" separator code separator "}")))
 
         (separator (org-texinfo--find-verb-separator code)))
     (concat "@verb{" separator code separator "}")))
 
-;;; Inlinetask
+;;;; Inlinetask
 
 (defun org-texinfo-inlinetask (inlinetask contents info)
   "Transcode an INLINETASK element from Org to Texinfo.
 
 (defun org-texinfo-inlinetask (inlinetask contents info)
   "Transcode an INLINETASK element from Org to Texinfo.
@@ -1163,7 +878,7 @@ holding contextual information."
                        "\n")
                full-title contents)))))
 
                        "\n")
                full-title contents)))))
 
-;;; Italic
+;;;; Italic
 
 (defun org-texinfo-italic (italic contents info)
   "Transcode ITALIC from Org to Texinfo.
 
 (defun org-texinfo-italic (italic contents info)
   "Transcode ITALIC from Org to Texinfo.
@@ -1171,18 +886,18 @@ CONTENTS is the text with italic markup.  INFO is a plist holding
 contextual information."
   (org-texinfo--text-markup contents 'italic))
 
 contextual information."
   (org-texinfo--text-markup contents 'italic))
 
-;;; Item
+;;;; Item
 
 (defun org-texinfo-item (item contents info)
   "Transcode an ITEM element from Org to Texinfo.
 CONTENTS holds the contents of the item.  INFO is a plist holding
 contextual information."
 
 (defun org-texinfo-item (item contents info)
   "Transcode an ITEM element from Org to Texinfo.
 CONTENTS holds the contents of the item.  INFO is a plist holding
 contextual information."
-  (let* ((tag (org-element-property :tag item))
-        (desc (org-export-data tag info)))
-    (concat "\n@item " (if tag desc) "\n"
-           (and contents (org-trim contents)) "\n")))
+  (format "@item%s\n%s"
+         (let ((tag (org-element-property :tag item)))
+           (if tag (concat " " (org-export-data tag info)) ""))
+         (or contents "")))
 
 
-;;; Keyword
+;;;; Keyword
 
 (defun org-texinfo-keyword (keyword contents info)
   "Transcode a KEYWORD element from Org to Texinfo.
 
 (defun org-texinfo-keyword (keyword contents info)
   "Transcode a KEYWORD element from Org to Texinfo.
@@ -1198,14 +913,14 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
      ((string= key "TINDEX") (format "@tindex %s" value))
      ((string= key "VINDEX") (format "@vindex %s" value)))))
 
      ((string= key "TINDEX") (format "@tindex %s" value))
      ((string= key "VINDEX") (format "@vindex %s" value)))))
 
-;;; Line Break
+;;;; Line Break
 
 (defun org-texinfo-line-break (line-break contents info)
   "Transcode a LINE-BREAK object from Org to Texinfo.
 CONTENTS is nil.  INFO is a plist holding contextual information."
   "@*\n")
 
 
 (defun org-texinfo-line-break (line-break contents info)
   "Transcode a LINE-BREAK object from Org to Texinfo.
 CONTENTS is nil.  INFO is a plist holding contextual information."
   "@*\n")
 
-;;; Link
+;;;; Link
 
 (defun org-texinfo-link (link desc info)
   "Transcode a LINK object from Org to Texinfo.
 
 (defun org-texinfo-link (link desc info)
   "Transcode a LINK object from Org to Texinfo.
@@ -1220,60 +935,72 @@ INFO is a plist holding contextual information.  See
         (path (cond
                ((member type '("http" "https" "ftp"))
                 (concat type ":" raw-path))
         (path (cond
                ((member type '("http" "https" "ftp"))
                 (concat type ":" raw-path))
-               ((string= type "file")
-                (if (file-name-absolute-p raw-path)
-                    (concat "file://" (expand-file-name raw-path))
-                  (concat "file://" raw-path)))
+               ((and (string= type "file") (file-name-absolute-p raw-path))
+                (concat "file:" raw-path))
                (t raw-path)))
                (t raw-path)))
-        (email (if (string= type "mailto")
-                   (let ((text (replace-regexp-in-string
-                                "@" "@@" raw-path)))
-                     (concat text (if desc (concat "," desc))))))
         protocol)
     (cond
         protocol)
     (cond
-     ;; Links pointing to a headline: Find destination and build
-     ;; appropriate referencing command.
-     ((member type '("custom-id" "id"))
-      (let ((destination (org-export-resolve-id-link link info)))
+     ((equal type "radio")
+      (let ((destination (org-export-resolve-radio-link link info)))
+       (if (not destination) desc
+         (format "@ref{%s,,%s}"
+                 (org-texinfo--get-node destination info)
+                 desc))))
+     ((member type '("custom-id" "id" "fuzzy"))
+      (let ((destination
+            (if (equal type "fuzzy")
+                (org-export-resolve-fuzzy-link link info)
+              (org-export-resolve-id-link link info))))
        (case (org-element-type destination)
        (case (org-element-type destination)
+         ((nil)
+          (format org-texinfo-link-with-unknown-path-format
+                  (org-texinfo--sanitize-content path)))
          ;; Id link points to an external file.
          (plain-text
           (if desc (format "@uref{file://%s,%s}" destination desc)
             (format "@uref{file://%s}" destination)))
          ;; Id link points to an external file.
          (plain-text
           (if desc (format "@uref{file://%s,%s}" destination desc)
             (format "@uref{file://%s}" destination)))
-         ;; LINK points to a headline.  Use the headline as the NODE target
          (headline
           (format "@ref{%s,%s}"
                   (org-texinfo--get-node destination info)
          (headline
           (format "@ref{%s,%s}"
                   (org-texinfo--get-node destination info)
-                  (or desc "")))
+                  (cond
+                   (desc)
+                   ((org-export-numbered-headline-p destination info)
+                    (org-export-data
+                     (org-element-property :title destination) info))
+                   (t
+                    (mapconcat
+                     #'number-to-string
+                     (org-export-get-headline-number destination info) ".")))))
          (otherwise
          (otherwise
-          (let ((path (org-export-solidify-link-text path)))
-            (if (not desc) (format "@ref{%s}" path)
-              (format "@ref{%s,,%s}" path desc)))))))
-     ((member type '("info"))
+          (let ((topic
+                 (or desc
+                     (if (and (eq (org-element-type destination) 'headline)
+                              (not (org-export-numbered-headline-p
+                                    destination info)))
+                         (org-export-data
+                          (org-element-property :title destination) info))
+                     (let ((n (org-export-get-ordinal destination info)))
+                       (cond
+                        ((not n) nil)
+                        ((integerp n) n)
+                        (t (mapconcat #'number-to-string n ".")))))))
+            (when topic
+              (format "@ref{%s,,%s}"
+                      (org-texinfo--get-node destination info)
+                      topic)))))))
+     ((equal type "info")
       (let* ((info-path (split-string path "[:#]"))
             (info-manual (car info-path))
             (info-node (or (cadr info-path) "top"))
             (title (or desc "")))
        (format "@ref{%s,%s,,%s,}" info-node title info-manual)))
       (let* ((info-path (split-string path "[:#]"))
             (info-manual (car info-path))
             (info-node (or (cadr info-path) "top"))
             (title (or desc "")))
        (format "@ref{%s,%s,,%s,}" info-node title info-manual)))
-     ((member type '("fuzzy"))
-      (let ((destination (org-export-resolve-fuzzy-link link info)))
-       (case (org-element-type destination)
-         ;; Id link points to an external file.
-         (plain-text
-          (if desc (format "@uref{file://%s,%s}" destination desc)
-            (format "@uref{file://%s}" destination)))
-         ;; LINK points to a headline.  Use the headline as the NODE target
-         (headline
-          (format "@ref{%s,%s}"
-                  (org-texinfo--get-node destination info)
-                  (or desc "")))
-         (otherwise
-          (let ((path (org-export-solidify-link-text path)))
-            (if (not desc) (format "@ref{%s}" path)
-              (format "@ref{%s,,%s}" path desc)))))))
-     ;; Special case for email addresses
-     (email
-      (format "@email{%s}" email))
+     ((string= type "mailto")
+      (format "@email{%s}"
+             (concat (org-texinfo--sanitize-content path)
+                     (and desc (concat "," desc)))))
+     ((let ((protocol (nth 2 (assoc type org-link-protocols))))
+       (and (functionp protocol)
+            (funcall protocol (org-link-unescape path) desc 'texinfo))))
      ;; External link with a description part.
      ((and path desc) (format "@uref{%s,%s}" path desc))
      ;; External link without a description part.
      ;; External link with a description part.
      ((and path desc) (format "@uref{%s,%s}" path desc))
      ;; External link without a description part.
@@ -1282,27 +1009,97 @@ INFO is a plist holding contextual information.  See
      (t (format org-texinfo-link-with-unknown-path-format desc)))))
 
 
      (t (format org-texinfo-link-with-unknown-path-format desc)))))
 
 
-;;; Menu
-
-(defun org-texinfo-make-menu (info level)
-  "Create the menu for inclusion in the texifo document.
-
-INFO is the parsed buffer that contains the headlines.  LEVEL
-determines whether to make the main menu, or the detailed menu.
-
-This is only used for generating the primary menu.  In-Node menus
-are generated directly."
-  (let ((parse (plist-get info :parse-tree)))
-    (cond
-     ;; Generate the main menu
-     ((eq level 'main) (org-texinfo--build-menu parse 1 info))
-     ;; Generate the detailed (recursive) menu
-     ((eq level 'detailed)
-      ;; Requires recursion
-      ;;(org-texinfo--build-detailed-menu parse top info)
-      (org-texinfo--build-menu parse 1 info 'detailed)))))
-
-;;; Paragraph
+;;;; Menu
+
+(defun org-texinfo-make-menu (scope info &optional master)
+  "Create the menu for inclusion in the Texinfo document.
+
+SCOPE is a headline or a full parse tree.  INFO is the
+communication channel, as a plist.
+
+When optional argument MASTER is non-nil, generate a master menu,
+including detailed node listing."
+  (let ((menu (org-texinfo--build-menu scope info)))
+    (when (org-string-nw-p menu)
+      (org-element-normalize-string
+       (format
+       "@menu\n%s@end menu"
+       (concat menu
+               (when master
+                 (let ((detailmenu
+                        (org-texinfo--build-menu
+                         scope info
+                         (let ((toc-depth (plist-get info :with-toc)))
+                           (if (wholenump toc-depth) toc-depth
+                             org-texinfo-max-toc-depth)))))
+                   (when (org-string-nw-p detailmenu)
+                     (concat "\n@detailmenu\n"
+                             "--- The Detailed Node Listing ---\n\n"
+                             detailmenu
+                             "@end detailmenu\n"))))))))))
+
+(defun org-texinfo--build-menu (scope info &optional level)
+  "Build menu for entries within SCOPE.
+SCOPE is a headline or a full parse tree.  INFO is a plist
+containing contextual information.  When optional argument LEVEL
+is an integer, build the menu recursively, down to this depth."
+  (cond
+   ((not level)
+    (org-texinfo--format-entries (org-texinfo--menu-entries scope info) info))
+   ((zerop level) nil)
+   (t
+    (org-element-normalize-string
+     (mapconcat
+      (lambda (h)
+       (let ((entries (org-texinfo--menu-entries h info)))
+         (when entries
+           (concat
+            (format "%s\n\n%s\n"
+                    (org-export-data (org-export-get-alt-title h info) info)
+                    (org-texinfo--format-entries entries info))
+            (org-texinfo--build-menu h info (1- level))))))
+      (org-texinfo--menu-entries scope info) "")))))
+
+(defun org-texinfo--format-entries (entries info)
+  "Format all direct menu entries in SCOPE, as a string.
+SCOPE is either a headline or a full Org document.  INFO is
+a plist containing contextual information."
+  (org-element-normalize-string
+   (mapconcat
+    (lambda (h)
+      (let* ((title (org-export-data
+                    (org-export-get-alt-title h info) info))
+            (node (org-texinfo--get-node h info))
+            (entry (concat "* " title ":"
+                           (if (string= title node) ":"
+                             (concat " " node ". "))))
+            (desc (org-element-property :DESCRIPTION h)))
+       (if (not desc) entry
+         (format (format "%%-%ds %%s" org-texinfo-node-description-column)
+                 entry desc))))
+    entries "\n")))
+
+(defun org-texinfo--menu-entries (scope info)
+  "List direct children in SCOPE needing a menu entry.
+SCOPE is a headline or a full parse tree.  INFO is a plist
+holding contextual information."
+  (let* ((cache (or (plist-get info :texinfo-entries-cache)
+                   (plist-get (plist-put info :texinfo-entries-cache
+                                         (make-hash-table :test #'eq))
+                              :texinfo-entries-cache)))
+        (cached-entries (gethash scope cache 'no-cache)))
+    (if (not (eq cached-entries 'no-cache)) cached-entries
+      (puthash scope
+              (org-element-map (org-element-contents scope) 'headline
+                (lambda (h)
+                  (and (not (org-not-nil (org-element-property :COPYING h)))
+                       (not (org-element-property :footnote-section-p h))
+                       (not (org-export-low-level-p h info))
+                       h))
+                info nil 'headline)
+              cache))))
+
+;;;; Paragraph
 
 (defun org-texinfo-paragraph (paragraph contents info)
   "Transcode a PARAGRAPH element from Org to Texinfo.
 
 (defun org-texinfo-paragraph (paragraph contents info)
   "Transcode a PARAGRAPH element from Org to Texinfo.
@@ -1310,32 +1107,27 @@ CONTENTS is the contents of the paragraph, as a string.  INFO is
 the plist used as a communication channel."
   contents)
 
 the plist used as a communication channel."
   contents)
 
-;;; Plain List
+;;;; Plain List
 
 (defun org-texinfo-plain-list (plain-list contents info)
   "Transcode a PLAIN-LIST element from Org to Texinfo.
 CONTENTS is the contents of the list.  INFO is a plist holding
 contextual information."
   (let* ((attr (org-export-read-attribute :attr_texinfo plain-list))
 
 (defun org-texinfo-plain-list (plain-list contents info)
   "Transcode a PLAIN-LIST element from Org to Texinfo.
 CONTENTS is the contents of the list.  INFO is a plist holding
 contextual information."
   (let* ((attr (org-export-read-attribute :attr_texinfo plain-list))
-        (indic (or (plist-get attr :indic)
-                   org-texinfo-def-table-markup))
-        (type (org-element-property :type plain-list))
+        (indic (or (plist-get attr :indic) org-texinfo-def-table-markup))
         (table-type (plist-get attr :table-type))
         (table-type (plist-get attr :table-type))
-        ;; Ensure valid texinfo table type.
-        (table-type (if (member table-type '("ftable" "vtable")) table-type
-                      "table"))
+        (type (org-element-property :type plain-list))
         (list-type (cond
                     ((eq type 'ordered) "enumerate")
                     ((eq type 'unordered) "itemize")
         (list-type (cond
                     ((eq type 'ordered) "enumerate")
                     ((eq type 'unordered) "itemize")
-                    ((eq type 'descriptive) table-type))))
-    (format "@%s%s\n@end %s"
-           (if (eq type 'descriptive)
-               (concat list-type " " indic)
-             list-type)
+                    ((member table-type '("ftable" "vtable")) table-type)
+                    (t "table"))))
+    (format "@%s\n%s@end %s"
+           (if (eq type 'descriptive) (concat list-type " " indic) list-type)
            contents
            list-type)))
 
            contents
            list-type)))
 
-;;; Plain Text
+;;;; Plain Text
 
 (defun org-texinfo-plain-text (text info)
   "Transcode a TEXT string from Org to Texinfo.
 
 (defun org-texinfo-plain-text (text info)
   "Transcode a TEXT string from Org to Texinfo.
@@ -1366,7 +1158,7 @@ contextual information."
     ;; Return value.
     output))
 
     ;; Return value.
     output))
 
-;;; Planning
+;;;; Planning
 
 (defun org-texinfo-planning (planning contents info)
   "Transcode a PLANNING element from Org to Texinfo.
 
 (defun org-texinfo-planning (planning contents info)
   "Transcode a PLANNING element from Org to Texinfo.
@@ -1402,7 +1194,7 @@ information."
     " ")
    "@*"))
 
     " ")
    "@*"))
 
-;;; Property Drawer
+;;;; Property Drawer
 
 (defun org-texinfo-property-drawer (property-drawer contents info)
   "Transcode a PROPERTY-DRAWER element from Org to Texinfo.
 
 (defun org-texinfo-property-drawer (property-drawer contents info)
   "Transcode a PROPERTY-DRAWER element from Org to Texinfo.
@@ -1412,7 +1204,7 @@ information."
   ;; lines nonetheless.
   "")
 
   ;; lines nonetheless.
   "")
 
-;;; Quote Block
+;;;; Quote Block
 
 (defun org-texinfo-quote-block (quote-block contents info)
   "Transcode a QUOTE-BLOCK element from Org to Texinfo.
 
 (defun org-texinfo-quote-block (quote-block contents info)
   "Transcode a QUOTE-BLOCK element from Org to Texinfo.
@@ -1424,7 +1216,7 @@ holding contextual information."
                                  (format " %s" title)))))
     (format "%s\n%s@end quotation" start-quote contents)))
 
                                  (format " %s" title)))))
     (format "%s\n%s@end quotation" start-quote contents)))
 
-;;; Quote Section
+;;;; Quote Section
 
 (defun org-texinfo-quote-section (quote-section contents info)
   "Transcode a QUOTE-SECTION element from Org to Texinfo.
 
 (defun org-texinfo-quote-section (quote-section contents info)
   "Transcode a QUOTE-SECTION element from Org to Texinfo.
@@ -1433,7 +1225,7 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
                (org-element-property :value quote-section))))
     (when value (format "@verbatim\n%s@end verbatim" value))))
 
                (org-element-property :value quote-section))))
     (when value (format "@verbatim\n%s@end verbatim" value))))
 
-;;; Radio Target
+;;;; Radio Target
 
 (defun org-texinfo-radio-target (radio-target text info)
   "Transcode a RADIO-TARGET object from Org to Texinfo.
 
 (defun org-texinfo-radio-target (radio-target text info)
   "Transcode a RADIO-TARGET object from Org to Texinfo.
@@ -1444,15 +1236,17 @@ contextual information."
           (org-element-property :value radio-target))
          text))
 
           (org-element-property :value radio-target))
          text))
 
-;;; Section
+;;;; Section
 
 (defun org-texinfo-section (section contents info)
   "Transcode a SECTION element from Org to Texinfo.
 CONTENTS holds the contents of the section.  INFO is a plist
 holding contextual information."
 
 (defun org-texinfo-section (section contents info)
   "Transcode a SECTION element from Org to Texinfo.
 CONTENTS holds the contents of the section.  INFO is a plist
 holding contextual information."
-  contents)
+  (concat contents
+         (let ((parent (org-export-get-parent-headline section)))
+           (and parent (org-texinfo-make-menu parent info)))))
 
 
-;;; Special Block
+;;;; Special Block
 
 (defun org-texinfo-special-block (special-block contents info)
   "Transcode a SPECIAL-BLOCK element from Org to Texinfo.
 
 (defun org-texinfo-special-block (special-block contents info)
   "Transcode a SPECIAL-BLOCK element from Org to Texinfo.
@@ -1460,34 +1254,26 @@ CONTENTS holds the contents of the block.  INFO is a plist used
 as a communication channel."
   contents)
 
 as a communication channel."
   contents)
 
-;;; Src Block
+;;;; Src Block
 
 (defun org-texinfo-src-block (src-block contents info)
   "Transcode a SRC-BLOCK element from Org to Texinfo.
 CONTENTS holds the contents of the item.  INFO is a plist holding
 contextual information."
 
 (defun org-texinfo-src-block (src-block contents info)
   "Transcode a SRC-BLOCK element from Org to Texinfo.
 CONTENTS holds the contents of the item.  INFO is a plist holding
 contextual information."
-  (let* ((lang (org-element-property :language src-block))
-        (lisp-p (string-match-p "lisp" lang))
-        (src-contents (org-texinfo--sanitize-content
-                  (org-export-format-code-default src-block info))))
-    (cond
-     ;; Case 1.  Lisp Block
-     (lisp-p
-      (format "@lisp\n%s@end lisp"
-             src-contents))
-     ;; Case 2.  Other blocks
-     (t
-      (format "@example\n%s@end example"
-             src-contents)))))
+  (let ((lispp (org-string-match-p "lisp"
+                                  (org-element-property :language src-block)))
+       (code (org-texinfo--sanitize-content
+              (org-export-format-code-default src-block info))))
+    (format (if lispp "@lisp\n%s@end lisp" "@example\n%s@end example") code)))
 
 
-;;; Statistics Cookie
+;;;; Statistics Cookie
 
 (defun org-texinfo-statistics-cookie (statistics-cookie contents info)
   "Transcode a STATISTICS-COOKIE object from Org to Texinfo.
 CONTENTS is nil.  INFO is a plist holding contextual information."
   (org-element-property :value statistics-cookie))
 
 
 (defun org-texinfo-statistics-cookie (statistics-cookie contents info)
   "Transcode a STATISTICS-COOKIE object from Org to Texinfo.
 CONTENTS is nil.  INFO is a plist holding contextual information."
   (org-element-property :value statistics-cookie))
 
-;;; Subscript
+;;;; Subscript
 
 (defun org-texinfo-subscript (subscript contents info)
   "Transcode a SUBSCRIPT object from Org to Texinfo.
 
 (defun org-texinfo-subscript (subscript contents info)
   "Transcode a SUBSCRIPT object from Org to Texinfo.
@@ -1495,7 +1281,7 @@ CONTENTS is the contents of the object.  INFO is a plist holding
 contextual information."
   (format "@math{_%s}" contents))
 
 contextual information."
   (format "@math{_%s}" contents))
 
-;;; Superscript
+;;;; Superscript
 
 (defun org-texinfo-superscript (superscript contents info)
   "Transcode a SUPERSCRIPT object from Org to Texinfo.
 
 (defun org-texinfo-superscript (superscript contents info)
   "Transcode a SUPERSCRIPT object from Org to Texinfo.
@@ -1503,96 +1289,47 @@ CONTENTS is the contents of the object.  INFO is a plist holding
 contextual information."
   (format "@math{^%s}" contents))
 
 contextual information."
   (format "@math{^%s}" contents))
 
-;;; Table
-;;
-;; `org-texinfo-table' is the entry point for table transcoding.  It
-;; takes care of tables with a "verbatim" attribute.  Otherwise, it
-;; delegates the job to either `org-texinfo-table--table.el-table' or
-;; `org-texinfo-table--org-table' functions, depending of the type of
-;; the table.
-;;
-;; `org-texinfo-table--align-string' is a subroutine used to build
-;; alignment string for Org tables.
+;;;; Table
 
 (defun org-texinfo-table (table contents info)
   "Transcode a TABLE element from Org to Texinfo.
 CONTENTS is the contents of the table.  INFO is a plist holding
 contextual information."
 
 (defun org-texinfo-table (table contents info)
   "Transcode a TABLE element from Org to Texinfo.
 CONTENTS is the contents of the table.  INFO is a plist holding
 contextual information."
-  (cond
-   ;; Case 1: verbatim table.
-   ((or org-texinfo-tables-verbatim
-       (let ((attr (mapconcat 'identity
-                              (org-element-property :attr_latex table)
-                              " ")))
-         (and attr (string-match "\\<verbatim\\>" attr))))
-    (format "@verbatim \n%s\n@end verbatim"
-           ;; Re-create table, without affiliated keywords.
-           (org-trim
-            (org-element-interpret-data
-             `(table nil ,@(org-element-contents table))))))
-   ;; Case 2: table.el table.  Convert it using appropriate tools.
-   ((eq (org-element-property :type table) 'table.el)
-    (org-texinfo-table--table.el-table table contents info))
-   ;; Case 3: Standard table.
-   (t (org-texinfo-table--org-table table contents info))))
+  (if (eq (org-element-property :type table) 'table.el)
+      (format "@verbatim\n%s@end verbatim"
+             (org-element-normalize-string
+              (org-element-property :value table)))
+    (let* ((col-width (org-export-read-attribute :attr_texinfo table :columns))
+          (columns
+           (if col-width (format "@columnfractions %s" col-width)
+             (org-texinfo-table-column-widths table info))))
+      (format "@multitable %s\n%s@end multitable"
+             columns
+             contents))))
 
 (defun org-texinfo-table-column-widths (table info)
   "Determine the largest table cell in each column to process alignment.
 
 (defun org-texinfo-table-column-widths (table info)
   "Determine the largest table cell in each column to process alignment.
-
 TABLE is the table element to transcode.  INFO is a plist used as
 a communication channel."
 TABLE is the table element to transcode.  INFO is a plist used as
 a communication channel."
-  (let* ((rows (org-element-map table 'table-row 'identity info))
-        (collected (loop for row in rows collect
-                         (org-element-map row 'table-cell 'identity info)))
-        (number-cells (length (car collected)))
-        cells counts)
-    (loop for row in collected do
-         (push (mapcar (lambda (ref)
-                         (let* ((start (org-element-property :contents-begin ref))
-                                (end (org-element-property :contents-end ref))
-                                (length (- end start)))
-                           length)) row) cells))
-    (setq cells (org-remove-if 'null cells))
-    (push (loop for count from 0 to (- number-cells 1) collect
-               (loop for item in cells collect
-                     (nth count item))) counts)
-    (mapconcat (lambda (size)
-                (make-string size ?a)) (mapcar (lambda (ref)
-                                                 (apply 'max `(,@ref))) (car counts))
-                "} {")))
-
-(defun org-texinfo-table--org-table (table contents info)
-  "Return appropriate Texinfo code for an Org table.
-
-TABLE is the table type element to transcode.  CONTENTS is its
-contents, as a string.  INFO is a plist used as a communication
-channel.
-
-This function assumes TABLE has `org' as its `:type' attribute."
-  (let* ((attr (org-export-read-attribute :attr_texinfo table))
-        (col-width (plist-get attr :columns))
-        (columns (if col-width
-                     (format "@columnfractions %s"
-                             col-width)
-                   (format "{%s}"
-                           (org-texinfo-table-column-widths
-                            table info)))))
-    ;; Prepare the final format string for the table.
-    (cond
-     ;; Longtable.
-     ;; Others.
-     (t (concat
-        (format "@multitable %s\n%s@end multitable"
-                columns
-                contents))))))
-
-(defun org-texinfo-table--table.el-table (table contents info)
-  "Returns nothing.
-
-Rather than return an invalid table, nothing is returned."
-  'nil)
-
-;;; Table Cell
+  (let ((widths (make-vector (cdr (org-export-table-dimensions table info)) 0)))
+    (org-element-map table 'table-row
+      (lambda (row)
+       (let ((idx 0))
+         (org-element-map row 'table-cell
+           (lambda (cell)
+             ;; Length of the cell in the original buffer is only an
+             ;; approximation of the length of the cell in the
+             ;; output.  It can sometimes fail (e.g. it considers
+             ;; "/a/" being larger than "ab").
+             (let ((w (- (org-element-property :contents-end cell)
+                         (org-element-property :contents-begin cell))))
+               (aset widths idx (max w (aref widths idx))))
+             (incf idx))
+           info)))
+      info)
+    (format "{%s}" (mapconcat (lambda (w) (make-string w ?a)) widths "} {"))))
+
+;;;; Table Cell
 
 (defun org-texinfo-table-cell (table-cell contents info)
   "Transcode a TABLE-CELL element from Org to Texinfo.
 
 (defun org-texinfo-table-cell (table-cell contents info)
   "Transcode a TABLE-CELL element from Org to Texinfo.
@@ -1609,7 +1346,7 @@ a communication channel."
            contents)
          (when (org-export-get-next-element table-cell info) "\n@tab ")))
 
            contents)
          (when (org-export-get-next-element table-cell info) "\n@tab ")))
 
-;;; Table Row
+;;;; Table Row
 
 (defun org-texinfo-table-row (table-row contents info)
   "Transcode a TABLE-ROW element from Org to Texinfo.
 
 (defun org-texinfo-table-row (table-row contents info)
   "Transcode a TABLE-ROW element from Org to Texinfo.
@@ -1618,21 +1355,15 @@ a communication channel."
   ;; Rules are ignored since table separators are deduced from
   ;; borders of the current row.
   (when (eq (org-element-property :type table-row) 'standard)
   ;; Rules are ignored since table separators are deduced from
   ;; borders of the current row.
   (when (eq (org-element-property :type table-row) 'standard)
-   (let ((rowgroup-tag
-         (cond
-          ;; Case 1: Belongs to second or subsequent rowgroup.
-          ((not (= 1 (org-export-table-row-group table-row info)))
-           "@item ")
-          ;; Case 2: Row is from first rowgroup.  Table has >=1 rowgroups.
-          ((org-export-table-has-header-p
-            (org-export-get-parent-table table-row) info)
-           "@headitem ")
-          ;; Case 3: Row is from first and only row group.
-          (t "@item "))))
-     (when (eq (org-element-property :type table-row) 'standard)
-       (concat rowgroup-tag contents "\n")))))
-
-;;; Target
+    (let ((rowgroup-tag
+          (if (and (= 1 (org-export-table-row-group table-row info))
+                   (org-export-table-has-header-p
+                    (org-export-get-parent-table table-row) info))
+              "@headitem "
+            "@item ")))
+      (concat rowgroup-tag contents "\n"))))
+
+;;;; Target
 
 (defun org-texinfo-target (target contents info)
   "Transcode a TARGET object from Org to Texinfo.
 
 (defun org-texinfo-target (target contents info)
   "Transcode a TARGET object from Org to Texinfo.
@@ -1641,7 +1372,7 @@ information."
   (format "@anchor{%s}"
          (org-export-solidify-link-text (org-element-property :value target))))
 
   (format "@anchor{%s}"
          (org-export-solidify-link-text (org-element-property :value target))))
 
-;;; Timestamp
+;;;; Timestamp
 
 (defun org-texinfo-timestamp (timestamp contents info)
   "Transcode a TIMESTAMP object from Org to Texinfo.
 
 (defun org-texinfo-timestamp (timestamp contents info)
   "Transcode a TIMESTAMP object from Org to Texinfo.
@@ -1656,7 +1387,7 @@ information."
        (format org-texinfo-inactive-timestamp-format value))
       (t (format org-texinfo-diary-timestamp-format value)))))
 
        (format org-texinfo-inactive-timestamp-format value))
       (t (format org-texinfo-diary-timestamp-format value)))))
 
-;;; Verbatim
+;;;; Verbatim
 
 (defun org-texinfo-verbatim (verbatim contents info)
   "Transcode a VERBATIM object from Org to Texinfo.
 
 (defun org-texinfo-verbatim (verbatim contents info)
   "Transcode a VERBATIM object from Org to Texinfo.
@@ -1664,26 +1395,13 @@ CONTENTS is nil.  INFO is a plist used as a communication
 channel."
   (org-texinfo--text-markup (org-element-property :value verbatim) 'verbatim))
 
 channel."
   (org-texinfo--text-markup (org-element-property :value verbatim) 'verbatim))
 
-;;; Verse Block
+;;;; Verse Block
 
 (defun org-texinfo-verse-block (verse-block contents info)
   "Transcode a VERSE-BLOCK element from Org to Texinfo.
 CONTENTS is verse block contents. INFO is a plist holding
 contextual information."
 
 (defun org-texinfo-verse-block (verse-block contents info)
   "Transcode a VERSE-BLOCK element from Org to Texinfo.
 CONTENTS is verse block contents. INFO is a plist holding
 contextual information."
-  ;; In a verse environment, add a line break to each newline
-  ;; character and change each white space at beginning of a line
-  ;; into a space of 1 em.  Also change each blank line with
-  ;; a vertical space of 1 em.
-  (progn
-    (setq contents (replace-regexp-in-string
-                   "^ *\\\\\\\\$" "\\\\vspace*{1em}"
-                   (replace-regexp-in-string
-                    "\\(\\\\\\\\\\)?[ \t]*\n" " \\\\\\\\\n" contents)))
-    (while (string-match "^[ \t]+" contents)
-      (let ((new-str (format "\\hspace*{%dem}"
-                            (length (match-string 0 contents)))))
-       (setq contents (replace-match new-str nil t contents))))
-    (format "\\begin{verse}\n%s\\end{verse}" contents)))
+  (format "@display\n%s@end display" contents))
 
 \f
 ;;; Interactive functions
 
 \f
 ;;; Interactive functions
@@ -1797,29 +1515,21 @@ Return INFO file name or an error if it couldn't be produced."
         errors)
     (message (format "Processing Texinfo file %s..." file))
     (save-window-excursion
         errors)
     (message (format "Processing Texinfo file %s..." file))
     (save-window-excursion
-      (cond
-       ;; A function is provided: Apply it.
-       ((functionp org-texinfo-info-process)
-       (funcall org-texinfo-info-process (shell-quote-argument file)))
-       ;; A list is provided: Replace %b, %f and %o with appropriate
-       ;; values in each command before applying it.  Output is
-       ;; redirected to "*Org INFO Texinfo Output*" buffer.
-       ((consp org-texinfo-info-process)
-       (let ((outbuf (get-buffer-create "*Org INFO Texinfo Output*")))
-         (mapc
-          (lambda (command)
-            (shell-command
-             (replace-regexp-in-string
-              "%b" (shell-quote-argument base-name)
-              (replace-regexp-in-string
-               "%f" (shell-quote-argument full-name)
-               (replace-regexp-in-string
-                "%o" (shell-quote-argument out-dir) command t t) t t) t t)
-             outbuf))
-          org-texinfo-info-process)
-         ;; Collect standard errors from output buffer.
-         (setq errors (org-texinfo-collect-errors outbuf))))
-       (t (error "No valid command to process to Info")))
+      ;; Replace %b, %f and %o with appropriate values in each command
+      ;; before applying it.  Output is redirected to "*Org INFO
+      ;; Texinfo Output*" buffer.
+      (let ((outbuf (get-buffer-create "*Org INFO Texinfo Output*")))
+       (dolist (command org-texinfo-info-process)
+         (shell-command
+          (replace-regexp-in-string
+           "%b" (shell-quote-argument base-name)
+           (replace-regexp-in-string
+            "%f" (shell-quote-argument full-name)
+            (replace-regexp-in-string
+             "%o" (shell-quote-argument out-dir) command t t) t t) t t)
+          outbuf))
+       ;; Collect standard errors from output buffer.
+       (setq errors (org-texinfo-collect-errors outbuf)))
       (let ((infofile (concat out-dir base-name ".info")))
        ;; Check for process failure.  Provide collected errors if
        ;; possible.
       (let ((infofile (concat out-dir base-name ".info")))
        ;; Check for process failure.  Provide collected errors if
        ;; possible.