| #! /usr/bin/gawk -f |
| |
| # prepinfo.awk --- correct node lines and build menus |
| |
| # Copyright (C) 1997, 2001, 2018 Arnold David Robbins |
| # (arnold@skeeve.com) |
| # |
| # PREPINFO is free software; you can redistribute it and/or |
| # modify it under the terms of the GNU General Public License |
| # as published by the Free Software Foundation; either |
| # version 3 of the License, or (at your option) any later |
| # version. |
| # |
| # PREPINFO is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software |
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
| # USA |
| # |
| # The most recent version of PREPINFO may be found at |
| # https://github.com/arnoldrobbins/prepinfo |
| BEGIN { |
| # Manifest constants |
| TRUE = 1 |
| FALSE = 0 |
| |
| # Levels at which different nodes can be |
| Level["@top"] = 0 |
| Level["@appendix"] = 1 |
| Level["@chapter"] = 1 |
| Level["@majorheading"] = 1 |
| Level["@unnumbered"] = 1 |
| Level["@appendixsec"] = 2 |
| Level["@section"] = 2 |
| Level["@heading"] = 2 |
| Level["@unnumberedsec"] = 2 |
| Level["@unnumberedsubsec"] = 3 |
| Level["@appendixsubsec"] = 3 |
| Level["@subsection"] = 3 |
| Level["@subheading"] = 3 |
| Level["@unnumberedsubsubsec"] = 4 |
| Level["@appendixsubsubsec"] = 4 |
| Level["@subsubsection"] = 4 |
| Level["@subsubheading"] = 4 |
| # Length of menus |
| if (Menumargin == 0) |
| Menumargin = 78 |
| |
| # Length of menu item |
| if (Min_menitem_length == 0) |
| Min_menitem_length = 29 |
| # Ensure that we were called correctly |
| if (ARGC != 2) { |
| print "usage: prepinfo texinfo-file > new-file\n" > "/dev/stderr" |
| exit 1 |
| } |
| # Arrange for two passes over input file |
| Pass = 1 |
| ARGV[2] = "Pass=2" |
| ARGV[3] = ARGV[1] |
| ARGC = 4 |
| # Initialize stacks |
| Lastlevel = -1 |
| Up[-1] = "(dir)" |
| Prev[0] = "(dir)" |
| if (Debug ~ "args") { |
| for (i = 0; i < ARGC; i++) |
| printf("ARGV[%d] = %s\n", i, ARGV[i]) > "/dev/stderr" |
| } |
| } |
| /^@ignore/ && Pass == 1, /^@end[ \t]+ignore/ && Pass == 1 { |
| next |
| } |
| # @node lines, save nodename |
| |
| $1 == "@node" { |
| Name = getnodename($0) |
| Nodeseen = TRUE |
| |
| if ((l = length(Name)) > Maxlen) |
| Maxlen = l |
| |
| if (Debug ~ "nodenames") |
| printf("Name = %s\n", Name) > "/dev/stderr" |
| |
| if (Pass == 1) |
| next |
| } |
| Pass == 1 && /^@c(omment)?[ \t]+fakenode/ { |
| if (Debug ~ "fakenodes") |
| printf("fakenode at %d\n", FNR) > "/dev/stderr" |
| Fakenode = TRUE |
| next |
| } |
| # Build the tree of nodes in associative array `Node' |
| Pass == 1 && ($1 in Level) { |
| # Skip fake nodes --- titles without @node lines |
| if (Fakenode) { |
| if (Debug ~ "fakenodes") |
| printf("%s at %d is a fakenode\n", $1, FNR) > "/dev/stderr" |
| Fakenode = FALSE |
| next |
| } |
| |
| if (Debug ~ "titles") |
| printf("Processing %s: Name = %s\n", $1, Name) > "/dev/stderr" |
| |
| # Save type |
| type = $1 |
| |
| if (! Nodeseen) { |
| err_prefix() |
| printf("%s line with no @node or fakenode line\n", type) \ |
| > "/dev/stderr" |
| next |
| } else |
| Nodeseen = FALSE # reset it |
| # Squirrel away the info |
| levelnum = Level[type] |
| Node[Name ".level"] = levelnum |
| |
| if (Debug ~ "titles") { |
| printf("Node[%s\".level\"] = %s\n", Name, Node[Name ".level"]) > "/dev/stderr" |
| printf("Node[%s\".name\"] = %s\n", Name, Node[Name ".name"]) > "/dev/stderr" |
| } |
| if (levelnum == Lastlevel) { |
| # E.g., two sections in a row |
| Node[Name ".up"] = Up[levelnum - 1] |
| if (levelnum in Prev) { |
| Node[Prev[levelnum] ".next"] = Name |
| Node[Name ".prev"] = Prev[levelnum] |
| } |
| Prev[levelnum] = Name |
| Up[levelnum] = Name |
| } |
| else if (levelnum < Lastlevel) { |
| # section, now chapter |
| Lastlevel = levelnum |
| Node[Name ".up"] = Up[levelnum - 1] |
| if (levelnum in Prev) { |
| Node[Name ".prev"] = Prev[levelnum] |
| Node[Prev[levelnum] ".next"] = Name |
| } |
| Prev[levelnum] = Name |
| Up[levelnum] = Name |
| } |
| else { |
| # chapter, now section, |
| # levelnum > Lastlevel |
| Node[Name ".up"] = Up[levelnum - 1] |
| Node[Up[Lastlevel] ".child"] = Name |
| Prev[levelnum] = Name |
| Up[levelnum] = Name |
| Lastlevel = levelnum |
| } |
| |
| # For master menu |
| if (Level[$1] >= 2) |
| List[++Sequence] = Name |
| |
| if (Debug ~ "titles") { |
| printf("Node[%s\".prev\"] = %s\n", Name, Node[Name ".prev"]) > "/dev/stderr" |
| printf("Node[%s\".up\"] = %s\n", Name, Node[Name ".up"]) > "/dev/stderr" |
| printf("Node[%s\".child\"] = %s\n", Name, Node[Name ".child"]) > "/dev/stderr" |
| } |
| } |
| /^@menu/ && Pass == 1, /^@end[ \t]menu/ && Pass == 1 { |
| if (/^@menu/ || /^@end[ \t]menu/) |
| next |
| |
| if (/^@detailmenu/ || /^@end[ \t]+detailmenu/) |
| next |
| if (Debug ~ "menu") |
| printf("processing: %s\n", $0) > "/dev/stderr" |
| if (/^\*/) { |
| if (In_menitem) { # Save info from previous line |
| Node[node ".mendesc"] = desc |
| Node[node ".longdesc"] = longdesc |
| if (Debug ~ "mendesc") { |
| printf("Node[%s.mendesc] = %s\n", node, Node[node ".mendesc"]) > "/dev/stderr" |
| printf("Node[%s.longdesc] = %s\n", node, Node[node ".longdesc"]) > "/dev/stderr" |
| } |
| } |
| In_menitem = TRUE |
| # Pull apart menu item |
| $1 = "" # nuke ``*'' |
| $0 = $0 # reparse line |
| i1 = index($0, ":") |
| if (i1 <= 0) { |
| err_prefix() |
| printf("badly formed menu item") > "/dev/stderr" |
| next |
| } |
| # desc: node. long desc |
| if (substr($0, i1 + 1, 1) != ":") { |
| i2 = index($0, ".") |
| if (i2 <= 0) { |
| err_prefix() |
| printf("badly formed menu item") > "/dev/stderr" |
| next |
| } |
| desc = substr($0, 1, i1 - 1) |
| node = substr($0, i1 + 1, i2 - i1 - 1) |
| sub(/^[ \t]+/, "", node) |
| sub(/[ \t]+$/, "", node) |
| sub(/^[ \t]+/, "", desc) |
| sub(/[ \t]+$/, "", desc) |
| longdesc = substr($0, i2 + 1) |
| } |
| else { # nodename:: long desc |
| desc = "" |
| node = substr($0, 1, i1 - 1) |
| sub(/^[ \t]+/, "", node) |
| sub(/[ \t]+$/, "", node) |
| longdesc = substr($0, i1 + 2) |
| } |
| } |
| else if (In_menitem) { # Continuation line |
| longdesc = longdesc " " $0 |
| } else |
| In_menitem = FALSE |
| |
| Node[node ".mendesc"] = desc |
| Node[node ".longdesc"] = longdesc |
| |
| if (Debug ~ "mendesc") { |
| printf("Node[%s.mendesc] = %s\n", node, Node[node ".mendesc"]) > "/dev/stderr" |
| printf("Node[%s.longdesc] = %s\n", node, Node[node ".longdesc"]) > "/dev/stderr" |
| } |
| if (Debug ~ "menu") { |
| printf("Menu:: Name %s: desc %s: longdesc %s\n", node, desc, longdesc) > "/dev/stderr" |
| } |
| } |
| Pass == 2 && Debug ~ "dumptitles" && FNR <= 1 { |
| for (i in Node) |
| printf("Node[%s] = %s\n", i, Node[i]) | "sort 1>&2" |
| close("sort 1>&2") |
| } |
| # Print @node line |
| Pass == 2 && /^@node/ { |
| Name = getnodename($0) |
| |
| # Top node is special. Its next is the first child |
| n = Node[Name ".next"] |
| if (Node[Name ".level"] == 0 && n == "") |
| n = Node[Name ".child"] |
| |
| printf("@node %s, %s, %s, %s\n", Name, n, |
| Node[Name ".prev"] ? Node[Name ".prev"] \ |
| : Node[Name ".up"], |
| Node[Name ".up"]) |
| next |
| } |
| # Print menu for current node |
| Pass == 2 && /^@menu/ { |
| # First, skip current contents of menu |
| do { |
| if ((getline) <= 0) { |
| err_prefix() |
| printf("unexpected EOF inside menu\n") > "/dev/stderr" |
| exit 1 |
| } |
| } while (! /^@end[ \t]menu/) |
| # Next, compute maximum length of a node name |
| max = 0 |
| for (n = Node[Name ".child"]; (n ".next") in Node; |
| n = Node[n ".next"]) { |
| if ((n ".desc") in Node) |
| s = Node[n ".desc"] ": " n "." |
| else |
| s = n "::" |
| l = length(s) |
| if (l > max) |
| max = l |
| } |
| if (max < Min_menitem_length) |
| max = Min_menitem_length |
| # Now dump the menu |
| print "@menu" |
| |
| for (n = Node[Name ".child"]; (n ".next") in Node; |
| n = Node[n ".next"]) { |
| print_menuitem(n, max) |
| } |
| print_menuitem(n, max) |
| |
| if (Name == "Top") { # Master Menu |
| if (Maxlen < Min_menitem_length) |
| Maxlen = Min_menitem_length |
| print "" |
| print "@detailmenu" |
| for (i = 1; i <= Sequence; i++) |
| print_menuitem(List[i], Maxlen) |
| print "@end detailmenu" |
| } |
| print "@end menu" |
| next |
| } |
| Pass == 2 # print |
| function err_prefix() |
| { |
| printf("prepinfo:%s:%d: ", FILENAME, FNR) > "/dev/stderr" |
| } |
| function getnodename(str) |
| { |
| sub(/^@node[ \t]+/, "", str) |
| sub(/[ \t]*,.*/, "", str) |
| |
| if (Debug ~ "nodenames") |
| printf("getnodename: return %s\n", str) > "/dev/stderr" |
| |
| return str |
| } |
| # Print nice description with reformatting as needed |
| |
| function print_menuitem(n, max, # params |
| nodesc, i, dwords, count, p) |
| { |
| nodesc = FALSE |
| if (! ((n ".longdesc") in Node)) { |
| err_prefix() |
| printf("warning: %s: no long description\n", n) > "/dev/stderr" |
| nodesc = TRUE |
| } else |
| count = split(Node[n ".longdesc"], dwords, " ") |
| |
| if ((n ".desc") in Node) |
| s = Node[n ".desc"] ": " n "." |
| else |
| s = n "::" |
| printf("* %-*s", max, s) |
| |
| if (Debug ~ "mendescitem") |
| printf("<* %-*s>\n", max, s) > "/dev/stderr" |
| p = max + 2 |
| if (! nodesc) { |
| for (i = 1; i <= count; i++) { |
| l = length(dwords[i]) |
| if (l == 0) |
| continue |
| if (p + l + 1 > Menumargin) { |
| printf("\n%*s", max + 2, " ") |
| p = max + 2 |
| } |
| printf(" %s", dwords[i]) |
| p += l + 1 |
| } |
| } |
| print "" |
| } |