#! /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 ""
}