Checkpoint: pull in mpm; merge pic from Taj's version of the world
This commit is contained in:
parent
c5561c23cf
commit
5f1cf8e6fb
21 changed files with 3878 additions and 39 deletions
1
src/cmd/mpm/.cvsignore
Normal file
1
src/cmd/mpm/.cvsignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
pm
|
||||
188
src/cmd/mpm/README
Normal file
188
src/cmd/mpm/README
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
An experiment in page makeup for troff output...
|
||||
|
||||
-mpm is a version of standard -ms that causes extra
|
||||
information for vertical justification and figure
|
||||
placement to be included in troff output. Commands that
|
||||
have been augmented to provide paddable space are
|
||||
|
||||
.SH and .NH
|
||||
.PP and .LP no space if \n(PD is 0; normally .nr PD 0.3v; leave at least 1u
|
||||
.IP and .QP also
|
||||
.EQ and .EN
|
||||
.TS and .TE no space if \n(TS is 0; normally .nr TS 0.5v
|
||||
.PS and .PE
|
||||
.P1 and .P2 display programs in CW font
|
||||
.DS and .DE
|
||||
.QS and .QE
|
||||
|
||||
Other commands, registers, strings, etc.:
|
||||
|
||||
.SP n explicit paddable space, just like .sp n.
|
||||
generally you should ALWAYS use .SP instead of .sp.
|
||||
if you need exactly a given vertical space, you can say
|
||||
.SP 3i exactly
|
||||
this space won't be padded.
|
||||
.Tm words prints "words" and the output page number on stderr
|
||||
sorry about the spelling; -ms pre-empted .TM
|
||||
.NE n like .ne. note: does not cause a break
|
||||
|
||||
Others may be added as the need arises.
|
||||
|
||||
.nr FO n Set the page length. This value is the bottom of
|
||||
the text on the page; a bottom title may lie below.
|
||||
default is 10i (== 10 inches).
|
||||
%o, %e are strings containing odd and even page titles
|
||||
%# is the current page number (often useless)
|
||||
.PT is a macro invoked at the top of each "page";
|
||||
it will normally use %e, %o and %#. There is also
|
||||
a .BT for page bottoms if desired.
|
||||
.BP force a page break
|
||||
.FL force all waiting figures out before any more running text
|
||||
.1C, .2C multiple columns; number registers CW and GW set
|
||||
the column and gutter width if you don't like the default.
|
||||
absent a .FC command, all two-column contents collect
|
||||
together on the page
|
||||
.FC freeze current two-column contents and start afresh.
|
||||
necessary if you want to switch between 1 and 2 column
|
||||
text and keep the relative order among them.
|
||||
|
||||
Usage is some variant of
|
||||
|
||||
... | troff -mpm
|
||||
|
||||
/usr/lib/tmac/pm is the page-justifier itself; it is called automatically
|
||||
by the -mpm macro package. If you are installing this yourself, you will
|
||||
have to edit the 2nd line of tmac.pm to arrange that pm is called directly
|
||||
from troff.
|
||||
|
||||
There are several lines in tmac.pm that say
|
||||
.so /n/coma/usr/bwk/...
|
||||
You should delete these; they are placeholders for some experiments.
|
||||
|
||||
If you use -mm, you are more or less out of luck, although we will be
|
||||
happy to provide a crude and incomplete program that purports to convert
|
||||
-mm to -ms. It may suggest what you need but it won't do the job.
|
||||
|
||||
To compile pm, you need a C++ compiler, preferably release 2.0 or later.
|
||||
Put the .c and .h files in a directory, and type
|
||||
make
|
||||
This process may well fail. The usual cause is that different systems
|
||||
put function declarations in different header files, and C++ insists that
|
||||
all functions be properly declared. You can almost always get through this
|
||||
part by adding function declarations. The most likely offender is malloc;
|
||||
a line like
|
||||
extern char *malloc(int);
|
||||
near the top of slug.c will solve this one.
|
||||
|
||||
|
||||
Bugs, etc.:
|
||||
|
||||
not all -ms commands have been decorated; in particular,
|
||||
the rich variety of document types (TM, CSTR, etc.,) is not
|
||||
really supported.
|
||||
|
||||
there are problems with funny first pages and troff input
|
||||
that moves back up the page.
|
||||
|
||||
multiple columns: only .2C is available. The program does not check
|
||||
whether something is wide or narrow: user has responsibility to mark
|
||||
which with .1C or .2C.
|
||||
|
||||
headings are a bit tricky if you want things like
|
||||
running titles that include the current section title.
|
||||
normally a two-pass procedure using .Tm is needed.
|
||||
|
||||
It's a pain to force a blank vertical space of specified height.
|
||||
Try this:
|
||||
.de x
|
||||
\v'\\$1'\0\h'-\w'\0'u'\c
|
||||
..
|
||||
.x 2.5i
|
||||
|
||||
|
||||
If you want to roll your own, the following components are
|
||||
included in pm's "command language". They are inserted in
|
||||
the troff output in the form of "x X ..." commands, which
|
||||
are created either by \X'...' or by the .X macro in -mpm.
|
||||
Look at how they are used in /usr/lib/tmac/tmac.pm for examples.
|
||||
|
||||
|
||||
BS n breakable stream n = min # lines that must appear on page
|
||||
use: PP, LP, IP, ...
|
||||
|
||||
US unbreakable stream use: KS/KE, DS/DE, TS/TE, EQ/EN, PS/PE, etc.
|
||||
|
||||
BF v breakable float v = preferred vertical location of box center
|
||||
use: FS/FE
|
||||
use two successive BF's to give two preferences
|
||||
|
||||
UF v unbreakable float v = preferred vertical location of box center
|
||||
use: KF/KE
|
||||
use two successive UF's to give two preferences
|
||||
|
||||
PT page title use: user has absolute control between PT and END
|
||||
no SP's or other pm commands inside are processed
|
||||
|
||||
BT bottom title use: user has absolute control between BT and END
|
||||
|
||||
END end end a US, BF, UF, PT, or BT
|
||||
all constructs nest, but a float within another float
|
||||
or a US block will not float within or outside the block
|
||||
|
||||
NE n need break page if a VBOX of height n would not fit on page
|
||||
use: .NE n
|
||||
|
||||
SP n space paddable space of n
|
||||
use: .SP n
|
||||
|
||||
PARM NP v top of pm text at v
|
||||
new page
|
||||
|
||||
PARM FO v bottom of pm text at v
|
||||
footer length of text on page = FO-NP
|
||||
|
||||
PARM PL v physical page ends at v
|
||||
page length default = FO + NP
|
||||
|
||||
PARM MF x tolerance to prevent padding
|
||||
minimum fullness default = 0.9
|
||||
|
||||
PARM CT x tolerance for two-column operation
|
||||
column tolerance default = 0.5
|
||||
|
||||
PARM DBG x debugging flag
|
||||
|
||||
TM str message .Tm words prints <pageno> <tab> <words> on stderr
|
||||
|
||||
MC n o multiple column n columns, offset o.
|
||||
Only 1 and 2 columns will work.
|
||||
|
||||
CMD BP break page force page break
|
||||
|
||||
CMD FL flush force all queued figures out before any more
|
||||
stream material is output
|
||||
|
||||
CMD FC freeze columns force out current two-column contents;
|
||||
start a fresh one
|
||||
|
||||
Something like this will probably have to be added:
|
||||
|
||||
NC new column HARD!
|
||||
|
||||
Known botches in the existing implementation of pm:
|
||||
|
||||
If a footnote is split across two pages, any associated separator line
|
||||
will not be copied. If there are multiple footnotes on one page, there
|
||||
will be multiple separators too. -mpm's .FS macro does not provide a
|
||||
separator. If you want a separator line, put it in explicitly with
|
||||
a call to the .FA macro.
|
||||
|
||||
There are not enough settable parameters; in particular, the
|
||||
way to control the height is a botch.
|
||||
|
||||
|
||||
Historical note: There is a simpler version of pm and -mpm
|
||||
called pj and -mpj that only does vertical justification of
|
||||
pages that have already been laid out by conventional means.
|
||||
This simpler version may be adequate, but it is no longer
|
||||
supported and memory of how it works is growing dim.
|
||||
12
src/cmd/mpm/misc.cc
Normal file
12
src/cmd/mpm/misc.cc
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#include "misc.h"
|
||||
|
||||
char errbuf[200];
|
||||
char *progname;
|
||||
int wantwarn = 0;
|
||||
|
||||
int dbg = 0;
|
||||
// dbg = 1 : dump slugs
|
||||
// dbg = 2 : dump ranges
|
||||
// dbg = 4 : report function entry
|
||||
// dbg = 8 : follow queue progress
|
||||
// dbg = 16: follow page fill progress
|
||||
41
src/cmd/mpm/misc.h
Normal file
41
src/cmd/mpm/misc.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
// XXX: Apparently necessary for g++
|
||||
#define typename tyname
|
||||
|
||||
extern char errbuf[];
|
||||
extern char *progname;
|
||||
extern int linenum;
|
||||
extern int wantwarn;
|
||||
|
||||
// #define ERROR fflush(stdout), fprintf(stderr, "%s: ", progname), fprintf(stderr,
|
||||
// #define FATAL ), exit(1)
|
||||
// #define WARNING )
|
||||
|
||||
#define ERROR fprintf(stdout, "\n#MESSAGE TO USER: "), sprintf(errbuf,
|
||||
#define FATAL ), fputs(errbuf, stdout), \
|
||||
fprintf(stderr, "%s: ", progname), \
|
||||
fputs(errbuf, stderr), \
|
||||
fflush(stdout), \
|
||||
exit(1)
|
||||
#define WARNING ), fputs(errbuf, stdout), \
|
||||
wantwarn ? \
|
||||
fprintf(stderr, "%s: ", progname), \
|
||||
fputs(errbuf, stderr) : 0, \
|
||||
fflush(stdout)
|
||||
|
||||
#define eq(s,t) (strcmp(s,t) == 0)
|
||||
|
||||
inline int max(int x, int y) { return x > y ? x : y; }
|
||||
inline int min(int x, int y) { return x > y ? y : x; }
|
||||
inline int abs(int x) { return (x >= 0) ? x : -x; }
|
||||
|
||||
extern int dbg;
|
||||
|
||||
extern int pn, userpn; // actual and user-defined page numbers
|
||||
extern int pagetop, pagebot; // printing margins
|
||||
extern int physbot; // physical bottom of the page
|
||||
24
src/cmd/mpm/mkfile
Normal file
24
src/cmd/mpm/mkfile
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
TARG=aux/pm
|
||||
OFILES=misc.$O\
|
||||
slug.$O\
|
||||
range.$O\
|
||||
queue.$O\
|
||||
page.$O\
|
||||
|
||||
HFILES=misc.h\
|
||||
|
||||
BIN=/$objtype/bin
|
||||
</sys/src/cmd/mkone
|
||||
CC=c++/$CC
|
||||
LD=c++/$LD
|
||||
CFLAGS=
|
||||
|
||||
slug.$O: slug.h
|
||||
range.$O: range.h slug.h
|
||||
queue.$O: page.h range.h slug.h
|
||||
page.$O: page.h range.h slug.h
|
||||
|
||||
test:V: $O.out
|
||||
tryout $O.out
|
||||
612
src/cmd/mpm/page.cc
Normal file
612
src/cmd/mpm/page.cc
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
#include "misc.h"
|
||||
#include "slug.h"
|
||||
#include "range.h"
|
||||
#include "page.h"
|
||||
|
||||
const int MAXRANGES = 1000;
|
||||
static range *ptemp[MAXRANGES]; // for movefloats()
|
||||
|
||||
static void swapright(int n) // used by movefloats()
|
||||
{
|
||||
range *t = ptemp[n];
|
||||
ptemp[n] = ptemp[n+1];
|
||||
ptemp[n+1] = t;
|
||||
ptemp[n]->setaccum( ptemp[n+1]->accum() -
|
||||
ptemp[n+1]->rawht() + ptemp[n]->rawht() );
|
||||
ptemp[n+1]->setaccum( ptemp[n]->accum() + ptemp[n+1]->rawht() );
|
||||
}
|
||||
|
||||
// Figure out the goal position for each floating range on scratch,
|
||||
// and move it past stream ranges until it's as close to its goal as possible.
|
||||
static void movefloats(stream *scratch, double scale)
|
||||
{
|
||||
int nranges, i;
|
||||
const int Huge = 100000;
|
||||
|
||||
for (nranges = 0; scratch->more(); scratch->advance())
|
||||
ptemp[nranges++] = scratch->current();
|
||||
scratch->freeall();
|
||||
ufrange rtemp;
|
||||
ptemp[nranges] = &rtemp;
|
||||
rtemp.setgoal(Huge);
|
||||
int accumV = 0; // compute accum values and
|
||||
for (i = 0; i < nranges; i++) { // pick closest goal for floats
|
||||
ptemp[i]->pickgoal(accumV, scale);
|
||||
ptemp[i]->setaccum(accumV += ptemp[i]->rawht());
|
||||
}
|
||||
int j; // index for inner loop below:
|
||||
for (i = nranges; --i >= 0; ) // stably sort floats to bottom
|
||||
for (j = i; j < nranges; j++)
|
||||
if (ptemp[j]->goal() > ptemp[j+1]->goal())
|
||||
swapright(j);
|
||||
else
|
||||
break;
|
||||
if (dbg & 16)
|
||||
printf("#movefloats: before floating, from bottom:\n");
|
||||
for (i = nranges; --i >= 0; ) { // find topmost float
|
||||
if (ptemp[i]->goal() == NOGOAL)
|
||||
break;
|
||||
if (dbg & 16)
|
||||
printf("# serialno %d goal %d height %d\n",
|
||||
ptemp[i]->serialno(), ptemp[i]->goal(),
|
||||
ptemp[i]->rawht());
|
||||
} // i+1 is topmost float
|
||||
for (i++ ; i < nranges; i++) // move each float up the page
|
||||
for (j = i; j > 0; j--) // as long as closer to its goal
|
||||
if (ptemp[j]->goal()
|
||||
<= ptemp[j-1]->accum() + ptemp[j]->rawht()/2
|
||||
&& ptemp[j-1]->goal() == NOGOAL)
|
||||
swapright(j-1);
|
||||
else
|
||||
break;
|
||||
if (ptemp[nranges] != &rtemp)
|
||||
ERROR "goal sentinel has disappeared from movefloats" FATAL;
|
||||
for (i = 0; i < nranges; i++) // copy sorted list back
|
||||
scratch->append(ptemp[i]);
|
||||
}
|
||||
|
||||
// Traverse the leaves of a tree of ranges, filtering out only SP and VBOX.
|
||||
static range *filter(generator *g)
|
||||
{
|
||||
range *r;
|
||||
while ((r = g->next()) != 0)
|
||||
if (r->isvbox() || r->issp())
|
||||
break;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Zero out leading and trailing spaces; coalesce adjacent SP's.
|
||||
static void trimspace(stream *scratch)
|
||||
{
|
||||
generator g;
|
||||
range *r, *prevr = 0;
|
||||
|
||||
for (g = scratch; (r = filter(&g)) != 0 && r->issp(); prevr = r)
|
||||
r->setheight(0); // zap leading SP
|
||||
for ( ; (r = filter(&g)) != 0; prevr = r)
|
||||
if (r->issp())
|
||||
if (prevr && prevr->issp()) {
|
||||
// coalesce adjacent SPs
|
||||
r->setheight(max(r->rawht(), prevr->height()));
|
||||
prevr->setheight(0);
|
||||
} else // a VBOX intervened
|
||||
r->setheight(r->rawht());
|
||||
if (prevr && prevr->issp()) // zap *all* trailing space
|
||||
prevr->setheight(0); // (since it all coalesced
|
||||
// into the last one)
|
||||
}
|
||||
|
||||
// Pad the non-zero SP's in scratch so the total height is wantht.
|
||||
// Note that the SP values in scratch are not the raw values, and
|
||||
// indeed may already have been padded.
|
||||
static void justify(stream *scratch, int wantht)
|
||||
{
|
||||
range *r;
|
||||
int nsp = 0, hsp = 0;
|
||||
|
||||
int adjht = scratch->height();
|
||||
// Find all the spaces.
|
||||
generator g;
|
||||
for (g = scratch; (r = g.next()) != 0; )
|
||||
if (r->issp() && r->height() > 0) {
|
||||
nsp++;
|
||||
hsp += r->height();
|
||||
}
|
||||
int excess = wantht - adjht;
|
||||
if (excess < 0)
|
||||
ERROR "something on page %d is oversize by %d\n",
|
||||
userpn, -excess WARNING;
|
||||
if (dbg & 16)
|
||||
printf("# justify %d: excess %d nsp %d hsp %d adjht %d\n",
|
||||
userpn, excess, nsp, hsp, adjht);
|
||||
if (excess <= 0 || nsp == 0)
|
||||
return;
|
||||
// Redistribute the excess space.
|
||||
for (g = scratch; (r = g.next()) != 0; )
|
||||
if (r->issp() && r->height() > 0) {
|
||||
int delta = (int) ((float)(r->height()*excess)/hsp + 0.5);
|
||||
if (dbg & 16)
|
||||
printf("# pad space %d by %d: hsp %d excess %d\n",
|
||||
r->height(), delta, hsp, excess);
|
||||
r->setheight(r->height() + delta);
|
||||
}
|
||||
}
|
||||
|
||||
// If r were added to s, would the height of the composed result be at most maxht?
|
||||
int wouldfit(range *r, stream *s, int maxht)
|
||||
{
|
||||
if (r->rawht() + s->rawht() <= maxht)
|
||||
return 1; // the conservative test succeeded
|
||||
stream scratch; // local playground for costly test
|
||||
for (stream cd = *s; cd.more(); cd.advance())
|
||||
scratch.append(cd.current());
|
||||
scratch.append(r);
|
||||
movefloats(&scratch, ((double) scratch.rawht())/maxht);
|
||||
trimspace(&scratch);
|
||||
int retval = scratch.height() <= maxht;
|
||||
scratch.freeall();
|
||||
return retval;
|
||||
}
|
||||
|
||||
// If s1 were added to s, would the height of the composed result be at most maxht?
|
||||
// The computational structure is similar to that above.
|
||||
int wouldfit(stream *s1, stream *s, int maxht)
|
||||
{
|
||||
if (s1->rawht() + s->rawht() <= maxht)
|
||||
return 1;
|
||||
stream scratch, cd;
|
||||
for (cd = *s; cd.more(); cd.advance())
|
||||
scratch.append(cd.current());
|
||||
for (cd = *s1; cd.more(); cd.advance())
|
||||
scratch.append(cd.current());
|
||||
movefloats(&scratch, ((double) scratch.rawht())/maxht);
|
||||
trimspace(&scratch);
|
||||
int retval = scratch.height() <= maxht;
|
||||
scratch.freeall();
|
||||
return retval;
|
||||
}
|
||||
|
||||
// All of stream *s is destined for one column or the other; which is it to be?
|
||||
void multicol::choosecol(stream *s, int goalht)
|
||||
{
|
||||
stream *dest;
|
||||
if (!leftblocked && wouldfit(s, &(column[0]), goalht))
|
||||
dest = &(column[0]);
|
||||
else {
|
||||
dest = &(column[1]);
|
||||
if (!s->current()->floatable())
|
||||
// a stream item is going into the right
|
||||
// column, so no more can go into the left.
|
||||
leftblocked = 1;
|
||||
}
|
||||
for (stream cd = *s; cd.more(); cd.advance())
|
||||
dest->append(cd.current());
|
||||
}
|
||||
|
||||
double coltol = 0.5;
|
||||
|
||||
// Try, very hard, to put everything in the multicol into two columns
|
||||
// so that the total height is at most htavail.
|
||||
void multicol::compose(int defonly)
|
||||
{
|
||||
if (!nonempty()) {
|
||||
setheight(0);
|
||||
return;
|
||||
}
|
||||
scratch.freeall(); // fill scratch with everything destined
|
||||
// for either column
|
||||
stream cd;
|
||||
for (cd = definite; cd.more(); cd.advance())
|
||||
scratch.append(cd.current());
|
||||
if (!defonly)
|
||||
for (cd = *(currpage->stage); cd.more(); cd.advance())
|
||||
if (cd.current()->numcol() == 2)
|
||||
scratch.append(cd.current());
|
||||
scratch.restoreall(); // in particular, floatables' goals
|
||||
int i;
|
||||
int rawht = scratch.rawht();
|
||||
int halfheight = (int)(coltol*rawht);
|
||||
// choose a goal height
|
||||
int maxht = defonly ? halfheight : htavail;
|
||||
secondtry:
|
||||
for (i = 0; i < 2; i++)
|
||||
column[i].freeall();
|
||||
leftblocked = 0;
|
||||
cd = scratch;
|
||||
while (cd.more()) {
|
||||
queue ministage; // for the minimally acceptable chunks
|
||||
ministage.freeall(); // that are to be added to either column
|
||||
while (cd.more() && !cd.current()->issentinel()) {
|
||||
ministage.enqueue(cd.current());
|
||||
cd.advance();
|
||||
}
|
||||
choosecol(&ministage, maxht);
|
||||
if (cd.more() && cd.current()->issentinel())
|
||||
cd.advance(); // past sentinel
|
||||
}
|
||||
if (height() > htavail && maxht != htavail) {
|
||||
// We tried to balance the columns, but
|
||||
// the result was too tall. Go back
|
||||
// and try again with the less ambitious
|
||||
// goal of fitting the space available.
|
||||
maxht = htavail;
|
||||
goto secondtry;
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
movefloats(&(column[i]), ((double) column[i].rawht())/currpage->pagesize);
|
||||
trimspace(&(column[i]));
|
||||
}
|
||||
if (dbg & 32) {
|
||||
printf("#multicol::compose: htavail %d maxht %d dv %d\n",
|
||||
htavail, maxht, height());
|
||||
dump();
|
||||
}
|
||||
if (defonly)
|
||||
stretch(height());
|
||||
}
|
||||
|
||||
// A sequence of two-column ranges waits on the stage.
|
||||
// So long as the page's skeleton hasn't changed--that is, the maximum height
|
||||
// available to the two-column chunk is the same--we just use the columns that
|
||||
// have been built up so far, and choose a column into which to put the stage.
|
||||
// If the skeleton has changed, however, then we may need to make entirely
|
||||
// new decisions about which column gets what, so we recompose the whole page.
|
||||
void multicol::tryout()
|
||||
{
|
||||
if (htavail == prevhtavail)
|
||||
choosecol(currpage->stage, htavail);
|
||||
else
|
||||
currpage->compose(DRAFT);
|
||||
prevhtavail = htavail;
|
||||
}
|
||||
|
||||
// Make both columns the same height.
|
||||
// (Maybe this should also be governed by minfull,
|
||||
// to prevent padding very underfull columns.)
|
||||
void multicol::stretch(int wantht)
|
||||
{
|
||||
if (wantht < height())
|
||||
ERROR "page %d: two-column chunk cannot shrink\n", userpn FATAL;
|
||||
for (int i = 0; i < 2; i++)
|
||||
justify(&(column[i]), wantht);
|
||||
if (dbg & 16)
|
||||
printf("#col hts: left %d right %d\n",
|
||||
column[0].height(), column[1].height());
|
||||
}
|
||||
|
||||
// Report an upper bound on how tall the current two-column object is.
|
||||
// The (possibly composed) heights of the two columns give a crude upper
|
||||
// bound on the total height. If the result is more than the height
|
||||
// available for the two-column object, then the columns are each
|
||||
// composed to give a better estimate of their heights.
|
||||
int multicol::height()
|
||||
{
|
||||
int retval = max(column[0].height(), column[1].height());
|
||||
if (retval < htavail)
|
||||
return retval;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
movefloats(&(column[i]), ((double) column[i].height())/currpage->pagesize);
|
||||
trimspace(&(column[i]));
|
||||
}
|
||||
return max(column[0].height(), column[1].height());
|
||||
}
|
||||
|
||||
void multicol::dump()
|
||||
{
|
||||
printf("####2COL dv %d\n", height());
|
||||
printf("# left column:\n");
|
||||
column[0].dump();
|
||||
printf("# right column:\n");
|
||||
column[1].dump();
|
||||
}
|
||||
|
||||
// From the head of queue qp, peel off a piece whose raw height is at most space.
|
||||
int peeloff(stream *qp, int space)
|
||||
{
|
||||
stream *s1 = qp->current()->children();
|
||||
if (!(s1 && s1->more() && s1->current()->height() <= space))
|
||||
// in other words, either qp's head is
|
||||
// not nested, or its first subrange
|
||||
return 0; // is also too big, so we give up
|
||||
qp->split();
|
||||
s1 = qp->current()->children();
|
||||
stream *s2 = qp->next()->children();
|
||||
while (s2->more() && s2->current()->rawht() <= space) {
|
||||
s1->append(s2->current());
|
||||
space -= s2->current()->rawht();
|
||||
s2->advance();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// There are four possibilities for consecutive calls to tryout().
|
||||
// If we're processing a sequence of single-column ranges, tryout()
|
||||
// uses the original algorithm: (1) conservative test; (2) costly test;
|
||||
// (3) split a breakable item.
|
||||
// If we're processing a sequence of double-column ranges, tryout()
|
||||
// defers to twocol->tryout(), which gradually builds up the contents
|
||||
// of the two columns until they're as tall as they can be without
|
||||
// exceeding twocol->htavail.
|
||||
// If we're processing a sequence of single-column ranges and we
|
||||
// get a double-column range, then we use compose() to build a
|
||||
// skeleton page and set twocol->htavail, the maximum height that
|
||||
// should be occupied by twocol.
|
||||
// If we're processing a sequence of double-column ranges and we
|
||||
// get a single-column range, then we should go back and squish
|
||||
// the double-column chunk as short as possible before we see if
|
||||
// we can fit the single-column range.
|
||||
void page::tryout()
|
||||
{
|
||||
if (!stage->more())
|
||||
ERROR "empty stage in page::tryout()\n" FATAL;
|
||||
int curnumcol = stage->current()->numcol();
|
||||
if (dbg & 32) {
|
||||
printf("#page::tryout(): ncol = %d, prevncol = %d; on stage:\n",
|
||||
curnumcol, prevncol);
|
||||
stage->dump();
|
||||
printf("#END of stage contents\n");
|
||||
}
|
||||
switch(curnumcol) {
|
||||
default:
|
||||
ERROR "unexpected number of columns in tryout(): %d\n",
|
||||
stage->current()->numcol() FATAL;
|
||||
break;
|
||||
case 1:
|
||||
if (prevncol == 2)
|
||||
compose(FINAL);
|
||||
if (wouldfit(stage, &definite, pagesize - twocol->height()))
|
||||
commit();
|
||||
else if (stage->current()->breakable() || blank()
|
||||
&& peeloff(stage,
|
||||
pagesize - (definite.height() + twocol->height()))) {
|
||||
// first add the peeled-off part that fits
|
||||
adddef(stage->dequeue());
|
||||
// then send the rest back for later
|
||||
stage->current()->setbreaking();
|
||||
welsh();
|
||||
} else if (blank()) {
|
||||
stage->current()->rdump();
|
||||
ERROR "A %s is too big to continue.\n",
|
||||
stage->current()->typename() FATAL;
|
||||
} else
|
||||
welsh();
|
||||
break;
|
||||
case 2:
|
||||
if (prevncol == 1)
|
||||
compose(DRAFT);
|
||||
else
|
||||
twocol->tryout();
|
||||
if (scratch.height() <= pagesize)
|
||||
commit();
|
||||
else
|
||||
welsh();
|
||||
break;
|
||||
}
|
||||
prevncol = curnumcol;
|
||||
}
|
||||
|
||||
// To compose the page, we (1) fill scratch with the stuff that's meant to
|
||||
// go on the page; (2) compose scratch as best we can; (3) set the maximum
|
||||
// height available to the two-column part of the page; (4) have the two-
|
||||
// column part compose itself.
|
||||
// In the computation of twocol->htavail, it does not matter that
|
||||
// twocol->height() is merely an upper bound, because it is merely being
|
||||
// subtracted out to give the exact total height of the single-column stuff.
|
||||
void page::compose(int final)
|
||||
{
|
||||
makescratch(final);
|
||||
int adjht = scratch.rawht();
|
||||
if (dbg & 16)
|
||||
printf("# page %d measure %d\n", userpn, adjht);
|
||||
movefloats(&scratch, ((double) adjht)/pagesize);
|
||||
trimspace(&scratch);
|
||||
twocol->htavail = pagesize - (scratch.height() - twocol->height());
|
||||
twocol->compose(final);
|
||||
adjht = scratch.height();
|
||||
if (dbg & 16)
|
||||
printf("# page %d measure %d after trim\n", userpn, adjht);
|
||||
}
|
||||
|
||||
// Fill the scratch area with ranges destined for the page.
|
||||
// If defonly == 0, then add anything that's on stage--this is a trial run.
|
||||
// If defonly != 0, use only what's definitely on the page.
|
||||
void page::makescratch(int defonly)
|
||||
{
|
||||
scratch.freeall();
|
||||
stream cd;
|
||||
for (cd = definite; cd.more(); cd.advance())
|
||||
scratch.append(cd.current());
|
||||
if (!defonly)
|
||||
for (cd = *stage; cd.more(); cd.advance())
|
||||
if (cd.current()->numcol() == 1)
|
||||
scratch.append(cd.current());
|
||||
if (twocol->nonempty())
|
||||
scratch.append(twocol);
|
||||
}
|
||||
|
||||
// Accept the current contents of the stage.
|
||||
// If the stage contains two-column ranges, add a sentinel to indicate the end
|
||||
// of a chunk of stage contents.
|
||||
void page::commit()
|
||||
{
|
||||
if (dbg & 4)
|
||||
printf("#entering page::commit()\n");
|
||||
int numcol = 0;
|
||||
while (stage->more()) {
|
||||
numcol = stage->current()->numcol();
|
||||
adddef(stage->dequeue());
|
||||
}
|
||||
if (numcol == 2)
|
||||
adddef(new sentrange);
|
||||
}
|
||||
|
||||
// Send the current contents of the stage back to its source.
|
||||
void page::welsh()
|
||||
{
|
||||
if (dbg & 4)
|
||||
printf("#entering page::welsh()\n");
|
||||
while (stage->more()) {
|
||||
range *r = stage->dequeue();
|
||||
r->enqueue(ANDBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
enum { USonly = 1 };
|
||||
|
||||
// So long as anything is eligible to go onto the page, keep trying.
|
||||
// Once nothing is eligible, compose and justify the page.
|
||||
void page::fill()
|
||||
{
|
||||
while (stage->prime())
|
||||
stage->pend();
|
||||
compose(FINAL);
|
||||
if (dbg & 16)
|
||||
scratch.dump();
|
||||
if (anymore()) {
|
||||
int adjht = scratch.height();
|
||||
if (adjht > minfull*pagesize) {
|
||||
justify(&scratch, pagesize);
|
||||
adjht = scratch.height();
|
||||
int stretchamt = max(pagesize - adjht, 0);
|
||||
twocol->stretch(twocol->height() + stretchamt);
|
||||
// in case the page's stretchability lies
|
||||
// entirely in its two-column part
|
||||
} else
|
||||
ERROR "page %d only %.0f%% full; will not be adjusted\n",
|
||||
userpn, 100*(double) adjht/pagesize WARNING;
|
||||
}
|
||||
}
|
||||
|
||||
void page::adddef(range *r)
|
||||
{
|
||||
if (dbg & 4)
|
||||
printf("#entering page::adddef()\n");
|
||||
switch (r->numcol()) {
|
||||
case 1: definite.append(r);
|
||||
break;
|
||||
case 2: twocol->definite.append(r);
|
||||
break;
|
||||
default: ERROR "%d-column range unexpected\n", r->numcol() FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
int multicol::print(int cv, int col)
|
||||
{
|
||||
if (col != 0)
|
||||
ERROR "multicolumn output must start in left column\n" FATAL;
|
||||
int curv = cv, maxv = cv; // print left column
|
||||
for ( ; column[0].more(); column[0].advance()) {
|
||||
curv = column[0].current()->print(curv, 0);
|
||||
maxv = max(maxv, curv);
|
||||
}
|
||||
curv = cv; // print right column
|
||||
for ( ; column[1].more(); column[1].advance()) {
|
||||
curv = column[1].current()->print(curv, 1);
|
||||
maxv = max(maxv, curv);
|
||||
}
|
||||
return maxv;
|
||||
}
|
||||
|
||||
void page::print()
|
||||
{
|
||||
static int tops = 1, bots = 1;
|
||||
if (!scratch.more()) {
|
||||
ERROR "## Here's what's left on squeue:\n" WARNING;
|
||||
squeue.dump();
|
||||
ERROR "## Here's what's left on bfqueue:\n" WARNING;
|
||||
bfqueue.dump();
|
||||
ERROR "## Here's what's left on ufqueue:\n" WARNING;
|
||||
ufqueue.dump();
|
||||
ERROR "page %d appears to be empty\n", userpn WARNING;
|
||||
fflush(stderr), fflush(stdout), exit(0);
|
||||
// something is very wrong if this happens
|
||||
}
|
||||
printf("p%d\n", userpn); // print troff output page number
|
||||
if (ptlist.more()) { // print page header
|
||||
ptlist.current()->print(0, 0);
|
||||
ptlist.advance();
|
||||
} else if (tops) {
|
||||
ERROR "ran out of page titles at %d\n", userpn WARNING;
|
||||
tops = 0;
|
||||
}
|
||||
int curv = 0;
|
||||
printf("V%d\n", curv = pagetop);// print page contents
|
||||
for ( ; scratch.more(); scratch.advance()) {
|
||||
curv = scratch.current()->print(curv, 0);
|
||||
}
|
||||
if (btlist.more()) { // print page footer
|
||||
btlist.current()->print(0, 0);
|
||||
btlist.advance();
|
||||
} else if (bots) {
|
||||
ERROR "ran out of page bottoms at %d\n", userpn WARNING;
|
||||
bots = 0;
|
||||
}
|
||||
printf("V%d\n", physbot); // finish troff output page
|
||||
}
|
||||
|
||||
int pagetop = 0; // top printing margin
|
||||
int pagebot = 0; // bottom printing margin
|
||||
int physbot = 0; // physical bottom of page
|
||||
|
||||
double minfull = 0.9; // minimum fullness before padding
|
||||
|
||||
int pn = 0; // cardinal page number
|
||||
int userpn = 0; // page number derived from PT slugs
|
||||
|
||||
static void makepage()
|
||||
{
|
||||
page pg(pagebot - pagetop);
|
||||
++pn;
|
||||
userpn = ptlist.more() ? ptlist.current()->pn() : pn;
|
||||
pg.fill();
|
||||
pg.print();
|
||||
}
|
||||
|
||||
static void conv(FILE *fp)
|
||||
{
|
||||
startup(fp); // read slugs, etc.
|
||||
while (anymore())
|
||||
makepage();
|
||||
lastrange->print(0, 0); // trailer
|
||||
checkout(); // check that everything was printed
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
static FILE *fp = stdin;
|
||||
progname = argv[0];
|
||||
while (argc > 1 && argv[1][0] == '-') {
|
||||
switch (argv[1][1]) {
|
||||
case 'd':
|
||||
dbg = atoi(&argv[1][2]);
|
||||
if (dbg == 0)
|
||||
dbg = ~0;
|
||||
break;
|
||||
case 'm':
|
||||
minfull = 0.01*atof(&argv[1][2]);
|
||||
break;
|
||||
case 'c':
|
||||
coltol = 0.01*atof(&argv[1][2]);
|
||||
break;
|
||||
case 'w':
|
||||
wantwarn = 1;
|
||||
break;
|
||||
}
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
if (argc <= 1)
|
||||
conv(stdin);
|
||||
else
|
||||
while (--argc > 0) {
|
||||
if (strcmp(*++argv, "-") == 0)
|
||||
fp = stdin;
|
||||
else if ((fp = fopen(*argv, "r")) == NULL)
|
||||
ERROR "can't open %s\n", *argv FATAL;
|
||||
conv(fp);
|
||||
fclose(fp);
|
||||
}
|
||||
exit(0);
|
||||
return 0; /* gcc */
|
||||
}
|
||||
119
src/cmd/mpm/page.h
Normal file
119
src/cmd/mpm/page.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
extern queue squeue; // the three queues on which ranges reside
|
||||
extern queue bfqueue;
|
||||
extern queue ufqueue;
|
||||
|
||||
extern double minfull;
|
||||
|
||||
extern double coltol;
|
||||
|
||||
int anymore();
|
||||
|
||||
// The following is used in some calls to range::enqueue(int = 0).
|
||||
#define ANDBLOCK 1
|
||||
|
||||
class page;
|
||||
|
||||
enum { DRAFT = 0, FINAL = 1 };
|
||||
|
||||
// The mergestream currpage->stage serves as a staging area for page makeup:
|
||||
// when primed, it contains a minimal acceptable chunk of input ranges.
|
||||
// The page must either take or leave everything that's on stage.
|
||||
class mergestream : public queue {
|
||||
page *currpage; // current page that's accepting stuff
|
||||
public:
|
||||
mergestream(page *cp) { currpage = cp; unblock(); }
|
||||
void unblock();
|
||||
int prime(); // stage next legal chunk
|
||||
void pend(); // process pending chunk on stage
|
||||
};
|
||||
|
||||
// The multicol currpage->twocol is the two-column piece of the page to which
|
||||
// two-column ranges are currently being added.
|
||||
// The page sets htavail to indicate how tall it is allowed to become.
|
||||
// All ranges on definite must be placed when the multicol is printed.
|
||||
// Each of these definite ranges also resides on one of column[0] and [1],
|
||||
// which represent the current best guess about how to divide definite
|
||||
// between the two columns.
|
||||
class multicol : public range {
|
||||
page *currpage; // current page that's accepting stuff
|
||||
stream definite; // definitely on page
|
||||
stream scratch; // for trial compositions
|
||||
stream column[2]; // left (0) and right (1) columns
|
||||
int leftblocked; // OK to add to left column?
|
||||
int htavail; // max possible ht, set by page::tryout()
|
||||
int prevhtavail; // max 2-colht last time we added something
|
||||
friend class page;
|
||||
public:
|
||||
multicol(page *cp) { currpage = cp;
|
||||
leftblocked = 0;
|
||||
htavail = 0;
|
||||
prevhtavail = -1;
|
||||
setgoal(NOGOAL); }
|
||||
// the two-column piece behaves as part
|
||||
// of the stream of single-column input.
|
||||
int numcol() { return 1; }
|
||||
int nonempty() { return definite.more(); }
|
||||
void choosecol(range *, int);// add first arg to one or other column
|
||||
void choosecol(stream*, int);// add *all ranges on first arg*
|
||||
// to one or other column
|
||||
// NOT the same as a mapcar of the
|
||||
// preceding function over the ranges
|
||||
// on the first argument!
|
||||
void compose(int); // divide into two columns
|
||||
void tryout(); // decide which column gets stage contents
|
||||
void stretch(int); // justify both columns to given height
|
||||
int print(int curv, int col);
|
||||
int height(); // an upper bound on actual height
|
||||
int rawht() { return max(column[0].rawht(), column[1].rawht()); }
|
||||
void reheight(int *cv, int *mv)
|
||||
{ *cv += height(); *mv = max(*mv, *cv); }
|
||||
void dump();
|
||||
int isvbox() { return nonempty(); } // during trimspace()
|
||||
};
|
||||
|
||||
// These sentinel ranges are used to separate the ranges on twocol::definite
|
||||
// into the chunks in which they came from the staging area.
|
||||
// Thus, they preserve the results of the computation that was done to prime
|
||||
// page::stage.
|
||||
class sentrange : public range {
|
||||
public:
|
||||
sentrange() { }
|
||||
int numcol() { return 2; }
|
||||
int issentinel() { return 1; }
|
||||
};
|
||||
|
||||
class page {
|
||||
int pagesize; // allowed maximum height
|
||||
int prevncol; // was last item tried 1- or 2-column?
|
||||
int vsince; // how many vboxes from "current" BS
|
||||
// (to avoid putting a single line on
|
||||
// a page with a very large floatable)
|
||||
stream definite; // definitely on page, in input order
|
||||
stream scratch; // playground in which to alter page
|
||||
void cmdproc(); // process any of several commands
|
||||
void parmproc(); // process any of several parameters
|
||||
void tryout(); // see whether current stage contents fit
|
||||
void compose(int); // float and trim current page contents
|
||||
void makescratch(int); // fill scratch area
|
||||
void commit(); // accept the items on stage
|
||||
void welsh(); // reject the items on stage
|
||||
void adddef(range *r); // add to one of the definite queues
|
||||
// (definite or twocol->definite)
|
||||
public:
|
||||
mergestream *stage;
|
||||
friend class mergestream;
|
||||
multicol *twocol;
|
||||
friend class multicol;
|
||||
page(int p) { pagesize = p;
|
||||
prevncol = 1;
|
||||
vsince = 0;
|
||||
stage = new mergestream(this);
|
||||
twocol = new multicol(this); }
|
||||
~page() { definite.freeall(); scratch.freeall(); }
|
||||
void fill();
|
||||
int blank() { return !definite.more() && !twocol->definite.more();}
|
||||
void print();
|
||||
};
|
||||
|
||||
// functions in page.c
|
||||
int main(int, char **);
|
||||
235
src/cmd/mpm/queue.cc
Normal file
235
src/cmd/mpm/queue.cc
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
#include "misc.h"
|
||||
#include "slug.h"
|
||||
#include "range.h"
|
||||
#include "page.h"
|
||||
|
||||
queue squeue;
|
||||
queue bfqueue;
|
||||
queue ufqueue;
|
||||
|
||||
// We use the stream function current() to access a queue's head.
|
||||
// Thus, queue member curr should always point to its first range.
|
||||
void queue::check(char *whence)
|
||||
{
|
||||
if (dbg & 8) {
|
||||
char *p;
|
||||
if (this == &squeue)
|
||||
p = "squeue";
|
||||
else if (this == &bfqueue)
|
||||
p = "bfqueue";
|
||||
else if (this == &ufqueue)
|
||||
p = "ufqueue";
|
||||
else
|
||||
p = "weird queue";
|
||||
printf("#checking %s\n", p);
|
||||
}
|
||||
if (first != curr)
|
||||
ERROR "check(%s): first != curr, line %d\n", whence, curr->rp->lineno() FATAL;
|
||||
}
|
||||
|
||||
// When ranges are told to enqueue themselves, they are being rejected from the
|
||||
// stage back onto their original queues.
|
||||
// They reset any parameters that may have been altered by staging or trial
|
||||
// composition.
|
||||
|
||||
void range::enqueue(int block)
|
||||
{
|
||||
squeue.enqueue(this);
|
||||
if (block)
|
||||
squeue.block();
|
||||
}
|
||||
|
||||
void ufrange::enqueue(int block)
|
||||
{
|
||||
restore(); // both goal positions
|
||||
ufqueue.enqueue(this);
|
||||
if (block)
|
||||
ufqueue.block();
|
||||
}
|
||||
|
||||
void bfrange::enqueue(int block)
|
||||
{
|
||||
restore(); // both goal positions
|
||||
bfqueue.enqueue(this);
|
||||
if (block)
|
||||
bfqueue.block();
|
||||
}
|
||||
|
||||
int anymore()
|
||||
{
|
||||
return !(squeue.empty() && ufqueue.empty() && bfqueue.empty());
|
||||
}
|
||||
|
||||
void mergestream::unblock()
|
||||
{
|
||||
squeue.unblock();
|
||||
bfqueue.unblock();
|
||||
ufqueue.unblock();
|
||||
}
|
||||
|
||||
// Fill the staging area with a minimal chunk of input ranges.
|
||||
int mergestream::prime()
|
||||
{
|
||||
if (dbg & 4)
|
||||
printf("#entering mergestream::prime()\n");
|
||||
if (!empty())
|
||||
return 1;
|
||||
int brkok = 1; // is it OK to break after the last
|
||||
// VBOX that was added to the stage?
|
||||
int needheight = -1; // minimum acceptable height of the
|
||||
// chunk being constructed on stage
|
||||
// If the range at the head of any queue is breaking,
|
||||
// deal with it first.
|
||||
if (squeue.more() && squeue.current()->breaking())
|
||||
enqueue(squeue.dequeue());
|
||||
else if (bfqueue.more() && (bfqueue.current()->breaking() ||
|
||||
(bfqueue.serialno() < squeue.serialno())))
|
||||
enqueue(bfqueue.dequeue());
|
||||
else if (ufqueue.more() && (ufqueue.current()->breaking() ||
|
||||
(ufqueue.serialno() < squeue.serialno())))
|
||||
enqueue(ufqueue.dequeue());
|
||||
else while (squeue.more()) {
|
||||
// Fill the stage with enough ranges to be a valid chunk.
|
||||
range *r = squeue.dequeue();
|
||||
if (r->isvbox()) { // VBOX
|
||||
if (dbg & 16)
|
||||
printf("#VBOX: !empty: %d; brkok: %d; vsince: %d\n",
|
||||
!empty(), brkok, currpage->vsince);
|
||||
if (!empty() // there's something there
|
||||
&& brkok
|
||||
// it's OK to break here
|
||||
&& currpage->vsince >= 2
|
||||
// enough stream has gone onto this page
|
||||
&& rawht() >= needheight
|
||||
// current need has been satisfied
|
||||
) {
|
||||
// the stage already contains enough
|
||||
// ranges, so this one can wait
|
||||
r->enqueue();
|
||||
break;
|
||||
} else {
|
||||
if (r->rawht() > 0) {
|
||||
++currpage->vsince;
|
||||
brkok = r->brkafter();
|
||||
}
|
||||
enqueue(r);
|
||||
}
|
||||
} else if (r->isnested() || r->issp()) { // US, SP
|
||||
if (!empty() && rawht() >= needheight) {
|
||||
// enough already, wait
|
||||
r->enqueue();
|
||||
break;
|
||||
}
|
||||
currpage->vsince = 0;
|
||||
enqueue(r);
|
||||
if (height() >= needheight)
|
||||
break;
|
||||
} else if (r->isneed()) { // NE
|
||||
if (!empty() && rawht() >= needheight) {
|
||||
// not currently working on an unsatisfied NEed
|
||||
r->enqueue();
|
||||
break;
|
||||
}
|
||||
// deal with overlapping NEeds
|
||||
needheight = rawht() + max(needheight - rawht(), r->needht());
|
||||
enqueue(r);
|
||||
} else if (r->forceflush() == NO) {
|
||||
enqueue(r);
|
||||
} else if (r->forceflush() == YES) {
|
||||
currpage->vsince = 0;
|
||||
if (!empty()) {
|
||||
// ready or not, r must wait
|
||||
r->enqueue();
|
||||
break;
|
||||
}
|
||||
enqueue(r);
|
||||
break;
|
||||
} else
|
||||
ERROR "unexpected %s[%s] in prime(), line %d\n",
|
||||
r->typename(), r->headstr(), r->lineno() FATAL;
|
||||
}
|
||||
return more(); // 0 if nothing was staged
|
||||
}
|
||||
|
||||
void page::cmdproc()
|
||||
{
|
||||
if (stage->next())
|
||||
ERROR "more than a single command on bsqueue\n" FATAL;
|
||||
switch (stage->current()->cmdtype()) {
|
||||
case FC: // freeze the current 2-column range and start a new one
|
||||
adddef(stage->dequeue());
|
||||
twocol->compose(FINAL);
|
||||
adddef(twocol);
|
||||
twocol = new multicol(this);
|
||||
break;
|
||||
case BP: // force a page break
|
||||
adddef(stage->dequeue());
|
||||
squeue.block();
|
||||
break;
|
||||
case FL: // flush out all floatables that precede this range:
|
||||
// no more stream input allowed until they're past
|
||||
if (stage->serialno() > ufqueue.serialno() ||
|
||||
stage->serialno() > bfqueue.serialno()) {
|
||||
range *r = stage->dequeue();
|
||||
r->enqueue(ANDBLOCK);
|
||||
} else
|
||||
adddef(stage->dequeue());
|
||||
break;
|
||||
default:
|
||||
stage->current()->dump();
|
||||
ERROR "unknown command\n" FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
void page::parmproc()
|
||||
{
|
||||
if (stage->next())
|
||||
ERROR "more than a single parameter on bsqueue\n" FATAL;
|
||||
switch (stage->current()->parmtype()) {
|
||||
case NP: // page top margin
|
||||
if (blank())
|
||||
pagetop = stage->current()->parm();
|
||||
pagesize = pagebot - pagetop;
|
||||
break;
|
||||
case FO:
|
||||
if (blank())
|
||||
pagebot = stage->current()->parm();
|
||||
pagesize = pagebot - pagetop;
|
||||
break;
|
||||
case PL:
|
||||
if (blank())
|
||||
physbot = stage->current()->parm();
|
||||
break;
|
||||
case MF:
|
||||
minfull = 0.01*stage->current()->parm();
|
||||
break;
|
||||
case CT:
|
||||
coltol = 0.01*stage->current()->parm();
|
||||
break;
|
||||
case WARN:
|
||||
wantwarn = stage->current()->parm();
|
||||
break;
|
||||
case DBG:
|
||||
dbg = stage->current()->parm();
|
||||
break;
|
||||
default:
|
||||
stage->current()->dump();
|
||||
ERROR "unknown parameter\n" FATAL;
|
||||
}
|
||||
adddef(stage->dequeue());
|
||||
}
|
||||
|
||||
// Process the contents of the staging area; a relic that used to do more.
|
||||
void mergestream::pend()
|
||||
{
|
||||
if (dbg & 4)
|
||||
printf("#entering mergestream::pend()\n");
|
||||
if (!more())
|
||||
return;
|
||||
if (current()->iscmd())
|
||||
currpage->cmdproc();
|
||||
else if (current()->isparm())
|
||||
currpage->parmproc();
|
||||
else
|
||||
currpage->tryout();
|
||||
}
|
||||
613
src/cmd/mpm/range.cc
Normal file
613
src/cmd/mpm/range.cc
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
#include <math.h>
|
||||
#include "misc.h"
|
||||
#include "slug.h"
|
||||
#include "range.h"
|
||||
|
||||
void sprange::reheight(int *cv, int *mv)
|
||||
{
|
||||
if (*cv != *mv)
|
||||
ERROR "slug %d: an imbedded SP, line %d\n",
|
||||
first->serialno(), first->lineno() WARNING;
|
||||
*cv += dv;
|
||||
*mv = max(*mv, *cv);
|
||||
}
|
||||
|
||||
void sprange::rerawht(int *cv, int *mv)
|
||||
{
|
||||
*cv += rawht();
|
||||
*mv = max(*mv, *cv);
|
||||
}
|
||||
|
||||
void nestrange::restore()
|
||||
{
|
||||
subrange->restoreall();
|
||||
}
|
||||
|
||||
void stream::freeall() // not a destructor; called explicitly
|
||||
{
|
||||
strblk *p, *q;
|
||||
for (p = first; p; p = q) {
|
||||
q = p->next;
|
||||
delete p;
|
||||
}
|
||||
first = last = curr = 0;
|
||||
}
|
||||
|
||||
void stream::dump()
|
||||
{
|
||||
for (stream s = *this; s.more(); s.advance())
|
||||
s.current()->dump();
|
||||
}
|
||||
|
||||
void stream::rdump()
|
||||
{
|
||||
for (stream s = *this; s.more(); s.advance())
|
||||
s.current()->rdump();
|
||||
}
|
||||
|
||||
int stream::restoreall()
|
||||
{
|
||||
for (stream s = *this; s.more(); s.advance())
|
||||
s.current()->restore();
|
||||
return measure(this);
|
||||
}
|
||||
|
||||
range *stream::append(range *r)
|
||||
{
|
||||
if (last == 0)
|
||||
curr = first = last = new strblk;
|
||||
else {
|
||||
last->next = new strblk;
|
||||
last = last->next;
|
||||
if (curr == 0)
|
||||
curr = last;
|
||||
}
|
||||
last->next = 0;
|
||||
return last->rp = r;
|
||||
}
|
||||
|
||||
void stream::split() // duplicate current() range
|
||||
{
|
||||
strblk *s2 = new strblk;
|
||||
range *r2 = curr->rp->clone();
|
||||
s2->rp = r2;
|
||||
s2->next = curr->next;
|
||||
if (last == curr)
|
||||
last = s2;
|
||||
curr->next = s2;
|
||||
curr->rp->killkids(); // children only in the 2nd one
|
||||
// r2->crosslink(r1);
|
||||
}
|
||||
|
||||
int stream::height()
|
||||
{
|
||||
int h;
|
||||
stream s = *this;
|
||||
for (h = 0; s.more(); s.advance())
|
||||
h += s.current()->height();
|
||||
return h;
|
||||
}
|
||||
|
||||
int stream::rawht()
|
||||
{
|
||||
int h;
|
||||
stream s = *this;
|
||||
for (h = 0; s.more(); s.advance())
|
||||
h += s.current()->rawht();
|
||||
return h;
|
||||
}
|
||||
|
||||
int measure(stream *sp) // record high-water mark of stream
|
||||
{ // sets nested stream heights
|
||||
stream s = *sp;
|
||||
int curv, maxv;
|
||||
for (maxv = curv = 0; s.more(); s.advance())
|
||||
s.current()->reheight(&curv, &maxv);
|
||||
return maxv;
|
||||
}
|
||||
|
||||
int rawmeasure(stream *sp)
|
||||
{
|
||||
stream s = *sp;
|
||||
int curv, maxv;
|
||||
for (maxv = curv = 0; s.more(); s.advance())
|
||||
s.current()->rerawht(&curv, &maxv);
|
||||
return maxv;
|
||||
}
|
||||
|
||||
void nestrange::rdump()
|
||||
{
|
||||
dump();
|
||||
if (subrange)
|
||||
subrange->rdump();
|
||||
}
|
||||
|
||||
void nestrange::killkids()
|
||||
{
|
||||
subrange = new stream;
|
||||
}
|
||||
|
||||
int nestrange::print(int curv, int col)
|
||||
{
|
||||
int ocurv = curv;
|
||||
first->slugout(col);
|
||||
for (stream s = *subrange; s.more(); s.advance())
|
||||
curv = s.current()->print(curv, col);
|
||||
return ocurv + height();
|
||||
}
|
||||
|
||||
#define macroclone(rangetype) range *rangetype::clone() {\
|
||||
rangetype *t = new rangetype;\
|
||||
*t = *this;\
|
||||
return t; }
|
||||
|
||||
macroclone(usrange);
|
||||
macroclone(ufrange);
|
||||
macroclone(bfrange);
|
||||
|
||||
#undef macroclone
|
||||
|
||||
#define macropickgoal(rangetype) void rangetype::pickgoal(int acv, double scale) {\
|
||||
if (scale > 1) {\
|
||||
goalV = (int)(scale*goalV);\
|
||||
goal2 = (int)(scale*goal2);\
|
||||
}\
|
||||
if (abs(acv - goalV) > abs(acv-goal2))\
|
||||
goalV = goal2; }
|
||||
|
||||
macropickgoal(ufrange)
|
||||
macropickgoal(bfrange)
|
||||
|
||||
#undef macropickgoal
|
||||
|
||||
range *generator::next()
|
||||
{
|
||||
range *r;
|
||||
if (child) {
|
||||
if ((r = child->next()) != 0)
|
||||
return r;
|
||||
delete child;
|
||||
child = 0;
|
||||
}
|
||||
if (!s.more())
|
||||
return 0;
|
||||
r = s.current();
|
||||
if (r->isnested())
|
||||
child = new generator(r->children());
|
||||
s.advance();
|
||||
return r;
|
||||
}
|
||||
|
||||
range *queue::enqueue(range *r)
|
||||
{
|
||||
if (dbg & 8)
|
||||
printf("#entering queue::enqueue()\n");
|
||||
check("queue::enqueue");
|
||||
if (!last || last->rp->serialno() < r->serialno()) // common case
|
||||
return append(r);
|
||||
if (dbg & 8)
|
||||
printf("#queue::enqueue() pushing back\n");
|
||||
newguy = new strblk;
|
||||
newguy->rp = r;
|
||||
if (r->serialno() < first->rp->serialno()) {
|
||||
newguy->next = first;
|
||||
curr = first = newguy;
|
||||
return newguy->rp;
|
||||
}
|
||||
if (dbg & 8)
|
||||
printf("#queue::enqueue() searching down queue\n");
|
||||
for (curr = first;
|
||||
next() && next()->serialno() < r->serialno();
|
||||
curr = curr->next)
|
||||
;
|
||||
newguy->next = curr->next;
|
||||
curr->next = newguy;
|
||||
curr = first; // restore important queue condition
|
||||
return newguy->rp;
|
||||
}
|
||||
|
||||
range *queue::dequeue()
|
||||
{
|
||||
if (dbg & 8)
|
||||
printf("#entering queue::dequeue()\n");
|
||||
check("queue::dequeue");
|
||||
curr = first->next;
|
||||
range *retval = first->rp;
|
||||
delete first;
|
||||
first = curr;
|
||||
if (!curr)
|
||||
last = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
|
||||
// functions that munge the troff output stored in slugs[]
|
||||
|
||||
// ================================================================================
|
||||
|
||||
static void doprefix(FILE *fp) // copy 1st "x" commands to output
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = getc(fp)) != EOF) {
|
||||
if (c != 'x') {
|
||||
ungetc(c, fp);
|
||||
break;
|
||||
}
|
||||
putchar(c);
|
||||
do {
|
||||
putchar(c = getc(fp));
|
||||
} while (c != '\n');
|
||||
linenum++;
|
||||
}
|
||||
// printf("x font 1 R\n"); // horrible kludge: ensure a font for first f1 command
|
||||
}
|
||||
|
||||
#define DELTASLUGS 15000
|
||||
|
||||
static slug *slugs = 0;
|
||||
static int nslugs = 0; // slugs has nslugs slots
|
||||
static slug *slugp = 0; // next free slug in slugs
|
||||
|
||||
static void readslugs(FILE *fp)
|
||||
{
|
||||
if ((slugs = (slug *) malloc((nslugs = DELTASLUGS)*sizeof(slug))) == NULL)
|
||||
ERROR "no room for %d-slug array\n", nslugs FATAL;
|
||||
slugp = slugs;
|
||||
for (slugp = slugs; ; slugp++) {
|
||||
if (slugp >= slugs+nslugs-2) {
|
||||
int where = slugp - slugs;
|
||||
if ((slugs = (slug *) realloc((char *) slugs, (nslugs += DELTASLUGS)*sizeof(slug))) == NULL)
|
||||
ERROR "no room for %d slugs\n", nslugs FATAL;
|
||||
ERROR "now slug array can hold %d slugs\n", nslugs WARNING;
|
||||
slugp = slugs + where;
|
||||
}
|
||||
*slugp = getslug(fp);
|
||||
if (slugp->type == EOF)
|
||||
break;
|
||||
}
|
||||
*++slugp = eofslug();
|
||||
printf("# %d slugs\n", slugp-slugs);
|
||||
}
|
||||
|
||||
static slug *findend(slug *sp)
|
||||
{
|
||||
slug *p;
|
||||
for (p = sp; p->type == sp->type; p++) // skip runs
|
||||
; // espec UF UF UF
|
||||
for ( ; p < slugp; p++)
|
||||
switch (p->type) {
|
||||
case US:
|
||||
case UF:
|
||||
case BF:
|
||||
case PT:
|
||||
case BT:
|
||||
p = findend(p);
|
||||
break;
|
||||
case END:
|
||||
return p;
|
||||
}
|
||||
ERROR "walked past EOF in findend looking for %d (%s), line %d\n",
|
||||
sp->type, sp->typename(), sp->lineno() FATAL;
|
||||
return sp;
|
||||
}
|
||||
|
||||
static int markp(int i, int n, int parm)
|
||||
{ // should VBOX i of n be marked to brevent breaking after it?
|
||||
if (i >= n-1)
|
||||
return 0;
|
||||
return i <= parm-2 || i >= n-parm;
|
||||
}
|
||||
|
||||
static void markbreak(slug *p)
|
||||
{
|
||||
// Mark impermissible breakpoints in BS's.
|
||||
// The parm field of a VBOX is >0 if we shouldn't break after it.
|
||||
int parm; // how many lines must stay on page
|
||||
int goahead = 1; // true until we see the next BS
|
||||
int nowmark = 0; // true when we should be marking
|
||||
int n = 0;
|
||||
while (p->type == BS)
|
||||
parm = p++->parm; // latest BS parm applies
|
||||
slug *op = p;
|
||||
while (goahead) {
|
||||
switch (p->type) {
|
||||
case VBOX: // count VBOXes so second pass knows
|
||||
if (p->dv > 0) // knows how far to end of BS
|
||||
n++;
|
||||
break;
|
||||
case US: // mark around EQ/EN, etc.
|
||||
nowmark = 1;
|
||||
p = findend(p);
|
||||
break;
|
||||
case UF: // but not around floats, PTs, and BTs
|
||||
case BF:
|
||||
case PT:
|
||||
case BT:
|
||||
p = findend(p);
|
||||
break;
|
||||
case SP: // naked SP: probable macro botch
|
||||
nowmark = 1; // mark around it anyhow
|
||||
break;
|
||||
case BS: // beginning of next paragraph
|
||||
case END: // probable macro botch
|
||||
case EOF:
|
||||
goahead = 0; // stop work after marking
|
||||
nowmark = 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
if (nowmark) {
|
||||
int i = 0; // VBOX counter for second pass
|
||||
while (op < p) {
|
||||
switch (op->type) {
|
||||
case VBOX:
|
||||
if (op->dv > 0)
|
||||
op->parm = markp(i, n, parm);
|
||||
i++;
|
||||
break;
|
||||
case US: // caused second pass to begin
|
||||
case SP:
|
||||
case BS:
|
||||
case END:
|
||||
case EOF:
|
||||
op = p;
|
||||
break;
|
||||
case UF: // skip on this pass too
|
||||
case BF:
|
||||
case PT:
|
||||
case BT:
|
||||
op = findend(op);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
op++;
|
||||
}
|
||||
if (i != n)
|
||||
ERROR "markbreak failed : i %d n %d\n",
|
||||
i, n WARNING;
|
||||
op = p;
|
||||
nowmark = n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fixslugs() // adjust bases and dv's, set parameters, etc.
|
||||
{
|
||||
slug *p, *prevV = 0;
|
||||
for (p = slugs; p < slugp; p++) {
|
||||
if (p->type == VBOX) {
|
||||
prevV = p;
|
||||
continue;
|
||||
}
|
||||
if (p->base != 0) {
|
||||
ERROR "%s slug (type %d) has base = %d, line %d\n",
|
||||
p->typename(), p->type, p->base, p->lineno() WARNING;
|
||||
}
|
||||
if ((p->type == SP) || (p->type == NE))
|
||||
continue;
|
||||
if (p->type == PAGE)
|
||||
prevV = 0;
|
||||
if (p->dv != 0)
|
||||
if (prevV) {
|
||||
prevV->base = max(prevV->base, p->dv);
|
||||
p->dv = 0;
|
||||
} else {
|
||||
ERROR "%s slug (type %d) has dv = %d, line %d\n",
|
||||
p->typename(), p->type, p->dv, p->lineno() WARNING;
|
||||
}
|
||||
}
|
||||
prevV = 0;
|
||||
int firstNP = 0, firstFO = 0, firstPL = 0;
|
||||
for (p = slugs; p < slugp; p++) {
|
||||
switch (p->type) {
|
||||
// adjust the dv in a sequence of VBOXes
|
||||
// by subtracting from each the base of the preceding VBOX
|
||||
case VBOX:
|
||||
if (prevV)
|
||||
p->dv -= prevV->base;
|
||||
prevV = p;
|
||||
break;
|
||||
case SP:
|
||||
p->dv = max(p->dv, 0);
|
||||
break;
|
||||
case PAGE:
|
||||
p->neutralize();
|
||||
prevV = 0;
|
||||
break;
|
||||
// record only first "declarations" of Page Top and bottom (FO);
|
||||
case PARM:
|
||||
switch (p->parm) {
|
||||
case NP:
|
||||
if (firstNP++ == 0)
|
||||
pagetop = p->parm2;
|
||||
p->neutralize();
|
||||
break;
|
||||
case FO:
|
||||
if (firstFO++ == 0)
|
||||
pagebot = p->parm2;
|
||||
p->neutralize();
|
||||
break;
|
||||
case PL:
|
||||
if (firstPL++ == 0)
|
||||
physbot = p->parm2;
|
||||
p->neutralize();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// things that begin groups; not US, which should nest properly
|
||||
case UF:
|
||||
case BF:
|
||||
while ((p+1)->type == p->type) {
|
||||
// join adjacent identical
|
||||
(p+1)->parm2 = p->parm; // parm is latest
|
||||
// parm2 is previous
|
||||
p->neutralize(); // so it's not seen later
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
// none of the above
|
||||
case US:
|
||||
case PT:
|
||||
case BT:
|
||||
case BS:
|
||||
case END:
|
||||
case TM:
|
||||
case COORD:
|
||||
case NE:
|
||||
case MC:
|
||||
case CMD:
|
||||
case EOF:
|
||||
break;
|
||||
default:
|
||||
ERROR "Unknown slug type %d in fixslugs, line %d\n",
|
||||
p->type, p->lineno() WARNING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int pagesize = pagebot - pagetop;
|
||||
if (pagesize == 0)
|
||||
ERROR "Page dimensions not declared\n" FATAL;
|
||||
if (physbot == 0)
|
||||
physbot = pagebot + pagetop;
|
||||
printf("# page top %d bot %d size %d physbot %d\n",
|
||||
pagetop, pagebot, pagesize, physbot);
|
||||
for (p = slugs; p < slugp; p++) {
|
||||
switch (p->type) {
|
||||
// normalize float parameters
|
||||
case BF:
|
||||
case UF:
|
||||
// primary goal
|
||||
p->parm = max(min(p->parm-pagetop, pagesize), 0);
|
||||
// secondary goal
|
||||
p->parm2 = max(min(p->parm2-pagetop, pagesize), 0);
|
||||
break;
|
||||
// normalize need parameters
|
||||
case NE:
|
||||
p->dv = max( min(p->dv, pagesize), 0);
|
||||
break;
|
||||
// mark permissible breaks
|
||||
case BS:
|
||||
markbreak(p);
|
||||
break;
|
||||
}
|
||||
if (dbg & 1)
|
||||
p->dump();
|
||||
}
|
||||
}
|
||||
|
||||
void checkout()
|
||||
{
|
||||
for (slug *p = slugs; p < slugp; p++)
|
||||
switch (p->type) {
|
||||
case PT:
|
||||
case BT:
|
||||
p = findend(p);
|
||||
break;
|
||||
case SP:
|
||||
case VBOX:
|
||||
if (p->seen != 1)
|
||||
ERROR "%s slug %d seen %d times\n",
|
||||
p->typename(), p->serialno(),
|
||||
p->seen WARNING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
eofrange *lastrange;
|
||||
stream ptlist, btlist;
|
||||
|
||||
static slug *makeranges(slug *p, stream *s, int level)
|
||||
{
|
||||
stream *t;
|
||||
|
||||
for ( ; p < slugp; p++)
|
||||
switch (p->type) {
|
||||
case VBOX:
|
||||
s->append(new vboxrange(p));
|
||||
break;
|
||||
case SP:
|
||||
s->append(new sprange(p));
|
||||
break;
|
||||
case BS:
|
||||
s->append(new bsrange(p));
|
||||
break;
|
||||
case US:
|
||||
s->append(new usrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case BF:
|
||||
s->append(new bfrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case UF:
|
||||
s->append(new ufrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case PT:
|
||||
ptlist.append(new ptrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case BT:
|
||||
btlist.append(new btrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case END:
|
||||
s->append(new endrange(p));
|
||||
return p;
|
||||
case TM:
|
||||
s->append(new tmrange(p));
|
||||
break;
|
||||
case COORD:
|
||||
s->append(new coordrange(p));
|
||||
break;
|
||||
case NE:
|
||||
if (level) {
|
||||
ERROR "Nested NE commands are ignored, line %d\n",
|
||||
p->lineno() WARNING;
|
||||
p->dv = 0;
|
||||
}
|
||||
s->append(new nerange(p));
|
||||
break;
|
||||
case MC:
|
||||
s->append(new mcrange(p));
|
||||
break;
|
||||
case CMD:
|
||||
if (level)
|
||||
ERROR "Nested command ignored, line %d\n",
|
||||
p->lineno() WARNING;
|
||||
s->append(new cmdrange(p));
|
||||
break;
|
||||
case PARM:
|
||||
if (level)
|
||||
ERROR "Nested parameter ignored, line %d\n",
|
||||
p->lineno() WARNING;
|
||||
s->append(new parmrange(p));
|
||||
break;
|
||||
case EOF:
|
||||
lastrange = new eofrange(p);
|
||||
return 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static queue text; // unexamined input ranges; the real data
|
||||
|
||||
void startup(FILE *fp)
|
||||
{
|
||||
doprefix(fp); // peel off 'x' commands
|
||||
readslugs(fp); // read everything into slugs[]
|
||||
fixslugs(); // measure parameters and clean up
|
||||
makeranges(slugs, &text, 0); // add range superstructure
|
||||
measure(&text); // heights of nested things
|
||||
rawmeasure(&text);
|
||||
while (text.more()) {
|
||||
range *r = text.dequeue();
|
||||
if (dbg & 2)
|
||||
r->dump();
|
||||
r->enqueue();
|
||||
}
|
||||
}
|
||||
334
src/cmd/mpm/range.h
Normal file
334
src/cmd/mpm/range.h
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
const int NOGOAL = -1;
|
||||
|
||||
class stream;
|
||||
|
||||
enum primeflush { NO, YES, EXPECTED, UNEXPECTED }; // mergestream::prime()
|
||||
|
||||
// Ranges do two things. They interpose a layer between slugs and the rest
|
||||
// of the program; this is important because of the grossness of the slug
|
||||
// data structure (made necessary by its origins in troff output). Ranges also
|
||||
// group together other ranges into meaningful chunks like unbreakable stream
|
||||
// objects, floatable objects, and page headers and footers.
|
||||
// Member function height() returns a range's height as of the latest composition.
|
||||
// Member function rawht() returns the range's original height in the input.
|
||||
class range {
|
||||
protected:
|
||||
slug *first; // earliest slug in range
|
||||
int accumV; // accumulated V to this point
|
||||
public:
|
||||
range() { first = 0; accumV = 0; }
|
||||
range(slug *p) { first = p; accumV = 0; }
|
||||
char *headstr() {
|
||||
return first ? first->headstr() : (char*)""; }
|
||||
char *typename() { return first->typename(); }
|
||||
int serialno() { return first->serialno(); }
|
||||
int lineno() { return first->lineno(); }
|
||||
virtual void dump() { first->dump(); }
|
||||
virtual void rdump() { dump(); }
|
||||
virtual int print(int cv, int col) {
|
||||
first->slugout(col); return cv; }
|
||||
virtual int floatable() { return 0; }
|
||||
virtual int brkafter() { return 1; }
|
||||
virtual int isnested() { return 0; }
|
||||
virtual int issp() { return 0; }
|
||||
virtual int isvbox() { return 0; }
|
||||
virtual int isneed() { return 0; }
|
||||
virtual int iscmd() { return 0; }
|
||||
virtual int cmdtype() { return -1; }
|
||||
virtual int isparm() { return 0; }
|
||||
virtual int parmtype() { return -1; }
|
||||
virtual int parm() { return -1; }
|
||||
virtual int breakable() { return 0; }
|
||||
virtual int forceflush() { return UNEXPECTED; }
|
||||
virtual int pn() { return 0; }
|
||||
virtual stream *children() { return 0; } // see page::peeloff()
|
||||
virtual void killkids() { }
|
||||
virtual void enqueue(int = 0);
|
||||
virtual int height() { return 0; }
|
||||
virtual int rawht() { return 0; }
|
||||
virtual int needht() { return 0; }
|
||||
virtual void reheight(int *, int *) { }
|
||||
virtual void rerawht(int *, int *) { }
|
||||
virtual void setheight(int) { }
|
||||
virtual void restore() { } // goals of floatables
|
||||
virtual int goal() { return NOGOAL; }
|
||||
int accum() { return accumV; }
|
||||
void setaccum(int n) { accumV = n; }
|
||||
virtual void setgoal(int) { }
|
||||
virtual void pickgoal(int, double) { }
|
||||
virtual int numcol() { return first->numcol(); }
|
||||
virtual int issentinel() { return 0; }
|
||||
virtual range *clone() { return 0; }
|
||||
virtual int breaking() { return 0; }
|
||||
virtual void setbreaking() { }
|
||||
};
|
||||
|
||||
class vboxrange : public range {
|
||||
int dv; // inherited from slug
|
||||
int base; // inherited from slug
|
||||
int brk; // 0 => ok to break after, 1 => no break
|
||||
public:
|
||||
vboxrange(slug *p) : range(p) { dv = p->dv; base = p->base; brk = p->parm; }
|
||||
void dump() {
|
||||
printf("#### VBOX brk? %d dv %d ht %d\n", brk, dv, dv+base); }
|
||||
int print(int cv, int col) {
|
||||
printf("V%d\n", cv += dv); first->slugout(col); return cv+base; }
|
||||
int brkafter() { return !brk; }
|
||||
int isvbox() { return 1; }
|
||||
int forceflush() { return NO; }
|
||||
int height() { return dv + base; }
|
||||
int rawht() { return first->dv + first->base; }
|
||||
void reheight(int *cv, int *mv) {
|
||||
*cv += dv+base; *mv = max(*mv, *cv); }
|
||||
void rerawht(int *cv, int *mv) {
|
||||
*cv += rawht(); *mv = max(*mv, *cv); }
|
||||
};
|
||||
|
||||
class sprange : public range {
|
||||
int dv;
|
||||
public:
|
||||
sprange(slug *p) : range(p) { dv = first->dv; }
|
||||
void dump() {
|
||||
printf("#### SP dv %d (originally %d)\n", dv, first->dv); }
|
||||
int print(int cv, int col) {
|
||||
first->slugout(col); return cv + dv; }
|
||||
int issp() { return 1; }
|
||||
int forceflush() { return YES; }
|
||||
int height() { return dv; }
|
||||
int rawht() { return first->dv; }
|
||||
void reheight(int *, int *);
|
||||
void rerawht(int *, int *);
|
||||
void setheight(int n) { dv = n; }
|
||||
};
|
||||
|
||||
class tmrange : public range {
|
||||
public:
|
||||
tmrange(slug *p) : range(p) { }
|
||||
int forceflush() { return NO; }
|
||||
int print(int cv, int col) { first->slugout(col); return cv; }
|
||||
};
|
||||
|
||||
class coordrange : public range {
|
||||
public:
|
||||
coordrange(slug *p) : range(p) { }
|
||||
int forceflush() { return NO; }
|
||||
int print(int cv, int col)
|
||||
{ first->slugout(col); printf(" Y %d\n", cv); return cv; }
|
||||
};
|
||||
|
||||
class nerange : public range {
|
||||
public:
|
||||
nerange(slug *p) : range(p) { }
|
||||
int isneed() { return 1; }
|
||||
int forceflush() { return YES; }
|
||||
int needht() { return first->dv; }
|
||||
};
|
||||
|
||||
class mcrange : public range {
|
||||
public:
|
||||
mcrange(slug *p) : range(p) { }
|
||||
int forceflush() { return YES; }
|
||||
};
|
||||
|
||||
class cmdrange : public range {
|
||||
public:
|
||||
cmdrange(slug *p) : range(p) { }
|
||||
int iscmd() { return 1; }
|
||||
int forceflush() { return YES; }
|
||||
int cmdtype() { return first->parm; }
|
||||
};
|
||||
|
||||
class parmrange : public range {
|
||||
public:
|
||||
parmrange(slug *p) : range(p) { }
|
||||
int isparm() { return 1; }
|
||||
int forceflush() { return YES; }
|
||||
int parmtype() { return first->parm; }
|
||||
int parm() { return first->parm2; }
|
||||
};
|
||||
|
||||
class bsrange : public range {
|
||||
public:
|
||||
bsrange(slug *p) : range(p) { }
|
||||
int forceflush() { return NO; }
|
||||
int print(int cv, int col) { first->slugout(col); return cv; }
|
||||
};
|
||||
|
||||
class endrange : public range {
|
||||
public:
|
||||
endrange(slug *p) : range(p) { }
|
||||
int forceflush() { return UNEXPECTED; }
|
||||
};
|
||||
|
||||
class eofrange : public range {
|
||||
public:
|
||||
eofrange(slug *p) : range(p) { }
|
||||
int forceflush() { return UNEXPECTED; }
|
||||
};
|
||||
|
||||
extern eofrange *lastrange; // the EOF block (trailer, etc.) goes here
|
||||
|
||||
int measure(stream *);
|
||||
int rawmeasure(stream *);
|
||||
|
||||
// A nestrange packages together a sequence of ranges, its subrange.
|
||||
// Other parts of the program reach in and alter the dimensions of
|
||||
// some of these ranges, so when the height of a range is requested
|
||||
// it is computed completely afresh.
|
||||
// (Note: the alternative, of keeping around many copies of ranges
|
||||
// with different dimensions, was abandoned because of the difficulty
|
||||
// of ensuring that exactly one copy of each original range would be
|
||||
// output.)
|
||||
class nestrange : public range {
|
||||
protected:
|
||||
stream *subrange;
|
||||
int isbreaking;
|
||||
int rawdv;
|
||||
public:
|
||||
nestrange() : range() { subrange = 0; isbreaking = 0; rawdv = -1; }
|
||||
nestrange(slug *p, stream *s) : range(p)
|
||||
{ subrange = s; isbreaking = 0; rawdv = -1; }
|
||||
void rdump();
|
||||
virtual void restore();
|
||||
stream *children() { return subrange; }
|
||||
void killkids();
|
||||
int height() { return measure(subrange); }
|
||||
int rawht() { if (rawdv < 0 || isbreaking) rawdv = rawmeasure(subrange);
|
||||
return rawdv; }
|
||||
void reheight(int *cv, int *mv) {
|
||||
*mv += measure(subrange); *cv = max(*mv, *cv); }
|
||||
void rerawht(int *cv, int *mv) {
|
||||
*mv += rawht(); *cv = max(*mv, *cv); }
|
||||
int isnested() { return 1; }
|
||||
int forceflush() { return EXPECTED; }
|
||||
int print(int cv, int col);
|
||||
int breaking() { return isbreaking; }
|
||||
void setbreaking() { isbreaking++; }
|
||||
};
|
||||
|
||||
class usrange : public nestrange {
|
||||
public:
|
||||
usrange() { }
|
||||
usrange(slug *p, stream *s) : nestrange(p, s) {}
|
||||
void dump() { printf("#### US dv %d\n", height()); }
|
||||
range *clone();
|
||||
};
|
||||
|
||||
class ufrange : public nestrange {
|
||||
int goalV, goal2;
|
||||
public:
|
||||
ufrange() { }
|
||||
ufrange(slug *p, stream *s) : nestrange(p, s) {
|
||||
goalV = p->parm; goal2 = p->parm2; }
|
||||
void dump() { printf("#### UF dv %d goal %d goal2 %d\n",
|
||||
height(), goalV, goal2); }
|
||||
int floatable() { return 1; }
|
||||
void enqueue(int = 0);
|
||||
range *clone();
|
||||
int goal() { return goalV; }
|
||||
void setgoal(int n) { goalV = goal2 = n; }
|
||||
void pickgoal(int acv, double scale);
|
||||
void restore() { goalV = first->parm; goal2 = first->ht; }
|
||||
};
|
||||
|
||||
class bfrange : public nestrange {
|
||||
int goalV, goal2;
|
||||
public:
|
||||
bfrange() { }
|
||||
bfrange(slug *p, stream *s) : nestrange(p, s) {
|
||||
goalV = p->parm; goal2 = p->parm2; }
|
||||
void dump() { printf("#### BF dv %d goal %d goal2 %d\n",
|
||||
height(), goalV, goal2); }
|
||||
int floatable() { return 1; }
|
||||
void enqueue(int = 0);
|
||||
range *clone();
|
||||
int goal() { return goalV; }
|
||||
void setgoal(int n) { goalV = goal2 = n; }
|
||||
void pickgoal(int acv, double scale);
|
||||
void restore() { goalV = first->parm; goal2 = first->parm2; }
|
||||
int breakable() { return 1; } // can be broken
|
||||
};
|
||||
|
||||
class ptrange : public nestrange {
|
||||
int pgno;
|
||||
public:
|
||||
int pn() { return pgno; }
|
||||
ptrange(slug *p, stream *s) : nestrange(p, s) { pgno = p->parm; }
|
||||
void dump() { printf("#### PT pgno %d dv %d\n", pgno, height()); }
|
||||
};
|
||||
|
||||
class btrange : public nestrange {
|
||||
int pgno;
|
||||
public:
|
||||
btrange(slug *p, stream *s) : nestrange(p, s) { pgno = p->parm; }
|
||||
void dump() { printf("#### BT pgno %d dv %d\n", pgno, height()); }
|
||||
};
|
||||
|
||||
// A stream is a sequence of ranges; we use this data structure a lot
|
||||
// to traverse various sequences that crop up in page-making.
|
||||
class stream {
|
||||
protected:
|
||||
public:
|
||||
struct strblk { // ranges are linked by these blocks
|
||||
strblk *next;
|
||||
range *rp;
|
||||
};
|
||||
strblk *first;
|
||||
strblk *last;
|
||||
strblk *curr;
|
||||
public:
|
||||
stream() { curr = last = first = 0; }
|
||||
stream(range *r) { curr = last = first = new strblk;
|
||||
last->rp = r; last->next = 0; }
|
||||
void freeall(); // note: not a destructor
|
||||
void dump(); // top level
|
||||
void rdump(); // recursive
|
||||
int restoreall();
|
||||
range *current() { return curr->rp; }
|
||||
range *next() { return curr && curr->next ? curr->next->rp : 0; }
|
||||
void advance() { curr = curr->next; }
|
||||
range *append(range *r);
|
||||
void split();
|
||||
int more() { return curr && curr->rp; }
|
||||
int height();
|
||||
int rawht();
|
||||
};
|
||||
|
||||
// A generator iterates through all the ranges of a stream
|
||||
// (not just the root ranges of nestranges).
|
||||
class generator {
|
||||
stream s;
|
||||
generator *child;
|
||||
public:
|
||||
generator() { child = 0; }
|
||||
generator(stream *sp) { s = *sp; child = 0; }
|
||||
range *next();
|
||||
};
|
||||
|
||||
extern stream ptlist, btlist; // page titles
|
||||
|
||||
#define INFINITY 1000001
|
||||
|
||||
// A queue is a distinguished kind of stream.
|
||||
// It keeps its contents in order by the serial numbers of the ranges.
|
||||
// A queue can be blocked from dequeuing something to indicate
|
||||
// that it's not worth considering the queue again on a given page.
|
||||
class queue : public stream {
|
||||
strblk *newguy;
|
||||
protected:
|
||||
int blocked;
|
||||
void check(char *);
|
||||
public:
|
||||
queue() : blocked(0) { }
|
||||
range *enqueue(range *r);
|
||||
range *dequeue();
|
||||
void block() { blocked = 1; }
|
||||
void unblock() { blocked = 0; }
|
||||
int more() { return !blocked && stream::more(); }
|
||||
int empty() { return !stream::more(); }
|
||||
int serialno() { return empty() ? INFINITY : current()->serialno(); }
|
||||
};
|
||||
|
||||
// functions in range.c
|
||||
void checkout();
|
||||
void startup(FILE *);
|
||||
603
src/cmd/mpm/slug.cc
Normal file
603
src/cmd/mpm/slug.cc
Normal file
|
|
@ -0,0 +1,603 @@
|
|||
#include "misc.h"
|
||||
#include "slug.h"
|
||||
//#include <libc.h>
|
||||
#include <math.h>
|
||||
|
||||
static char *bufptr(int);
|
||||
|
||||
void slug::coalesce()
|
||||
{
|
||||
(this+1)->dp = dp; // pretty grimy, but meant to ensure
|
||||
// that all output goes out.
|
||||
// maybe it has to skip over PT's;
|
||||
// some stuff is getting pushed inside PT..END
|
||||
}
|
||||
|
||||
void slug::neutralize()
|
||||
{
|
||||
switch (type) {
|
||||
case PAGE:
|
||||
case UF:
|
||||
case BF:
|
||||
case PARM:
|
||||
type = NEUTRAL;
|
||||
coalesce();
|
||||
break;
|
||||
default:
|
||||
ERROR "neutralized %d (%s) with %s\n",
|
||||
type, typename(), headstr() WARNING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void slug::dump() // print contents of a slug
|
||||
{
|
||||
printf("# %d %-4.4s parm %d dv %d base %d s%d f%d H%d\n#\t\t%s\n",
|
||||
serialno(), typename(), parm, dv, base,
|
||||
size, font, hpos, headstr());
|
||||
}
|
||||
|
||||
char *slug::headstr()
|
||||
{
|
||||
const int HEADLEN = 65;
|
||||
static char buf[2*HEADLEN];
|
||||
int j = 0;
|
||||
char *s = bufptr(dp);
|
||||
int n = (this+1)->dp - dp;
|
||||
if (n >= HEADLEN)
|
||||
n = HEADLEN;
|
||||
for (int i = 0; i < n; i++)
|
||||
switch (s[i]) {
|
||||
case '\n':
|
||||
case '\t':
|
||||
case '\0':
|
||||
case ' ':
|
||||
break;
|
||||
default:
|
||||
buf[j++] = s[i];
|
||||
break;
|
||||
}
|
||||
buf[j] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *strindex(char s[], char t[]) // index of earliest t[] in s[]
|
||||
{
|
||||
for (int i = 0; s[i] != '\0'; i++) {
|
||||
int j, k;
|
||||
for (j = i, k = 0; t[k]!='\0' && s[j] == t[k]; j++, k++)
|
||||
;
|
||||
if (k > 0 && t[k] == '\0')
|
||||
return s+i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void slug::slugout(int col)
|
||||
{
|
||||
static int numout = 0;
|
||||
if (seen++)
|
||||
ERROR "%s slug #%d seen %d times [%s]\n",
|
||||
typename(), serialno(), seen, headstr() WARNING;
|
||||
if (type == TM) {
|
||||
char *p;
|
||||
if ((p = strindex(bufptr(dp), "x X TM ")) != 0)
|
||||
p += strlen("x X TM "); // skip junk
|
||||
else
|
||||
ERROR "strange TM [%s]\n", headstr() FATAL;
|
||||
fprintf(stderr, "%d\t", userpn); // page # as prefix
|
||||
for ( ; p < bufptr((this+1)->dp); p++)
|
||||
putc(*p, stderr);
|
||||
} else if (type == COORD) {
|
||||
for (char *p = bufptr(dp); p < bufptr((this+1)->dp) && *p != '\n'; p++)
|
||||
putc(*p, stdout);
|
||||
printf(" # P %d X %d", userpn, hpos + col*offset);
|
||||
return;
|
||||
} else if (type == VBOX) {
|
||||
if (numout++ > 0) // BUG??? might miss something
|
||||
printf("s%d\nf%d\n", size, font);
|
||||
printf("H%d\n", hpos + col*offset);
|
||||
}
|
||||
fwrite(bufptr(dp), sizeof(char), (this+1)->dp - dp, stdout);
|
||||
}
|
||||
|
||||
char *slug::typename()
|
||||
{
|
||||
static char buf[50];
|
||||
char *p = buf; // return value
|
||||
switch(type) {
|
||||
case EOF: p = "EOF"; break;
|
||||
case VBOX: p = "VBOX"; break;
|
||||
case SP: p = "SP"; break;
|
||||
case BS: p = "BS"; break;
|
||||
case US: p = "US"; break;
|
||||
case BF: p = "BF"; break;
|
||||
case UF: p = "UF"; break;
|
||||
case PT: p = "PT"; break;
|
||||
case BT: p = "BT"; break;
|
||||
case END: p = "END"; break;
|
||||
case NEUTRAL: p = "NEUT"; break;
|
||||
case PAGE: p = "PAGE"; break;
|
||||
case TM: p = "TM"; break;
|
||||
case COORD: p = "COORD"; break;
|
||||
case NE: p = "NE"; break;
|
||||
case CMD: p = "CMD"; break;
|
||||
case PARM: p = "PARM"; break;
|
||||
default: sprintf(buf, "weird type %d", type);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
|
||||
// troff output-specific functions
|
||||
|
||||
// ================================================================================
|
||||
|
||||
const int DELTABUF = 500000; // grow the input buffer in chunks
|
||||
|
||||
static char *inbuf = 0; // raw text input collects here
|
||||
static int ninbuf = 0; // byte count for inbuf
|
||||
static char *inbp = 0; // next free slot in inbuf
|
||||
int linenum = 0; // input line number
|
||||
|
||||
static inline void addc(int c) { *inbp++ = c; }
|
||||
|
||||
static void adds(char *s)
|
||||
{
|
||||
for (char *p = s; *p; p++)
|
||||
addc(*p);
|
||||
}
|
||||
|
||||
static char *getutf(FILE *fp) // get 1 utf-encoded char (might be multiple bytes)
|
||||
{
|
||||
static char buf[100];
|
||||
char *p = buf;
|
||||
|
||||
for (*p = 0; (*p++ = getc(fp)) != EOF; ) {
|
||||
*p = 0;
|
||||
if (mblen(buf, sizeof buf) > 0) // found a valid character
|
||||
break;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *bufptr(int n) { return inbuf + n; } // scope of inbuf is too local
|
||||
|
||||
static inline int wherebuf() { return inbp - inbuf; }
|
||||
|
||||
static char *getstr(char *p, char *temp)
|
||||
{ // copy next non-blank string from p to temp, update p
|
||||
while (*p == ' ' || *p == '\t' || *p == '\n')
|
||||
p++;
|
||||
if (*p == '\0') {
|
||||
temp[0] = 0;
|
||||
return(NULL);
|
||||
}
|
||||
while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
|
||||
*temp++ = *p++;
|
||||
*temp = '\0';
|
||||
return(p);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
bounding box of a circular arc Eric Grosse 24 May 84
|
||||
|
||||
Conceptually, this routine generates a list consisting of the start,
|
||||
end, and whichever north, east, south, and west points lie on the arc.
|
||||
The bounding box is then the range of this list.
|
||||
list = {start,end}
|
||||
j = quadrant(start)
|
||||
k = quadrant(end)
|
||||
if( j==k && long way 'round ) append north,west,south,east
|
||||
else
|
||||
while( j != k )
|
||||
append center+radius*[j-th of north,west,south,east unit vectors]
|
||||
j += 1 (mod 4)
|
||||
return( bounding box of list )
|
||||
The following code implements this, with simple optimizations.
|
||||
***********************************************************************/
|
||||
|
||||
static int quadrant(double x, double y)
|
||||
{
|
||||
if ( x>=0.0 && y> 0.0) return(1);
|
||||
else if( x< 0.0 && y>=0.0) return(2);
|
||||
else if( x<=0.0 && y< 0.0) return(3);
|
||||
else if( x> 0.0 && y<=0.0) return(4);
|
||||
else return 0; /* shut up lint */
|
||||
}
|
||||
|
||||
static double xmin, ymin, xmax, ymax; // used by getDy
|
||||
|
||||
static void arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc)
|
||||
/* start, end, center */
|
||||
{ /* assumes center isn't too far out */
|
||||
double r;
|
||||
int j, k;
|
||||
printf("#start %g,%g, end %g,%g, ctr %g,%g\n", x0,y0, x1,y1, xc,yc);
|
||||
y0 = -y0; y1 = -y1; yc = -yc; // troff's up is eric's down
|
||||
x0 -= xc; y0 -= yc; /* move to center */
|
||||
x1 -= xc; y1 -= yc;
|
||||
xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1;
|
||||
xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1;
|
||||
r = sqrt(x0*x0 + y0*y0);
|
||||
if (r > 0.0) {
|
||||
j = quadrant(x0,y0);
|
||||
k = quadrant(x1,y1);
|
||||
if (j == k && y1*x0 < x1*y0) {
|
||||
/* viewed as complex numbers, if Im(z1/z0)<0, arc is big */
|
||||
if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r;
|
||||
if( xmax < r) xmax = r; if( ymax < r) ymax = r;
|
||||
} else {
|
||||
while (j != k) {
|
||||
switch (j) {
|
||||
case 1: if( ymax < r) ymax = r; break; /* north */
|
||||
case 2: if( xmin > -r) xmin = -r; break; /* west */
|
||||
case 3: if( ymin > -r) ymin = -r; break; /* south */
|
||||
case 4: if( xmax < r) xmax = r; break; /* east */
|
||||
}
|
||||
j = j%4 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
xmin += xc; ymin += yc; ymin = -ymin;
|
||||
xmax += xc; ymax += yc; ymax = -ymax;
|
||||
}
|
||||
|
||||
|
||||
static int getDy(char *p, int *dx, int *maxv)
|
||||
// figure out where we are after a D'...'
|
||||
{
|
||||
int x, y, x1, y1; // for input values
|
||||
char temp[50];
|
||||
p++; // get to command letter
|
||||
switch (*p++) {
|
||||
case 'l': // line
|
||||
sscanf(p, "%d %d", dx, &y);
|
||||
return *maxv = y;
|
||||
case 'a': // arc
|
||||
sscanf(p, "%d %d %d %d", &x, &y, &x1, &y1);
|
||||
*dx = x1 - x;
|
||||
arc_extreme(0, 0, x+x1, y+y1, x, y); // sets [xy][max|min]
|
||||
printf("#arc bounds x %g, %g; y %g, %g\n",
|
||||
xmin, xmax, ymin, ymax);
|
||||
*maxv = (int) (ymin+0.5);
|
||||
return y + y1;
|
||||
case '~': // spline
|
||||
for (*dx = *maxv = y = 0; (p=getstr(p, temp)) != NULL; ) {
|
||||
// above getstr() gets x value
|
||||
*dx += atoi(temp);
|
||||
p = getstr(p, temp); // this one gets y value
|
||||
y += atoi(temp);
|
||||
*maxv = max(*maxv, y); // ok???
|
||||
if (*p == '\n' || *p == 0) // input is a single line;
|
||||
break; // don't walk off end if realloc
|
||||
}
|
||||
return y;
|
||||
case 'c': // circle, ellipse
|
||||
sscanf(p, "%d", dx);
|
||||
*maxv = *dx/2; // high water mark is ht/2
|
||||
return 0;
|
||||
case 'e':
|
||||
sscanf(p, "%d %d", dx, &y);
|
||||
*maxv = y/2; // high water mark is ht/2
|
||||
return 0;
|
||||
default: // weird stuff
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int serialnum = 0;
|
||||
|
||||
slug eofslug()
|
||||
{
|
||||
slug ret;
|
||||
ret.serialnum = serialnum;
|
||||
ret.type = EOF;
|
||||
ret.dp = wherebuf();
|
||||
return ret;
|
||||
}
|
||||
|
||||
slug getslug(FILE *fp)
|
||||
{
|
||||
if (inbuf == NULL) {
|
||||
if ((inbuf = (char *) malloc(ninbuf = DELTABUF)) == NULL)
|
||||
ERROR "no room for %d character input buffer\n", ninbuf FATAL;
|
||||
inbp = inbuf;
|
||||
}
|
||||
if (wherebuf() > ninbuf-5000) {
|
||||
// this is still flaky -- lines can be very long
|
||||
int where = wherebuf(); // where we were
|
||||
if ((inbuf = (char *) realloc(inbuf, ninbuf += DELTABUF)) == NULL)
|
||||
ERROR "no room for %d character input buffer\n", ninbuf FATAL;
|
||||
ERROR "grew input buffer to %d characters\n", ninbuf WARNING;
|
||||
inbp = inbuf + where; // same offset in new array
|
||||
}
|
||||
static int baseV = 0; // first V command of preceding slug
|
||||
static int curV = 0, curH = 0;
|
||||
static int font = 0, size = 0;
|
||||
static int baseadj = 0;
|
||||
static int ncol = 1, offset = 0; // multi-column stuff
|
||||
char str[1000], str2[1000], buf[3000], *p;
|
||||
int firstV = 0, firstH = 0;
|
||||
int maxV = curV;
|
||||
int ocurV = curV, mxv = 0, dx = 0;
|
||||
int sawD = 0; // > 0 if have seen D...
|
||||
slug ret;
|
||||
ret.serialnum = serialnum++;
|
||||
ret.type = VBOX; // use the same as last by default
|
||||
ret.dv = curV - baseV;
|
||||
ret.hpos = curH;
|
||||
ret.base = ret.parm = ret.parm2 = ret.seen = 0;
|
||||
ret.font = font;
|
||||
ret.size = size;
|
||||
ret.dp = wherebuf();
|
||||
ret.ncol = ncol;
|
||||
ret.offset = offset;
|
||||
ret.linenum = linenum; // might be low
|
||||
|
||||
for (;;) {
|
||||
int c, m, n; // for input values
|
||||
int sign; // hoisted from case 'h' below
|
||||
switch (c = getc(fp)) {
|
||||
case EOF:
|
||||
ret.type = EOF;
|
||||
ret.dv = 0;
|
||||
if (baseadj)
|
||||
printf("# adjusted %d bases\n", baseadj);
|
||||
printf("# %d characters, %d lines\n", wherebuf(), linenum);
|
||||
return ret;
|
||||
case 'V':
|
||||
fscanf(fp, "%d", &n);
|
||||
if (firstV++ == 0) {
|
||||
ret.dv = n - baseV;
|
||||
baseV = n;
|
||||
} else {
|
||||
sprintf(buf, "v%d", n - curV);
|
||||
adds(buf);
|
||||
}
|
||||
curV = n;
|
||||
maxV = max(maxV, curV);
|
||||
break;
|
||||
case 'H': // absolute H motion
|
||||
fscanf(fp, "%d", &n);
|
||||
if (firstH++ == 0) {
|
||||
ret.hpos = n;
|
||||
} else {
|
||||
sprintf(buf, "h%d", n - curH);
|
||||
adds(buf);
|
||||
}
|
||||
curH = n;
|
||||
break;
|
||||
case 'h': // relative H motion
|
||||
addc(c);
|
||||
sign = 1;
|
||||
if ((c = getc(fp)) == '-') {
|
||||
addc(c);
|
||||
sign = -1;
|
||||
c = getc(fp);
|
||||
}
|
||||
for (n = 0; isdigit(c); c = getc(fp)) {
|
||||
addc(c);
|
||||
n = 10 * n + c - '0';
|
||||
}
|
||||
curH += n * sign;
|
||||
ungetc(c, fp);
|
||||
break;
|
||||
case 'x': // device control: x ...
|
||||
addc(c);
|
||||
fgets(buf, (int) sizeof(buf), fp);
|
||||
linenum++;
|
||||
adds(buf);
|
||||
if (buf[0] == ' ' && buf[1] == 'X') { // x X ...
|
||||
if (2 != sscanf(buf+2, "%s %d", str, &n))
|
||||
n = 0;
|
||||
if (eq(str, "SP")) { // X SP n
|
||||
ret.type = SP; // paddable SPace
|
||||
ret.dv = n; // of height n
|
||||
} else if (eq(str, "BS")) {
|
||||
ret.type = BS; // Breakable Stream
|
||||
ret.parm = n; // >=n VBOXES on a page
|
||||
} else if (eq(str, "BF")) {
|
||||
ret.type = BF; // Breakable Float
|
||||
ret.parm = ret.parm2 = n;
|
||||
// n = pref center (as UF)
|
||||
} else if (eq(str, "US")) {
|
||||
ret.type = US; // Unbreakable Stream
|
||||
ret.parm = n;
|
||||
} else if (eq(str, "UF")) {
|
||||
ret.type = UF; // Unbreakable Float
|
||||
ret.parm = ret.parm2 = n;
|
||||
// n = preferred center
|
||||
// to select several,
|
||||
// use several UF lines
|
||||
} else if (eq(str, "PT")) {
|
||||
ret.type = PT; // Page Title
|
||||
ret.parm = n;
|
||||
} else if (eq(str, "BT")) {
|
||||
ret.type = BT; // Bottom Title
|
||||
ret.parm = n;
|
||||
} else if (eq(str, "END")) {
|
||||
ret.type = END;
|
||||
ret.parm = n;
|
||||
} else if (eq(str, "TM")) {
|
||||
ret.type = TM; // Terminal Message
|
||||
ret.dv = 0;
|
||||
} else if (eq(str, "COORD")) {
|
||||
ret.type = COORD;// page COORDinates
|
||||
ret.dv = 0;
|
||||
} else if (eq(str, "NE")) {
|
||||
ret.type = NE; // NEed to break page
|
||||
ret.dv = n; // if <n units left
|
||||
} else if (eq(str, "MC")) {
|
||||
ret.type = MC; // Multiple Columns
|
||||
sscanf(buf+2, "%s %d %d",
|
||||
str, &ncol, &offset);
|
||||
ret.ncol = ncol;
|
||||
ret.offset = offset;
|
||||
} else if (eq(str, "CMD")) {
|
||||
ret.type = CMD; // CoMmaNd
|
||||
sscanf(buf+2, "%s %s", str2, str);
|
||||
if (eq(str, "FC")) // Freeze 2-Col
|
||||
ret.parm = FC;
|
||||
else if (eq(str, "FL")) // FLush
|
||||
ret.parm = FL;
|
||||
else if (eq(str, "BP")) // Break Page
|
||||
ret.parm = BP;
|
||||
else ERROR "unknown command %s\n",
|
||||
str WARNING;
|
||||
} else if (eq(str, "PARM")) {
|
||||
ret.type = PARM;// PARaMeter
|
||||
sscanf(buf+2, "%s %s %d", str2, str, &ret.parm2);
|
||||
if (eq(str, "NP")) // New Page
|
||||
ret.parm = NP;
|
||||
else if (eq(str, "FO")) // FOoter
|
||||
ret.parm = FO;
|
||||
else if (eq(str, "PL")) // Page Length
|
||||
ret.parm = PL;
|
||||
else if (eq(str, "MF")) // MinFull
|
||||
ret.parm = MF;
|
||||
else if (eq(str, "CT")) // ColTol
|
||||
ret.parm = CT;
|
||||
else if (eq(str, "WARN")) //WARNings?
|
||||
ret.parm = WARN;
|
||||
else if (eq(str, "DBG"))// DeBuG
|
||||
ret.parm = DBG;
|
||||
else ERROR "unknown parameter %s\n",
|
||||
str WARNING;
|
||||
} else
|
||||
break; // out of switch
|
||||
if (firstV > 0)
|
||||
ERROR "weird x X %s in mid-VBOX\n",
|
||||
str WARNING;
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case 'n': // end of line
|
||||
fscanf(fp, "%d %d", &n, &m);
|
||||
ret.ht = n;
|
||||
ret.base = m;
|
||||
getc(fp); // newline
|
||||
linenum++;
|
||||
sprintf(buf, "n%d %d\n", ret.ht, ret.base);
|
||||
adds(buf);
|
||||
if (!firstV++)
|
||||
baseV = curV;
|
||||
// older incarnations of this program used ret.base
|
||||
// in complicated and unreliable ways;
|
||||
// example: if ret.ht + ret.base < ret.dv, ret.base = 0
|
||||
// this was meant to avoid double-counting the space
|
||||
// around displayed equations; it didn't work
|
||||
// Now, we believe ret.base = 0, otherwise we give it
|
||||
// a value we have computed.
|
||||
if (ret.base == 0 && sawD == 0)
|
||||
return ret; // don't fiddle 0-bases
|
||||
if (ret.base != maxV - baseV) {
|
||||
ret.base = maxV - baseV;
|
||||
baseadj++;
|
||||
}
|
||||
if (ret.type != VBOX)
|
||||
ERROR "%s slug (type %d) has base = %d\n",
|
||||
ret.typename(), ret.type, ret.base WARNING;
|
||||
return ret;
|
||||
case 'p': // new page
|
||||
fscanf(fp, "%d", &n);
|
||||
ret.type = PAGE;
|
||||
curV = baseV = ret.dv = 0;
|
||||
ret.parm = n; // just in case someone needs it
|
||||
return ret;
|
||||
case 's': // size change snnn
|
||||
fscanf(fp, "%d", &size);
|
||||
sprintf(buf, "s%d\n", size);
|
||||
adds(buf);
|
||||
break;
|
||||
case 'f': // font fnnn
|
||||
fscanf(fp, "%d", &font);
|
||||
sprintf(buf, "f%d\n", font);
|
||||
adds(buf);
|
||||
break;
|
||||
case '\n':
|
||||
linenum++;
|
||||
/* fall through */
|
||||
case ' ':
|
||||
addc(c);
|
||||
break;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
// two motion digits plus a character
|
||||
addc(c);
|
||||
n = c - '0';
|
||||
addc(c = getc(fp));
|
||||
curH += 10 * n + c - '0';
|
||||
adds(getutf(fp));
|
||||
if (!firstV++)
|
||||
baseV = curV;
|
||||
break;
|
||||
case 'c': // single ascii character
|
||||
addc(c);
|
||||
adds(getutf(fp));
|
||||
if (!firstV++)
|
||||
baseV = curV;
|
||||
break;
|
||||
case 'C': // Cxyz\n
|
||||
case 'N': // Nnnn\n
|
||||
addc(c);
|
||||
while ((c = getc(fp)) != ' ' && c != '\n')
|
||||
addc(c);
|
||||
addc(c);
|
||||
if (!firstV++)
|
||||
baseV = curV;
|
||||
linenum++;
|
||||
break;
|
||||
case 'D': // draw function: D.*\n
|
||||
sawD++;
|
||||
p = bufptr(wherebuf()); // where does the D start
|
||||
addc(c);
|
||||
while ((c = getc(fp)) != '\n')
|
||||
addc(c);
|
||||
addc(c);
|
||||
if (!firstV++)
|
||||
baseV = curV;
|
||||
ocurV = curV, mxv = 0, dx = 0;
|
||||
curV += getDy(p, &dx, &mxv); // figure out how big it is
|
||||
maxV = max(max(maxV, curV), ocurV+mxv);
|
||||
curH += dx;
|
||||
linenum++;
|
||||
break;
|
||||
case 'v': // relative vertical vnnn
|
||||
addc(c);
|
||||
if (!firstV++)
|
||||
baseV = curV;
|
||||
sign = 1;
|
||||
if ((c = getc(fp)) == '-') {
|
||||
addc(c);
|
||||
sign = -1;
|
||||
c = getc(fp);
|
||||
}
|
||||
for (n = 0; isdigit(c); c = getc(fp)) {
|
||||
addc(c);
|
||||
n = 10 * n + c - '0';
|
||||
}
|
||||
ungetc(c, fp);
|
||||
curV += n * sign;
|
||||
maxV = max(maxV, curV);
|
||||
addc('\n');
|
||||
break;
|
||||
case 'w': // word space
|
||||
addc(c);
|
||||
break;
|
||||
case '#': // comment
|
||||
addc(c);
|
||||
while ((c = getc(fp)) != '\n')
|
||||
addc(c);
|
||||
addc('\n');
|
||||
linenum++;
|
||||
break;
|
||||
default:
|
||||
ERROR "unknown input character %o %c (%50.50s)\n",
|
||||
c, c, bufptr(wherebuf()-50) WARNING;
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
74
src/cmd/mpm/slug.h
Normal file
74
src/cmd/mpm/slug.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
enum slugtypes {
|
||||
NONE, // can't happen
|
||||
VBOX, // Vertical Box -- printable stuff
|
||||
SP, // paddable SPace
|
||||
BS, // start Breakable Stream
|
||||
US, // start Unbreakable Stream
|
||||
BF, // start Breakable Float
|
||||
UF, // start Unbreakable Float
|
||||
PT, // start Page Top material (header)
|
||||
BT, // start page BoTtom material (footer)
|
||||
END, // ENDs of groups
|
||||
NEUTRAL, // NEUTRALized slugs can do no harm (cf. CIA)
|
||||
PAGE, // beginning of PAGE in troff input
|
||||
TM, // Terminal Message to appear during output
|
||||
COORD, // output page COORDinates
|
||||
NE, // NEed command
|
||||
MC, // Multiple-Column command
|
||||
CMD, // misc CoMmanDs: FC, FL, BP
|
||||
PARM, // misc PARaMeters: NP, FO
|
||||
LASTTYPE // can't happen either
|
||||
};
|
||||
|
||||
enum cmdtypes {
|
||||
FC, // Freeze 2-Column material
|
||||
FL, // FLush all floats before reading more stream
|
||||
BP // Break Page
|
||||
};
|
||||
|
||||
enum parmtypes {
|
||||
NP, // distance of top margin from page top (New Page)
|
||||
FO, // distance of bottom margin from page top (FOoter)
|
||||
PL, // distance of physical page bottom from page top (Page Length)
|
||||
MF, // minimum fullness required for padding
|
||||
CT, // tolerance for division into two columns
|
||||
WARN, // warnings to stderr?
|
||||
DBG // debugging flag
|
||||
};
|
||||
|
||||
class slug {
|
||||
int serialnum;
|
||||
int dp; // offset of data for this slug in inbuf
|
||||
int linenum; // input line number (approx) for this slug
|
||||
short font; // font in effect at slug beginning
|
||||
short size; // size in effect at slug beginning
|
||||
short seen; // 0 until output
|
||||
short ncol; // number of columns (1 or 2)
|
||||
short offset; // horizontal offset for 2 columns
|
||||
public:
|
||||
short type; // VBOX, PP, etc.
|
||||
short parm; // parameter
|
||||
short base; // "depth" of this slug (from n command)
|
||||
int hpos; // abs horizontal position
|
||||
int dv; // height of this slug above its input Vpos
|
||||
union {
|
||||
int ht; // "height" of this slug (from n command)
|
||||
int parm2; // second parameter, since only VBOXes have ht
|
||||
};
|
||||
friend slug getslug(FILE *);
|
||||
friend void checkout();
|
||||
friend slug eofslug();
|
||||
void coalesce(); // with next slug in array slugs[]
|
||||
void neutralize(); // render this one a no-op
|
||||
void dump(); // dump its contents for debugging
|
||||
char *headstr(); // string value of text
|
||||
void slugout(int); // add the slug to the output
|
||||
char *typename(); // printable slug type
|
||||
int serialno() { return serialnum; }
|
||||
int numcol() { return ncol; }
|
||||
int lineno() { return linenum; }
|
||||
};
|
||||
|
||||
// functions in slug.c
|
||||
slug eofslug();
|
||||
slug getslug(FILE *);
|
||||
961
src/cmd/mpm/tmac.pm
Normal file
961
src/cmd/mpm/tmac.pm
Normal file
|
|
@ -0,0 +1,961 @@
|
|||
.\" 10/22/92 activate next line before installing
|
||||
.pi /$objtype/bin/aux/pm
|
||||
.
|
||||
. \" IZ - initialization
|
||||
.de IZ
|
||||
.fp 1 R \" force a font out into prefix
|
||||
.nr PS 10 \" point size
|
||||
.nr VS 12 \" line spacing
|
||||
.ps \\n(PS
|
||||
.ie \\n(VS>=41 .vs \\n(VSu
|
||||
.el .vs \\n(VSp
|
||||
.nr LL 6i \" line length
|
||||
.ll \\n(LLu
|
||||
.nr LT \\n(.l \" title length
|
||||
.lt \\n(LTu
|
||||
.if !\\n(HM .nr HM 1i \" top of page
|
||||
.if !\\n(FM .nr FM 1i \" footer margin
|
||||
.if !\\n(FO .nr FO \\n(.p-\\n(FM \" bottom of page
|
||||
. \" to set text ht to N, set FO to N + \n(HM. default is 10i
|
||||
.pl 32767u \" safety first: big pages for pm
|
||||
.if !\\n(PO .nr PO \\n(.ou \" page offset
|
||||
.nr PI 5n \" .PP paragraph indent
|
||||
.nr QI 5n \" .QS indent
|
||||
.nr DI 5n \" .DS indent
|
||||
.nr PD 0.3v \" paragraph vertical separation
|
||||
.nr TS 0.5v \" space around tables
|
||||
.nr Kf 0.5v \" space around .KF/.KE
|
||||
.nr Ks 0.5v \" space around .KS/.KE
|
||||
.
|
||||
.nr P1 .4i \" indent for .P1/.P2
|
||||
.nr dP 1 \" delta point size for programs in .P1/.P2
|
||||
.nr dV 2p \" delta vertical for programs
|
||||
.nr dT 8 \" delta tab stop for programs
|
||||
.nr DV .5v \" space before start of program
|
||||
.nr IP 0 \" ?
|
||||
.nr IR 0 \" ?
|
||||
.nr I1 \\n(PIu
|
||||
.ev 1
|
||||
.if !\\n(FL .nr FL \\n(LLu \" footnote length
|
||||
.ll \\n(FLu
|
||||
.ps 8 \" text size & leading in footnote
|
||||
.vs 10p
|
||||
.ev
|
||||
.if \\*(CH .ds CH "\(hy \\\\n(PN \(hy
|
||||
.ds # #\\\\n(.c \\\\n(.F
|
||||
.
|
||||
.
|
||||
.ME \" initialize date strings
|
||||
.rm ME
|
||||
. \" accents: \*'e \*`e \*:u \*^e \*~n \*va \*,c
|
||||
.ds ' \h'\w'e'u*4/10'\z\(aa\h'-\w'e'u*4/10'
|
||||
.ds ` \h'\w'e'u*4/10'\z\(ga\h'-\w'e'u*4/10'
|
||||
.ds : \\v'-0.6m'\\h'(1u-(\\\\n(.fu%2u))*0.13m+0.00m'\\z.\\h'0.2m'\\z.\\h'-((1u-(\\\\n(.fu%2u))*0.13m+0.20m)'\\v'0.6m'
|
||||
.ds ^ \\\\k:\\h'-\\\\n(.fu+1u/2u*2u+\\\\n(.fu-1u*0.13m+0.06m'\\z^\\h'|\\\\n:u'
|
||||
.ds ~ \\\\k:\\h'-\\\\n(.fu+1u/2u*2u+\\\\n(.fu-1u*0.13m+0.06m'\\z~\\h'|\\\\n:u'
|
||||
.ds v \\\\k:\\\\h'+\\\\w'e'u/4u'\\\\v'-0.6m'\\\\s6v\\\\s0\\\\v'0.6m'\\\\h'|\\\\n:u'
|
||||
.ds , \\\\k:\\\\h'\\\\w'c'u*0.4u'\\\\z,\\\\h'|\\\\n:u'
|
||||
..
|
||||
.
|
||||
.
|
||||
. \" SP - generate paddable space
|
||||
.de SP
|
||||
.br
|
||||
.nr X 1v
|
||||
.if \\n(.$ .nr X \\$1v
|
||||
.ie '\\$2'exactly' \{\
|
||||
\v'\\nXu'\ \h'-\w'\ 'u'\c
|
||||
.sp \\$1\}
|
||||
.el .X "SP \\nX \\$2"
|
||||
..
|
||||
. \" NE - need space on this page
|
||||
.de NE
|
||||
.nr X 1v
|
||||
.if \\n(.$ .nr X \\$1v
|
||||
.X "NE \\nX \\$2"
|
||||
..
|
||||
. \" BP, FL, FC - begin page, flush figures, flush column
|
||||
.de BP
|
||||
.br
|
||||
.X CMD BP
|
||||
..
|
||||
.de FL
|
||||
.br
|
||||
.X CMD FL
|
||||
..
|
||||
.de FC
|
||||
.br
|
||||
.X CMD FC
|
||||
..
|
||||
. \" X - generate an x X ... command in the output
|
||||
.de X
|
||||
....ie '\\n(.z'' \\!x X \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
|
||||
....el \\!.X "\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
|
||||
...
|
||||
.if !'\\n(.z'' .if \\n(.$=1 \\!.X "\\$1
|
||||
.if !'\\n(.z'' .if \\n(.$=2 \\!.X "\\$1 \\$2
|
||||
.if !'\\n(.z'' .if \\n(.$=3 \\!.X "\\$1 \\$2 \\$3
|
||||
.if !'\\n(.z'' .if \\n(.$>3 \\!.X "\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
|
||||
.if '\\n(.z'' .if \\n(.$=1 \\!x X \\$1 \\*#
|
||||
.if '\\n(.z'' .if \\n(.$=2 \\!x X \\$1 \\$2 \\*#
|
||||
.if '\\n(.z'' .if \\n(.$=3 \\!x X \\$1 \\$2 \\$3 \\*#
|
||||
.if '\\n(.z'' .if \\n(.$=4 \\!x X \\$1 \\$2 \\$3 \\$4 \\*#
|
||||
.if '\\n(.z'' .if \\n(.$>4 \\!x X \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 \\*#
|
||||
..
|
||||
. \" DA - force date
|
||||
.de DA
|
||||
.if \\n(.$ .ds DY \\$1 \\$2 \\$3 \\$4
|
||||
.ds CF \\*(DY
|
||||
..
|
||||
. \" ND - set new or no date
|
||||
.de ND
|
||||
.ds DY \\$1 \\$2 \\$3 \\$4
|
||||
.rm CF
|
||||
..
|
||||
.de ME \" ME - set month strings
|
||||
.if \\n(mo-0 .ds MO January
|
||||
.if \\n(mo-1 .ds MO February
|
||||
.if \\n(mo-2 .ds MO March
|
||||
.if \\n(mo-3 .ds MO April
|
||||
.if \\n(mo-4 .ds MO May
|
||||
.if \\n(mo-5 .ds MO June
|
||||
.if \\n(mo-6 .ds MO July
|
||||
.if \\n(mo-7 .ds MO August
|
||||
.if \\n(mo-8 .ds MO September
|
||||
.if \\n(mo-9 .ds MO October
|
||||
.if \\n(mo-10 .ds MO November
|
||||
.if \\n(mo-11 .ds MO December
|
||||
.if \\n(dw-0 .ds DW Sunday
|
||||
.if \\n(dw-1 .ds DW Monday
|
||||
.if \\n(dw-2 .ds DW Tuesday
|
||||
.if \\n(dw-3 .ds DW Wednesday
|
||||
.if \\n(dw-4 .ds DW Thursday
|
||||
.if \\n(dw-5 .ds DW Friday
|
||||
.if \\n(dw-6 .ds DW Saturday
|
||||
.if "\\*(DY"" .ds DY \\*(MO \\n(dy, 19\\n(yr
|
||||
..
|
||||
. \" FP - font position for a family
|
||||
.de FP
|
||||
.if '\\$1'palatino'\{\
|
||||
. fp 1 PA
|
||||
. fp 2 PI
|
||||
. fp 3 PB
|
||||
. fp 4 PX\}
|
||||
.if '\\$1'century'\{\
|
||||
. ie '\\*(.T'202'\{\
|
||||
. fp 1 NR Centsb
|
||||
. fp 2 NI CentI
|
||||
. fp 3 NB CentB
|
||||
. fp 4 NX CentBI\}
|
||||
. el \{\
|
||||
. fp 1 NR
|
||||
. fp 2 NI
|
||||
. fp 3 NB
|
||||
. fp 4 NX\}\}
|
||||
.if '\\$1'helvetica'\{\
|
||||
. fp 1 H
|
||||
. fp 2 HI
|
||||
. fp 3 HB
|
||||
. fp 4 HX\}
|
||||
.if '\\$1'bembo'\{\
|
||||
. ie '\\*(.T'202'\{\
|
||||
. fp 1 B1 Bembo
|
||||
. fp 2 B2 BemboI
|
||||
. fp 3 B3 BemboB
|
||||
. fp 4 B4 BemboBI\}
|
||||
. el \{\
|
||||
. fp 1 B1
|
||||
. fp 2 B2
|
||||
. fp 3 B3
|
||||
. fp 4 B4\}\}
|
||||
.if '\\$1'optima'\{\
|
||||
. fp 1 R Optima
|
||||
. fp 2 I OptimaI
|
||||
. fp 3 B OptimaB
|
||||
. fp 4 BI OptimaBI\}
|
||||
.if '\\$1'souvenir'\{\
|
||||
. fp 1 R Souvenir
|
||||
. fp 2 I SouvenirI
|
||||
. fp 3 B SouvenirB
|
||||
. fp 4 BI SouvenirBI\}
|
||||
.if '\\$1'melior'\{\
|
||||
. fp 1 R Melior
|
||||
. fp 2 I MeliorI
|
||||
. fp 3 B MeliorB
|
||||
. fp 4 BI MeliorBI\}
|
||||
.if '\\$1'times'\{\
|
||||
. fp 1 R
|
||||
. fp 2 I
|
||||
. fp 3 B
|
||||
. fp 4 BI\}
|
||||
..
|
||||
. \" TL - title
|
||||
.de TL
|
||||
.br
|
||||
.if !\\n(1T .BG
|
||||
....hy 0
|
||||
.ft 3
|
||||
.ps \\n(PS+2p
|
||||
.vs \\n(VS+2p
|
||||
.ll \\n(LLu
|
||||
.ce 100 \" turned off in .RT
|
||||
.sp .5i
|
||||
..
|
||||
. \" AU - remember author(s)
|
||||
.de AU
|
||||
.ft 1
|
||||
.ps \\n(PS
|
||||
.ie \\n(VS>=41 .vs \\n(VSu
|
||||
.el .vs \\n(VSp
|
||||
.SP .5
|
||||
..
|
||||
. \" AI - author's institution
|
||||
.de AI
|
||||
.SP .25
|
||||
.ft 2
|
||||
..
|
||||
. \" AB - begin abstract
|
||||
.de AB
|
||||
.nr AB 1 \" we're in abstract
|
||||
.if !\\n(1T .BG
|
||||
.ft 1
|
||||
.ps \\n(PS
|
||||
.vs \\n(VSp
|
||||
.ce
|
||||
.in +\\n(.lu/12u
|
||||
.ll -\\n(.lu/12u
|
||||
.SP 1
|
||||
.ie \\n(.$ \\$1
|
||||
.el ABSTRACT
|
||||
.SP .75
|
||||
.RT
|
||||
..
|
||||
. \" AE - end of abstract
|
||||
.de AE
|
||||
.br
|
||||
.nr AB 0
|
||||
.in 0
|
||||
.ll \\n(LLu
|
||||
.ps \\n(PS
|
||||
.ie \\n(VS>=41 .vs \\n(VSu
|
||||
.el .vs \\n(VSp
|
||||
.SP
|
||||
..
|
||||
. \" 2C - 2 columns
|
||||
.de 2C
|
||||
.MC 2
|
||||
..
|
||||
. \" 1C - 1 column
|
||||
.de 1C
|
||||
.MC 1
|
||||
..
|
||||
. \" MC - multiple columns
|
||||
.de MC
|
||||
.br
|
||||
.if \\n(1T .RT
|
||||
.if \\n(1T .NP
|
||||
.if !\\n(OL .nr OL \\n(LL
|
||||
.if \\n(CW=0 .nr CW \\n(LL*7/15
|
||||
.if \\n(GW=0 .nr GW \\n(LL-(2*\\n(CW)
|
||||
.nr x \\n(CW+\\n(GW
|
||||
.if "\\$1"" .MC 2
|
||||
.if \\$1=1 \{\
|
||||
. X MC 1 0
|
||||
. nr LL \\n(OLu\}
|
||||
.if \\$1=2 \{\
|
||||
. X MC 2 \\nx
|
||||
. nr LL \\n(CWu\}
|
||||
.ll \\n(LLu
|
||||
.if \\$1>2 .tm -mpm can't handle more than two columns
|
||||
.if \\n(1T .RT
|
||||
..
|
||||
. \" TS - table start, TE - table end; also TC, TQ, TH
|
||||
.de TS
|
||||
.br
|
||||
.if !\\n(1T .RT
|
||||
.SP \\n(TSu TS
|
||||
.X "US TS
|
||||
.if \\$1H .TQ
|
||||
.nr IX 1
|
||||
..
|
||||
.de TC
|
||||
.nr TZ \\n(.lu
|
||||
.if \\n(.$ .nr TZ \\$1n
|
||||
.ta \\n(TZuR
|
||||
..
|
||||
.de TD
|
||||
.LP
|
||||
.nr TZ 0
|
||||
..
|
||||
.de TQ
|
||||
.di TT
|
||||
.nr IT 1
|
||||
..
|
||||
.de TH
|
||||
.if \\n(.d>0.5v \{\
|
||||
. nr T. 0
|
||||
. T# 0\}
|
||||
.di
|
||||
.nr TQ \\n(.i
|
||||
.nr HT 1
|
||||
.in 0
|
||||
.mk #a
|
||||
.mk #b
|
||||
.mk #c
|
||||
.mk #d
|
||||
.mk #e
|
||||
.mk #f
|
||||
.TT
|
||||
.in \\n(TQu
|
||||
.mk #T
|
||||
..
|
||||
. \" TE - table end
|
||||
.de TE
|
||||
.nr IX 0
|
||||
.if \\n(IT .if !\\n(HT \{\
|
||||
. di
|
||||
. nr EF \\n(.u
|
||||
. nf
|
||||
. TT
|
||||
. if \\n(EF .fi\}
|
||||
.nr IT 0
|
||||
.nr HT 0
|
||||
.rm a+ b+ c+ d+ e+ f+ g+ h+ i+ j+ k+ l+ n+ m+
|
||||
.rr 32 33 34 35 36 37 38 40 79 80 81 82
|
||||
.rr a| b| c| d| e| f| g| h| i| j| k| l| m|
|
||||
.rr a- b- c- d- e- f- g- h- i- j- k- l- m-
|
||||
.X "END US TE
|
||||
.SP \\n(TSu TE
|
||||
.bp
|
||||
..
|
||||
. \" EQ - equation, breakout and display
|
||||
.de EQ
|
||||
.nr EF \\n(.u
|
||||
.rm EE
|
||||
.nr LE 1 \" 1 is center
|
||||
.ds EL \\$1
|
||||
.if "\\$1"L" \{\
|
||||
. ds EL \\$2
|
||||
. nr LE 0\}
|
||||
.if "\\$1"C" .ds EL \\$2
|
||||
.if "\\$1"R" \{\
|
||||
. ds EL \\$2 \" 2 is right adjust
|
||||
. nr LE 2\}
|
||||
.if "\\$1"I" \{\
|
||||
. nr LE 0
|
||||
. if "\\$3"" .ds EE \\h'|10n'
|
||||
. el .ds EE \\h'\\$3'
|
||||
. ds EL \\$2\}
|
||||
.if \\n(YE .nf
|
||||
.di EZ
|
||||
..
|
||||
. \" EN - end of equation
|
||||
.de EN
|
||||
.br
|
||||
.di
|
||||
.rm EZ
|
||||
.nr ZN \\n(dn
|
||||
.if \\n(ZN .if !\\n(YE .LP
|
||||
.if !\\n(ZN .if !"\\*(EL"" .nr ZN 1
|
||||
.if \\n(ZN \{\
|
||||
. SP .5v EQ
|
||||
. X "US EQ"\}
|
||||
'pc
|
||||
.if \\n(BD .nr LE 0 \" don't center if block display or mark/lineup
|
||||
.if \\n(MK \{\
|
||||
. if \\n(LE=1 .ds EE \\h'|10n'
|
||||
. nr LE 0\}
|
||||
'lt \\n(.lu
|
||||
.if !\\n(EP .if \\n(ZN \{\
|
||||
. if \\n(LE=1 .tl \(ts\(ts\\*(10\(ts\\*(EL\(ts
|
||||
. if \\n(LE=2 .tl \(ts\(ts\(ts\\*(10\\*(EL\(ts
|
||||
. if !\\n(LE \{\
|
||||
. if !\\n(BD .tl \(ts\\*(EE\\*(10\(ts\(ts\\*(EL\(ts
|
||||
. if \\n(BD .if \\n(BD<\\w\(ts\\*(10\(ts .nr BD \\w\(ts\\*(10\(ts
|
||||
. if \\n(BD \!\\*(10\\t\\*(EL\}\}
|
||||
.if \\n(EP .if \\n(ZN \{\
|
||||
. if \\n(LE=1 .tl \(ts\\*(EL\(ts\\*(10\(ts\(ts
|
||||
. if \\n(LE=2 .tl \(ts\\*(EL\(ts\(ts\\*(10\(ts
|
||||
. if !\\n(LE \{\
|
||||
. if !\\n(BD .tl \(ts\\*(EL\\*(EE\\*(10\(ts\(ts\(ts
|
||||
. if \\n(BD .if \\n(BD<\\w\(ts\\*(10\(ts .nr BD \\w\(ts\\*(10\(ts
|
||||
. if \\n(BD \!\\h'-\\\\n(.iu'\\*(EL\\h'|0'\\*(10\}\}
|
||||
'lt \\n(LLu
|
||||
'pc %
|
||||
.if \\n(YE .if \\n(EF .fi
|
||||
.if \\n(ZN .X "END US EQ"
|
||||
.if \\n(ZN .SP .5v EN
|
||||
.if \\n(ZN .bp
|
||||
..
|
||||
. \" PS - start picture
|
||||
.de PS \" $1 is height, $2 is width, in inches
|
||||
.br
|
||||
.nr X 0.35v
|
||||
.if \\$1>0 .X "SP \\nX PS"
|
||||
.ie \\$1>0 .nr $1 \\$1
|
||||
.el .nr $1 0
|
||||
.X "US PS \\$1
|
||||
.in (\\n(.lu-\\$2)/2u
|
||||
..
|
||||
. \" PE - end of picture
|
||||
.de PE
|
||||
.in
|
||||
.X "END US PE
|
||||
.nr X .65v
|
||||
.if \\n($1>0 .X "SP \\nX PE"
|
||||
.bp
|
||||
..
|
||||
.de IS \" for -mpm only
|
||||
.KS
|
||||
..
|
||||
.de IE
|
||||
.KE
|
||||
.bp
|
||||
..
|
||||
. \" NP - new page
|
||||
.de NP
|
||||
.ev 2
|
||||
.bp
|
||||
.if \\n(KF=0 \{\
|
||||
. nr PX \\n(.s
|
||||
. nr PF \\n(.f
|
||||
. nr PV \\n(.v
|
||||
. lt \\n(LTu
|
||||
. ps \\n(PS
|
||||
. vs \\n(PS+2
|
||||
. ft 1
|
||||
. if \\n(PO .po \\n(POu \" why isn't this reset???
|
||||
. PT \\$1
|
||||
. bp
|
||||
. rs
|
||||
. BT
|
||||
. bp
|
||||
. nr %# +1
|
||||
. ps \\n(PX
|
||||
. vs \\n(PVu
|
||||
. ft \\n(PF \}
|
||||
.ev
|
||||
..
|
||||
.
|
||||
.ds %e .tl '\\*(LH'\\*(CH'\\*(RH'
|
||||
.ds %o .tl '\\*(LH'\\*(CH'\\*(RH'
|
||||
.ds %E .tl '\\*(LF'\\*(CF'\\*(RF'
|
||||
.ds %O .tl '\\*(LF'\\*(CF'\\*(RF'
|
||||
.
|
||||
. \" PT - page title
|
||||
.de PT
|
||||
.nr PN \\n(%#
|
||||
.X "PT \\n(%#
|
||||
.sp \\n(HMu/2u
|
||||
.if \\n(OL .lt \\n(OLu \" why isn't this reset???
|
||||
.if \\n(BT>0 .if \\n(%#%2 \\*(%o
|
||||
.if \\n(BT>0 .if !\\n(%#%2 \\*(%e
|
||||
.if \\n(BT=0 .tl '\0''' \" put out something or spacing is curdled
|
||||
.X "END PT \\n(%#
|
||||
..
|
||||
. \" BT - bottom title
|
||||
.de BT
|
||||
.X "BT \\n(%#
|
||||
.sp |\\n(FMu/2u+\\n(FOu-1v
|
||||
.if \\n(%#%2 \\*(%O
|
||||
.if !\\n(%#%2 \\*(%E
|
||||
.nr BT \\n(BT+1
|
||||
.X "END BT \\n(%#
|
||||
..
|
||||
. \" KS - non-floating keep
|
||||
.de KS
|
||||
.br
|
||||
.if "\\n(.z"" .NP \" defends poorly against including ht of page stuff in diversion for .B1
|
||||
.X "US KS 0
|
||||
.nr KS +1
|
||||
.SP \\n(Ksu
|
||||
..
|
||||
. \" KF - floating keep
|
||||
.de KF
|
||||
.ev 1
|
||||
.br
|
||||
.if \\n(KS>0 .tm KF won't work inside KS, line \\n(.c, file \\n(.F
|
||||
.if \\n(KF>0 .tm KF won't work inside KF, line \\n(.c, file \\n(.F
|
||||
.nr KF 1
|
||||
.nr 10 0
|
||||
. if !'\\$1'' .nr 10 \\$1u
|
||||
. if '\\$1'bottom' .nr 10 \\n(FOu-1u
|
||||
. if '\\$1'top' .nr 10 \\n(HM
|
||||
. if \\n(10 .X "UF \\n(10 KF"
|
||||
. if !\\n(10 .X "UF \\n(HM KF"
|
||||
. nr X \\n(FOu-2u
|
||||
. if \\n(10 .X "UF \\n(10 KF"
|
||||
. if !\\n(10 .X "UF \\nX KF"
|
||||
.nr SJ \\n(.u
|
||||
.ps \\n(PS
|
||||
.if \\n(VS>40 .vs \\n(VSu
|
||||
.if \\n(VS<=39 .vs \\n(VSp
|
||||
.ll \\n(LLu
|
||||
.lt \\n(LTu
|
||||
.SP \\n(Kfu
|
||||
..
|
||||
. \" KE - end of KS/KF
|
||||
.de KE
|
||||
.bp
|
||||
.ie \\n(KS>0 \{\
|
||||
. SP \\n(Ksu
|
||||
. X "END US KS
|
||||
. nr KS -1 \}
|
||||
.el .ie \\n(KF>0 \{\
|
||||
. SP \\n(Kfu
|
||||
. nr KF 0
|
||||
. X "END UF KF"
|
||||
. if \\n(SJ .fi
|
||||
. ev \}
|
||||
.el .tm .KE without preceding .KS or .KF, line \\n(.c, file \\n(.F
|
||||
..
|
||||
.
|
||||
. \" DS - display. .DS C center; L left-adjust; I indent (default)
|
||||
.de DS \" $2 = amount of indent
|
||||
.KS
|
||||
.nf
|
||||
.\\$1D \\$2 \\$1
|
||||
.ft 1
|
||||
.if !\\n(IF \{\
|
||||
. ps \\n(PS
|
||||
. if \\n(VS>40 .vs \\n(VSu
|
||||
. if \\n(VS<=39 .vs \\n(VSp\}
|
||||
..
|
||||
.de D
|
||||
.ID \\$1
|
||||
..
|
||||
.de CD
|
||||
.XD
|
||||
.ce 1000
|
||||
..
|
||||
.de ID
|
||||
.XD
|
||||
.if \\n(.$=0 .in +\\n(DIu
|
||||
.if \\n(.$=1 .if "\\$1"I" .in +\\n(DIu
|
||||
.if \\n(.$=1 .if !"\\$1"I" .in +\\$1n
|
||||
.if \\n(.$>1 .in +\\$2n
|
||||
.....in +0.5i
|
||||
.....if \\n(.$ .if !"\\$1"I" .if !"\\$1"" .in \\n(DIu
|
||||
.....if \\n(.$ .if !"\\$1"I" .if !"\\$1"" .in +\\$1n
|
||||
..
|
||||
.de LD
|
||||
.XD
|
||||
..
|
||||
.de XD
|
||||
.nf
|
||||
.nr OI \\n(.i
|
||||
.SP \\n(DVu
|
||||
..
|
||||
. \" BD - block display: save everything, then center it.
|
||||
.de BD
|
||||
.XD
|
||||
.nr BD 1
|
||||
.nf
|
||||
.in \\n(OIu
|
||||
.di DD
|
||||
..
|
||||
. \" DE - display end
|
||||
.de DE
|
||||
.ce 0
|
||||
.if \\n(BD>0 .XF
|
||||
.nr BD 0
|
||||
.in \\n(OIu
|
||||
.SP \\n(DVu
|
||||
.KE
|
||||
.fi
|
||||
..
|
||||
. \" XF - finish a block display to be recentered.
|
||||
.de XF
|
||||
.di
|
||||
.if \\n(dl>\\n(BD .nr BD \\n(dl
|
||||
.if \\n(BD<\\n(.l .in (\\n(.lu-\\n(BDu)/2u
|
||||
.nr EI \\n(.l-\\n(.i
|
||||
.ta \\n(EIuR
|
||||
.nf
|
||||
.DD
|
||||
.in \\n(OIu
|
||||
..
|
||||
.
|
||||
.
|
||||
. \" SH - (unnumbered) section heading
|
||||
.de SH
|
||||
.RT
|
||||
.nr X 1v
|
||||
.nr Y 3v
|
||||
.if \\n(1T .NP
|
||||
.if \\n(1T .X "NE \\nY SH" \" should these be reversed, change Y to 4v
|
||||
.if \\n(1T .X "SP \\nX SH
|
||||
.ft 3
|
||||
..
|
||||
. \" NH - numbered heading
|
||||
.de NH
|
||||
.RT
|
||||
.nr X 1v
|
||||
.nr Y 3v
|
||||
.if \\n(1T .NP
|
||||
.if \\n(1T .X "NE \\nY NH" \" should these be reversed, change Y to 4v
|
||||
.if \\n(1T .X "SP \\nX NH
|
||||
.ft 3
|
||||
.nr NS \\$1
|
||||
.if !\\n(.$ .nr NS 1
|
||||
.if !\\n(NS .nr NS 1
|
||||
.nr H\\n(NS +1
|
||||
.if !\\n(NS-4 .nr H5 0
|
||||
.if !\\n(NS-3 .nr H4 0
|
||||
.if !\\n(NS-2 .nr H3 0
|
||||
.if !\\n(NS-1 .nr H2 0
|
||||
.if !\\$1 .if \\n(.$ .nr H1 1
|
||||
.ds SN \\n(H1.
|
||||
.if \\n(NS-1 .as SN \\n(H2.
|
||||
.if \\n(NS-2 .as SN \\n(H3.
|
||||
.if \\n(NS-3 .as SN \\n(H4.
|
||||
.if \\n(NS-4 .as SN \\n(H5.
|
||||
\\*(SN
|
||||
..
|
||||
. \" RT - reset at beginning of each PP, LP, etc.
|
||||
.de RT
|
||||
.if !\\n(AB .if !\\n(1T .BG
|
||||
.ce 0
|
||||
.if !\\n(AB .if !\\n(KF .if !\\n(IF .if !\\n(IX .if !\\n(BE .di
|
||||
.if \\n(QP \{\
|
||||
. ll +\\n(QIu
|
||||
. in -\\n(QIu
|
||||
. nr QP -1\}
|
||||
.if !\\n(AB \{\
|
||||
. ll \\n(LLu\}
|
||||
.if !\\n(IF .if !\\n(AB \{\
|
||||
. ps \\n(PS
|
||||
. ie \\n(VS>=41 .vs \\n(VSu
|
||||
. el .vs \\n(VSp\}
|
||||
.ie \\n(IP \{\
|
||||
. in \\n(I\\n(IRu
|
||||
. nr IP -1\}
|
||||
.el .if !\\n(IR \{\
|
||||
. nr I1 \\n(PIu
|
||||
. nr I2 0
|
||||
. nr I3 0
|
||||
. nr I4 0
|
||||
. nr I5 0\}
|
||||
.if !\\n(AB .ft 1
|
||||
.ta 5n 10n 15n 20n 25n 30n 35n 40n 45n 50n 55n 60n 65n 70n 75n 80n
|
||||
.fi
|
||||
..
|
||||
. \" BG - begin, execute at first TL, AB, NH, SH, PP, etc.
|
||||
.de BG \" IZ has been called, so registers have some value
|
||||
.br
|
||||
.if \\n(CW>0 .if \\n(LL=0 .nr LL \\n(CW+\\n(CW+\\n(GW
|
||||
.ll \\n(LLu
|
||||
.lt \\n(LLu
|
||||
.po \\n(POu
|
||||
.nr YE 1 \" ok to cause break in .EQ (earlier ones won't)
|
||||
.ev 0
|
||||
.hy 14
|
||||
.ev
|
||||
.ev 1
|
||||
.hy 14
|
||||
.ev
|
||||
.ev 2
|
||||
.hy 14
|
||||
.ev
|
||||
.nr 1T 1
|
||||
.X "PARM NP \\n(HM
|
||||
.X "PARM FO \\n(FO
|
||||
.if !\\n(%# .nr %# 1
|
||||
..
|
||||
. \" PP - paragraph
|
||||
.de PP
|
||||
.RT
|
||||
.if \\n(1T .NP
|
||||
.if \\n(1T .X "SP \\n(PD PP"
|
||||
.if \\n(1T .X "BS 2 PP"
|
||||
.ti +\\n(PIu
|
||||
..
|
||||
. \" LP - left aligned paragraph
|
||||
.de LP
|
||||
.RT
|
||||
.if \\n(1T .NP
|
||||
.if \\n(1T .X "SP \\n(PD LP"
|
||||
.if \\n(1T .X "BS 2 LP"
|
||||
..
|
||||
. \" IP - indented paragraph
|
||||
.de IP
|
||||
.RT
|
||||
.if !\\n(IP .nr IP +1
|
||||
.if \\n(1T .NP
|
||||
.if \\n(1T .X "SP \\n(PD PP"
|
||||
.if \\n(1T .X "BS 2 IP"
|
||||
.nr IU \\n(IR+1
|
||||
.if \\n(.$>1 .nr I\\n(IU \\$2n+\\n(I\\n(IRu
|
||||
.if \\n(I\\n(IU=0 .nr I\\n(IU \\n(PIu+\\n(I\\n(IRu
|
||||
.in \\n(I\\n(IUu
|
||||
.nr TY \\n(TZ-\\n(.i
|
||||
.nr JQ \\n(I\\n(IU-\\n(I\\n(IR
|
||||
.ta \\n(JQu \\n(TYuR
|
||||
.if \\n(.$ \{\
|
||||
.ti \\n(I\\n(IRu
|
||||
\&\\$1\t\c\}
|
||||
..
|
||||
. \" QP - quoted paragraph (within IP)
|
||||
.de QP
|
||||
.RT
|
||||
.if \\n(1T .NP
|
||||
.if \\n(1T .X "SP \\n(PD QP"
|
||||
.if \\n(1T .X "BS 2 QP"
|
||||
.nr QP 1
|
||||
.in +\\n(QIu
|
||||
.ll -\\n(QIu
|
||||
.ti \\n(.iu
|
||||
..
|
||||
. \" RS - prepare for double indenting
|
||||
.de RS
|
||||
.nr IS \\n(IP
|
||||
.RT
|
||||
.nr IP \\n(IS
|
||||
.nr IU \\n(IR
|
||||
.nr IR +1
|
||||
.if !\\n(I\\n(IR .nr I\\n(IR \\n(I\\n(IU+\\n(PIu
|
||||
.in \\n(I\\n(IRu
|
||||
.nr TY \\n(TZ-\\n(.i
|
||||
.ta \\n(TYuR
|
||||
..
|
||||
. \" RE - retreat to the left
|
||||
.de RE
|
||||
.nr IS \\n(IP
|
||||
.RT
|
||||
.nr IP \\n(IS
|
||||
.if \\n(IR>0 .nr IR -1
|
||||
.in \\n(I\\n(IRu
|
||||
..
|
||||
. \" B - bold font
|
||||
.de B
|
||||
.nr PQ \\n(.f
|
||||
.ft 3
|
||||
.if \\n(.$ \&\\$1\\f\\n(PQ\\$2
|
||||
..
|
||||
. \" BI - bold italic
|
||||
.de BI
|
||||
.nr PQ \\n(.f
|
||||
.ft 4
|
||||
.if \\n(.$ \&\\$1\\f\\n(PQ\\$2
|
||||
..
|
||||
. \" R - Roman font
|
||||
.de R
|
||||
.nr PQ \\n(.f
|
||||
.ft 1
|
||||
.if \\n(.$ \&\\$1\f\\n(PQ\\$2
|
||||
..
|
||||
. \" I - italic font
|
||||
.de I
|
||||
.nr PQ \\n(.f
|
||||
.ft 2
|
||||
.if \\n(.$ \&\\$1\^\f\\n(PQ\\$2
|
||||
..
|
||||
. \" CW - constant width font from -ms
|
||||
.de CW
|
||||
.nr PQ \\n(.f
|
||||
.if \\n(.$=0 .ft CW
|
||||
.if \\n(.$>0 \%\&\\$3\f(CW\\$1\\f\\n(PQ\\$2
|
||||
..
|
||||
.de IT \" ditto to italicize argument
|
||||
.nr Sf \\n(.f
|
||||
\%\&\\$3\f2\\$1\f\\n(Sf\&\\$2
|
||||
..
|
||||
. \" TA - tabs set in ens or chars
|
||||
.de TA
|
||||
.ta \\$1n \\$2n \\$3n \\$4n \\$5n \\$6n \\$7n \\$8n \\$9n
|
||||
..
|
||||
. \" SM - make smaller size
|
||||
.de SM
|
||||
.ie \\n(.$ \&\\$3\s-2\\$1\s0\\$2
|
||||
.el .ps -2
|
||||
..
|
||||
. \" LG - make larger size
|
||||
.de LG
|
||||
.ie \\n(.$ \&\\$3\s+2\\$1\s0\\$2
|
||||
.el .ps +2
|
||||
..
|
||||
. \" NL - return to normal size
|
||||
.de NL
|
||||
.ps \\n(PS
|
||||
..
|
||||
. \" FS - begin footnote
|
||||
.de FS
|
||||
.if \\n(IF>0 .tm .FS within .FS/.FE, line \\n(.c, file \\n(.F
|
||||
.if \\n(KF>0 .tm .FS won't work inside .KF, line \\n(.c, file \\n(.F
|
||||
.if \\n(KS>0 .tm .FS won't work inside .KS, line \\n(.c, file \\n(.F
|
||||
.nr IF 1
|
||||
.ev 1
|
||||
.ps \\n(PS-2
|
||||
.ie \\n(VS>=41 .vs \\n(VSu-2p
|
||||
.el .vs \\n(VSp-2p
|
||||
.ll \\n(LLu
|
||||
.br
|
||||
.nr X \\n(FOu
|
||||
.X "BF \\nX FS
|
||||
.SP .3v
|
||||
....FA \" deleted by authority of cvw, 10/17/88
|
||||
..
|
||||
. \" FE - end footnote
|
||||
.de FE
|
||||
.if !\\n(IF .tm .FE without .FS, line \\n(.c, file \\n(.F
|
||||
.br
|
||||
.X "END BF FE
|
||||
.bp
|
||||
.ev
|
||||
.nr IF 0
|
||||
..
|
||||
. \" FA - the line for a footnote
|
||||
.de FA
|
||||
\l'1i'
|
||||
.br
|
||||
..
|
||||
. \" Tm - message to be passed on
|
||||
.de Tm
|
||||
.ev 2
|
||||
.if \\n(.$=1 .X "TM \\$1
|
||||
.if \\n(.$=2 .X "TM \\$1 \\$2
|
||||
.if \\n(.$=3 .X "TM \\$1 \\$2 \\$3
|
||||
.if \\n(.$=4 .X "TM \\$1 \\$2 \\$3 \\$4
|
||||
.if \\n(.$=5 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5
|
||||
.if \\n(.$=6 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5 \\$6
|
||||
.if \\n(.$=7 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7
|
||||
.if \\n(.$=8 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
|
||||
.if \\n(.$=9 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
|
||||
.br
|
||||
.ev
|
||||
..
|
||||
.de MH
|
||||
AT&T Bell Laboratories
|
||||
Murray Hill, New Jersey 07974
|
||||
..
|
||||
.de HO
|
||||
AT&T Bell Laboratories
|
||||
Holmdel, New Jersey 07733
|
||||
..
|
||||
.de WH
|
||||
AT&T Bell Laboratories
|
||||
Whippany, New Jersey 07981
|
||||
..
|
||||
.de IH
|
||||
AT&T Bell Laboratories
|
||||
Naperville, Illinois 60540
|
||||
..
|
||||
. \" UL - underline argument, don't italicize
|
||||
.de UL
|
||||
\\$1\l'|0\(ul'\\$2
|
||||
..
|
||||
. \" UX - print $2 UNIX $1
|
||||
.de UX
|
||||
.ie \\n(UX \\$2\s-1UNIX\s0\\$1
|
||||
.el \{\
|
||||
\\$2\s-1UNIX\\s0\\$1\(rg
|
||||
.nr UX 1\}
|
||||
..
|
||||
. \" QS - start quote
|
||||
.de QS
|
||||
.br
|
||||
.LP
|
||||
.in +\\n(QIu
|
||||
.ll -\\n(QIu
|
||||
..
|
||||
. \" QE - end quote
|
||||
.de QE
|
||||
.br
|
||||
.ll +\\n(QIu
|
||||
.in -\\n(QIu
|
||||
.LP
|
||||
..
|
||||
. \" B1 - begin boxed stuff
|
||||
.de B1
|
||||
.br
|
||||
.di BB
|
||||
.nr BC 0
|
||||
.if "\\$1"C" .nr BC 1
|
||||
.nr BE 1
|
||||
..
|
||||
. \" B2 - end boxed stuff
|
||||
.de B2
|
||||
.br
|
||||
.nr BI 1n
|
||||
.if \\n(.$>0 .nr BI \\$1n
|
||||
.di
|
||||
.nr BE 0
|
||||
.nr BW \\n(dl
|
||||
.nr BH \\n(dn
|
||||
.ne \\n(BHu+\\n(.Vu
|
||||
.nr BQ \\n(.j
|
||||
.nf
|
||||
.ti 0
|
||||
.if \\n(BC>0 .in +(\\n(.lu-\\n(BWu)/2u
|
||||
.in +\\n(BIu
|
||||
.ls 1
|
||||
.BB
|
||||
.ls
|
||||
.in -\\n(BIu
|
||||
.nr BW +2*\\n(BI
|
||||
.sp -1
|
||||
\l'\\n(BWu\(ul'\L'-\\n(BHu'\l'|0\(ul'\h'|0'\L'\\n(BHu'
|
||||
.if \\n(BC>0 .in -(\\n(.lu-\\n(BWu)/2u
|
||||
.if \\n(BQ .fi
|
||||
.br
|
||||
..
|
||||
. \" BX - boxed stuff
|
||||
.de BX
|
||||
\(br\|\\$1\|\(br\l'|0\(rn'\l'|0\(ul'
|
||||
..
|
||||
.
|
||||
. \" macros for programs, etc.
|
||||
.
|
||||
.ig
|
||||
programs are displayed between .P1/.P2 pairs
|
||||
default is to indent by 1/2 inch, nofill, dP smaller
|
||||
.P1 x causes an indent of x instead.
|
||||
|
||||
.P3 can be used to specify optional page-break points
|
||||
inside .P1/.P2
|
||||
..
|
||||
.
|
||||
. \" P1 - start of program
|
||||
.de P1
|
||||
.nr $1 \\n(P1
|
||||
.if \\n(.$ .nr $1 \\$1n
|
||||
.br
|
||||
.X "SP \\n(DV P1"
|
||||
.X "US P1"
|
||||
.in \\n($1u
|
||||
.nf
|
||||
.nr v \\n(.v
|
||||
.ps -\\n(dP
|
||||
.vs -\\n(dVu
|
||||
.ft CW
|
||||
.nr t \\n(dT*\\w'x'u
|
||||
.ta 1u*\\ntu 2u*\\ntu 3u*\\ntu 4u*\\ntu 5u*\\ntu 6u*\\ntu 7u*\\ntu 8u*\\ntu 9u*\\ntu 10u*\\ntu 11u*\\ntu 12u*\\ntu 13u*\\ntu 14u*\\ntu
|
||||
..
|
||||
. \" P2 - end of program
|
||||
.de P2
|
||||
.br
|
||||
.ps \\n(PS
|
||||
.vs \\nvu
|
||||
.ft 1
|
||||
.in
|
||||
.X "END US P1
|
||||
.X "SP \\n(DV P2"
|
||||
.fi
|
||||
..
|
||||
. \" P3 - provides optional unpadded break in P1/P2
|
||||
.de P3
|
||||
.nr x \\n(DV
|
||||
.nr DV 0
|
||||
.P2
|
||||
.P1 \\n($1u
|
||||
.nr DV \\nx
|
||||
..
|
||||
.de [
|
||||
[
|
||||
..
|
||||
.de ]
|
||||
]
|
||||
..
|
||||
.IZ
|
||||
.rm IZ
|
||||
.so /sys/lib/tmac/tmac.srefs
|
||||
|
|
@ -11,17 +11,19 @@ obj *arcgen(int type) /* handles circular and (eventually) elliptical arcs */
|
|||
static double prevw = HT10;
|
||||
static double prevh = HT5;
|
||||
static double prevrad = HT2;
|
||||
static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 };
|
||||
static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 };
|
||||
static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 };
|
||||
static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 };
|
||||
static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR };
|
||||
static int dtox[2][4] ={ { 1, -1, -1, 1}, {1, 1, -1, -1} };
|
||||
static int dtoy[2][4] ={ {1, 1, -1, -1}, {-1, 1, 1, -1} };
|
||||
static int dctrx[2][4] ={ {0, -1, 0, 1}, {0, 1, 0, -1} };
|
||||
static int dctry[2][4] ={ {1, 0, -1, 0}, {-1, 0, 1, 0} };
|
||||
static int nexthv[2][4] ={ {U_DIR, L_DIR, D_DIR, R_DIR}, {D_DIR, R_DIR, U_DIR, L_DIR} };
|
||||
double dx2, dy2, ht, phi, r, d;
|
||||
int i, head, to, at, cw, invis, ddtype, battr;
|
||||
obj *p, *ppos;
|
||||
double fromx, fromy, tox, toy, fillval = 0;
|
||||
Attr *ap;
|
||||
|
||||
tox=toy=0.0; /* Botch? (gcc) */
|
||||
|
||||
prevrad = getfval("arcrad");
|
||||
prevh = getfval("arrowht");
|
||||
prevw = getfval("arrowwid");
|
||||
|
|
@ -210,6 +212,7 @@ void arc_extreme(double x0, double y0, double x1, double y1, double xc, double y
|
|||
extreme(xmax, ymax);
|
||||
}
|
||||
|
||||
int
|
||||
quadrant(double x, double y)
|
||||
{
|
||||
if ( x>=0.0 && y> 0.0) return(1);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ obj *circgen(int type)
|
|||
obj *p, *ppos;
|
||||
Attr *ap;
|
||||
|
||||
r = r2 = 0.0; /* Botch? (gcc) */
|
||||
|
||||
battr = at = 0;
|
||||
with = xwith = ywith = fillval = ddval = 0;
|
||||
t = (type == CIRCLE) ? 0 : 1;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ void pushsrc(int type, char *ptr) /* new input source */
|
|||
srcp->type = type;
|
||||
srcp->sp = ptr;
|
||||
if (dbg > 1) {
|
||||
printf("\n%3d ", srcp - src);
|
||||
printf("\n%3d ", (int) (srcp - src));
|
||||
switch (srcp->type) {
|
||||
case File:
|
||||
printf("push file %s\n", ((Infile *)ptr)->fname);
|
||||
|
|
@ -57,7 +57,7 @@ void popsrc(void) /* restore an old one */
|
|||
if (srcp <= src)
|
||||
ERROR "too many inputs popped" FATAL;
|
||||
if (dbg > 1) {
|
||||
printf("%3d ", srcp - src);
|
||||
printf("%3d ", (int) (srcp - src));
|
||||
switch (srcp->type) {
|
||||
case File:
|
||||
printf("pop file\n");
|
||||
|
|
@ -142,6 +142,7 @@ char *delimstr(char *s) /* get body of X ... X */
|
|||
return tostring(buf);
|
||||
}
|
||||
|
||||
int
|
||||
baldelim(int c, char *s) /* replace c by balancing entry in s */
|
||||
{
|
||||
for ( ; *s; s += 2)
|
||||
|
|
@ -187,11 +188,12 @@ void dodef(struct symtab *stp) /* collect args and switch input to defn */
|
|||
ap->argstk[i] = "";
|
||||
if (dbg)
|
||||
for (i = 0; i < argcnt; i++)
|
||||
printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
|
||||
printf("arg %d.%d = <%s>\n", (int) (ap-args), i+1, ap->argstk[i]);
|
||||
argfp = ap;
|
||||
pushsrc(Macro, stp->s_val.p);
|
||||
}
|
||||
|
||||
int
|
||||
getarg(char *p) /* pick up single argument, store in p, return length */
|
||||
{
|
||||
int n, c, npar;
|
||||
|
|
@ -232,6 +234,7 @@ extern int thru;
|
|||
extern struct symtab *thrudef;
|
||||
extern char *untilstr;
|
||||
|
||||
int
|
||||
input(void)
|
||||
{
|
||||
register int c;
|
||||
|
|
@ -248,10 +251,13 @@ input(void)
|
|||
return *ep++ = c;
|
||||
}
|
||||
|
||||
int
|
||||
nextchar(void)
|
||||
{
|
||||
register int c;
|
||||
|
||||
c = 0; /* Botch: gcc */
|
||||
|
||||
loop:
|
||||
switch (srcp->type) {
|
||||
case Free: /* free string */
|
||||
|
|
@ -289,9 +295,9 @@ nextchar(void)
|
|||
ERROR "argfp underflow" FATAL;
|
||||
popsrc();
|
||||
goto loop;
|
||||
} else if (c == '$' && isdigit(*srcp->sp)) {
|
||||
} else if (c == '$' && isdigit((unsigned char) *srcp->sp)) {
|
||||
int n = 0;
|
||||
while (isdigit(*srcp->sp))
|
||||
while (isdigit((unsigned char) *srcp->sp))
|
||||
n = 10 * n + *srcp->sp++ - '0';
|
||||
if (n > 0 && n <= MAXARGS)
|
||||
pushsrc(String, argfp->argstk[n-1]);
|
||||
|
|
@ -380,7 +386,7 @@ void do_thru(void) /* read one line, make into a macro expansion */
|
|||
ap->argstk[i] = "";
|
||||
if (dbg)
|
||||
for (i = 0; i < argcnt; i++)
|
||||
printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
|
||||
printf("arg %d.%d = <%s>\n", (int) (ap-args), i+1, ap->argstk[i]);
|
||||
if (strcmp(ap->argstk[0], ".PE") == 0) {
|
||||
thru = 0;
|
||||
thrudef = 0;
|
||||
|
|
@ -400,6 +406,7 @@ void do_thru(void) /* read one line, make into a macro expansion */
|
|||
pushsrc(Macro, thrudef->s_val.p);
|
||||
}
|
||||
|
||||
int
|
||||
unput(int c)
|
||||
{
|
||||
if (++pb >= pbuf + sizeof pbuf)
|
||||
|
|
@ -580,7 +587,7 @@ void shell_init(void) /* set up to interpret a shell command */
|
|||
|
||||
void shell_text(char *s) /* add string to command being collected */
|
||||
{
|
||||
while (*shellp++ = *s++)
|
||||
while ((*shellp++ = *s++))
|
||||
;
|
||||
shellp--;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ void getdata(void), setdefaults(void);
|
|||
void setfval(char *, double);
|
||||
int getpid(void);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char buf[20];
|
||||
|
|
@ -120,27 +121,27 @@ static struct {
|
|||
double val;
|
||||
short scalable; /* 1 => adjust when "scale" changes */
|
||||
} defaults[] ={
|
||||
"scale", SCALE, 1,
|
||||
"lineht", HT, 1,
|
||||
"linewid", HT, 1,
|
||||
"moveht", HT, 1,
|
||||
"movewid", HT, 1,
|
||||
"dashwid", HT10, 1,
|
||||
"boxht", HT, 1,
|
||||
"boxwid", WID, 1,
|
||||
"circlerad", HT2, 1,
|
||||
"arcrad", HT2, 1,
|
||||
"ellipseht", HT, 1,
|
||||
"ellipsewid", WID, 1,
|
||||
"arrowht", HT5, 1,
|
||||
"arrowwid", HT10, 1,
|
||||
"arrowhead", 2, 0, /* arrowhead style */
|
||||
"textht", 0.0, 1, /* 6 lines/inch is also a useful value */
|
||||
"textwid", 0.0, 1,
|
||||
"maxpsht", MAXHT, 0,
|
||||
"maxpswid", MAXWID, 0,
|
||||
"fillval", 0.7, 0, /* gray value for filling boxes */
|
||||
NULL, 0, 0
|
||||
{ "scale", SCALE, 1, },
|
||||
{ "lineht", HT, 1, },
|
||||
{ "linewid", HT, 1, },
|
||||
{ "moveht", HT, 1, },
|
||||
{ "movewid", HT, 1, },
|
||||
{ "dashwid", HT10, 1, },
|
||||
{ "boxht", HT, 1, },
|
||||
{ "boxwid", WID, 1, },
|
||||
{ "circlerad", HT2, 1, },
|
||||
{ "arcrad", HT2, 1, },
|
||||
{ "ellipseht", HT, 1, },
|
||||
{ "ellipsewid", WID, 1, },
|
||||
{ "arrowht", HT5, 1, },
|
||||
{ "arrowwid", HT10, 1, },
|
||||
{ "arrowhead", 2, 0, }, /* arrowhead style */
|
||||
{ "textht", 0.0, 1, }, /* 6 lines/inch is also a useful value */
|
||||
{ "textwid", 0.0, 1, },
|
||||
{ "maxpsht", MAXHT, 0, },
|
||||
{ "maxpswid", MAXWID, 0, },
|
||||
{ "fillval", 0.7, 0, }, /* gray value for filling boxes */
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
|
||||
void setdefaults(void) /* set default sizes for variables like boxht */
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ int whatpos(obj *p, int corner, double *px, double *py);
|
|||
void makeattr(int type, int sub, YYSTYPE val);
|
||||
YYSTYPE getblk(obj *, char *);
|
||||
|
||||
int
|
||||
setdir(int n) /* set direction (hvmode) from LEFT, RIGHT, etc. */
|
||||
{
|
||||
switch (n) {
|
||||
|
|
@ -20,6 +21,7 @@ setdir(int n) /* set direction (hvmode) from LEFT, RIGHT, etc. */
|
|||
return(hvmode);
|
||||
}
|
||||
|
||||
int
|
||||
curdir(void) /* convert current dir (hvmode) to RIGHT, LEFT, etc. */
|
||||
{
|
||||
switch (hvmode) {
|
||||
|
|
@ -32,7 +34,8 @@ curdir(void) /* convert current dir (hvmode) to RIGHT, LEFT, etc. */
|
|||
return 0;
|
||||
}
|
||||
|
||||
double getcomp(obj *p, int t) /* return component of a position */
|
||||
double
|
||||
getcomp(obj *p, int t) /* return component of a position */
|
||||
{
|
||||
switch (t) {
|
||||
case DOTX:
|
||||
|
|
@ -207,7 +210,9 @@ int whatpos(obj *p, int corner, double *px, double *py) /* what is the position
|
|||
{
|
||||
double x, y, x1, y1;
|
||||
|
||||
dprintf("whatpos %o %d %d\n", p, p->o_type, corner);
|
||||
x1 = y1 = 0.0; /* Botch? (gcc) */
|
||||
|
||||
dprintf("whatpos %p %d %d\n", p, p->o_type, corner);
|
||||
x = p->o_x;
|
||||
y = p->o_y;
|
||||
if (p->o_type != PLACE && p->o_type != MOVE) {
|
||||
|
|
@ -320,7 +325,7 @@ obj *getlast(int n, int t) /* find n-th previous occurrence of type t */
|
|||
dprintf("got a last of x,y= %g,%g\n", p->o_x, p->o_y);
|
||||
return(p);
|
||||
}
|
||||
ERROR "there is no %dth last", n FATAL;
|
||||
ERROR "there is no %dth last", n WARNING;
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
|
@ -343,7 +348,7 @@ obj *getfirst(int n, int t) /* find n-th occurrence of type t */
|
|||
dprintf("got a first of x,y= %g,%g\n", p->o_x, p->o_y);
|
||||
return(p);
|
||||
}
|
||||
ERROR "there is no %dth ", n FATAL;
|
||||
ERROR "there is no %dth ", n WARNING;
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ WS [ \t]
|
|||
<A>ccw { yylval.i = CCW; return(ATTR); }
|
||||
<A>invis(ible)? { yylval.i = INVIS; return(ATTR); }
|
||||
<A>noedge { yylval.i = INVIS; return ATTR; }
|
||||
<A>fill return(yylval.i = FILL);
|
||||
<A>fill { yylval.i = FILL; return ATTR; }
|
||||
<A>solid ;
|
||||
<A>dot(ted)? return(yylval.i = DOT);
|
||||
<A>dash(ed)? return(yylval.i = DASH);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ void print(void)
|
|||
int fill, vis, invis;
|
||||
double x0, y0, x1, y1, ox, oy, dx, dy, ndx, ndy;
|
||||
|
||||
x1 = y1 = 0.0; /* Botch? (gcc) */
|
||||
|
||||
for (i = 0; i < nobj; i++) {
|
||||
p = objlist[i];
|
||||
ox = p->o_x;
|
||||
|
|
@ -180,6 +182,8 @@ void dotline(double x0, double y0, double x1, double y1, int ddtype, double ddva
|
|||
int i, numdots;
|
||||
double a, b, dx, dy;
|
||||
|
||||
b = 0.0; /* Botch? (gcc) */
|
||||
|
||||
if (ddval == 0)
|
||||
ddval = prevval;
|
||||
prevval = ddval;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ YYSTYPE getvar(char *s) /* return value of variable s (usually pointer) */
|
|||
|
||||
p = lookup(s);
|
||||
if (p == NULL) {
|
||||
if (islower(s[0]))
|
||||
if (islower((int) s[0]))
|
||||
ERROR "no such variable as %s", s WARNING;
|
||||
else
|
||||
ERROR "no such place as %s", s WARNING;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue