333 lines
9.3 KiB
Plaintext
333 lines
9.3 KiB
Plaintext
# third pass: setting lines and pages
|
|
# The input language of this pass is basically <width, word> pairs, where
|
|
# "word" may have imbedded strangeness (backspaces, etc.) for font changes
|
|
# and special characters. Zero width is not special. A third field may
|
|
# appear to indicate that this is a fragment of a word that can be
|
|
# hyphenated; the third field is the width of the hyphen that would have
|
|
# to be added if the line broke after this fragment.
|
|
# Negative widths denote special operations. -3 is an error message, the
|
|
# second field being the message. -1 and -2 are control messages to this
|
|
# pass, the difference being whether a break is implied or not. The second
|
|
# field is a message type string; more fields may appear as arguments. The
|
|
# semantics of control messages are often -- but not always! -- similar to
|
|
# those of troff commands. For example, "linelen" is .ll, but "center" is
|
|
# not semantically equivalent to .ce -- it is related but more primitive.
|
|
BEGIN {
|
|
# input and output details
|
|
FS = "\t"
|
|
nobreak = -1
|
|
dobreak = -2
|
|
message = -3
|
|
errs = "awf.errs" # default only, normally changed by "errsto"
|
|
|
|
# page setup -- some are defaults only, normally altered by pass 2
|
|
nextlineno = 1
|
|
thispageno = 1
|
|
topmargin = 5
|
|
botmargin = 5
|
|
ind = 0 # current indent
|
|
tmpind = 0
|
|
pageoffset = "" # string to emit at start of each line
|
|
nospacemode = 1
|
|
hdrs["CH"] = "- % -"
|
|
nop = split("LH,CH,RH,LF,CF,RF", hdrnames, ",")
|
|
fph = 0 # print header on first page?
|
|
fill = 1
|
|
adj = "both"
|
|
pagelen = 66
|
|
linelen = 78
|
|
|
|
# line-builder setup
|
|
line = "" # line so far, without padding
|
|
paddable = "" # x means corresp. char in "line" is paddable
|
|
thislinelen = -1 # -1 means nothing there
|
|
cont = " " # thing to append to continue line
|
|
contlen = 1
|
|
eol = "" # thing to append to break line
|
|
eollen = 0
|
|
padfrom = "R" # "L" or "R", alternating for river avoidance
|
|
|
|
# many spaces, so we can use substr to get any number we need
|
|
sps = " "
|
|
sps = sps sps sps sps sps sps sps sps sps sps
|
|
}
|
|
{
|
|
# process word, if any, causing a break if appropriate
|
|
if ($1 >= 0 && thislinelen < 0) { # word, and first on line
|
|
line = $2
|
|
paddable = substr(sps, 1, length($2))
|
|
thislinelen = $1
|
|
} else if ($1 >= 0 && thislinelen+contlen+$1+$3 <= linelen-ind-tmpind) {
|
|
# word, and it fits on line
|
|
line = line cont $2
|
|
if (cont == " ")
|
|
paddable = paddable "x"
|
|
else
|
|
paddable = paddable substr(sps, 1, length(cont))
|
|
paddable = paddable substr(sps, 1, length($2))
|
|
thislinelen += contlen + $1
|
|
} else if ($1 == nobreak || $1 == message)
|
|
nop = 0 # no attention (i.e. break) needed here
|
|
else if ($1 == dobreak && $2 == "need" && \
|
|
nextlineno + $3 < pagelen + 1 - botmargin)
|
|
nop = 0 # enough space is available, no action needed
|
|
else if ($1 == dobreak && $2 == "toindent" && \
|
|
ind + tmpind + thislinelen < ind) {
|
|
# move to indent position within line; there is room
|
|
n = ind - (ind + tmpind + thislinelen)
|
|
line = line substr(sps, 1, n)
|
|
# nothing before this is paddable
|
|
paddable = substr(sps, 1, length(line))
|
|
thislinelen += n
|
|
# prevent padding immediately after this point
|
|
cont = ""
|
|
contlen = 0
|
|
} else if (thislinelen >= 0 || ($1 == dobreak && $2 == "need")) {
|
|
# must emit output, either due to break or "need"
|
|
|
|
# if at top of page, header
|
|
if (nextlineno == 1) {
|
|
for (i = int((topmargin-1)/2); i > 0; i--) {
|
|
print ""
|
|
nextlineno++
|
|
}
|
|
for (hno in hdrnames) {
|
|
h = hdrnames[hno]
|
|
if (hdrs[h] ~ /%/) {
|
|
n = split(hdrs[h], t, "%")
|
|
thispagehdrs[h] = t[1] thispageno t[2]
|
|
} else
|
|
thispagehdrs[h] = hdrs[h]
|
|
}
|
|
if (fph || thispageno > 1) {
|
|
lh = thispagehdrs["LH"]
|
|
ch = thispagehdrs["CH"]
|
|
rh = thispagehdrs["RH"]
|
|
lsp = int((linelen - length(lh ch rh)) / 2)
|
|
rsp = linelen - length(lh ch rh) - lsp
|
|
print pageoffset lh substr(sps, 1, lsp) ch substr(sps, 1, rsp) rh
|
|
} else
|
|
print ""
|
|
nextlineno++
|
|
while (nextlineno <= topmargin) {
|
|
print ""
|
|
nextlineno++
|
|
}
|
|
}
|
|
|
|
# the current line
|
|
# first, add a trailing hyphen if any
|
|
line = line eol
|
|
paddable = paddable substr(sps, 1, length(eol))
|
|
thislinelen += eollen
|
|
|
|
# trim trailing spaces if any
|
|
while (line ~ / $/) {
|
|
line = substr(line, 1, length(line)-1)
|
|
paddable = substr(paddable, 1, length(line))
|
|
thislinelen--
|
|
}
|
|
|
|
# print it in a suitable way
|
|
if (line == "") # empty always prints as nothing
|
|
print ""
|
|
else if ($1 < 0 && $2 == "center") {
|
|
# center it
|
|
hsp = int((linelen - thislinelen) / 2)
|
|
if (hsp < 0)
|
|
hsp = 0
|
|
print pageoffset substr(sps, 1, ind+tmpind+hsp) line
|
|
} else if (adj == "left" || (adj == "both" && \
|
|
($1 < 0 || index(paddable, "x") == 0)))
|
|
# no right-margin adjustment (disabled, inappropriate
|
|
# (line ended by break), or impossible)
|
|
print pageoffset substr(sps, 1, ind+tmpind) line
|
|
else if (adj == "both") {
|
|
# hard case -- adjust right margin
|
|
# sanity check
|
|
if (length(paddable) != length(line)) # aieeeee
|
|
printf "awf: %f != %f!\n", length(paddable), \
|
|
length(line) >errs
|
|
|
|
# compute parameters
|
|
textlen = linelen - (ind+tmpind)
|
|
mustadd = textlen - thislinelen
|
|
npad = 0 # number of paddable spaces
|
|
for (tmp = paddable; (i = index(tmp, "x")) > 0; \
|
|
tmp = substr(tmp, i+1))
|
|
npad++
|
|
addatall = int(mustadd/npad) # all grow this much
|
|
spall = substr(sps, 1, addatall)
|
|
nmore = mustadd - addatall*npad # this many grow more
|
|
|
|
# build padded output text
|
|
out = substr(sps, 1, ind+tmpind)
|
|
padno = 0
|
|
while ((i = index(paddable, "x")) > 0) {
|
|
out = out substr(line, 1, i)
|
|
padno++
|
|
out = out spall
|
|
if (padfrom == "L") {
|
|
if (padno <= nmore)
|
|
out = out " "
|
|
} else {
|
|
if (padno > npad-nmore)
|
|
out = out " "
|
|
}
|
|
line = substr(line, i+1)
|
|
paddable = substr(paddable, i+1)
|
|
}
|
|
|
|
# print it, plus remnant not processed by loop
|
|
print pageoffset out line
|
|
|
|
# tidy up
|
|
if (padfrom == "L")
|
|
padfrom = "R"
|
|
else
|
|
padfrom = "L"
|
|
}
|
|
|
|
# tidy up after output line
|
|
nextlineno++
|
|
line = ""
|
|
paddable = ""
|
|
thislinelen = -1
|
|
tmpind = 0
|
|
nospacemode = 0
|
|
|
|
# if we broke from a "need", go to bottom of page
|
|
if ($1 == dobreak && $2 == "need")
|
|
while (nextlineno < pagelen + 1 - botmargin) {
|
|
print ""
|
|
nextlineno++
|
|
}
|
|
|
|
# footer, if at bottom of page
|
|
if (nextlineno >= pagelen + 1 - botmargin) {
|
|
for (i = int((botmargin-1)/2); i > 0; i--) {
|
|
print ""
|
|
nextlineno++
|
|
}
|
|
# header code prepared thispagehdrs
|
|
lf = thispagehdrs["LF"]
|
|
cf = thispagehdrs["CF"]
|
|
rf = thispagehdrs["RF"]
|
|
lsp = int((linelen - length(lf cf rf)) / 2)
|
|
rsp = linelen - length(lf cf rf) - lsp
|
|
print pageoffset lf substr(sps, 1, lsp) cf substr(sps, 1, rsp) rf
|
|
nextlineno++
|
|
while (nextlineno <= pagelen) {
|
|
print ""
|
|
nextlineno++
|
|
}
|
|
nextlineno = 1
|
|
thispageno++
|
|
|
|
# after page break, should not space unnecessarily,
|
|
# and should pad first line from right
|
|
nospacemode = 1
|
|
padfrom = "R"
|
|
}
|
|
|
|
# we are finally done with emitting output
|
|
# pick up input word, if any
|
|
if ($1 >= 0) {
|
|
line = $2
|
|
paddable = substr(sps, 1, length($2))
|
|
thislinelen = $1
|
|
}
|
|
}
|
|
|
|
# if we broke, next line should pad from right
|
|
if ($1 == dobreak)
|
|
padfrom = "R"
|
|
|
|
# cleanup and post-break command processing
|
|
if ($1 >= 0 || $2 == "nohyphen") {
|
|
# reset hyphenation trickery after each word (fragment)
|
|
cont = " "
|
|
contlen = 1
|
|
eol = ""
|
|
eollen = 0
|
|
} else if ($2 == "need" || $2 == "toindent")
|
|
nop = 0 # dealt with above
|
|
else if ($2 == "flush" || $2 == "center")
|
|
nop = 0 # exist only to cause break
|
|
else if ($1 == message)
|
|
print "awf: " $2 >errs
|
|
else if ($2 == "gap") {
|
|
# gap between last word and next one should be >= $3
|
|
if (thislinelen >= 0) {
|
|
line = line substr(sps, 1, $3-1)
|
|
paddable = paddable substr(sps, 1, $3-1)
|
|
thislinelen += $3-1
|
|
}
|
|
} else if ($2 == "tabto") {
|
|
# move to tab stop at $3
|
|
if (thislinelen < 0)
|
|
thislinelen = 0 # make line exist
|
|
n = $3 - thislinelen
|
|
if (n > 0) { # must emit some space
|
|
line = line substr(sps, 1, n)
|
|
# nothing before a tab is paddable
|
|
paddable = substr(sps, 1, length(line))
|
|
thislinelen += n
|
|
# suppress space following
|
|
cont = ""
|
|
contlen = 0
|
|
}
|
|
} else if ($2 == "errsto")
|
|
errs = $3
|
|
else if ($2 ~ /^[LCR][HF]$/)
|
|
hdrs[$2] = $3
|
|
else if ($2 == "fph")
|
|
fph = $3
|
|
else if ($2 == "space") {
|
|
if (!nospacemode) {
|
|
# generate an empty line, which will be flushed by
|
|
# the next word; NB we know "space" caused a flush
|
|
line = ""
|
|
paddable = ""
|
|
thislinelen = linelen + 1
|
|
nospacemode = 0
|
|
}
|
|
} else if ($2 == "left")
|
|
adj = "left"
|
|
else if ($2 == "both")
|
|
adj = "both"
|
|
else if ($2 == "indent")
|
|
ind = $3
|
|
else if ($2 == "tempindent")
|
|
tmpind = $3
|
|
else if ($2 == "linelen")
|
|
linelen = $3
|
|
else if ($2 == "pagelen")
|
|
pagelen = $3
|
|
else if ($2 == "nospace")
|
|
nospacemode = 1
|
|
else if ($2 == "yesspace")
|
|
nospacemode = 0
|
|
else if ($2 == "hyphen") {
|
|
# discretionary hyphen at this point
|
|
cont = ""
|
|
contlen = 0
|
|
eol = $3
|
|
eollen = $4
|
|
} else if ($2 == "userhyphen") {
|
|
# user-supplied hyphen at this point
|
|
cont = $3
|
|
contlen = $4
|
|
eol = $3
|
|
eollen = $4
|
|
} else if ($2 == "pageoffset")
|
|
pageoffset = substr(sps, 1, $3)
|
|
else
|
|
print "awf: URK -- INTERNAL OPCODE `" $2 "' UNKNOWN" >errs
|
|
}
|
|
END {
|
|
# second pass is supposed to fake a .ne to flush the last page
|
|
if (nextlineno != 1)
|
|
print "awf: last page not flushed!" >errs
|
|
}
|