# third pass: setting lines and pages # The input language of this pass is basically 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 }