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