#! /usr/bin/gawk -f
#
# Copyright (C) 2013, 2014, 2015, 2016, 2019 Arnold David Robbins
#
# This file is part of TexiWeb Jr., a literate programming system.
#
# TexiWeb Jr. 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.
#
# TexiWeb Jr. 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, see .
#
# Up-to-date source code for TexiWeb Jr. can be obtained via
# Git from github:
#
# git clone http://github.com/arnoldrobbins/texiwebjr
#
BEGIN {
v[0] = 0
if ("version" in PROCINFO) {
match(PROCINFO["version"], /^[0-9]+\./, v);
}
if (v[0] < 4) {
print("gawk >= 4.0 required") > "/dev/stderr"
exit 63 # for "missing" script
}
}
BEGIN {
TRUE = 1
FALSE = 0
File_chunk_pattern = "^@\\(([^)]+)@\\)[[:space:]]*=[[:space:]]*$"
Code_chunk_pattern = "^@" "<(.+)" "@>[[:space:]]*=[[:space:]]*$"
Chunk_name_pattern = "@<[^>\n]+@>"
}
# Error checking:
# Use brackets to avoid triggering the warning on ourselves!
/(^<[@])|(>[@]([[:space:]]*=[[:space:]]*)?$)/ {
# Ditto, with concatenation
warning("<" "@ or >" "@ used instead of @" "< or @" ">\n\t%s\n",
$0)
}
END {
check_unfinished()
}
BEGIN {
if (ARGC < 2)
fatal(_"usage: jrweave file.twjr [...]\n")
Pass = 1
n = ARGC
ARGV[ARGC++] = "Pass=2"
for (i = 1; i < n; i++) {
if (ARGV[i] == "-" || ARGV[i] == "/dev/stdin")
fatal(_"jrweave: standard input not allowed\n")
ARGV[ARGC++] = ARGV[i]
}
}
Pass == 2 && FNR == 1 && Debug ~ /pass2/ {
junk++
}
BEGIN {
print_do_not_edit(ARGV[1])
}
# print_do_not_edit --- create and print warning
function print_do_not_edit(filename, i, pl, pr, l, s, t)
{
t = _"DO NOT EDIT THIS FILE!!!!"
if (ARGC > 4) # more than one file
s = sprintf(_"It was created by jrweave from `%s' (or maybe others).",
filename)
else
s = sprintf(_"It was created by jrweave from `%s'.", filename)
l = length(s)
pl = int((l - length(t)) / 2) # padding on left side, integer division!
pr = l - (pl + length(t)) # padding on right side
if ((pl + pr + length(t)) < l) # account for odd lengths
pr++
for (i = 1; i <= l + 4; i++)
printf("%%")
printf "\n"
# print the titles with their padding
printf("%% %*s%s%*s %%\n", pl, " ", t, pr, " ")
printf("%% %s %%\n", s)
for (i = 1; i <= l + 4; i++)
printf("%%")
printf "\n"
}
# check_unfinished --- print a fatal error when an unfinished code or
# file chunk is detected. Also ifweave / iftangle.
function check_unfinished()
{
if (Flags["file chunk"])
fatal(_"unfinished file chunk (started at %s)\n",
Line_numbers["file chunk"])
else if (Flags["code chunk"])
fatal(_"unfinished code chunk (started at %s)\n",
Line_numbers["code chunk"])
if ("ifweave" in Line_numbers)
fatal(_"unfinished @ifweave section (started at %s)\n",
Line_numbers["ifweave"])
if ("iftangle" in Line_numbers)
fatal(_"unfinished @iftangle section (started at %s)\n",
Line_numbers["iftangle"])
}
# strip_out_name --- get the name from name
function strip_out_name(name, l)
{
l = length(name)
name = substr(name, 3, l - 4)
return name
}
# Helper functions
# message --- write a particular kind of message out to stderr
function message(msg, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
{
printf("%s:%d: %s: " format, FILENAME, FNR, msg,
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) > "/dev/stderr"
if (substr(format, length(format), 1) != "\n")
printf("\n") > "/dev/stderr"
}
# fatal --- print a fatal error message and exit.
# No varargs, so fake it with lots of parameters.
function fatal(format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
{
message(_"fatal", format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
exit 1
}
# warning --- print a warning message to stderr
# No varargs, so fake it with lots of parameters.
function warning(format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
{
message(_"warning", format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
}
# join.awk --- join an array into a string
#
# Arnold Robbins, arnold@skeeve.com, Public Domain
# May 1993
function join(array, start, end, sep, result, i)
{
if (sep == "")
sep = " "
else if (sep == SUBSEP) # magic value
sep = ""
result = array[start]
for (i = start + 1; i <= end; i++)
result = result sep array[i]
return result
}
/^@ignore/, /^@end ignore/ { next }
# iftangle lines should be removed
/^@iftangle[[:space:]]*$/, /^@end iftangle[[:space:]]*$/ {
if (Pass == 1)
next
if ("ifweave" in Line_numbers)
fatal(_"cannot nest @iftangle inside @ifweave\n")
# start of construct, save line number
if (/^@iftangle[[:space:]]*$/)
Line_numbers["iftangle"] = (FILENAME ":" FNR)
# end of construct, delete line number
if (/^@end iftangle[[:space:]]*$/)
delete Line_numbers["iftangle"]
# simply skip these lines, this is weaving
next
}
# For weaving we remove the bracketing control lines and let anything
# in between fall through.
/^@ifweave[[:space:]]*$/, /^@end ifweave[[:space:]]*$/ {
if (Pass == 1)
next
if ("iftangle" in Line_numbers)
fatal(_"cannot nest @ifweave inside @iftangle\n")
# start of construct, save line number, skip this line
if (/^@ifweave[[:space:]]*$/) {
Line_numbers["ifweave"] = (FILENAME ":" FNR)
next
}
# end of construct, delete line number, skip this line
if (/^@end ifweave[[:space:]]*$/) {
delete Line_numbers["ifweave"]
next
}
# otherwise fall through into the rest of the code
}
/^@file_update_recipe[[:space:]]*$/,
/^@end file_update_recipe[[:space:]]*$/ {
next
}
/^@file_update[[:space:]]/ {
next
}
/^@post_create[[:space:]]+/ {
next
}
BEGIN {
Example_start = "@example"
Example_end = "@end example"
}
Pass == 2 && /^@use_smallexample[[:space:]]*$/ {
Example_start = "@smallexample"
Example_end = "@end smallexample"
next
}
Pass == 2 && /^@use_example[[:space:]]*$/ {
Example_start = "@example"
Example_end = "@end example"
next
}
Pass == 2 && /^@titlepage/ {
print "@c Let texinfo.tex give us full section titles"
print "@xrefautomaticsectiontitle on"
print ""
print "@c Start extra commands added by jrweave\n"
print "@c For HTML, spell out email addresses, to avoid problems with"
print "@c address harvesters for spammers."
print "@ifhtml"
print "@macro EMAIL{real,spelled}"
print "``\\spelled\\''"
print "@end macro"
print "@end ifhtml"
print "@ifnothtml"
print "@macro EMAIL{real,spelled}"
print "@email{\\real\\}"
print "@end macro"
print "@end ifnothtml"
print ""
print "@macro FIXME{text}"
print "@strong{FIXME}: \\text\\"
print "@end macro"
print ""
print "@macro oldnum{value}"
print "\\value\\"
print "@end macro"
print ""
print "@tex"
print "\\gdef\\oldnum#1{\\begingroup\\oldstyle #1\\endgroup}%"
print "@end tex"
print "\n@c End extra commands added by jrweave"
print ""
print # print the line
next
}
/^@numberedchunks[[:space:]]*$/ {
if (Pass == 1)
Numbered_chunks = TRUE
next
}
/^@file_chunk_full_defs[[:space:]]*$/ {
if (Pass == 1)
Print_file_full_defs = TRUE
next
}
Pass == 2 && /^@sidebar[[:space:]]+/ {
sub(/^@sidebar[[:space:]]+/, "", $0)
Sidebar_title = $0
Sidebar_body = ""
Collecting_sidebar = TRUE
next
}
Pass == 2 && /^@end[[:space:]]+sidebar[[:space:]]*$/ {
Collecting_sidebar = FALSE
printf "@cindex sidebar, %s\n", Sidebar_title
printf "@ifdocbook\n"
printf "@docbook\n"
printf "%s\n", Sidebar_title
printf "@end docbook\n"
print Sidebar_body
print ""
printf "@docbook\n"
printf "\n"
printf "@end docbook\n"
printf "@end ifdocbook\n\n"
printf "@ifnotdocbook\n"
printf "@cartouche\n"
printf "@center @b{%s}\n\n", Sidebar_title
print "@noindent"
sub(/^\n*/, "", Sidebar_body) # remove initial newlines
print Sidebar_body
printf "@end cartouche\n"
printf "@end ifnotdocbook\n"
Sidebar_body = ""
next
}
Pass == 2 && Collecting_sidebar {
Sidebar_body = Sidebar_body "\n" $0
next
}
Pass == 2 && /^@dquotexrefs[[:space:]]*$/ {
print "@tex"
# print "%\\gdef\\xrefprintnodename#1{{\\it #1}}"
print "\\gdef\\xrefprintnodename#1{``#1''}"
print "@end tex"
next
}
Pass == 2 && /^@pdflinkcolor[[:space:]]*.*$/ {
if (NF != 1 && NF != 4)
fatal(_"@pdflinkcolor: wrong number of arguments\n")
if (NF == 1)
Link_color = Dark_red
else {
$1 = ""
$0 = $0
Link_color = $0
}
print "@tex"
print "\\gdef\\linkcolor{" Link_color "}"
print "@end tex"
next
}
Pass == 2 && /^@urllinkcolor[[:space:]]*.*$/ {
if (NF != 1 && NF != 4)
fatal(_"@urllinkcolor: wrong number of arguments\n")
if (NF == 1)
URL_color = Dark_red
else {
$1 = ""
$0 = $0
URL_color = $0
}
print "@tex"
print "\\gdef\\urlcolor{" URL_color "}"
print "@end tex"
next
}
Pass == 2 && /^@hideurls[[:space:]]*$/ {
print "@tex"
print "\\global\\urefurlonlylinktrue" # NOTE: *not* \gdef
print "@end tex"
next
}
Pass == 2 && /^@allowindexbraces[[:space:]]*$/ {
print "@tex"
print "\\global\\usebracesinindexestrue" # NOTE: *not* \gdef
print "@end tex"
next
}
BEGIN {
Dark_red = "0.5 0.09 0.12"
}
Pass == 2 && /^@c %\*\*end of header/ {
print # print the line
print "\n@c Extra indices added by jrweave"
print "@defindex cd @c chunk definition"
print "@defindex cr @c chunk reference"
next
}
$0 ~ File_chunk_pattern {
Chunk_type = "file chunk"
Pattern = File_chunk_pattern
Debug_pat = "filename"
new_chunk = gensub(Pattern, "\\1", 1)
if (Flags[Chunk_type]) {
fatal(_"%s start of %s found while still collecting %s\n",
Chunk_type, new_chunk, Current_chunk)
}
check_unfinished()
Flags[Chunk_type] = TRUE
Line_numbers[Chunk_type] = (FILENAME ":" FNR)
Current_chunk = new_chunk
Chunk_info[Current_chunk]["type"] = Chunk_type
if (Debug ~ Debug_pat)
printf("saw new %s %s\n", Debug_pat, Current_chunk) > "/dev/stderr"
next
}
$0 ~ Code_chunk_pattern {
Chunk_type = "code chunk"
Pattern = Code_chunk_pattern
Debug_pat = "code"
new_chunk = gensub(Pattern, "\\1", 1)
if (Flags[Chunk_type]) {
fatal(_"%s start of %s found while still collecting %s\n",
Chunk_type, new_chunk, Current_chunk)
}
check_unfinished()
Flags[Chunk_type] = TRUE
Line_numbers[Chunk_type] = (FILENAME ":" FNR)
Current_chunk = new_chunk
Chunk_info[Current_chunk]["type"] = Chunk_type
if (Debug ~ Debug_pat)
printf("saw new %s %s\n", Debug_pat, Current_chunk) > "/dev/stderr"
next
}
/^@[[:space:]]*$/ {
if (Flags["file chunk"])
end_file_gathering()
else if (Flags["code chunk"])
end_code_gathering()
else
warning(_"unmatched terminating @-sign: ignored\n")
Chunk_lines = ""
Flags[Chunk_type] = FALSE
Line_numbers[Chunk_type] = ""
Chunk_type = ""
next
}
Flags["file chunk"] || Flags["code chunk"] {
if (Chunk_lines == "")
Chunk_lines = $0
else
Chunk_lines = Chunk_lines "\n" $0
next
}
function end_chunk_gathering()
{
if (Pass == 1) {
collect_chunk_info()
Chunk_lines = ""
Flags[Chunk_type] = FALSE
} else
print_out_chunk()
}
function end_file_gathering()
{
end_chunk_gathering()
}
function collect_chunk_info( i, n, x, called, junk)
{
# Current_chunk, Chunk_type already set by initial code
# Chunk number:
if (! ("chunk number" in Chunk_info[Current_chunk])) {
Chunk_info[Current_chunk]["chunk number"] = \
++Chunk_numbers[Chunk_type]
}
# Definition instance
Chunk_info[Current_chunk]["defn"]++
# Get names of called chunks into called
n = split(Chunk_lines, junk, Chunk_name_pattern, called)
# Add ourselves to the callers
for (i in called) {
x = strip_out_name(called[i])
Chunk_info[x]["callers"][Current_chunk] = TRUE
}
}
function print_out_chunk( x, y, n, i, parts, names,
name, anchor, chunk_being_used)
{
# Redefinition instance
Chunk_info[Current_chunk]["redefn"]++
print "@need 400"
anchor = format_anchor(Current_chunk,
Chunk_info[Current_chunk]["redefn"],
Chunk_info[Current_chunk]["defn"])
printf("%s\n", anchor)
x = expand_tabs(Chunk_lines, Tabstop)
# extract code chunks
n = split(x, parts, Chunk_name_pattern, names)
# escape special chars in parts of code that aren't chunk names
for (i = 1; i in parts; i++)
gsub(/[@{}]/, "@&", parts[i])
y = parts[1]
if (n > 1) { # embedded chunk names
for (i = 1; i in names; i++) {
name = strip_out_name(names[i])
chunk_being_used = \
format_chunk_name(name, Chunk_info[name]["chunk number"],
Chunk_info[name]["type"])
printf("@crindex %s @sortas{%s} @subentry use\n", chunk_being_used, remove_markup(name))
y = y chunk_being_used
y = y parts[i+1]
}
}
chunk_being_defined = \
format_chunk_name(Current_chunk,
Chunk_info[Current_chunk]["chunk number"],
Chunk_type)
printf("@cdindex %s @sortas{%s} @subentry definition\n",
chunk_being_defined, remove_markup(Current_chunk))
printf("@noindent\n%s %s@equiv{}\n",
chunk_being_defined,
Chunk_info[Current_chunk]["redefn"] == 1 ? "" : "+")
print Example_start
printf("%s\n", y)
print Example_end
for (i in names)
names[i] = strip_out_name(names[i]) # remove delimiters
print "@iftex"
print "@smallfonts @rm"
print "@end iftex"
# Print other definition sites for code chunks, or for file
# chunk if Print_file_full_defs is true
if (Chunk_type == "code chunk" || Print_file_full_defs) {
print_other_defns(Current_chunk,
Chunk_info[Current_chunk]["defn"],
Chunk_info[Current_chunk]["redefn"])
}
# Print callers for code chunks
if (Chunk_type == "code chunk") {
if ("callers" in Chunk_info[Current_chunk]) {
print ""
asorti(Chunk_info[Current_chunk]["callers"], my_callers)
if (length(my_callers) > 1) {
print "@noindent"
print "This chunk is called by the following chunks:\n"
print_ref_table(my_callers)
} else {
n = Chunk_info[my_callers[1]]["defn"]
print "@noindent"
printf("This chunk is called by %s; see its first definition at %s.\n",
format_chunk_name(my_callers[1],
Chunk_info[my_callers[1]]["chunk number"],
Chunk_info[my_callers[1]]["type"]),
format_xref(my_callers[1], n > 1 ? 1 : 0))
}
} else
warning(_"chunk %s has no callers\n", Current_chunk)
}
sort_and_remove_duplicates(names)
switch (length(names)) {
case 0:
break
case 1:
print "\n@noindent"
printf("The called chunk %s is first defined at\n%s.\n",
format_chunk_name(names[1],
Chunk_info[names[1]]["chunk number"],
Chunk_info[names[1]]["type"]),
format_xref(names[1],
(Chunk_info[names[1]]["defn"] > 1) ? 1 : 0))
break;
default:
print "\n@noindent"
printf("The following table lists called chunk definition points.\n")
print_ref_table(names)
break;
}
print "@iftex"
print "@textfonts @rm"
print "@end iftex"
}
function print_other_defns(chunk, total_defns, current_defn,
other_defns, i, j) # locals
{
if (total_defns == 1)
return
print ""
print "@noindent"
print "This chunk is also defined in"
for (i = j = 1; i <= total_defns; i++) {
if (i == current_defn)
continue
other_defns[j++] = i
}
for (i = 1; i < j; i++) {
printf("%s", format_xref(chunk, other_defns[i]))
if (i + 2 == j)
print ", and"
else if (i + 2 < j)
print ","
}
print "."
}
function print_ref_table(chunklist, i, x, n)
{
print "@multitable @columnfractions .35 .65"
print "@headitem Chunk name @tab First definition point"
for (i = 1; i in chunklist; i++) {
x = chunklist[i]
n = (Chunk_info[x]["defn"] > 1) ? 1 : 0
printf("@item %s @tab See %s.\n",
format_chunk_name(x,
Chunk_info[x]["chunk number"],
Chunk_info[x]["type"]),
format_xref(x, n))
}
print "@end multitable"
}
function sort_and_remove_duplicates(names, i, dups)
{
for (i in names)
dups[names[i]] = 1
asorti(dups)
delete names
for (i in dups)
names[i] = dups[i]
}
function end_code_gathering()
{
end_chunk_gathering()
if (Debug ~ /code/)
printf("finished formatting code %s\n",
Code_chunk) > "/dev/stderr"
}
Pass == 2 && /^@print_file_defs[[:space:]]*$/ {
delete Sorted_file_names
j = 1
for (i in Chunk_info) {
if (Chunk_info[i]["type"] == "file chunk")
Sorted_file_names[j++] = i
}
asort(Sorted_file_names) # Sorted by value
print "@table @asis"
for (i = 1; i in Sorted_file_names; i++) {
name = Sorted_file_names[i]
x = format_chunk_name(name,
Chunk_info[name]["chunk number"],
Chunk_info[name]["type"])
printf("@item %s\n", x)
n = Chunk_info[name]["defn"]
if (n == 1) {
printf("This chunk is defined in\n")
printf("%s.\n", format_xref(name, 0))
} else {
printf("Multiple definitions occur in\n")
for (j = 1; j <= n; j++) {
printf("%s", format_xref(name, j))
if (j == n - 1)
printf(",\nand\n")
else if (j < n - 1)
printf(",\n")
}
print ".\n"
}
}
print "@end table"
next
}
Pass == 2 && /^@print_code_defs[[:space:]]*$/ {
delete Sorted_code_names
j = 1
for (i in Chunk_info) {
if (Chunk_info[i]["type"] == "code chunk")
Sorted_code_names[j++] = i
}
asort(Sorted_code_names) # Sorted by value
print "@table @asis"
for (i = 1; i in Sorted_code_names; i++) {
name = Sorted_code_names[i]
x = format_chunk_name(name,
Chunk_info[name]["chunk number"],
Chunk_info[name]["type"])
printf("@item %s\n", x)
n = Chunk_info[name]["defn"]
if (n == 1) {
printf("This chunk is defined in\n")
printf("%s.\n", format_xref(name, 0))
} else {
printf("Multiple definitions occur in\n")
for (j = 1; j <= n; j++) {
printf("%s", format_xref(name, j))
if (j == n - 1)
printf(",\nand\n")
else if (j < n - 1)
printf(",\n")
}
print ".\n"
}
}
print "@end table"
next
}
Pass == 2 && /^@print_code_refs[[:space:]]*$/ {
delete Sorted_code_names
j = 1
for (i in Chunk_info) {
if (Chunk_info[i]["type"] == "code chunk")
Sorted_code_names[j++] = i
}
asort(Sorted_code_names) # Sorted by value
print "@table @asis"
for (i = 1; i in Sorted_code_names; i++) {
name = Sorted_code_names[i]
n = Chunk_info[name]["defn"]
if (n == 0) # warning printed elsewhere
continue
fmt_name = format_chunk_name(name, Chunk_info[name]["chunk number"],
Chunk_info[name]["type"])
printf("@item %s\n", fmt_name)
Current_chunk = name # for use by next chunk
if ("callers" in Chunk_info[Current_chunk]) {
print ""
asorti(Chunk_info[Current_chunk]["callers"], my_callers)
if (length(my_callers) > 1) {
print "@noindent"
print "This chunk is called by the following chunks:\n"
print_ref_table(my_callers)
} else {
n = Chunk_info[my_callers[1]]["defn"]
print "@noindent"
printf("This chunk is called by %s; see its first definition at %s.\n",
format_chunk_name(my_callers[1],
Chunk_info[my_callers[1]]["chunk number"],
Chunk_info[my_callers[1]]["type"]),
format_xref(my_callers[1], n > 1 ? 1 : 0))
}
} else
warning(_"chunk %s has no callers\n", Current_chunk)
}
print "@end table"
next
}
/^@print_initial_setup([[:space:]]+.*|[[:space:]]*)$/ {
Print_initial_setup = TRUE
if (NF > 1) {
$1 = ""
$0 = $0
Initial_setup_name = $0
}
else
Initial_setup_name = "Initial setup"
next
}
/^@initial_setup[[:space:]]*$/, /^@end initial_setup[[:space:]]*$/ {
if (Pass == 1 || ! Print_initial_setup)
next
Chunk_info[Initial_setup_name]["type"] = "code chunk"
Chunk_info[Initial_setup_name]["defn"] = 1
if (/^@initial_setup[[:space:]]*$/) {
print "@need 400"
printf("%s\n", format_anchor(Initial_setup_name, 0))
printf("@noindent\n%s @equiv{}\n",
format_chunk_name(Initial_setup_name, 0, "code chunk"))
print Example_start
} else if (/^@end initial_setup[[:space:]]*$/) {
print Example_end
} else {
x = expand_tabs($0, Tabstop)
gsub(/[@{}]/, "@&", x)
print x
}
next
}
Pass == 2 { print }
BEGIN {
if (Tabstop == 0)
Tabstop = 4 # default tab stops
}
# expand_tabs --- expand tabs in the string
function expand_tabs(string, tabstop, chars, out, i, j, k, n)
{
if (tabstop < 2)
fatal(_"expand_tabs: tabstop %d < 2\n", tabstop)
n = split(string, chars, "")
j = k = 0
for (i = 1; i <= n;) {
if (chars[i] == "\n") {
out[j++] = chars[i++]
k = 0
continue
}
if (chars[i] != "\t") {
out[j++] = chars[i++]
k++
continue
}
i++ # skip the tab
do {
out[j++] = " "
k++
} while (and(k, tabstop-1) != 0)
}
return join(out, 0, j, SUBSEP)
}
function sanitize_name(name)
{
gsub(/[^[:alnum:]]/, "-", name)
return name
}
function remove_markup(text, result)
{
result = gensub(/([^@])(@[[:alpha:]]+[{]([^}]+)[}])/, "\\1\\3", "g", text)
result = gensub(/@TeX[{][}]/, "TeX", "g", result)
return result
}
function format_anchor_or_ref(type, name, defn,
clean_name, result) # locals
{
clean_name = sanitize_name(name)
if (defn > 0)
result = sprintf("@%s{%s-%d}", type, clean_name, defn)
else
result = sprintf("@%s{%s}", type, clean_name)
return result
}
function format_xref(name, defn)
{
return format_anchor_or_ref("ref", name, defn)
}
function format_anchor(name, cur_defn, total_defns, defn)
{
if (total_defns == 1)
defn = 0
else if (cur_defn <= total_defns)
defn = cur_defn
return format_anchor_or_ref("anchor", name, defn)
}
function format_chunk_name(name, count, type,
result, left, right, style) # locals
{
if (type == "file chunk") {
left = "@{"
right = "@}"
style = "file"
} else if (type == "code chunk") {
left = "<"
right = ">"
style = "i"
} else
fatal(_"format_chunk_name: Unknown chunk type `%s'\n", type)
if (count > 0 && Numbered_chunks)
result = sprintf("@r{%s@%s{%s} @oldnum{%d}%s}",
left, style, name, count, right)
else
result = sprintf("@r{%s@%s{%s}%s}", left, style, name, right)
return result
}