589 lines
12 KiB
Plaintext
589 lines
12 KiB
Plaintext
# second pass: interpretation of directives
|
|
# A macro-set-specific portion gets interpolated into this at the "#include"
|
|
# line. As a minimum, it must deal with .^b and .^e and with any input traps.
|
|
BEGIN {
|
|
# stuff for output to third pass
|
|
nobreak = -1
|
|
dobreak = -2
|
|
message = -3
|
|
OFS = "\t"
|
|
|
|
# special-character table; this one character needs to be hardcoded
|
|
chars[" "] = " " ; charwidths[" "] = 1
|
|
|
|
debug["e"] = 0 # just to make it an array
|
|
strings["a"] = "" # just to make it an array
|
|
numbers["a"] = 0 # just to make it an array
|
|
hyphens["a"] = "" # just to make it an array
|
|
hyphenwidths["a"] = "" # just to make it an array
|
|
|
|
# stuff for expression decoding
|
|
signfactor["+"] = 1
|
|
signfactor["-"] = -1
|
|
scale["i"] = 240
|
|
scale["c"] = 240*50/127
|
|
scale["P"] = 240/6
|
|
# we get m, n, and v when we see .^r
|
|
scale["p"] = 240/72
|
|
scale["u"] = 1
|
|
|
|
# stuff for basic parameters that just get passed to third pass
|
|
parms["in"] = 0 # just to make it an array
|
|
prevparms["in"] = 0 # just to make it an array
|
|
setcmd["ll"] = "linelen"
|
|
setcmd["in"] = "indent"
|
|
setcmd["ti"] = "tempindent"
|
|
setcmd["po"] = "pageoffset"
|
|
setcmd["pl"] = "pagelen"
|
|
|
|
# did last word end with \c ? (in which case, it's still in "word")
|
|
backc = 0
|
|
|
|
# stuff for error reporting
|
|
il = 0 # input line number
|
|
lockil = 0 # il is locked, we're inside a macro
|
|
inname = "?" # input filename
|
|
msg = message "\t" # later picks up filename etc.
|
|
|
|
# current input trap
|
|
afternext = ""
|
|
}
|
|
{
|
|
if (!lockil)
|
|
il++
|
|
msg = message "\t" inname "," il ": "
|
|
# fallthrough
|
|
}
|
|
/^[ \t]*$/ { # empty line
|
|
print dobreak, "space"
|
|
next
|
|
}
|
|
/^[ \t]/ { # line starting with white space
|
|
print dobreak, "flush"
|
|
print 0, "" # empty word
|
|
# fallthrough
|
|
}
|
|
/^[^.]/ { # text
|
|
# dispose of the easy case
|
|
if (font == "R" && $0 !~ /\\|\t|-| / && !backc && afternext == "") {
|
|
for (i = 1; i <= NF; i++)
|
|
print length($i), $i
|
|
if ($0 ~ /[.!?:][\])'"*]*$/)
|
|
print nobreak, "gap", 2
|
|
if (centering > 0) {
|
|
print dobreak, "center"
|
|
centering--
|
|
} else if (!fill)
|
|
print dobreak, "flush"
|
|
next
|
|
}
|
|
|
|
# the hard case, needs a character-by-character scan
|
|
s = $0 " " # the space flushes the last word
|
|
n = 1 # current position in s
|
|
inword = 0 # have we been processing a word?
|
|
period = "" # "." if this word ended a sentence
|
|
nsp = 0 # count of spaces seen so far
|
|
tabpos = 0 # which tab position was used last
|
|
while (n <= length(s)) {
|
|
c = substr(s, n, 1)
|
|
|
|
# handle state transitions
|
|
if (c == " " || c == "\t") {
|
|
if (inword) { # ends word
|
|
if (!backc) {
|
|
print wordlen, word
|
|
if (usedhyphen)
|
|
print nobreak, "nohyphen"
|
|
}
|
|
inword = 0
|
|
nsp = 0
|
|
}
|
|
} else {
|
|
if (!inword) { # begins word
|
|
if (!backc) {
|
|
word = ""
|
|
wordlen = 0
|
|
usedhyphen = 0
|
|
}
|
|
backc = 0
|
|
inword = 1
|
|
if (nsp > 1)
|
|
print nobreak, "gap", nsp
|
|
}
|
|
}
|
|
|
|
# deal with the character
|
|
if (c == " ") {
|
|
nsp++
|
|
if (n != length(s)) # not the trailing flusher
|
|
period = ""
|
|
} else if (c == "\t") {
|
|
# not really right, should be based on input position
|
|
# also, one space following tab gets ignored
|
|
tabpos++
|
|
if (tabpos <= ntabs)
|
|
print nobreak, "tabto", tabs[tabpos]
|
|
nsp = 0
|
|
period = ""
|
|
} else if (c == "-" && wordlen > 0) {
|
|
# hyphen within word
|
|
print wordlen, word, hyphenwidths[font]
|
|
print nobreak, "userhyphen", hyphens[font], hyphenwidths[font]
|
|
word = ""
|
|
wordlen = 0
|
|
period = ""
|
|
usedhyphen = 1
|
|
} else if (c != "\\") {
|
|
# ordinary character
|
|
if (font == "B")
|
|
word = word c "\b" c "\b" c
|
|
else if (font == "I" && c ~ /[a-zA-Z0-9]/)
|
|
word = word "_\b" c
|
|
else
|
|
word = word c
|
|
wordlen++
|
|
if (c ~ /[.!?:]/)
|
|
period = "."
|
|
else if (c !~ /[\])'"*]/)
|
|
period = ""
|
|
} else { # backslash
|
|
n++
|
|
c = substr(s, n, 1)
|
|
if (c == "f") {
|
|
# font change
|
|
n++
|
|
code = substr(s, n, 1)
|
|
if (code == "(") {
|
|
n++
|
|
code = substr(s, n, 2)
|
|
n++
|
|
}
|
|
if (code == "P")
|
|
font = prevfont
|
|
else if (fontok[code] == "")
|
|
print msg "unknown font `" code "'"
|
|
else {
|
|
prevfont = font
|
|
font = code
|
|
}
|
|
} else if (c == "n") {
|
|
# number-register value
|
|
n++
|
|
code = substr(s, n, 1)
|
|
if (code == "(") {
|
|
n++
|
|
code = substr(s, n, 2)
|
|
n++
|
|
}
|
|
s = substr(s, 1, n) numbers[code] substr(s, n+1)
|
|
} else if (c == "s") {
|
|
# size change
|
|
n++
|
|
if (substr(s, n, 1) ~ /[0-9]/)
|
|
n++
|
|
# just ignore it
|
|
} else if (c == "c")
|
|
# word continuation
|
|
backc = 1
|
|
else if (c == "*") {
|
|
# string-variable value
|
|
n++
|
|
code = substr(s, n, 1)
|
|
if (code == "(") {
|
|
n++
|
|
code = substr(s, n, 2)
|
|
n++
|
|
}
|
|
s = substr(s, 1, n) strings[code] substr(s, n+1)
|
|
} else if (c == "%") {
|
|
# discretionary hyphen
|
|
if (wordlen > 0) {
|
|
print wordlen, word, hyphenwidths[font]
|
|
print nobreak, "hyphen", hyphens[font], hyphenwidths[font]
|
|
word = ""
|
|
wordlen = 0
|
|
usedhyphen = 1
|
|
}
|
|
} else if (c == "(" && substr(s, n+1, 2) == "em" && \
|
|
chars["em"] != "") {
|
|
# em-dash, special case due to hyphenation
|
|
n += 2
|
|
emw = charwidths["em"]
|
|
print wordlen, word, emw
|
|
print nobreak, "userhyphen", chars["em"], emw
|
|
word = ""
|
|
wordlen = 0
|
|
period = ""
|
|
usedhyphen = 1
|
|
} else {
|
|
# special-character name
|
|
code = c
|
|
if (code == "(") {
|
|
n++
|
|
code = substr(s, n, 2)
|
|
n++
|
|
}
|
|
word = word chars[code]
|
|
wordlen += charwidths[code]
|
|
period = ""
|
|
}
|
|
}
|
|
|
|
# on to the next character, at last
|
|
n++
|
|
}
|
|
|
|
# end-of-line processing
|
|
if (!backc) {
|
|
if (period == ".")
|
|
print nobreak, "gap", 2
|
|
if (centering > 0) {
|
|
print dobreak, "center"
|
|
centering--
|
|
} else if (!fill)
|
|
print dobreak, "flush"
|
|
}
|
|
|
|
# if no input trap, we're done
|
|
if (afternext == "")
|
|
next
|
|
|
|
# if there is an input trap, fall into the macro-dependent section
|
|
}
|
|
#
|
|
#
|
|
#
|
|
# at this point we plug in the macro-specific stuff, keyed on the next line
|
|
#include note that this is an awk comment
|
|
#
|
|
#
|
|
#
|
|
/^\.it/ { # plant an input trap, sort of
|
|
if (NF > 1 && $2 != 1)
|
|
print msg ".it first argument must be 1"
|
|
if (NF > 2)
|
|
afternext = afternext "," $3
|
|
else
|
|
afternext = ""
|
|
next
|
|
}
|
|
/^\.\^r cpi / { # set resolutions, in cpi: .^r cpi hor vert
|
|
scale["m"] = 240/$3
|
|
scale["n"] = 240/$3
|
|
scale["v"] = 240/$4
|
|
next
|
|
}
|
|
/^\.(ta|ll|in|ti|po|ne|sp|pl|nr)/ { # expression processing
|
|
# sort out default scale factor
|
|
if ($1 ~ /^\.(ne|sp|pl)/)
|
|
exprscale = "v"
|
|
else if ($1 ~ /^\.(nr)/)
|
|
exprscale = "u"
|
|
else
|
|
exprscale = "n"
|
|
|
|
# which argument should we start with?
|
|
offset = length($1) + 1
|
|
if ($1 == ".nr")
|
|
offset += length($2) + 1
|
|
|
|
# beginning of debugging message
|
|
if (debug["e"])
|
|
printf "%s ", msg substr($0, 1, offset)
|
|
|
|
# do the expressions
|
|
s = substr($0, offset+1)
|
|
n = 1
|
|
while (s != "") {
|
|
while (s ~ /^[ \t]/)
|
|
s = substr(s, 2)
|
|
|
|
# an initial sign is magic
|
|
ssign = ""
|
|
if (s ~ /^[+-]/) {
|
|
ssign = substr(s, 1, 1)
|
|
s = substr(s, 2)
|
|
}
|
|
s = "+" s # this is an un-magic addition operator
|
|
|
|
# process terms
|
|
sval = 0
|
|
# there is no portable way to put a slash in a regexp
|
|
while (s ~ /^[+*%)-]/ || substr(s, 1, 1) == "/") {
|
|
# figure out what it is and what it contributes
|
|
if (debug["e"] > 1)
|
|
print "s=`" s "'"
|
|
termop = substr(s, 1, 1)
|
|
s = substr(s, 2)
|
|
termscale = exprscale
|
|
if (termop == ")") {
|
|
if (debug["e"] > 1)
|
|
print "pop " valstack[vsp] " " opstack[vsp] " with " sval
|
|
termval = sval
|
|
sval = valstack[vsp]
|
|
termop = opstack[vsp]
|
|
vsp--
|
|
termscale = "u"
|
|
} else if (s ~ /^\(/) {
|
|
if (debug["e"] > 1)
|
|
print "push " sval " " termop
|
|
vsp++
|
|
valstack[vsp] = sval
|
|
opstack[vsp] = termop
|
|
sval = 0
|
|
termop = "+" # dummy op and value
|
|
termval = 0
|
|
s = "+" substr(s, 2)
|
|
} else if (s ~ /^\\w/) {
|
|
delim = substr(s, 3, 1)
|
|
s = substr(s, 4)
|
|
endp = index(s, delim)
|
|
if (endp == 0)
|
|
endp = length(s) + 1
|
|
termval = (endp - 1) * scale["n"] # crude
|
|
s = substr(s, endp+1)
|
|
} else if (s ~ /^\\n/) {
|
|
if (s ~ /^\\n\(/) {
|
|
code = substr(s, 4, 2)
|
|
s = substr(s, 6)
|
|
} else {
|
|
code = substr(s, 3, 1)
|
|
s = substr(s, 4)
|
|
}
|
|
termval = numbers[code]
|
|
} else if (s ~ /^[0-9.]/) {
|
|
for (endp = 1; endp <= length(s); endp++)
|
|
if (substr(s, endp, 1) !~ /[0-9.]/)
|
|
break
|
|
termval = substr(s, 1, endp-1) + 0
|
|
s = substr(s, endp)
|
|
}
|
|
|
|
# add it in, with scaling factor
|
|
c = substr(s, 1, 1)
|
|
if (scale[c] != "") {
|
|
termval *= scale[c]
|
|
s = substr(s, 2)
|
|
} else
|
|
termval *= scale[termscale]
|
|
if (termop == "+")
|
|
sval += termval
|
|
else if (termop == "-")
|
|
sval -= termval
|
|
else if (termop == "*")
|
|
sval *= termval
|
|
else if (termop == "/")
|
|
sval = int(sval) / int(termval)
|
|
else if (termop == "%")
|
|
sval = int(sval) % int(termval)
|
|
}
|
|
|
|
# remember the value, print if debugging
|
|
expr[n] = sval
|
|
exprsign[n] = ssign
|
|
iexpr[n] = signfactor[ssign] * sval # convenience
|
|
if (debug["e"])
|
|
printf "%s ", ssign "(" sval ")"
|
|
|
|
# proceed to next, skipping trash if necessary
|
|
while (s ~ /^[^ \t]/)
|
|
s = substr(s, 2)
|
|
n++
|
|
}
|
|
|
|
# final cleanup
|
|
nexprs = n - 1
|
|
if (debug["e"])
|
|
printf "\n"
|
|
|
|
# fallthrough
|
|
}
|
|
/^\.(ll|in|ti|po|pl)/ { # common code for set-parameter requests
|
|
# relies on expression processing, including setting of exprscale
|
|
name = substr($1, 2, 2)
|
|
n = parms[name]
|
|
if (nexprs == 0)
|
|
n = prevparms[name]
|
|
else if (exprsign[1] == "" || name == "ti")
|
|
n = expr[1] / scale[exprscale]
|
|
else
|
|
n += iexpr[1] / scale[exprscale]
|
|
prevparms[name] = parms[name]
|
|
parms[name] = int(n)
|
|
print dobreak, setcmd[name], parms[name]
|
|
next
|
|
}
|
|
/^\.nr/ {
|
|
# relies on expression processing
|
|
n = numbers[$2]
|
|
if (exprsign[1] == "")
|
|
n = expr[1]
|
|
else
|
|
n += iexpr[1]
|
|
numbers[$2] = int(n)
|
|
next
|
|
}
|
|
/^\.ne/ {
|
|
# relies on expression processing
|
|
print dobreak, "need", int(expr[1]/scale["v"] + 0.99)
|
|
next
|
|
}
|
|
/^\.sp/ {
|
|
# relies on expression processing
|
|
if (nexprs == 0)
|
|
i = 1
|
|
else
|
|
i = int(expr[1]/scale["v"] + 0.99)
|
|
for (; i > 0; i--)
|
|
print dobreak, "space"
|
|
next
|
|
}
|
|
/^\.ta/ {
|
|
# relies on expression processing
|
|
tabstop = 0
|
|
for (n = 1; n <= nexprs; n++) {
|
|
if (exprsign[n] == "")
|
|
tabstop = expr[n]
|
|
else
|
|
tabstop += iexpr[n]
|
|
tabs[n] = int(tabstop/scale["n"])
|
|
}
|
|
ntabs = nexprs
|
|
next
|
|
}
|
|
/^\.ft/ {
|
|
if (NF > 1)
|
|
code = $2
|
|
else
|
|
code = "P"
|
|
|
|
if (code == "P")
|
|
font = prevfont
|
|
else if (fontok[code] == "")
|
|
print msg "unknown font `" code "'"
|
|
else {
|
|
prevfont = font
|
|
font = code
|
|
}
|
|
next
|
|
}
|
|
/^\.br/ {
|
|
print dobreak, "flush"
|
|
next
|
|
}
|
|
/^\.ds/ {
|
|
# note, macro-set-specific code often looks at .ds as well
|
|
if ($3 !~ /^"/)
|
|
strings[$2] = $3
|
|
else
|
|
strings[$2] = substr($0, index($0, "\"")+1)
|
|
next
|
|
}
|
|
/^\.ns/ {
|
|
print nobreak, "nospace"
|
|
next
|
|
}
|
|
/^\.rs/ {
|
|
print nobreak, "yesspace"
|
|
next
|
|
}
|
|
/^\.ad/ {
|
|
print nobreak, "both"
|
|
next
|
|
}
|
|
/^\.na/ {
|
|
print nobreak, "left"
|
|
next
|
|
}
|
|
/^\.nf/ {
|
|
fill = 0
|
|
print dobreak, "flush"
|
|
next
|
|
}
|
|
/^\.fi/ {
|
|
fill = 1
|
|
print dobreak, "flush"
|
|
next
|
|
}
|
|
/^\.\^x/ { # direct errors to this file: .^x filename
|
|
print nobreak, "errsto", $2
|
|
next
|
|
}
|
|
/^\.\^c/ { # define character: .^c name width text
|
|
if ($4 ~ /^"/)
|
|
s = substr($0, index($0, "\"")+1)
|
|
else
|
|
s = $4
|
|
ns = ""
|
|
while ((n = index(s, "\\")) > 0) {
|
|
if (n > 1)
|
|
ns = ns substr(s, 1, n-1)
|
|
n++
|
|
c = substr(s, n, 1)
|
|
if (c == "\\")
|
|
ns = ns "\\"
|
|
else if (c == "b")
|
|
ns = ns "\b"
|
|
s = substr(s, n+1)
|
|
}
|
|
ns = ns s
|
|
if ($2 == "hy") {
|
|
hyphens[font] = ns
|
|
hyphenwidths[font] = $3
|
|
} else {
|
|
chars[$2] = ns
|
|
charwidths[$2] = $3
|
|
}
|
|
next
|
|
}
|
|
/^\.\^f/ { # this font is okay: .^f fontname
|
|
# someday, this might take font-change codes as further arguments
|
|
fontok[$2] = "yes"
|
|
next
|
|
}
|
|
/^\.tm/ {
|
|
print msg $0
|
|
next
|
|
}
|
|
/^\.ps/ {
|
|
# ignore
|
|
next
|
|
}
|
|
/^\.ce/ {
|
|
if (NF > 1)
|
|
centering = $2
|
|
else
|
|
centering = 1
|
|
next
|
|
}
|
|
/^\.bp/ {
|
|
print dobreak, "need", 999
|
|
next
|
|
}
|
|
/^\.\^d/ { # debug control: .^d debugvar value
|
|
debug[$2] = $3
|
|
next
|
|
}
|
|
/^\.\^#/ { # set line number of next line: .^# no file
|
|
il = $2 - 1
|
|
lockil = 0
|
|
inname = $3
|
|
next
|
|
}
|
|
/^\.\^=/ { # lock line number to value: .^= no file
|
|
il = $2
|
|
lockil = 1
|
|
inname = $3
|
|
next
|
|
}
|
|
/^\.\\"/ { # comment
|
|
next
|
|
}
|
|
/^\./ {
|
|
print msg "command `" $1 "' unsupported or unknown"
|
|
}
|
|
END {
|
|
print dobreak, "need", 999 # flush page
|
|
}
|