aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-05-15 23:24:00 +0000
committerrsc <devnull@localhost>2004-05-15 23:24:00 +0000
commit5cedca1b69d020c32466f70843a11767773d7e3b (patch)
treea15a3d84e92aa262543b0010763a5e6920c9ba24 /src/cmd
parent76e6aca867e3e48ea04fbcf7284c45369a69829e (diff)
downloadplan9port-5cedca1b69d020c32466f70843a11767773d7e3b.tar.gz
plan9port-5cedca1b69d020c32466f70843a11767773d7e3b.tar.bz2
plan9port-5cedca1b69d020c32466f70843a11767773d7e3b.zip
Let's try this. It's BUGGERED.
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/eqn/diacrit.c75
-rw-r--r--src/cmd/eqn/e.h166
-rw-r--r--src/cmd/eqn/eqn.c741
-rw-r--r--src/cmd/eqn/eqn.y140
-rw-r--r--src/cmd/eqn/eqnbox.c24
-rw-r--r--src/cmd/eqn/font.c70
-rw-r--r--src/cmd/eqn/fromto.c52
-rw-r--r--src/cmd/eqn/funny.c30
-rw-r--r--src/cmd/eqn/glob.c35
-rw-r--r--src/cmd/eqn/input.c289
-rw-r--r--src/cmd/eqn/integral.c30
-rw-r--r--src/cmd/eqn/lex.c265
-rw-r--r--src/cmd/eqn/lookup.c219
-rw-r--r--src/cmd/eqn/main.c333
-rw-r--r--src/cmd/eqn/mark.c19
-rw-r--r--src/cmd/eqn/matrix.c78
-rw-r--r--src/cmd/eqn/mkfile42
-rw-r--r--src/cmd/eqn/move.c19
-rwxr-xr-xsrc/cmd/eqn/o.eqnbin0 -> 224764 bytes
-rw-r--r--src/cmd/eqn/over.c35
-rw-r--r--src/cmd/eqn/paren.c135
-rw-r--r--src/cmd/eqn/pile.c76
-rw-r--r--src/cmd/eqn/prevy.tab.h57
-rw-r--r--src/cmd/eqn/shift.c116
-rw-r--r--src/cmd/eqn/size.c70
-rw-r--r--src/cmd/eqn/sqrt.c35
-rw-r--r--src/cmd/eqn/text.c318
-rw-r--r--src/cmd/eqn/tuning.c153
-rw-r--r--src/cmd/eqn/y.tab.h57
-rw-r--r--src/cmd/grap/coord.c71
-rw-r--r--src/cmd/grap/find1
-rw-r--r--src/cmd/grap/for.c89
-rw-r--r--src/cmd/grap/frame.c71
-rw-r--r--src/cmd/grap/grap.h236
-rw-r--r--src/cmd/grap/grap.y396
-rw-r--r--src/cmd/grap/grapl.lx213
-rw-r--r--src/cmd/grap/input.c580
-rw-r--r--src/cmd/grap/label.c123
-rw-r--r--src/cmd/grap/main.c164
-rw-r--r--src/cmd/grap/misc.c263
-rw-r--r--src/cmd/grap/mkfile37
-rw-r--r--src/cmd/grap/plot.c132
-rw-r--r--src/cmd/grap/print.c236
-rw-r--r--src/cmd/grap/ticks.c492
-rw-r--r--src/cmd/mkfile2
-rw-r--r--src/cmd/pic/arcgen.c221
-rw-r--r--src/cmd/pic/blockgen.c226
-rw-r--r--src/cmd/pic/boxgen.c115
-rw-r--r--src/cmd/pic/circgen.c126
-rw-r--r--src/cmd/pic/for.c95
-rw-r--r--src/cmd/pic/input.c593
-rw-r--r--src/cmd/pic/linegen.c240
-rw-r--r--src/cmd/pic/main.c282
-rw-r--r--src/cmd/pic/makefile39
-rw-r--r--src/cmd/pic/misc.c436
-rw-r--r--src/cmd/pic/mkfile38
-rw-r--r--src/cmd/pic/movegen.c86
-rw-r--r--src/cmd/pic/pic.h219
-rw-r--r--src/cmd/pic/picl.lx273
-rw-r--r--src/cmd/pic/picy.y328
-rw-r--r--src/cmd/pic/pltroff.c357
-rw-r--r--src/cmd/pic/prevy.tab.h97
-rw-r--r--src/cmd/pic/print.c238
-rw-r--r--src/cmd/pic/symtab.c104
-rw-r--r--src/cmd/pic/textgen.c115
-rw-r--r--src/cmd/tbl/mkfile32
-rw-r--r--src/cmd/tbl/t.h192
-rw-r--r--src/cmd/tbl/t0.c49
-rw-r--r--src/cmd/tbl/t1.c95
-rw-r--r--src/cmd/tbl/t2.c25
-rw-r--r--src/cmd/tbl/t3.c104
-rw-r--r--src/cmd/tbl/t4.c405
-rw-r--r--src/cmd/tbl/t5.c198
-rw-r--r--src/cmd/tbl/t6.c223
-rw-r--r--src/cmd/tbl/t7.c150
-rw-r--r--src/cmd/tbl/t8.c367
-rw-r--r--src/cmd/tbl/t9.c76
-rw-r--r--src/cmd/tbl/tb.c101
-rw-r--r--src/cmd/tbl/tc.c65
-rw-r--r--src/cmd/tbl/te.c75
-rw-r--r--src/cmd/tbl/tf.c74
-rw-r--r--src/cmd/tbl/tg.c81
-rw-r--r--src/cmd/tbl/ti.c75
-rw-r--r--src/cmd/tbl/tm.c65
-rw-r--r--src/cmd/tbl/tr.c28
-rw-r--r--src/cmd/tbl/ts.c71
-rw-r--r--src/cmd/tbl/tt.c127
-rw-r--r--src/cmd/tbl/tu.c257
-rw-r--r--src/cmd/tbl/tv.c183
-rw-r--r--src/cmd/troff/FIXES821
-rw-r--r--src/cmd/troff/README31
-rw-r--r--src/cmd/troff/cvt45
-rw-r--r--src/cmd/troff/dwbinit.c313
-rw-r--r--src/cmd/troff/dwbinit.h19
-rw-r--r--src/cmd/troff/ext.h184
-rw-r--r--src/cmd/troff/find1
-rw-r--r--src/cmd/troff/fns.h384
-rw-r--r--src/cmd/troff/hytab.c126
-rw-r--r--src/cmd/troff/mbwc.c165
-rw-r--r--src/cmd/troff/mk.log136
-rw-r--r--src/cmd/troff/mkfile58
-rw-r--r--src/cmd/troff/n1.c1136
-rw-r--r--src/cmd/troff/n10.c549
-rw-r--r--src/cmd/troff/n2.c325
-rw-r--r--src/cmd/troff/n3.c954
-rw-r--r--src/cmd/troff/n4.c828
-rw-r--r--src/cmd/troff/n5.c1149
-rw-r--r--src/cmd/troff/n6.c362
-rw-r--r--src/cmd/troff/n7.c834
-rw-r--r--src/cmd/troff/n8.c540
-rw-r--r--src/cmd/troff/n9.c488
-rw-r--r--src/cmd/troff/ni.c390
-rw-r--r--src/cmd/troff/suftab.c612
-rw-r--r--src/cmd/troff/t10.c512
-rw-r--r--src/cmd/troff/t11.c255
-rw-r--r--src/cmd/troff/t6.c881
-rw-r--r--src/cmd/troff/tdef.h670
-rw-r--r--src/cmd/troff/unansi49
118 files changed, 26947 insertions, 1 deletions
diff --git a/src/cmd/eqn/diacrit.c b/src/cmd/eqn/diacrit.c
new file mode 100644
index 00000000..10dc8c75
--- /dev/null
+++ b/src/cmd/eqn/diacrit.c
@@ -0,0 +1,75 @@
+#include "e.h"
+#include "y.tab.h"
+
+extern double Dvshift, Dhshift, Dh2shift, Dheight, Barv, Barh, Ubarv, Ubarh;
+
+void diacrit(int p1, int type)
+{
+ int c, t;
+
+ c = salloc();
+ t = salloc();
+ nrwid(p1, ps, p1);
+ printf(".nr 10 %gm\n", max(REL(eht[p1]-ebase[p1]-1,ps), 0)); /* vert shift if high */
+ if (type == HIGHBAR)
+ printf(".nr 10 \\n(10+%gm\n", Dvshift);
+ else if (type == LOWBAR)
+ printf(".nr 10 0\n");
+ else
+ printf(".if \\n(ct>1 .nr 10 \\n(10+%gm\n", Dvshift);
+ printf(".nr %d %gm\n", t, Dhshift); /* horiz shift if high */
+ printf(".if \\n(ct>1 .nr %d %gm\n", t, Dh2shift); /* was .1 and .15 */
+ switch (type) {
+ case VEC:
+ printf(".ds %d %s\n", c, lookup(deftbl, "vec_def")->cval);
+ break;
+ case DYAD:
+ printf(".ds %d %s\n", c, lookup(deftbl, "dyad_def")->cval);
+ break;
+ case HAT:
+ printf(".ds %d %s\n", c, lookup(deftbl, "hat_def")->cval);
+ break;
+ case TILDE:
+ printf(".ds %d %s\n", c, lookup(deftbl, "tilde_def")->cval);
+ break;
+ case DOT:
+ printf(".ds %d %s\n", c, lookup(deftbl, "dot_def")->cval);
+ break;
+ case DOTDOT:
+ printf(".ds %d %s\n", c, lookup(deftbl, "dotdot_def")->cval);
+ break;
+ case BAR:
+ case LOWBAR:
+ case HIGHBAR:
+ printf(".ds %d \\v'%gm'\\h'%gm'\\l'\\n(%du-%gm'\\h'%gm'\\v'%gm'\n",
+ c, -Barv, Barh, p1, 2*Barh, Barh, Barv);
+ break;
+ case UNDER:
+ printf(".ds %d \\v'%gm'\\l'\\n(%du-%gm\\(ul'\\h'%gm'\\v'%gm'\n",
+ c, -Ubarv, p1, Ubarh, Ubarh, Ubarv);
+ /* printf(".ds %d \\v'-%gm'\\l'\\n(%du\\(ul'\\v'%gm'\n",
+ c, Ubarv, p1, Ubarv);
+ */
+ printf(".nr %d 0\n", t);
+ printf(".nr 10 0-.1m-%gm\n", REL(ebase[p1],ps));
+ printf(".if \\n(ct%%2=1 .nr 10 0\\n(10-.1m\n");
+ break;
+ case UTILDE:
+ printf(".ds %d %s\n", c, lookup(deftbl, "utilde_def")->cval);
+ printf(".nr %d 0\n", t);
+ printf(".nr 10 0-%gm\n", REL(ebase[p1],ps));
+ printf(".if \\n(ct%%2=1 .nr 10 0\\n(10-%gm\n", 0.1);
+ break;
+ }
+ nrwid(c, ps, c);
+ if (lfont[p1] != ITAL)
+ printf(".nr %d 0\n", t);
+ printf(".as %d \\h'-\\n(%du-\\n(%du/2u+\\n(%du'\\v'0-\\n(10u'\\*(%d",
+ p1, p1, c, t, c);
+ printf("\\v'\\n(10u'\\h'-\\n(%du+\\n(%du/2u-\\n(%du'\n", c, p1, t);
+ if (type != UNDER && type != UTILDE)
+ eht[p1] += EM(Dheight, ps); /* was .15 */
+ dprintf(".\tdiacrit: %c over S%d, lf=%c, rf=%c, h=%g, b=%g\n",
+ type, p1, lfont[p1], rfont[p1], eht[p1], ebase[p1]);
+ sfree(c); sfree(t);
+}
diff --git a/src/cmd/eqn/e.h b/src/cmd/eqn/e.h
new file mode 100644
index 00000000..70c47023
--- /dev/null
+++ b/src/cmd/eqn/e.h
@@ -0,0 +1,166 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+enum charclass {
+ OTHER, OLET, ILET, DIG, LPAR, RPAR, SLASH, PLUS, ILETF, ILETJ, VBAR,
+ NONE, LAST
+};
+extern int class[LAST][LAST];
+
+#define dprintf if (dbg) printf
+#define max(x,y) (((x) >= (y)) ? (x) : (y)) /* beware of side effects */
+#define min(x,y) (((x) <= (y)) ? (x) : (y))
+
+extern char errbuf[200];
+extern char *cmdname;
+#define ERROR sprintf(errbuf,
+#define FATAL ), error(1, errbuf)
+#define WARNING ), error(0, errbuf)
+#define SYNTAX ), yyerror(errbuf)
+
+#define ROM '1'
+#define ITAL '2'
+#define BLD '3'
+#define BDIT '4'
+
+#define DEFGAP -999 /* default gap in piles */
+
+extern int dbg;
+extern int ct;
+extern int lp[];
+extern int used[]; /* available registers */
+extern int ps; /* dflt init pt size */
+extern int deltaps; /* default change in ps */
+extern int dps_set; /* 1 => -p option used */
+extern int gsize; /* global size */
+extern int ft; /* default font */
+extern int display; /* 1 => inline, 0 => .EQ/.EN */
+extern int synerr; /* 1 if syntax error in this eqn */
+
+extern char *typesetter; /* typesetter name for -T... */
+extern int minsize; /* min size it can print */
+extern int ttype; /* actual type of typesetter: */
+
+#define DEVCAT 1
+#define DEV202 2
+#define DEVAPS 3
+#define DEVPOST 4
+
+extern double eht[];
+extern double ebase[];
+extern int lfont[];
+extern int rfont[];
+extern int lclass[];
+extern int rclass[];
+extern int yyval;
+extern int yylval;
+extern int eqnreg;
+extern double eqnht;
+extern int lefteq, righteq;
+extern int markline; /* 1 if this EQ/EN contains mark or lineup */
+
+#define TBLSIZE 100
+
+typedef struct s_tbl {
+ char *name; /* e.g., "max" or "sum" */
+ char *cval; /* e.g., "\\f1max\\fP" */
+ int ival; /* or SUM */
+ struct s_tbl *next;
+} tbl;
+
+extern char *spaceval; /* use in place of normal \x (for pic) */
+
+#define String 01
+#define Macro 02
+#define File 04
+#define Char 010
+#define Free 040
+
+typedef struct infile {
+ FILE *fin;
+ char *fname;
+ int lineno;
+} Infile;
+
+typedef struct { /* input source */
+ int type; /* Macro, String, File */
+ char *sp; /* if String or Macro */
+} Src;
+
+extern Src src[], *srcp; /* input source stack */
+
+#define MAXARGS 20
+typedef struct { /* argument stack */
+ char *argstk[MAXARGS]; /* pointers to args */
+ char *argval; /* points to space containing args */
+} Arg;
+
+typedef struct { /* font number and name */
+ int ft;
+ char name[10];
+} Font;
+
+extern Font ftstack[];
+extern Font *ftp;
+
+extern int szstack[];
+extern int nszstack;
+
+extern Infile infile[10];
+extern Infile *curfile;
+
+extern tbl *lookup(tbl **tblp, char *name);
+extern void install(tbl **tblp, char *name, char *cval, int ival);
+extern tbl *keytbl[], *deftbl[], *restbl[], *ftunetbl[];
+
+extern int salloc(void);
+extern void sfree(int);
+extern void nrwid(int, int, int);
+extern char *ABSPS(int);
+extern char *DPS(int, int);
+extern int EFFPS(int);
+extern double EM(double, int);
+extern double REL(double, int);
+extern char *pad(int);
+extern void getstr(char *, int);
+extern char *strsave(char *);
+
+extern int input(void);
+extern int unput(int);
+extern void pbstr(char *);
+extern void error(int, char *);
+extern void yyerror(char *);
+
+extern void diacrit(int, int);
+extern void eqnbox(int, int, int);
+extern void setfont(char *);
+extern void font(int, int);
+extern void globfont(void);
+extern void fatbox(int);
+extern void fromto(int, int, int);
+extern void funny(int);
+extern void integral(int, int, int);
+extern void setintegral(void);
+extern void pushsrc(int, char *);
+extern void popsrc(void);
+extern void putout(int);
+extern void text(int, char *);
+extern void subsup(int, int, int);
+extern void bshiftb(int, int, int);
+extern void shift2(int, int, int);
+extern void setsize(char *);
+extern void size(int, int);
+extern void globsize(void);
+#define sqrt esqrt
+extern void sqrt(int);
+extern void text(int, char *);
+extern void boverb(int, int);
+extern void lineup(int);
+extern void mark(int);
+extern void paren(int, int, int);
+extern void move(int, int, int);
+extern void pile(int);
+extern int startcol(int);
+extern void column(int, int);
+extern void matrix(int);
diff --git a/src/cmd/eqn/eqn.c b/src/cmd/eqn/eqn.c
new file mode 100644
index 00000000..527e5e08
--- /dev/null
+++ b/src/cmd/eqn/eqn.c
@@ -0,0 +1,741 @@
+#define CONTIG 57346
+#define QTEXT 57347
+#define SPACE 57348
+#define THIN 57349
+#define TAB 57350
+#define MATRIX 57351
+#define LCOL 57352
+#define CCOL 57353
+#define RCOL 57354
+#define COL 57355
+#define ABOVE 57356
+#define MARK 57357
+#define LINEUP 57358
+#define SUM 57359
+#define INT 57360
+#define PROD 57361
+#define UNION 57362
+#define INTER 57363
+#define DEFINE 57364
+#define TDEFINE 57365
+#define NDEFINE 57366
+#define DELIM 57367
+#define GSIZE 57368
+#define GFONT 57369
+#define INCLUDE 57370
+#define IFDEF 57371
+#define DOTEQ 57372
+#define DOTEN 57373
+#define FROM 57374
+#define TO 57375
+#define OVER 57376
+#define SQRT 57377
+#define SUP 57378
+#define SUB 57379
+#define SIZE 57380
+#define FONT 57381
+#define ROMAN 57382
+#define ITALIC 57383
+#define BOLD 57384
+#define FAT 57385
+#define UP 57386
+#define DOWN 57387
+#define BACK 57388
+#define FWD 57389
+#define LEFT 57390
+#define RIGHT 57391
+#define DOT 57392
+#define DOTDOT 57393
+#define HAT 57394
+#define TILDE 57395
+#define BAR 57396
+#define LOWBAR 57397
+#define HIGHBAR 57398
+#define UNDER 57399
+#define VEC 57400
+#define DYAD 57401
+#define UTILDE 57402
+
+#line 17 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+#include "e.h"
+
+int yylex(void);
+extern int yyerrflag;
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 150
+#endif
+#ifndef YYSTYPE
+#define YYSTYPE int
+#endif
+YYSTYPE yylval;
+YYSTYPE yyval;
+#define YYEOFCODE 1
+#define YYERRCODE 2
+
+#line 140 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+
+short yyexca[] =
+{-1, 0,
+ 1, 3,
+ -2, 0,
+-1, 1,
+ 1, -1,
+ -2, 0,
+};
+#define YYNPROD 90
+#define YYPRIVATE 57344
+#define YYLAST 469
+short yyact[] =
+{
+ 4, 103, 119, 45, 27, 118, 104, 2, 102, 41,
+ 42, 43, 44, 65, 80, 81, 79, 66, 67, 68,
+ 69, 70, 50, 49, 74, 75, 76, 77, 105, 73,
+ 40, 80, 81, 80, 81, 114, 61, 64, 54, 62,
+ 57, 58, 59, 60, 55, 56, 63, 78, 91, 92,
+ 82, 26, 83, 85, 86, 87, 88, 90, 51, 52,
+ 48, 124, 50, 49, 117, 25, 45, 117, 72, 71,
+ 80, 81, 113, 24, 45, 23, 61, 64, 54, 62,
+ 57, 58, 59, 60, 55, 56, 63, 53, 89, 100,
+ 84, 22, 96, 95, 106, 107, 108, 109, 99, 110,
+ 111, 41, 42, 43, 44, 45, 98, 115, 21, 94,
+ 93, 18, 130, 123, 17, 116, 121, 46, 112, 125,
+ 127, 128, 1, 129, 126, 0, 0, 45, 8, 7,
+ 9, 10, 11, 28, 41, 42, 43, 44, 0, 16,
+ 47, 12, 34, 13, 14, 15, 61, 64, 54, 62,
+ 57, 58, 59, 60, 55, 56, 63, 0, 0, 20,
+ 0, 0, 29, 33, 30, 31, 32, 19, 37, 39,
+ 38, 36, 35, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6, 97, 8, 7, 9,
+ 10, 11, 28, 41, 42, 43, 44, 0, 16, 47,
+ 12, 34, 13, 14, 15, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 20, 0,
+ 0, 29, 33, 30, 31, 32, 19, 37, 39, 38,
+ 36, 35, 101, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 6, 8, 7, 9, 10, 11,
+ 28, 41, 42, 43, 44, 0, 16, 5, 12, 34,
+ 13, 14, 15, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 20, 0, 0, 29,
+ 33, 30, 31, 32, 19, 37, 39, 38, 36, 35,
+ 0, 0, 8, 7, 9, 10, 11, 28, 41, 42,
+ 43, 44, 6, 16, 47, 12, 34, 13, 14, 15,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 20, 0, 0, 29, 33, 30, 31,
+ 32, 19, 37, 39, 38, 36, 35, 0, 0, 8,
+ 7, 9, 10, 11, 28, 41, 42, 43, 44, 6,
+ 16, 5, 12, 34, 13, 14, 15, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 20, 0, 0, 29, 33, 30, 31, 32, 19, 37,
+ 39, 38, 36, 35, 8, 7, 9, 10, 11, 28,
+ 41, 42, 43, 44, 0, 16, 6, 12, 34, 13,
+ 14, 15, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 20, 0, 0, 29, 33,
+ 30, 31, 32, 19, 37, 39, 38, 36, 35, 51,
+ 122, 48, 0, 50, 49, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 120, 49, 0, 61, 64, 54,
+ 62, 57, 58, 59, 60, 55, 56, 63, 61, 64,
+ 54, 62, 57, 58, 59, 60, 55, 56, 63
+};
+short yypact[] =
+{
+ 241,-1000, 288,-1000, 26,-1000, 335,-1000,-1000,-1000,
+-1000,-1000,-1000,-1000,-1000,-1000, 380, 380, 380, 380,
+ 380, 32, 335, 380, 380, 380, 380,-1000,-1000, 66,
+-1000,-1000,-1000, 66,-1000, 29, 66, 66, 66, 66,
+ 27,-1000,-1000,-1000,-1000, 26,-1000, 380, 380,-1000,
+-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+-1000,-1000,-1000,-1000,-1000, 124, 26, 96, 96, 96,
+ -14,-1000,-1000, 183, 96, 96, 96, 96, -53,-1000,
+-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, 335,
+-1000, 26, -14, 380, 380, 380, 380,-1000, 380, 380,
+-1000, 10, 91, 53, 288, -56, 408, -14, 397, 26,
+ 408, -14,-1000,-1000, -1,-1000,-1000, 335, 335,-1000,
+ 380,-1000, 380,-1000,-1000,-1000, 288, 50, -14, 26,
+-1000
+};
+short yypgo[] =
+{
+ 0, 122, 6, 0, 117, 2, 116, 114, 111, 110,
+ 109, 108, 106, 98, 93, 92, 91, 89, 87, 75,
+ 73, 65, 51, 4, 47, 35, 16, 30, 1, 28
+};
+short yyr1[] =
+{
+ 0, 1, 1, 1, 2, 2, 2, 2, 4, 5,
+ 5, 6, 6, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 9,
+ 3, 10, 3, 12, 3, 13, 3, 3, 14, 3,
+ 15, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 24, 3, 11, 19, 20, 21, 22, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18, 16, 16,
+ 17, 17, 25, 25, 23, 29, 23, 27, 27, 27,
+ 27, 28, 28, 7, 8, 8, 8, 8, 26, 26
+};
+short yyr2[] =
+{
+ 0, 1, 1, 0, 1, 2, 2, 1, 2, 2,
+ 0, 2, 0, 3, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 3, 2, 2, 2, 2, 2, 0,
+ 5, 0, 4, 0, 5, 0, 4, 1, 0, 5,
+ 0, 4, 3, 2, 2, 2, 2, 2, 2, 1,
+ 0, 5, 1, 2, 2, 2, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
+ 2, 2, 1, 2, 4, 0, 6, 1, 1, 1,
+ 1, 1, 3, 2, 1, 1, 1, 2, 1, 1
+};
+short yychk[] =
+{
+-1000, -1, -2, 2, -3, 16, 61, 5, 4, 6,
+ 7, 8, 17, 19, 20, 21, 15, -7, -8, 43,
+ 35, -11, -16, -19, -20, -21, -22, -23, 9, 38,
+ 40, 41, 42, 39, 18, 48, 47, 44, 46, 45,
+ -27, 10, 11, 12, 13, -3, -4, 16, 34, 37,
+ 36, 32, 33, -18, 52, 58, 59, 54, 55, 56,
+ 57, 50, 53, 60, 51, -2, -3, -3, -3, -3,
+ -3, 37, 36, -2, -3, -3, -3, -3, -24, -26,
+ 4, 5, -26, -26, 61, -26, -26, -26, -26, 61,
+ -26, -3, -3, -9, -10, -14, -15, 62, -12, -13,
+ -17, 49, 61, -28, -2, -29, -3, -3, -3, -3,
+ -3, -3, -26, 62, -25, -23, 62, 14, 61, -5,
+ 36, -6, 33, -5, 62, -23, -2, -28, -3, -3,
+ 62
+};
+short yydef[] =
+{
+ -2, -2, 1, 2, 4, 7, 0, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 0, 0, 0, 0,
+ 0, 37, 0, 0, 0, 0, 0, 49, 50, 0,
+ 84, 85, 86, 0, 52, 0, 0, 0, 0, 0,
+ 0, 77, 78, 79, 80, 5, 6, 0, 0, 29,
+ 31, 38, 40, 44, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 0, 24, 25, 26, 27,
+ 28, 33, 35, 43, 45, 46, 47, 48, 0, 83,
+ 88, 89, 87, 68, 69, 53, 54, 55, 56, 0,
+ 75, 8, 23, 0, 0, 0, 0, 13, 0, 0,
+ 42, 0, 0, 0, 81, 0, 10, 32, 12, 41,
+ 10, 36, 70, 71, 0, 72, 74, 0, 0, 30,
+ 0, 39, 0, 34, 51, 73, 82, 0, 9, 11,
+ 76
+};
+short yytok1[] =
+{
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 61, 0, 62
+};
+short yytok2[] =
+{
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60
+};
+long yytok3[] =
+{
+ 0
+};
+#define YYFLAG -1000
+#define YYERROR goto yyerrlab
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define yyclearin yychar = -1
+#define yyerrok yyerrflag = 0
+
+#ifdef yydebug
+#include "y.debug"
+#else
+#define yydebug 0
+char* yytoknames[1]; /* for debugging */
+char* yystates[1]; /* for debugging */
+#endif
+
+/* parser for yacc output */
+
+int yynerrs = 0; /* number of errors */
+int yyerrflag = 0; /* error recovery flag */
+
+char*
+yytokname(int yyc)
+{
+ static char x[10];
+
+ if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
+ if(yytoknames[yyc-1])
+ return yytoknames[yyc-1];
+ sprintf(x, "<%d>", yyc);
+ return x;
+}
+
+char*
+yystatname(int yys)
+{
+ static char x[10];
+
+ if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
+ if(yystates[yys])
+ return yystates[yys];
+ sprintf(x, "<%d>\n", yys);
+ return x;
+}
+
+long
+yylex1(void)
+{
+ long yychar;
+ long *t3p;
+ int c;
+
+ yychar = yylex();
+ if(yychar <= 0) {
+ c = yytok1[0];
+ goto out;
+ }
+ if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
+ c = yytok1[yychar];
+ goto out;
+ }
+ if(yychar >= YYPRIVATE)
+ if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
+ c = yytok2[yychar-YYPRIVATE];
+ goto out;
+ }
+ for(t3p=yytok3;; t3p+=2) {
+ c = t3p[0];
+ if(c == yychar) {
+ c = t3p[1];
+ goto out;
+ }
+ if(c == 0)
+ break;
+ }
+ c = 0;
+
+out:
+ if(c == 0)
+ c = yytok2[1]; /* unknown char */
+ if(yydebug >= 3)
+ printf("lex %.4lX %s\n", yychar, yytokname(c));
+ return c;
+}
+
+int
+yyparse(void)
+{
+ struct
+ {
+ YYSTYPE yyv;
+ int yys;
+ } yys[YYMAXDEPTH], *yyp, *yypt;
+ short *yyxi;
+ int yyj, yym, yystate, yyn, yyg;
+ YYSTYPE save1, save2;
+ int save3, save4;
+ long yychar;
+
+ save1 = yylval;
+ save2 = yyval;
+ save3 = yynerrs;
+ save4 = yyerrflag;
+
+ yystate = 0;
+ yychar = -1;
+ yynerrs = 0;
+ yyerrflag = 0;
+ yyp = &yys[-1];
+ goto yystack;
+
+ret0:
+ yyn = 0;
+ goto ret;
+
+ret1:
+ yyn = 1;
+ goto ret;
+
+ret:
+ yylval = save1;
+ yyval = save2;
+ yynerrs = save3;
+ yyerrflag = save4;
+ return yyn;
+
+yystack:
+ /* put a state and value onto the stack */
+ if(yydebug >= 4)
+ printf("char %s in %s", yytokname(yychar), yystatname(yystate));
+
+ yyp++;
+ if(yyp >= &yys[YYMAXDEPTH]) {
+ yyerror("yacc stack overflow");
+ goto ret1;
+ }
+ yyp->yys = yystate;
+ yyp->yyv = yyval;
+
+yynewstate:
+ yyn = yypact[yystate];
+ if(yyn <= YYFLAG)
+ goto yydefault; /* simple state */
+ if(yychar < 0)
+ yychar = yylex1();
+ yyn += yychar;
+ if(yyn < 0 || yyn >= YYLAST)
+ goto yydefault;
+ yyn = yyact[yyn];
+ if(yychk[yyn] == yychar) { /* valid shift */
+ yychar = -1;
+ yyval = yylval;
+ yystate = yyn;
+ if(yyerrflag > 0)
+ yyerrflag--;
+ goto yystack;
+ }
+
+yydefault:
+ /* default state action */
+ yyn = yydef[yystate];
+ if(yyn == -2) {
+ if(yychar < 0)
+ yychar = yylex1();
+
+ /* look through exception table */
+ for(yyxi=yyexca;; yyxi+=2)
+ if(yyxi[0] == -1 && yyxi[1] == yystate)
+ break;
+ for(yyxi += 2;; yyxi += 2) {
+ yyn = yyxi[0];
+ if(yyn < 0 || yyn == yychar)
+ break;
+ }
+ yyn = yyxi[1];
+ if(yyn < 0)
+ goto ret0;
+ }
+ if(yyn == 0) {
+ /* error ... attempt to resume parsing */
+ switch(yyerrflag) {
+ case 0: /* brand new error */
+ yyerror("syntax error");
+ if(yydebug >= 1) {
+ printf("%s", yystatname(yystate));
+ printf("saw %s\n", yytokname(yychar));
+ }
+yyerrlab:
+ yynerrs++;
+
+ case 1:
+ case 2: /* incompletely recovered error ... try again */
+ yyerrflag = 3;
+
+ /* find a state where "error" is a legal shift action */
+ while(yyp >= yys) {
+ yyn = yypact[yyp->yys] + YYERRCODE;
+ if(yyn >= 0 && yyn < YYLAST) {
+ yystate = yyact[yyn]; /* simulate a shift of "error" */
+ if(yychk[yystate] == YYERRCODE)
+ goto yystack;
+ }
+
+ /* the current yyp has no shift onn "error", pop stack */
+ if(yydebug >= 2)
+ printf("error recovery pops state %d, uncovers %d\n",
+ yyp->yys, (yyp-1)->yys );
+ yyp--;
+ }
+ /* there is no state on the stack with an error shift ... abort */
+ goto ret1;
+
+ case 3: /* no shift yet; clobber input char */
+ if(yydebug >= YYEOFCODE)
+ printf("error recovery discards %s\n", yytokname(yychar));
+ if(yychar == YYEOFCODE)
+ goto ret1;
+ yychar = -1;
+ goto yynewstate; /* try again in the same state */
+ }
+ }
+
+ /* reduction by production yyn */
+ if(yydebug >= 2)
+ printf("reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+ yypt = yyp;
+ yyp -= yyr2[yyn];
+ yyval = (yyp+1)->yyv;
+ yym = yyn;
+
+ /* consult goto table to find next state */
+ yyn = yyr1[yyn];
+ yyg = yypgo[yyn];
+ yyj = yyg + yyp->yys + 1;
+
+ if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+ yystate = yyact[yyg];
+ switch(yym) {
+
+case 1:
+#line 24 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ putout(yypt[-0].yyv); } break;
+case 2:
+#line 25 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ ERROR "syntax error" WARNING; } break;
+case 3:
+#line 26 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ eqnreg = 0; } break;
+case 5:
+#line 30 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ eqnbox(yypt[-1].yyv, yypt[-0].yyv, 0); } break;
+case 6:
+#line 31 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ eqnbox(yypt[-1].yyv, yypt[-0].yyv, 1); } break;
+case 7:
+#line 32 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ lineup(0); } break;
+case 8:
+#line 35 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = yypt[-0].yyv; lineup(1); } break;
+case 9:
+#line 38 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = yypt[-0].yyv; } break;
+case 10:
+#line 39 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = 0; } break;
+case 11:
+#line 42 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = yypt[-0].yyv; } break;
+case 12:
+#line 43 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = 0; } break;
+case 13:
+#line 46 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = yypt[-1].yyv; } break;
+case 14:
+#line 47 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ text(QTEXT, (char *) yypt[-0].yyv); } break;
+case 15:
+#line 48 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ text(CONTIG, (char *) yypt[-0].yyv); } break;
+case 16:
+#line 49 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ text(SPACE, (char *) 0); } break;
+case 17:
+#line 50 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ text(THIN, (char *) 0); } break;
+case 18:
+#line 51 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ text(TAB, (char *) 0); } break;
+case 19:
+#line 52 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ funny(SUM); } break;
+case 20:
+#line 53 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ funny(PROD); } break;
+case 21:
+#line 54 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ funny(UNION); } break;
+case 22:
+#line 55 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ funny(INTER); } break;
+case 23:
+#line 56 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ boverb(yypt[-2].yyv, yypt[-0].yyv); } break;
+case 24:
+#line 57 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ mark(yypt[-0].yyv); } break;
+case 25:
+#line 58 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ size(yypt[-1].yyv, yypt[-0].yyv); } break;
+case 26:
+#line 59 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ font(yypt[-1].yyv, yypt[-0].yyv); } break;
+case 27:
+#line 60 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ fatbox(yypt[-0].yyv); } break;
+case 28:
+#line 61 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ sqrt(yypt[-0].yyv); } break;
+case 29:
+#line 62 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ps -= deltaps;} break;
+case 30:
+#line 62 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ subsup(yypt[-4].yyv, yypt[-1].yyv, yypt[-0].yyv); } break;
+case 31:
+#line 63 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ps -= deltaps;} break;
+case 32:
+#line 63 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ subsup(yypt[-3].yyv, 0, yypt[-0].yyv); } break;
+case 33:
+#line 64 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ps -= deltaps;} break;
+case 34:
+#line 64 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ integral(yypt[-4].yyv, yypt[-1].yyv, yypt[-0].yyv); } break;
+case 35:
+#line 65 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ps -= deltaps;} break;
+case 36:
+#line 65 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ integral(yypt[-3].yyv, 0, yypt[-0].yyv); } break;
+case 37:
+#line 66 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ integral(yypt[-0].yyv, 0, 0); } break;
+case 38:
+#line 67 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ps -= deltaps;} break;
+case 39:
+#line 67 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ fromto(yypt[-4].yyv, yypt[-1].yyv, yypt[-0].yyv); } break;
+case 40:
+#line 68 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ps -= deltaps;} break;
+case 41:
+#line 68 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ fromto(yypt[-3].yyv, 0, yypt[-0].yyv); } break;
+case 42:
+#line 69 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ paren(yypt[-2].yyv, yypt[-1].yyv, yypt[-0].yyv); } break;
+case 43:
+#line 70 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ paren(yypt[-1].yyv, yypt[-0].yyv, 0); } break;
+case 44:
+#line 71 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ diacrit(yypt[-1].yyv, yypt[-0].yyv); } break;
+case 45:
+#line 72 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ move(FWD, yypt[-1].yyv, yypt[-0].yyv); } break;
+case 46:
+#line 73 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ move(UP, yypt[-1].yyv, yypt[-0].yyv); } break;
+case 47:
+#line 74 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ move(BACK, yypt[-1].yyv, yypt[-0].yyv); } break;
+case 48:
+#line 75 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ move(DOWN, yypt[-1].yyv, yypt[-0].yyv); } break;
+case 49:
+#line 76 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ pile(yypt[-0].yyv); ct = yypt[-0].yyv; } break;
+case 50:
+#line 77 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{yyval=ct;} break;
+case 51:
+#line 77 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ matrix(yypt[-3].yyv); ct = yypt[-3].yyv; } break;
+case 52:
+#line 80 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ setintegral(); } break;
+case 53:
+#line 83 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = atoi((char *) yypt[-1].yyv); } break;
+case 54:
+#line 84 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = atoi((char *) yypt[-1].yyv); } break;
+case 55:
+#line 85 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = atoi((char *) yypt[-1].yyv); } break;
+case 56:
+#line 86 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = atoi((char *) yypt[-1].yyv); } break;
+case 57:
+#line 88 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = HAT; } break;
+case 58:
+#line 89 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = VEC; } break;
+case 59:
+#line 90 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = DYAD; } break;
+case 60:
+#line 91 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = BAR; } break;
+case 61:
+#line 92 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = LOWBAR; } break;
+case 62:
+#line 93 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = HIGHBAR; } break;
+case 63:
+#line 94 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = UNDER; } break;
+case 64:
+#line 95 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = DOT; } break;
+case 65:
+#line 96 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = TILDE; } break;
+case 66:
+#line 97 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = UTILDE; } break;
+case 67:
+#line 98 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = DOTDOT; } break;
+case 68:
+#line 101 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = ((char *)yypt[-0].yyv)[0]; } break;
+case 69:
+#line 102 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = '{'; } break;
+case 70:
+#line 105 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = ((char *)yypt[-0].yyv)[0]; } break;
+case 71:
+#line 106 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = '}'; } break;
+case 74:
+#line 113 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ column(yypt[-3].yyv, DEFGAP); } break;
+case 75:
+#line 114 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{yyval=atoi((char*)yypt[-0].yyv);} break;
+case 76:
+#line 114 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ column(yypt[-5].yyv, yypt[-3].yyv); } break;
+case 77:
+#line 117 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = startcol(LCOL); } break;
+case 78:
+#line 118 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = startcol(CCOL); } break;
+case 79:
+#line 119 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = startcol(RCOL); } break;
+case 80:
+#line 120 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = startcol(COL); } break;
+case 81:
+#line 123 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ lp[ct++] = yypt[-0].yyv; } break;
+case 82:
+#line 124 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ lp[ct++] = yypt[-0].yyv; } break;
+case 83:
+#line 127 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ yyval = ps; setsize((char *) yypt[-0].yyv); } break;
+case 84:
+#line 130 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ static char R[]="R"; setfont(R); } break;
+case 85:
+#line 131 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ static char I[]="I"; setfont(I); } break;
+case 86:
+#line 132 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ static char B[]="B"; setfont(B); } break;
+case 87:
+#line 133 "/usr/local/plan9/src/cmd/eqn/eqn.y"
+{ setfont((char *)yypt[-0].yyv); } break;
+ }
+ goto yystack; /* stack new state and value */
+}
diff --git a/src/cmd/eqn/eqn.y b/src/cmd/eqn/eqn.y
new file mode 100644
index 00000000..74796d7f
--- /dev/null
+++ b/src/cmd/eqn/eqn.y
@@ -0,0 +1,140 @@
+%term CONTIG QTEXT SPACE THIN TAB
+%term MATRIX LCOL CCOL RCOL COL ABOVE
+%term MARK LINEUP
+%term SUM INT PROD UNION INTER
+%term DEFINE TDEFINE NDEFINE DELIM GSIZE GFONT INCLUDE SPACE IFDEF
+%term DOTEQ DOTEN
+
+%right FROM TO
+%left OVER SQRT
+%right SUP SUB
+%right SIZE FONT ROMAN ITALIC BOLD FAT
+%right UP DOWN BACK FWD
+%left LEFT RIGHT
+%right DOT DOTDOT HAT TILDE BAR LOWBAR HIGHBAR UNDER VEC DYAD UTILDE
+
+%{
+#include "e.h"
+
+int yylex(void);
+%}
+
+%%
+
+stuff : eqn { putout($1); }
+ | error { ERROR "syntax error" WARNING; } /* should be SYNTAX */
+ | { eqnreg = 0; }
+ ;
+
+eqn : box
+ | eqn box { eqnbox($1, $2, 0); }
+ | eqn lineupbox { eqnbox($1, $2, 1); }
+ | LINEUP { lineup(0); }
+ ;
+
+lineupbox: LINEUP box { $$ = $2; lineup(1); }
+ ;
+
+sbox : SUP box %prec SUP { $$ = $2; }
+ | %prec SUP { $$ = 0; }
+ ;
+
+tbox : TO box %prec TO { $$ = $2; }
+ | %prec FROM { $$ = 0; }
+ ;
+
+box : '{' eqn '}' { $$ = $2; }
+ | QTEXT { text(QTEXT, (char *) $1); }
+ | CONTIG { text(CONTIG, (char *) $1); }
+ | SPACE { text(SPACE, (char *) 0); }
+ | THIN { text(THIN, (char *) 0); }
+ | TAB { text(TAB, (char *) 0); }
+ | SUM { funny(SUM); }
+ | PROD { funny(PROD); }
+ | UNION { funny(UNION); }
+ | INTER { funny(INTER); }
+ | box OVER box { boverb($1, $3); }
+ | MARK box { mark($2); }
+ | size box %prec SIZE { size($1, $2); }
+ | font box %prec FONT { font($1, $2); }
+ | FAT box { fatbox($2); }
+ | SQRT box { sqrt($2); }
+ | box SUB {ps -= deltaps;} box sbox %prec SUB { subsup($1, $4, $5); }
+ | box SUP {ps -= deltaps;} box %prec SUP { subsup($1, 0, $4); }
+ | int SUB {ps -= deltaps;} box sbox %prec SUB { integral($1, $4, $5); }
+ | int SUP {ps -= deltaps;} box %prec SUP { integral($1, 0, $4); }
+ | int { integral($1, 0, 0); }
+ | box FROM {ps -= deltaps;} box tbox %prec FROM { fromto($1, $4, $5); }
+ | box TO {ps -= deltaps;} box %prec TO { fromto($1, 0, $4); }
+ | left eqn right { paren($1, $2, $3); }
+ | left eqn { paren($1, $2, 0); }
+ | box diacrit { diacrit($1, $2); }
+ | fwd box %prec UP { move(FWD, $1, $2); }
+ | up box %prec UP { move(UP, $1, $2); }
+ | back box %prec UP { move(BACK, $1, $2); }
+ | down box %prec UP { move(DOWN, $1, $2); }
+ | column { pile($1); ct = $1; }
+ | MATRIX {$$=ct;} '{' collist '}' { matrix($2); ct = $2; }
+ ;
+
+int : INT { setintegral(); }
+ ;
+
+fwd : FWD text { $$ = atoi((char *) $1); } ;
+up : UP text { $$ = atoi((char *) $1); } ;
+back : BACK text { $$ = atoi((char *) $1); } ;
+down : DOWN text { $$ = atoi((char *) $1); } ;
+
+diacrit : HAT { $$ = HAT; }
+ | VEC { $$ = VEC; }
+ | DYAD { $$ = DYAD; }
+ | BAR { $$ = BAR; }
+ | LOWBAR { $$ = LOWBAR; }
+ | HIGHBAR { $$ = HIGHBAR; }
+ | UNDER { $$ = UNDER; } /* underbar */
+ | DOT { $$ = DOT; }
+ | TILDE { $$ = TILDE; }
+ | UTILDE { $$ = UTILDE; }
+ | DOTDOT { $$ = DOTDOT; } /* umlaut = double dot */
+ ;
+
+left : LEFT text { $$ = ((char *)$2)[0]; }
+ | LEFT '{' { $$ = '{'; }
+ ;
+
+right : RIGHT text { $$ = ((char *)$2)[0]; }
+ | RIGHT '}' { $$ = '}'; }
+ ;
+
+collist : column
+ | collist column
+ ;
+
+column : col '{' list '}' { column($1, DEFGAP); }
+ | col text {$$=atoi((char*)$2);} '{' list '}' { column($1, $3); }
+ ;
+
+col : LCOL { $$ = startcol(LCOL); }
+ | CCOL { $$ = startcol(CCOL); }
+ | RCOL { $$ = startcol(RCOL); }
+ | COL { $$ = startcol(COL); }
+ ;
+
+list : eqn { lp[ct++] = $1; }
+ | list ABOVE eqn { lp[ct++] = $3; }
+ ;
+
+size : SIZE text { $$ = ps; setsize((char *) $2); }
+ ;
+
+font : ROMAN { static char R[]="R"; setfont(R); }
+ | ITALIC { static char I[]="I"; setfont(I); }
+ | BOLD { static char B[]="B"; setfont(B); }
+ | FONT text { setfont((char *)$2); }
+ ;
+
+text : CONTIG
+ | QTEXT
+ ;
+
+%%
diff --git a/src/cmd/eqn/eqnbox.c b/src/cmd/eqn/eqnbox.c
new file mode 100644
index 00000000..7dd0b867
--- /dev/null
+++ b/src/cmd/eqn/eqnbox.c
@@ -0,0 +1,24 @@
+#include "e.h"
+
+void eqnbox(int p1, int p2, int lu)
+{
+ double b, h;
+ char *sh;
+ extern char *IRspace;
+
+ yyval = p1;
+ b = max(ebase[p1], ebase[p2]);
+ eht[yyval] = h = b + max(eht[p1]-ebase[p1],
+ eht[p2]-ebase[p2]);
+ ebase[yyval] = b;
+ dprintf(".\tS%d <- %d %d; b=%g, h=%g\n", yyval, p1, p2, b, h);
+ sh = pad(class[rclass[p1]][lclass[p2]]);
+ if (lu) {
+ printf(".nr %d \\w'\\*(%d%s'\n", p1, p1, sh);
+ printf(".ds %d \\h'|\\n(09u-\\n(%du'\\*(%d\n", p1, p1, p1);
+ }
+ printf(".as %d \"%s\\*(%d\n", yyval, sh, p2);
+ rfont[p1] = rfont[p2];
+ rclass[p1] = rclass[p2];
+ sfree(p2);
+}
diff --git a/src/cmd/eqn/font.c b/src/cmd/eqn/font.c
new file mode 100644
index 00000000..3fb01119
--- /dev/null
+++ b/src/cmd/eqn/font.c
@@ -0,0 +1,70 @@
+# include "e.h"
+
+void setfont(char *ch1)
+{
+ yyval = ft;
+ if (strcmp(ch1, "I") == 0) { /* I and italic mean merely position 2 */
+ *ch1 = '2';
+ ft = ITAL;
+ } else if (strcmp(ch1, "B") == 0) { /* and similarly for B & bold */
+ *ch1 = '3';
+ ft = BLD;
+ } else if (strcmp(ch1, "R") == 0) { /* and R and roman */
+ *ch1 = '1';
+ ft = ROM;
+ } else {
+ ft = ROM; /* assume it's a roman style */
+ }
+ ftp++;
+ if (ftp >= &ftstack[10])
+ ERROR "font stack overflow (10)" FATAL;
+ ftp->ft = ft;
+ if (ch1[1] == 0) { /* 1-char name */
+ ftp->name[0] = *ch1;
+ ftp->name[1] = '\0';
+ } else
+ sprintf(ftp->name, "(%s", ch1);
+ dprintf(".\tsetfont %s %c\n", ch1, ft);
+}
+
+void font(int p1, int p2)
+{
+ /* old font in p1, new in ft */
+ yyval = p2;
+ lfont[yyval] = rfont[yyval] = ft==ITAL ? ITAL : ROM;
+ ftp--;
+ ft = p1;
+}
+
+void globfont(void)
+{
+ char temp[20];
+
+ getstr(temp, sizeof(temp));
+ yyval = eqnreg = 0;
+ if (strcmp(temp, "I") == 0 || strncmp(temp, "it", 2) == 0) {
+ ft = ITAL;
+ strcpy(temp, "2");
+ } else if (strcmp(temp, "B") == 0 || strncmp(temp, "bo", 2) == 0) {
+ ft = BLD;
+ strcpy(temp, "3");
+ } else if (strcmp(temp, "R") == 0 || strncmp(temp, "ro", 2) == 0) {
+ ft = ROM;
+ strcpy(temp, "1");
+ } else {
+ ft = ROM; /* assume it's a roman style */
+ }
+ ftstack[0].ft = ft;
+ if (temp[1] == 0) /* 1-char name */
+ strcpy(ftstack[0].name, temp);
+ else
+ sprintf(ftstack[0].name, "(%.2s", temp);
+}
+
+void fatbox(int p)
+{
+ extern double Fatshift;
+
+ yyval = p;
+ printf(".ds %d \\*(%d\\h'-\\w'\\*(%d'u+%gm'\\*(%d\n", p, p, p, Fatshift, p);
+}
diff --git a/src/cmd/eqn/fromto.c b/src/cmd/eqn/fromto.c
new file mode 100644
index 00000000..2d4d7f74
--- /dev/null
+++ b/src/cmd/eqn/fromto.c
@@ -0,0 +1,52 @@
+# include "e.h"
+
+void fromto(int p1, int p2, int p3)
+{
+ double b, h1, b1, t;
+ int subps;
+
+ yyval = salloc();
+ lfont[yyval] = rfont[yyval] = 0;
+ h1 = eht[yyval] = eht[p1];
+ b1 = ebase[p1];
+ b = 0;
+ subps = ps;
+ ps += deltaps;
+ nrwid(p1, ps, p1);
+ printf(".nr %d \\n(%d\n", yyval, p1);
+ if (p2 > 0) {
+ nrwid(p2, subps, p2);
+ printf(".if \\n(%d>\\n(%d .nr %d \\n(%d\n", p2, yyval, yyval, p2);
+ eht[yyval] += eht[p2];
+ b = eht[p2];
+ }
+ if (p3 > 0) {
+ nrwid(p3, subps, p3);
+ printf(".if \\n(%d>\\n(%d .nr %d \\n(%d\n", p3, yyval, yyval, p3);
+ eht[yyval] += eht[p3];
+ }
+ printf(".ds %d ", yyval); /* bottom of middle box */
+ if (p2 > 0) {
+ t = eht[p2]-ebase[p2]+b1;
+ printf("\\v'%gm'\\h'\\n(%du-\\n(%du/2u'%s\\*(%d%s",
+ REL(t,ps), yyval, p2, DPS(ps,subps), p2, DPS(subps,ps));
+ printf("\\h'-\\n(%du-\\n(%du/2u'\\v'%gm'\\\n",
+ yyval, p2, REL(-t,ps));
+ }
+ printf("\\h'\\n(%du-\\n(%du/2u'\\*(%d\\h'\\n(%du-\\n(%du/2u'\\\n",
+ yyval, p1, p1, yyval, p1);
+ if (p3 >0) {
+ t = h1-b1+ebase[p3];
+ printf("\\v'%gm'\\h'-\\n(%du-\\n(%du/2u'%s\\*(%d%s\\h'\\n(%du-\\n(%du/2u'\\v'%gm'\\\n",
+ REL(-t,ps), yyval, p3, DPS(ps,subps), p3, DPS(subps,ps), yyval, p3, REL(t,ps));
+ }
+ printf("\n");
+ ebase[yyval] = b + b1;
+ dprintf(".\tS%d <- %d from %d to %d; h=%g b=%g\n",
+ yyval, p1, p2, p3, eht[yyval], ebase[yyval]);
+ sfree(p1);
+ if (p2 > 0)
+ sfree(p2);
+ if (p3 > 0)
+ sfree(p3);
+}
diff --git a/src/cmd/eqn/funny.c b/src/cmd/eqn/funny.c
new file mode 100644
index 00000000..85955fce
--- /dev/null
+++ b/src/cmd/eqn/funny.c
@@ -0,0 +1,30 @@
+#include "e.h"
+#include "y.tab.h"
+
+extern int Funnyps;
+extern double Funnyht, Funnybase;
+
+void funny(int n)
+{
+ char *f = 0;
+
+ yyval = salloc();
+ switch (n) {
+ case SUM:
+ f = lookup(deftbl, "sum_def")->cval; break;
+ case UNION:
+ f = lookup(deftbl, "union_def")->cval; break;
+ case INTER: /* intersection */
+ f = lookup(deftbl, "inter_def")->cval; break;
+ case PROD:
+ f = lookup(deftbl, "prod_def")->cval; break;
+ default:
+ ERROR "funny type %d in funny", n FATAL;
+ }
+ printf(".ds %d %s\n", yyval, f);
+ eht[yyval] = EM(1.0, ps+Funnyps) - EM(Funnyht, ps);
+ ebase[yyval] = EM(Funnybase, ps);
+ dprintf(".\tS%d <- %s; h=%g b=%g\n",
+ yyval, f, eht[yyval], ebase[yyval]);
+ lfont[yyval] = rfont[yyval] = ROM;
+}
diff --git a/src/cmd/eqn/glob.c b/src/cmd/eqn/glob.c
new file mode 100644
index 00000000..6ca1a1da
--- /dev/null
+++ b/src/cmd/eqn/glob.c
@@ -0,0 +1,35 @@
+#include "e.h"
+
+ /* YOU MAY WANT TO CHANGE THIS */
+char *typesetter = "post"; /* type of typesetter today */
+int ttype = DEVPOST;
+int minsize = 4; /* min size it can handle */
+
+
+int dbg; /* debugging print if non-zero */
+int lp[200]; /* stack for things like piles and matrices */
+int ct; /* pointer to lp */
+int used[100]; /* available registers */
+int ps; /* default init point size */
+int deltaps = 3; /* default change in ps */
+int dps_set = 0; /* 1 => -p option used */
+int gsize = 10; /* default initial point size */
+int ft = '2';
+Font ftstack[10] = { '2', "2" }; /* bottom is global font */
+Font *ftp = ftstack;
+int szstack[10]; /* non-zero if absolute size set at this level */
+int nszstack = 0;
+int display = 0; /* 1=>display, 0=>.EQ/.EN */
+
+int synerr; /* 1 if syntax err in this eqn */
+double eht[100]; /* height in ems at gsize */
+double ebase[100]; /* base: where one enters above bottom */
+int lfont[100]; /* leftmost and rightmost font associated with this thing */
+int rfont[100];
+int lclass[100]; /* leftmost and rightmost class associated with this thing */
+int rclass[100];
+int eqnreg; /* register where final string appears */
+double eqnht; /* final height of equation */
+int lefteq = '\0'; /* left in-line delimiter */
+int righteq = '\0'; /* right in-line delimiter */
+int markline = 0; /* 1 if this EQ/EN contains mark; 2 if lineup */
diff --git a/src/cmd/eqn/input.c b/src/cmd/eqn/input.c
new file mode 100644
index 00000000..3b865671
--- /dev/null
+++ b/src/cmd/eqn/input.c
@@ -0,0 +1,289 @@
+#include "e.h"
+#include "y.tab.h"
+#include <ctype.h>
+#include <errno.h>
+
+Infile infile[10];
+Infile *curfile = infile;
+
+#define MAXSRC 50
+Src src[MAXSRC]; /* input source stack */
+Src *srcp = src;
+
+extern int getarg(char *);
+extern void eprint(void);
+
+void pushsrc(int type, char *ptr) /* new input source */
+{
+ if (++srcp >= src + MAXSRC)
+ ERROR "inputs nested too deep" FATAL;
+ srcp->type = type;
+ srcp->sp = ptr;
+ if (dbg > 1) {
+ printf("\n%3d ", srcp - src);
+ switch (srcp->type) {
+ case File:
+ printf("push file %s\n", ((Infile *)ptr)->fname);
+ break;
+ case Macro:
+ printf("push macro <%s>\n", ptr);
+ break;
+ case Char:
+ printf("push char <%c>\n", *ptr);
+ break;
+ case String:
+ printf("push string <%s>\n", ptr);
+ break;
+ case Free:
+ printf("push free <%s>\n", ptr);
+ break;
+ default:
+ ERROR "pushed bad type %d\n", srcp->type FATAL;
+ }
+ }
+}
+
+void popsrc(void) /* restore an old one */
+{
+ if (srcp <= src)
+ ERROR "too many inputs popped" FATAL;
+ if (dbg > 1) {
+ printf("%3d ", srcp - src);
+ switch (srcp->type) {
+ case File:
+ printf("pop file\n");
+ break;
+ case Macro:
+ printf("pop macro\n");
+ break;
+ case Char:
+ printf("pop char <%c>\n", *srcp->sp);
+ break;
+ case String:
+ printf("pop string\n");
+ break;
+ case Free:
+ printf("pop free\n");
+ break;
+ default:
+ ERROR "pop weird input %d\n", srcp->type FATAL;
+ }
+ }
+ srcp--;
+}
+
+Arg args[10]; /* argument frames */
+Arg *argfp = args; /* frame pointer */
+int argcnt; /* number of arguments seen so far */
+
+void dodef(tbl *stp) /* collect args and switch input to defn */
+{
+ int i, len;
+ char *p;
+ Arg *ap;
+
+ ap = argfp+1;
+ if (ap >= args+10)
+ ERROR "more than arguments\n" FATAL;
+ argcnt = 0;
+ if (input() != '(')
+ ERROR "disaster in dodef\n"FATAL;
+ if (ap->argval == 0)
+ ap->argval = malloc(1000);
+ for (p = ap->argval; (len = getarg(p)) != -1; p += len) {
+ ap->argstk[argcnt++] = p;
+ if (input() == ')')
+ break;
+ }
+ for (i = argcnt; i < MAXARGS; i++)
+ ap->argstk[i] = "";
+ if (dbg)
+ for (i = 0; i < argcnt; i++)
+ printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
+ argfp = ap;
+ pushsrc(Macro, stp->cval);
+}
+
+getarg(char *p) /* pick up single argument, store in p, return length */
+{
+ int n, c, npar;
+
+ n = npar = 0;
+ for ( ;; ) {
+ c = input();
+ if (c == EOF)
+ ERROR "end of file in getarg!\n" FATAL;
+ if (npar == 0 && (c == ',' || c == ')'))
+ break;
+ if (c == '"') /* copy quoted stuff intact */
+ do {
+ *p++ = c;
+ n++;
+ } while ((c = input()) != '"' && c != EOF);
+ else if (c == '(')
+ npar++;
+ else if (c == ')')
+ npar--;
+ n++;
+ *p++ = c;
+ }
+ *p = 0;
+ unput(c);
+ return(n + 1);
+}
+
+#define PBSIZE 2000
+char pbuf[PBSIZE]; /* pushback buffer */
+char *pb = pbuf-1; /* next pushed back character */
+
+char ebuf[200]; /* collect input here for error reporting */
+char *ep = ebuf;
+
+input(void)
+{
+ register int c = 0;
+
+ loop:
+ switch (srcp->type) {
+ case File:
+ c = getc(curfile->fin);
+ if (c == EOF) {
+ if (curfile == infile)
+ break;
+ if (curfile->fin != stdin) {
+ fclose(curfile->fin);
+ free(curfile->fname); /* assumes allocated */
+ }
+ curfile--;
+ printf(".lf %d %s\n", curfile->lineno, curfile->fname);
+ popsrc();
+ goto loop;
+ }
+ if (c == '\n')
+ curfile->lineno++;
+ break;
+ case Char:
+ if (pb >= pbuf) {
+ c = *pb--;
+ popsrc();
+ break;
+ } else { /* can't happen? */
+ popsrc();
+ goto loop;
+ }
+ case String:
+ c = *srcp->sp++;
+ if (c == '\0') {
+ popsrc();
+ goto loop;
+ } else {
+ if (*srcp->sp == '\0') /* empty, so pop */
+ popsrc();
+ break;
+ }
+ case Macro:
+ c = *srcp->sp++;
+ if (c == '\0') {
+ if (--argfp < args)
+ ERROR "argfp underflow" FATAL;
+ popsrc();
+ goto loop;
+ } else if (c == '$' && isdigit(*srcp->sp)) {
+ int n = 0;
+ while (isdigit(*srcp->sp))
+ n = 10 * n + *srcp->sp++ - '0';
+ if (n > 0 && n <= MAXARGS)
+ pushsrc(String, argfp->argstk[n-1]);
+ goto loop;
+ }
+ break;
+ case Free: /* free string */
+ free(srcp->sp);
+ popsrc();
+ goto loop;
+ }
+ if (ep >= ebuf + sizeof ebuf)
+ ep = ebuf;
+ *ep++ = c;
+ return c;
+}
+
+
+unput(int c)
+{
+ if (++pb >= pbuf + sizeof pbuf)
+ ERROR "pushback overflow\n"FATAL;
+ if (--ep < ebuf)
+ ep = ebuf + sizeof(ebuf) - 1;
+ *pb = c;
+ pushsrc(Char, pb);
+ return c;
+}
+
+void pbstr(char *s)
+{
+ pushsrc(String, s);
+}
+
+void error(int die, char *s)
+{
+ extern char *cmdname;
+
+ if (synerr)
+ return;
+ fprintf(stderr, "%s: ", cmdname);
+ fprintf(stderr, s);
+ if (errno > 0)
+ perror("???");
+ if (curfile->fin)
+ fprintf(stderr, " near %s:%d",
+ curfile->fname, curfile->lineno+1);
+ fprintf(stderr, "\n");
+ eprint();
+ synerr = 1;
+ errno = 0;
+ if (die) {
+ if (dbg)
+ abort();
+ else
+ exit(1);
+ }
+}
+
+void yyerror(char *s)
+{
+ error(0, s); /* temporary */
+}
+
+char errbuf[200];
+
+void eprint(void) /* try to print context around error */
+{
+ char *p, *q;
+
+ if (ep == ebuf)
+ return; /* no context */
+ p = ep - 1;
+ if (p > ebuf && *p == '\n')
+ p--;
+ for ( ; p >= ebuf && *p != '\n'; p--)
+ ;
+ while (*p == '\n')
+ p++;
+ fprintf(stderr, " context is\n\t");
+ for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
+ ;
+ while (p < q)
+ putc(*p++, stderr);
+ fprintf(stderr, " >>> ");
+ while (p < ep)
+ putc(*p++, stderr);
+ fprintf(stderr, " <<< ");
+ while (pb >= pbuf)
+ putc(*pb--, stderr);
+ if (curfile->fin)
+ fgets(ebuf, sizeof ebuf, curfile->fin);
+ fprintf(stderr, "%s", ebuf);
+ pbstr("\n.EN\n"); /* safety first */
+ ep = ebuf;
+}
diff --git a/src/cmd/eqn/integral.c b/src/cmd/eqn/integral.c
new file mode 100644
index 00000000..2b1c9e3f
--- /dev/null
+++ b/src/cmd/eqn/integral.c
@@ -0,0 +1,30 @@
+#include "e.h"
+#include "y.tab.h"
+
+extern int Intps;
+extern double Intht, Intbase, Int1h, Int1v, Int2h, Int2v;
+
+void integral(int p, int p1, int p2)
+{
+ if (p1 != 0)
+ printf(".ds %d \\h'%gm'\\v'%gm'\\*(%d\\v'%gm'\n", p1, -Int1h, Int1v, p1, -Int1v);
+ if (p2 != 0)
+ printf(".ds %d \\v'%gm'\\h'%gm'\\*(%d\\v'%gm'\n", p2, -Int2v, Int2h, p2, Int2v);
+ if (p1 != 0 && p2 != 0)
+ shift2(p, p1, p2);
+ else if (p1 != 0)
+ bshiftb(p, SUB, p1);
+ else if (p2 != 0)
+ bshiftb(p, SUP, p2);
+ dprintf(".\tintegral: S%d; h=%g b=%g\n", p, eht[p], ebase[p]);
+ lfont[p] = ROM;
+}
+
+void setintegral(void)
+{
+ yyval = salloc();
+ printf(".ds %d %s\n", yyval, lookup(deftbl, "int_def")->cval);
+ eht[yyval] = EM(Intht, ps+Intps);
+ ebase[yyval] = EM(Intbase, ps);
+ lfont[yyval] = rfont[yyval] = ROM;
+}
diff --git a/src/cmd/eqn/lex.c b/src/cmd/eqn/lex.c
new file mode 100644
index 00000000..e535b869
--- /dev/null
+++ b/src/cmd/eqn/lex.c
@@ -0,0 +1,265 @@
+#include "e.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+#define SSIZE 1000
+char token[SSIZE];
+int sp;
+
+void space(void);
+void dodef(tbl *);
+void define(int);
+void ifdef(void);
+void include(void);
+void delim(void);
+
+yylex(void)
+{
+ register int c;
+ tbl *tp;
+
+ begin:
+ while ((c = input()) == ' ' || c == '\n' || c == '\t')
+ ;
+ yylval = c;
+ switch (c) {
+ case EOF:
+ ERROR "unexpected end of input inside equation" WARNING;
+ return(EOF);
+ case '~':
+ return(SPACE);
+ case '^':
+ return(THIN);
+ /* case '\t':
+ return(TAB);
+ */
+ case '{':
+ return('{');
+ case '}':
+ return('}');
+ case '"':
+ for (sp = 0; (c=input())!='"' && c != '\n'; ) {
+ if (c == '\\')
+ if ((c = input()) != '"')
+ token[sp++] = '\\';
+ token[sp++] = c;
+ if (sp >= SSIZE)
+ ERROR "quoted string %.20s... too long", token FATAL;
+ }
+ token[sp] = '\0';
+ yylval = (int) &token[0];
+ if (c == '\n')
+ ERROR "missing \" in %.20s", token WARNING;
+ return(QTEXT);
+ }
+ if (!display && c == righteq)
+ return(EOF);
+
+ unput(c);
+ getstr(token, SSIZE);
+ dprintf(".\tlex token = |%s|\n", token);
+ if ((tp = lookup(deftbl, token)) != NULL) { /* defined term */
+ c = input();
+ unput(c);
+ if (c == '(') /* macro with args */
+ dodef(tp);
+ else { /* no args */
+ unput(' ');
+ pbstr(tp->cval);
+ dprintf(".\tfound %s|=%s|\n", token, tp->cval);
+ }
+ goto begin;
+ }
+
+ if ((tp = lookup(keytbl, token)) == NULL) /* not a keyword */
+ return CONTIG;
+
+ switch (tp->ival) { /* some kind of keyword */
+ case DEFINE: case TDEFINE: case NDEFINE:
+ define(tp->ival);
+ break;
+ case IFDEF:
+ ifdef();
+ break;
+ case DELIM:
+ delim();
+ break;
+ case GSIZE:
+ globsize();
+ break;
+ case GFONT:
+ globfont();
+ break;
+ case INCLUDE:
+ include();
+ break;
+ case SPACE:
+ space();
+ break;
+ case DOTEQ:
+ /* .EQ inside equation -- should warn if at bottom level */
+ break;
+ case DOTEN:
+ if (curfile == infile)
+ return EOF;
+ /* else ignore nested .EN */
+ break;
+ default:
+ return tp->ival;
+ }
+ goto begin;
+}
+
+void getstr(char *s, int n)
+{
+ register int c;
+ register char *p;
+
+ p = s;
+ while ((c = input()) == ' ' || c == '\n')
+ ;
+ if (c == EOF) {
+ *s = 0;
+ return;
+ }
+ while (c != ' ' && c != '\t' && c != '\n' && c != '{' && c != '}'
+ && c != '"' && c != '~' && c != '^') {
+ if (!display && c == righteq)
+ break;
+ if (c == '(' && p > s) { /* might be defined(...) */
+ *p = '\0';
+ if (lookup(deftbl, s) != NULL)
+ break;
+ }
+ if (c == '\\')
+ if ((c = input()) != '"')
+ *p++ = '\\';
+ *p++ = c;
+ if (--n <= 0)
+ ERROR "token %.20s... too long", s FATAL;
+ c = input();
+ }
+ unput(c);
+ *p = '\0';
+ yylval = (int) s;
+}
+
+cstr(char *s, int quote, int maxs)
+{
+ int del, c, i;
+
+ s[0] = 0;
+ while ((del=input()) == ' ' || del == '\t')
+ ;
+ if (quote)
+ for (i=0; (c=input()) != del && c != EOF;) {
+ s[i++] = c;
+ if (i >= maxs)
+ return(1); /* disaster */
+ }
+ else {
+ if (del == '\n')
+ return(1);
+ s[0] = del;
+ for (i=1; (c=input())!=' ' && c!= '\t' && c!='\n' && c!=EOF;) {
+ s[i++] = c;
+ if (i >= maxs)
+ return(1); /* disaster */
+ }
+ }
+ s[i] = '\0';
+ if (c == EOF)
+ ERROR "Unexpected end of input at %.20s", s FATAL;
+ return(0);
+}
+
+void define(int type)
+{
+ char *p1, *p2;
+ extern int ftune(char *, char *);
+
+ getstr(token, SSIZE); /* get name */
+ if (type != DEFINE) {
+ cstr(token, 1, SSIZE); /* skip the definition too */
+ return;
+ }
+ p1 = strsave(token);
+ if (cstr(token, 1, SSIZE))
+ ERROR "Unterminated definition at %.20s", token FATAL;
+ if (lookup(ftunetbl, p1) != NULL) { /* double tuning param */
+ dprintf(".\ttune %s %s\n", p1, token);
+ ftune(p1, token);
+ } else {
+ p2 = strsave(token);
+ install(deftbl, p1, p2, 0);
+ dprintf(".\tname %s defined as %s\n", p1, p2);
+ }
+}
+
+void ifdef(void) /* do body if name is defined */
+{
+ char name[100], *p;
+
+ getstr(name, sizeof(name)); /* get name */
+ cstr(token, 1, SSIZE); /* and body */
+ if (lookup(deftbl, name) != NULL) { /* found it */
+ p = strsave(token);
+ pushsrc(Free, p);
+ pushsrc(String, p);
+ }
+}
+
+char *spaceval = NULL;
+
+void space(void) /* collect line of form "space amt" to replace \x in output */
+{
+ getstr(token, SSIZE);
+ spaceval = strsave(token);
+ dprintf(".\tsetting spaceval to %s\n", token);
+}
+
+char *strsave(char *s)
+{
+ register char *q;
+
+ q = malloc(strlen(s)+1);
+ if (q == NULL)
+ ERROR "out of space in strsave on %s", s FATAL;
+ strcpy(q, s);
+ return(q);
+}
+
+void include(void)
+{
+ char name[100];
+ FILE *fin;
+ int c;
+ extern int errno;
+
+ while ((c = input()) == ' ')
+ ;
+ unput(c);
+ cstr(name, c == '"', sizeof(name)); /* gets it quoted or not */
+ if ((fin = fopen(name, "r")) == NULL)
+ ERROR "can't open file %s", name FATAL;
+ errno = 0;
+ curfile++;
+ curfile->fin = fin;
+ curfile->fname = strsave(name);
+ curfile->lineno = 0;
+ printf(".lf 1 %s\n", curfile->fname);
+ pushsrc(File, curfile->fname);
+}
+
+void delim(void)
+{
+ yyval = eqnreg = 0;
+ if (cstr(token, 0, SSIZE))
+ ERROR "Bizarre delimiters" FATAL;
+ lefteq = token[0];
+ righteq = token[1];
+ if (!isprint(lefteq) || !isprint(righteq))
+ ERROR "Bizarre delimiters" FATAL;
+ if (lefteq == 'o' && righteq == 'f')
+ lefteq = righteq = '\0';
+}
diff --git a/src/cmd/eqn/lookup.c b/src/cmd/eqn/lookup.c
new file mode 100644
index 00000000..4eb94373
--- /dev/null
+++ b/src/cmd/eqn/lookup.c
@@ -0,0 +1,219 @@
+#include "e.h"
+#include "y.tab.h"
+
+tbl *keytbl[TBLSIZE]; /* key words */
+tbl *restbl[TBLSIZE]; /* reserved words */
+tbl *deftbl[TBLSIZE]; /* user-defined names */
+
+struct keyword {
+ char *key;
+ int keyval;
+} keyword[] ={
+ "sub", SUB,
+ "sup", SUP,
+ ".EN", DOTEN,
+ ".EQ", DOTEQ,
+ "from", FROM,
+ "to", TO,
+ "sum", SUM,
+ "hat", HAT,
+ "vec", VEC,
+ "dyad", DYAD,
+ "dot", DOT,
+ "dotdot", DOTDOT,
+ "bar", BAR,
+ "lowbar", LOWBAR,
+ "highbar", HIGHBAR,
+ "tilde", TILDE,
+ "utilde", UTILDE,
+ "under", UNDER,
+ "prod", PROD,
+ "int", INT,
+ "integral", INT,
+ "union", UNION,
+ "inter", INTER,
+ "matrix", MATRIX,
+ "col", COL,
+ "lcol", LCOL,
+ "ccol", CCOL,
+ "rcol", RCOL,
+ "pile", COL, /* synonyms ... */
+ "lpile", LCOL,
+ "cpile", CCOL,
+ "rpile", RCOL,
+ "over", OVER,
+ "sqrt", SQRT,
+ "above", ABOVE,
+ "size", SIZE,
+ "font", FONT,
+ "fat", FAT,
+ "roman", ROMAN,
+ "italic", ITALIC,
+ "bold", BOLD,
+ "left", LEFT,
+ "right", RIGHT,
+ "delim", DELIM,
+ "define", DEFINE,
+ "tdefine", DEFINE,
+ "ndefine", NDEFINE,
+ "ifdef", IFDEF,
+ "gsize", GSIZE,
+ ".gsize", GSIZE,
+ "gfont", GFONT,
+ "include", INCLUDE,
+ "copy", INCLUDE,
+ "space", SPACE,
+ "up", UP,
+ "down", DOWN,
+ "fwd", FWD,
+ "back", BACK,
+ "mark", MARK,
+ "lineup", LINEUP,
+ 0, 0
+};
+
+struct resword {
+ char *res;
+ char *resval;
+} resword[] ={
+ ">=", "\\(>=",
+ "<=", "\\(<=",
+ "==", "\\(==",
+ "!=", "\\(!=",
+ "+-", "\\(+-",
+ "->", "\\(->",
+ "<-", "\\(<-",
+ "inf", "\\(if",
+ "infinity", "\\(if",
+ "partial", "\\(pd",
+ "half", "\\f1\\(12\\fP",
+ "prime", "\\f1\\v'.5m'\\s+3\\(fm\\s-3\\v'-.5m'\\fP",
+ "dollar", "\\f1$\\fP",
+ "nothing", "",
+ "times", "\\(mu",
+ "del", "\\(gr",
+ "grad", "\\(gr",
+ "approx", "\\v'-.2m'\\z\\(ap\\v'.25m'\\(ap\\v'-.05m'",
+ "cdot", "\\v'-.3m'.\\v'.3m'",
+ "...", "\\v'-.25m'\\ .\\ .\\ .\\ \\v'.25m'",
+ ",...,", "\\f1,\\fP\\ .\\ .\\ .\\ \\f1,\\fP\\|",
+ "alpha", "α",
+ "ALPHA", "Α",
+ "beta", "β",
+ "BETA", "Β",
+ "gamma", "γ",
+ "GAMMA", "Γ",
+ "delta", "δ",
+ "DELTA", "Δ",
+ "epsilon", "ε",
+ "EPSILON", "Ε",
+ "omega", "ω",
+ "OMEGA", "Ω",
+ "lambda", "λ",
+ "LAMBDA", "Λ",
+ "mu", "μ",
+ "MU", "Μ",
+ "nu", "ν",
+ "NU", "Ν",
+ "theta", "θ",
+ "THETA", "Θ",
+ "phi", "φ",
+ "PHI", "Φ",
+ "pi", "π",
+ "PI", "Π",
+ "sigma", "σ",
+ "SIGMA", "Σ",
+ "xi", "ξ",
+ "XI", "Ξ",
+ "zeta", "ζ",
+ "ZETA", "Ζ",
+ "iota", "ι",
+ "IOTA", "Ι",
+ "eta", "η",
+ "ETA", "Η",
+ "kappa", "κ",
+ "KAPPA", "Κ",
+ "rho", "ρ",
+ "RHO", "Ρ",
+ "tau", "τ",
+ "TAU", "Τ",
+ "omicron", "ο",
+ "OMICRON", "Ο",
+ "upsilon", "υ",
+ "UPSILON", "Υ",
+ "psi", "ψ",
+ "PSI", "Ψ",
+ "chi", "χ",
+ "CHI", "Χ",
+ "and", "\\f1and\\fP",
+ "for", "\\f1for\\fP",
+ "if", "\\f1if\\fP",
+ "Re", "\\f1Re\\fP",
+ "Im", "\\f1Im\\fP",
+ "sin", "\\f1sin\\fP",
+ "cos", "\\f1cos\\fP",
+ "tan", "\\f1tan\\fP",
+ "arc", "\\f1arc\\fP",
+ "sinh", "\\f1sinh\\fP",
+ "coth", "\\f1coth\\fP",
+ "tanh", "\\f1tanh\\fP",
+ "cosh", "\\f1cosh\\fP",
+ "lim", "\\f1lim\\fP",
+ "log", "\\f1log\\fP",
+ "ln", "\\f1ln\\fP",
+ "max", "\\f1max\\fP",
+ "min", "\\f1min\\fP",
+ "exp", "\\f1exp\\fP",
+ "det", "\\f1det\\fP",
+ 0, 0
+};
+
+int hash(char *s)
+{
+ register unsigned int h;
+
+ for (h = 0; *s != '\0'; )
+ h += *s++;
+ h %= TBLSIZE;
+ return h;
+}
+
+tbl *lookup(tbl **tblp, char *name) /* find name in tbl */
+{
+ register tbl *p;
+
+ for (p = tblp[hash(name)]; p != NULL; p = p->next)
+ if (strcmp(name, p->name) == 0)
+ return(p);
+ return(NULL);
+}
+
+void install(tbl **tblp, char *name, char *cval, int ival) /* install name, vals in tblp */
+{
+ register tbl *p;
+ int h;
+
+ if ((p = lookup(tblp, name)) == NULL) {
+ p = (tbl *) malloc(sizeof(tbl));
+ if (p == NULL)
+ ERROR "out of space in install" FATAL;
+ h = hash(name); /* bad visibility here */
+ p->name = name;
+ p->next = tblp[h];
+ tblp[h] = p;
+ }
+ p->cval = cval;
+ p->ival = ival;
+}
+
+void init_tbl(void) /* initialize tables */
+{
+ int i;
+ extern int init_tune(void);
+
+ for (i = 0; keyword[i].key != NULL; i++)
+ install(keytbl, keyword[i].key, (char *) 0, keyword[i].keyval);
+ for (i = 0; resword[i].res != NULL; i++)
+ install(restbl, resword[i].res, resword[i].resval, 0);
+ init_tune(); /* tuning table done in tuning.c */
+}
diff --git a/src/cmd/eqn/main.c b/src/cmd/eqn/main.c
new file mode 100644
index 00000000..a06fd165
--- /dev/null
+++ b/src/cmd/eqn/main.c
@@ -0,0 +1,333 @@
+#include "e.h"
+
+#define MAXLINE 3600 /* maximum input line */
+
+char *version = "version Oct 24, 1991";
+
+char in[MAXLINE]; /* input buffer */
+int noeqn;
+char *cmdname;
+
+int yyparse(void);
+void settype(char *);
+int getdata(void);
+int getline(char *);
+#define inline einline
+void inline(void);
+void init(void);
+void init_tbl(void);
+
+void
+main(int argc, char *argv[])
+{
+ char *p, buf[20];
+
+ cmdname = argv[0];
+ if (p = getenv("TYPESETTER"))
+ typesetter = p;
+ while (argc > 1 && argv[1][0] == '-') {
+ switch (argv[1][1]) {
+
+ case 'd':
+ if (argv[1][2] == '\0') {
+ dbg++;
+ printf("...\teqn %s\n", version);
+ } else {
+ lefteq = argv[1][2];
+ righteq = argv[1][3];
+ }
+ break;
+ case 's': szstack[0] = gsize = atoi(&argv[1][2]); break;
+ case 'p': deltaps = atoi(&argv[1][2]); dps_set = 1; break;
+ case 'm': minsize = atoi(&argv[1][2]); break;
+ case 'f': strcpy(ftstack[0].name,&argv[1][2]); break;
+ case 'e': noeqn++; break;
+ case 'T': typesetter = &argv[1][2]; break;
+ default:
+ fprintf(stderr, "%s: unknown option %s\n", cmdname, argv[1]);
+ break;
+ }
+ argc--;
+ argv++;
+ }
+ settype(typesetter);
+ sprintf(buf, "\"%s\"", typesetter);
+ install(deftbl, strsave(typesetter), strsave(buf), 0);
+ init_tbl(); /* install other keywords in tables */
+ curfile = infile;
+ pushsrc(File, curfile->fname);
+ if (argc <= 1) {
+ curfile->fin = stdin;
+ curfile->fname = strsave("-");
+ getdata();
+ } else
+ while (argc-- > 1) {
+ if (strcmp(*++argv, "-") == 0)
+ curfile->fin = stdin;
+ else if ((curfile->fin = fopen(*argv, "r")) == NULL)
+ ERROR "can't open file %s", *argv FATAL;
+ curfile->fname = strsave(*argv);
+ getdata();
+ if (curfile->fin != stdin)
+ fclose(curfile->fin);
+ }
+ exit(0);
+}
+
+void settype(char *s) /* initialize data for particular typesetter */
+ /* the minsize could profitably come from the */
+{ /* troff description file /usr/lib/font/dev.../DESC.out */
+ if (strcmp(s, "202") == 0)
+ { minsize = 5; ttype = DEV202; }
+ else if (strcmp(s, "aps") == 0)
+ { minsize = 5; ttype = DEVAPS; }
+ else if (strcmp(s, "cat") == 0)
+ { minsize = 6; ttype = DEVCAT; }
+ else if (strcmp(s, "post") == 0)
+ { minsize = 4; ttype = DEVPOST; }
+ else
+ { minsize = 5; ttype = DEV202; }
+}
+
+getdata(void)
+{
+ int i, type, ln;
+ char fname[100];
+ extern int errno;
+
+ errno = 0;
+ curfile->lineno = 0;
+ printf(".lf 1 %s\n", curfile->fname);
+ while ((type = getline(in)) != EOF) {
+ if (in[0] == '.' && in[1] == 'E' && in[2] == 'Q') {
+ for (i = 11; i < 100; i++)
+ used[i] = 0;
+ printf("%s", in);
+ if (markline) { /* turn off from last time */
+ printf(".nr MK 0\n");
+ markline = 0;
+ }
+ display = 1;
+ init();
+ yyparse();
+ if (eqnreg > 0) {
+ if (markline)
+ printf(".nr MK %d\n", markline); /* for -ms macros */
+ printf(".if %gm>\\n(.v .ne %gm\n", eqnht, eqnht);
+ printf(".rn %d 10\n", eqnreg);
+ if (!noeqn)
+ printf("\\&\\*(10\n");
+ }
+ printf(".EN");
+ while (putchar(input()) != '\n')
+ ;
+ printf(".lf %d\n", curfile->lineno+1);
+ }
+ else if (type == lefteq)
+ inline();
+ else if (in[0] == '.' && in[1] == 'l' && in[2] == 'f') {
+ if (sscanf(in+3, "%d %s", &ln, fname) == 2) {
+ free(curfile->fname);
+ printf(".lf %d %s\n", curfile->lineno = ln, curfile->fname = strsave(fname));
+ } else
+ printf(".lf %d\n", curfile->lineno = ln);
+ } else
+ printf("%s", in);
+ }
+ return(0);
+}
+
+getline(char *s)
+{
+ register c;
+
+ while ((c=input()) != '\n' && c != EOF && c != lefteq) {
+ if (s >= in+MAXLINE) {
+ ERROR "input line too long: %.20s\n", in WARNING;
+ in[MAXLINE] = '\0';
+ break;
+ }
+ *s++ = c;
+ }
+ if (c != lefteq)
+ *s++ = c;
+ *s = '\0';
+ return(c);
+}
+
+void inline(void)
+{
+ int ds, n, sz1 = 0;
+
+ n = curfile->lineno;
+ if (szstack[0] != 0)
+ printf(".nr %d \\n(.s\n", sz1 = salloc());
+ ds = salloc();
+ printf(".rm %d \n", ds);
+ display = 0;
+ do {
+ if (*in)
+ printf(".as %d \"%s\n", ds, in);
+ init();
+ yyparse();
+ if (eqnreg > 0) {
+ printf(".as %d \\*(%d\n", ds, eqnreg);
+ sfree(eqnreg);
+ printf(".lf %d\n", curfile->lineno+1);
+ }
+ } while (getline(in) == lefteq);
+ if (*in)
+ printf(".as %d \"%s", ds, in);
+ if (sz1)
+ printf("\\s\\n(%d", sz1);
+ printf("\\*(%d\n", ds);
+ printf(".lf %d\n", curfile->lineno+1);
+ if (curfile->lineno > n+3)
+ fprintf(stderr, "eqn warning: multi-line %c...%c, file %s:%d,%d\n",
+ lefteq, righteq, curfile->fname, n, curfile->lineno);
+ sfree(ds);
+ if (sz1) sfree(sz1);
+}
+
+void putout(int p1)
+{
+ double before, after;
+ extern double BeforeSub, AfterSub;
+
+ dprintf(".\tanswer <- S%d, h=%g,b=%g\n",p1, eht[p1], ebase[p1]);
+ eqnht = eht[p1];
+ before = eht[p1] - ebase[p1] - BeforeSub; /* leave room for sub or superscript */
+ after = ebase[p1] - AfterSub;
+ if (spaceval || before > 0.01 || after > 0.01) {
+ printf(".ds %d ", p1); /* used to be \\x'0' here: why? */
+ if (spaceval != NULL)
+ printf("\\x'0-%s'", spaceval);
+ else if (before > 0.01)
+ printf("\\x'0-%gm'", before);
+ printf("\\*(%d", p1);
+ if (spaceval == NULL && after > 0.01)
+ printf("\\x'%gm'", after);
+ putchar('\n');
+ }
+ if (szstack[0] != 0)
+ printf(".ds %d %s\\*(%d\\s\\n(99\n", p1, DPS(gsize,gsize), p1);
+ eqnreg = p1;
+ if (spaceval != NULL) {
+ free(spaceval);
+ spaceval = NULL;
+ }
+}
+
+void init(void)
+{
+ synerr = 0;
+ ct = 0;
+ ps = gsize;
+ ftp = ftstack;
+ ft = ftp->ft;
+ nszstack = 0;
+ if (szstack[0] != 0) /* absolute gsize in effect */
+ printf(".nr 99 \\n(.s\n");
+}
+
+salloc(void)
+{
+ int i;
+
+ for (i = 11; i < 100; i++)
+ if (used[i] == 0) {
+ used[i]++;
+ return(i);
+ }
+ ERROR "no eqn strings left (%d)", i FATAL;
+ return(0);
+}
+
+void sfree(int n)
+{
+ used[n] = 0;
+}
+
+void nrwid(int n1, int p, int n2)
+{
+ printf(".nr %d 0\\w'%s\\*(%d'\n", n1, DPS(gsize,p), n2); /* 0 defends against - width */
+}
+
+char *ABSPS(int dn) /* absolute size dn in printable form \sd or \s(dd (dd >= 40) */
+{
+ static char buf[100], *lb = buf;
+ char *p;
+
+ if (lb > buf + sizeof(buf) - 10)
+ lb = buf;
+ p = lb;
+ *lb++ = '\\';
+ *lb++ = 's';
+ if (dn >= 10) { /* \s(dd only works in new troff */
+ if (dn >= 40)
+ *lb++ = '(';
+ *lb++ = dn/10 + '0';
+ *lb++ = dn%10 + '0';
+ } else {
+ *lb++ = dn + '0';
+ }
+ *lb++ = '\0';
+ return p;
+}
+
+char *DPS(int f, int t) /* delta ps (t-f) in printable form \s+d or \s-d or \s+-(dd */
+{
+ static char buf[100], *lb = buf;
+ char *p;
+ int dn;
+
+ if (lb > buf + sizeof(buf) - 10)
+ lb = buf;
+ p = lb;
+ *lb++ = '\\';
+ *lb++ = 's';
+ dn = EFFPS(t) - EFFPS(f);
+ if (szstack[nszstack] != 0) /* absolute */
+ dn = EFFPS(t); /* should do proper \s(dd */
+ else if (dn >= 0)
+ *lb++ = '+';
+ else {
+ *lb++ = '-';
+ dn = -dn;
+ }
+ if (dn >= 10) { /* \s+(dd only works in new troff */
+ *lb++ = '(';
+ *lb++ = dn/10 + '0';
+ *lb++ = dn%10 + '0';
+ } else {
+ *lb++ = dn + '0';
+ }
+ *lb++ = '\0';
+ return p;
+}
+
+EFFPS(int n) /* effective value of n */
+{
+ if (n >= minsize)
+ return n;
+ else
+ return minsize;
+}
+
+double EM(double m, int ps) /* convert m to ems in gsize */
+{
+ m *= (double) EFFPS(ps) / gsize;
+ if (m <= 0.001 && m >= -0.001)
+ return 0;
+ else
+ return m;
+}
+
+double REL(double m, int ps) /* convert m to ems in ps */
+{
+ m *= (double) gsize / EFFPS(ps);
+ if (m <= 0.001 && m >= -0.001)
+ return 0;
+ else
+ return m;
+}
diff --git a/src/cmd/eqn/mark.c b/src/cmd/eqn/mark.c
new file mode 100644
index 00000000..f02e6779
--- /dev/null
+++ b/src/cmd/eqn/mark.c
@@ -0,0 +1,19 @@
+#include "e.h"
+
+void mark(int p1)
+{
+ markline = 1;
+ printf(".ds %d \\k(09\\*(%d\n", p1, p1);
+ yyval = p1;
+ dprintf(".\tmark %d\n", p1);
+}
+
+void lineup(int p1)
+{
+ markline = 2;
+ if (p1 == 0) {
+ yyval = salloc();
+ printf(".ds %d \\h'|\\n(09u'\n", yyval);
+ }
+ dprintf(".\tlineup %d\n", p1);
+}
diff --git a/src/cmd/eqn/matrix.c b/src/cmd/eqn/matrix.c
new file mode 100644
index 00000000..9df6cff2
--- /dev/null
+++ b/src/cmd/eqn/matrix.c
@@ -0,0 +1,78 @@
+#include "e.h"
+
+startcol(int type) /* mark start of column in lp[] array */
+{
+ int oct = ct;
+
+ lp[ct++] = type;
+ lp[ct++] = 0; /* count, to come */
+ lp[ct++] = 0; /* separation, to come */
+ return oct;
+}
+
+void column(int oct, int sep) /* remember end of column that started at lp[oct] */
+{
+ int i, type;
+
+ lp[oct+1] = ct - oct - 3;
+ lp[oct+2] = sep;
+ type = lp[oct];
+ if (dbg) {
+ printf(".\t%d column of", type);
+ for (i = oct+3; i < ct; i++ )
+ printf(" S%d", lp[i]);
+ printf(", rows=%d, sep=%d\n", lp[oct+1], lp[oct+2]);
+ }
+}
+
+void matrix(int oct) /* matrix is list of columns */
+{
+ int nrow, ncol, i, j, k, val[100];
+ double b, hb;
+ char *space;
+ extern char *Matspace;
+
+ space = Matspace; /* between columns of matrix */
+ nrow = lp[oct+1]; /* disaster if rows inconsistent */
+ /* also assumes just columns */
+ /* fix when add other things */
+ ncol = 0;
+ for (i = oct+1; i < ct; i += lp[i]+3 ) {
+ ncol++;
+ dprintf(".\tcolct=%d\n", lp[i]);
+ }
+ for (k=1; k <= nrow; k++) {
+ hb = b = 0;
+ j = oct + k + 2;
+ for (i=0; i < ncol; i++) {
+ hb = max(hb, eht[lp[j]]-ebase[lp[j]]);
+ b = max(b, ebase[lp[j]]);
+ j += nrow + 3;
+ }
+ dprintf(".\trow %d: b=%g, hb=%g\n", k, b, hb);
+ j = oct + k + 2;
+ for (i=0; i<ncol; i++) {
+ ebase[lp[j]] = b;
+ eht[lp[j]] = b + hb;
+ j += nrow + 3;
+ }
+ }
+ j = oct;
+ for (i=0; i<ncol; i++) {
+ pile(j);
+ val[i] = yyval;
+ j += nrow + 3;
+ }
+ yyval = salloc();
+ eht[yyval] = eht[val[0]];
+ ebase[yyval] = ebase[val[0]];
+ lfont[yyval] = rfont[yyval] = 0;
+ dprintf(".\tmatrix S%d: r=%d, c=%d, h=%g, b=%g\n",
+ yyval,nrow,ncol,eht[yyval],ebase[yyval]);
+ printf(".ds %d \"", yyval);
+ for( i=0; i<ncol; i++ ) {
+ printf("\\*(%d%s", val[i], i==ncol-1 ? "" : space);
+ sfree(val[i]);
+ }
+ printf("\n");
+}
diff --git a/src/cmd/eqn/mkfile b/src/cmd/eqn/mkfile
new file mode 100644
index 00000000..5f8e559c
--- /dev/null
+++ b/src/cmd/eqn/mkfile
@@ -0,0 +1,42 @@
+<$PLAN9/src/mkhdr
+
+TARG=eqn
+OFILES=main.$O\
+ tuning.$O\
+ diacrit.$O\
+ eqnbox.$O\
+ font.$O\
+ fromto.$O\
+ funny.$O\
+ glob.$O\
+ integral.$O\
+ input.$O\
+ lex.$O\
+ lookup.$O\
+ mark.$O\
+ matrix.$O\
+ move.$O\
+ over.$O\
+ paren.$O\
+ pile.$O\
+ shift.$O\
+ size.$O\
+ sqrt.$O\
+ text.$O\
+ eqn.$O\
+
+YFILES=eqn.y\
+
+HFILES=e.h\
+ y.tab.h\
+
+SHORTLIB=bio 9
+<$PLAN9/src/mkone
+
+YFLAGS=-d -S
+
+eqn.c: y.tab.c prevy.tab.h
+ mv y.tab.c $target
+
+prevy.tab.h: y.tab.h
+ sh -c 'cmp -s y.tab.h prevy.tab.h || cp y.tab.h prevy.tab.h'
diff --git a/src/cmd/eqn/move.c b/src/cmd/eqn/move.c
new file mode 100644
index 00000000..a622487c
--- /dev/null
+++ b/src/cmd/eqn/move.c
@@ -0,0 +1,19 @@
+# include "e.h"
+# include "y.tab.h"
+
+void move(int dir, int amt, int p)
+{
+ double a;
+
+ yyval = p;
+ a = EM(amt/100.0, ps);
+ printf(".ds %d ", yyval);
+ if (dir == FWD || dir == BACK)
+ printf("\\h'%s%gm'\\*(%d\n", (dir==BACK) ? "-" : "", a, p);
+ else if (dir == UP)
+ printf("\\v'-%gm'\\*(%d\\v'%gm'\n", a, p, a);
+ else if (dir == DOWN)
+ printf("\\v'%gm'\\*(%d\\v'-%gm'\n", a, p, a);
+ dprintf(".\tmove %d dir %d amt %g; h=%g b=%g\n",
+ p, dir, a, eht[yyval], ebase[yyval]);
+}
diff --git a/src/cmd/eqn/o.eqn b/src/cmd/eqn/o.eqn
new file mode 100755
index 00000000..61f93ddf
--- /dev/null
+++ b/src/cmd/eqn/o.eqn
Binary files differ
diff --git a/src/cmd/eqn/over.c b/src/cmd/eqn/over.c
new file mode 100644
index 00000000..2794a739
--- /dev/null
+++ b/src/cmd/eqn/over.c
@@ -0,0 +1,35 @@
+#include "e.h"
+
+void boverb(int p1, int p2)
+{
+ int treg;
+ double h, b, d, d1, d2;
+ extern double Overgap, Overwid, Overline;
+
+ treg = salloc();
+ yyval = p1;
+ d = EM(Overgap, ps);
+ h = eht[p1] + eht[p2] + d;
+ b = eht[p2] - d;
+ dprintf(".\tS%d <- %d over %d; b=%g, h=%g\n",
+ yyval, p1, p2, b, h);
+ nrwid(p1, ps, p1);
+ nrwid(p2, ps, p2);
+ printf(".nr %d \\n(%d\n", treg, p1);
+ printf(".if \\n(%d>\\n(%d .nr %d \\n(%d\n", p2, treg, treg, p2);
+ printf(".nr %d \\n(%d+%gm\n", treg, treg, Overwid);
+ d2 = eht[p2]-ebase[p2]-d; /* denom */
+ printf(".ds %d \\v'%gm'\\h'\\n(%du-\\n(%du/2u'\\*(%d\\v'%gm'\\\n",
+ yyval, REL(d2,ps), treg, p2, p2, REL(-d2,ps));
+ d1 = 2 * d + ebase[p1]; /* num */
+ printf("\\h'-\\n(%du-\\n(%du/2u'\\v'%gm'\\*(%d\\v'%gm'\\\n",
+ p2, p1, REL(-d1,ps), p1, REL(d1,ps));
+ printf("\\h'-\\n(%du-\\n(%du/2u+%gm'\\v'%gm'\\l'\\n(%du-%gm'\\h'%gm'\\v'%gm'\n",
+ treg, p1, Overline, REL(-d,ps),
+ treg, 2*Overline, Overline, REL(d,ps));
+ ebase[yyval] = b;
+ eht[yyval] = h;
+ lfont[yyval] = rfont[yyval] = 0;
+ sfree(p2);
+ sfree(treg);
+}
diff --git a/src/cmd/eqn/paren.c b/src/cmd/eqn/paren.c
new file mode 100644
index 00000000..bb019bf7
--- /dev/null
+++ b/src/cmd/eqn/paren.c
@@ -0,0 +1,135 @@
+#include "e.h"
+
+#define abs(x) ((x) > 0 ? (x) : (-(x)))
+
+extern void brack(int, char *, char *, char *);
+
+void paren(int leftc, int p1, int rightc)
+{
+ int n, m, j;
+ double h1, b1;
+ double v, bv; /* v = shift of inside, bv = shift of brackets */
+ extern double Parenbase, Parenshift, Parenheight;
+
+ bv = ttype == DEVPOST ? Parenshift : 0; /* move brackets down this much */
+ h1 = eht[p1];
+ b1 = ebase[p1];
+ yyval = p1;
+ lfont[yyval] = rfont[yyval] = 0;
+ n = REL(h1,ps) + 0.99; /* ceiling */
+ if (n < 2)
+ n = 1;
+ m = n - 2;
+ if (leftc == '{' || rightc == '}') {
+ n = n%2 ? n : ++n;
+ if (n < 3)
+ n = 3;
+ m = n-3;
+ }
+ eht[yyval] = EM((double) n + Parenheight, ps);
+ ebase[yyval] = eht[yyval]/2 - EM(Parenbase, ps);
+
+ /* try to cope with things that are badly centered */
+ /* (top heavy or bottom heavy) */
+ if (abs(h1/2 - b1) >= EM(0.5, ps))
+ v = REL(-ebase[yyval] + (eht[yyval]-h1)/2 + b1, ps);
+ else
+ v = 0; /* don't shift it at all */
+
+ printf(".ds %d \\^", yyval); /* was \| */
+ if (bv)
+ printf("\\v'%gm'", bv);
+ switch (leftc) {
+ case 'n': /* nothing */
+ case '\0':
+ break;
+ case 'f': /* floor */
+ if (n <= 1)
+ printf("\\(lf");
+ else
+ brack(m, "\\(bv", "\\(bv", "\\(lf");
+ break;
+ case 'c': /* ceiling */
+ if (n <= 1)
+ printf("\\(lc");
+ else
+ brack(m, "\\(lc", "\\(bv", "\\(bv");
+ break;
+ case '{':
+ printf("\\b'\\(lt");
+ for(j = 0; j < m; j += 2) printf("\\(bv");
+ printf("\\(lk");
+ for(j = 0; j < m; j += 2) printf("\\(bv");
+ printf("\\(lb'");
+ break;
+ case '(':
+ brack(m, "\\(lt", "\\(bv", "\\(lb");
+ break;
+ case '[':
+ brack(m, "\\(lc", "\\(bv", "\\(lf");
+ break;
+ case '|':
+ brack(m, "|", "|", "|");
+ break;
+ default:
+ brack(m, (char *) &leftc, (char *) &leftc, (char *) &leftc);
+ break;
+ }
+ if (bv)
+ printf("\\v'%gm'", -bv);
+ if (v)
+ printf("\\v'%gm'\\*(%d\\v'%gm'", -v, p1, v);
+ else
+ printf("\\*(%d", p1);
+ if (rightc) {
+ if (bv)
+ printf("\\v'%gm'", bv);
+ switch (rightc) {
+ case 'f': /* floor */
+ if (n <= 1)
+ printf("\\(rf");
+ else
+ brack(m, "\\(bv", "\\(bv", "\\(rf");
+ break;
+ case 'c': /* ceiling */
+ if (n <= 1)
+ printf("\\(rc");
+ else
+ brack(m, "\\(rc", "\\(bv", "\\(bv");
+ break;
+ case '}':
+ printf("\\b'\\(rt");
+ for(j = 0; j < m; j += 2) printf("\\(bv");
+ printf("\\(rk");
+ for(j = 0; j < m; j += 2) printf("\\(bv");
+ printf("\\(rb'");
+ break;
+ case ']':
+ brack(m, "\\(rc", "\\(bv", "\\(rf");
+ break;
+ case ')':
+ brack(m, "\\(rt", "\\(bv", "\\(rb");
+ break;
+ case '|':
+ brack(m, "|", "|", "|");
+ break;
+ default:
+ brack(m, (char *) &rightc, (char *) &rightc, (char *) &rightc);
+ break;
+ }
+ if (bv)
+ printf("\\v'%gm'", -bv);
+ }
+ printf("\n");
+ dprintf(".\tcurly: h=%g b=%g n=%d v=%g l=%c, r=%c\n",
+ eht[yyval], ebase[yyval], n, v, leftc, rightc);
+}
+
+void brack(int m, char *t, char *c, char *b)
+{
+ int j;
+ printf("\\b'%s", t);
+ for( j=0; j < m; j++)
+ printf("%s", c);
+ printf("%s'", b);
+}
diff --git a/src/cmd/eqn/pile.c b/src/cmd/eqn/pile.c
new file mode 100644
index 00000000..a2d16239
--- /dev/null
+++ b/src/cmd/eqn/pile.c
@@ -0,0 +1,76 @@
+#include "e.h"
+#include "y.tab.h"
+
+void pile(int oct)
+{
+ int i, nlist, nlist2, mid;
+ double bi, h, b, gap, sb;
+ extern double Pilegap, Pilebase;
+ int type, p1, p2;
+
+ yyval = salloc();
+ type = lp[oct];
+ p1 = oct + 3; /* first entry */
+ p2 = p1 + lp[oct+1]; /* 1 after last */
+ gap = lp[oct+2];
+ if (gap != DEFGAP)
+ gap = EM(gap/100.0, ps);
+ else if (type == COL)
+ gap = 0;
+ else
+ gap = EM(Pilegap, ps); /* 0.4 m between LCOL, etc. */
+ nlist = p2 - p1;
+ nlist2 = (nlist+1)/2;
+ mid = p1 + nlist2 - 1;
+ h = 0;
+ for (i = p1; i < p2; i++)
+ h += eht[lp[i]];
+ eht[yyval] = h + (nlist-1)*gap;
+ b = 0;
+ for (i = p2-1; i > mid; i--)
+ b += eht[lp[i]] + gap;
+ ebase[yyval] = (nlist%2) ? b + ebase[lp[mid]]
+ : b - EM(Pilebase, ps) - gap;
+ if (dbg) {
+ printf(".\tS%d <- %d pile of:", yyval, type);
+ for (i = p1; i < p2; i++)
+ printf(" S%d", lp[i]);
+ printf("; h=%g b=%g\n", eht[yyval], ebase[yyval]);
+ }
+ nrwid(lp[p1], ps, lp[p1]);
+ printf(".nr %d \\n(%d\n", yyval, lp[p1]);
+ for (i = p1+1; i < p2; i++) {
+ nrwid(lp[i], ps, lp[i]);
+ printf(".if \\n(%d>\\n(%d .nr %d \\n(%d\n",
+ lp[i], yyval, yyval, lp[i]);
+ }
+ printf(".ds %d \\v'%gm'\\h'%du*\\n(%du'\\\n", yyval, REL(ebase[yyval],ps),
+ type==RCOL ? 1 : 0, yyval);
+ sb = 0; /* sum of box hts */
+ for (i = p2-1; i >= p1; i--) {
+ bi = sb + ebase[lp[i]];
+ switch (type) {
+ case LCOL:
+ printf("\\v'%gm'\\*(%d\\h'-\\n(%du'\\v'%gm'\\\n",
+ REL(-bi,ps), lp[i], lp[i], REL(bi,ps));
+ break;
+ case RCOL:
+ printf("\\v'%gm'\\h'-\\n(%du'\\*(%d\\v'%gm'\\\n",
+ REL(-bi,ps), lp[i], lp[i], REL(bi,ps));
+ break;
+ case CCOL:
+ case COL:
+ printf("\\v'%gm'\\h'\\n(%du-\\n(%du/2u'\\*(%d",
+ REL(-bi,ps), yyval, lp[i], lp[i]);
+ printf("\\h'-\\n(%du-\\n(%du/2u'\\v'%gm'\\\n",
+ yyval, lp[i], REL(bi,ps));
+ break;
+ }
+ sb += eht[lp[i]] + gap;
+ }
+ printf("\\v'%gm'\\h'%du*\\n(%du'\n", REL(-ebase[yyval],ps),
+ type!=RCOL ? 1 : 0, yyval);
+ for (i = p1; i < p2; i++)
+ sfree(lp[i]);
+ lfont[yyval] = rfont[yyval] = 0;
+}
diff --git a/src/cmd/eqn/prevy.tab.h b/src/cmd/eqn/prevy.tab.h
new file mode 100644
index 00000000..1f6d3c71
--- /dev/null
+++ b/src/cmd/eqn/prevy.tab.h
@@ -0,0 +1,57 @@
+#define CONTIG 57346
+#define QTEXT 57347
+#define SPACE 57348
+#define THIN 57349
+#define TAB 57350
+#define MATRIX 57351
+#define LCOL 57352
+#define CCOL 57353
+#define RCOL 57354
+#define COL 57355
+#define ABOVE 57356
+#define MARK 57357
+#define LINEUP 57358
+#define SUM 57359
+#define INT 57360
+#define PROD 57361
+#define UNION 57362
+#define INTER 57363
+#define DEFINE 57364
+#define TDEFINE 57365
+#define NDEFINE 57366
+#define DELIM 57367
+#define GSIZE 57368
+#define GFONT 57369
+#define INCLUDE 57370
+#define IFDEF 57371
+#define DOTEQ 57372
+#define DOTEN 57373
+#define FROM 57374
+#define TO 57375
+#define OVER 57376
+#define SQRT 57377
+#define SUP 57378
+#define SUB 57379
+#define SIZE 57380
+#define FONT 57381
+#define ROMAN 57382
+#define ITALIC 57383
+#define BOLD 57384
+#define FAT 57385
+#define UP 57386
+#define DOWN 57387
+#define BACK 57388
+#define FWD 57389
+#define LEFT 57390
+#define RIGHT 57391
+#define DOT 57392
+#define DOTDOT 57393
+#define HAT 57394
+#define TILDE 57395
+#define BAR 57396
+#define LOWBAR 57397
+#define HIGHBAR 57398
+#define UNDER 57399
+#define VEC 57400
+#define DYAD 57401
+#define UTILDE 57402
diff --git a/src/cmd/eqn/shift.c b/src/cmd/eqn/shift.c
new file mode 100644
index 00000000..dbac530b
--- /dev/null
+++ b/src/cmd/eqn/shift.c
@@ -0,0 +1,116 @@
+#include "e.h"
+#include "y.tab.h"
+
+void subsup(int p1, int p2, int p3)
+{
+ if (p2 != 0 && p3 != 0)
+ shift2(p1, p2, p3);
+ else if (p2 != 0)
+ bshiftb(p1, SUB, p2);
+ else if (p3 != 0)
+ bshiftb(p1, SUP, p3);
+}
+
+extern double Subbase, Supshift;
+extern char *Sub1space, *Sup1space, *Sub2space;
+extern char *SS1space, *SS2space;
+
+void bshiftb(int p1, int dir, int p2)
+{
+ int subps, n;
+ double shval, d1, h1, b1, h2, b2;
+ char *sh1, *sh2;
+
+ yyval = p1;
+ h1 = eht[p1];
+ b1 = ebase[p1];
+ h2 = eht[p2];
+ b2 = ebase[p2];
+ subps = ps;
+ ps += deltaps;
+ if (dir == SUB) {
+ /* base .2m below bottom of main box */
+ shval = b1 + EM(Subbase, ps);
+ ebase[yyval] = shval + b2;
+ eht[yyval] = max(h1-b1+shval+b2, h2);
+ if (rfont[p1] == ITAL && lfont[p2] == ROM)
+ n = 2; /* Sub1space */
+ else
+ n = max(2, class[rclass[p1]][lclass[p2]]);
+ sh1 = pad(n);
+ rclass[p1] = OTHER; /* OTHER leaves too much after sup */
+ } else { /* superscript */
+ /* 4/10 up main box */
+ d1 = EM(Subbase, subps);
+ ebase[yyval] = b1;
+ shval = -(Supshift * (h1-b1)) - b2;
+ if (Supshift*(h1-b1) + h2 < h1-b1) /* raise little super */
+ shval = -(h1-b1) + h2-b2 - d1;
+ eht[yyval] = h1 + max(0, h2 - (1-Supshift)*(h1-b1));
+ if (rclass[p1] == ILETF)
+ n = 4;
+ else if (rfont[p1] == ITAL)
+ n = 2; /* Sup1space */
+ else
+ n = max(1, class[rclass[p1]][lclass[p2]]);
+ sh1 = pad(n);
+ rclass[p1] = rclass[p2]; /* OTHER leaves too much after sup */
+ }
+ dprintf(".\tS%d <- %d shift %g %d; b=%g, h=%g, ps=%d, subps=%d\n",
+ yyval, p1, shval, p2, ebase[yyval], eht[yyval], ps, subps);
+ sh2 = Sub2space; /* was Sub2space; */
+ printf(".as %d \\v'%gm'%s%s\\*(%d%s%s\\v'%gm'\n",
+ yyval, REL(shval,ps), DPS(ps,subps), sh1, p2,
+ DPS(subps,ps), sh2, REL(-shval,ps));
+ rfont[p1] = 0;
+ sfree(p2);
+}
+
+void shift2(int p1, int p2, int p3)
+{
+ int subps;
+ double h1, h2, h3, b1, b2, b3, subsh, d2, supsh;
+ int treg;
+ char *sh2;
+
+ treg = salloc();
+ yyval = p1;
+ subps = ps; /* sub and sup at this size */
+ ps += deltaps; /* outer size */
+ h1 = eht[p1]; b1 = ebase[p1];
+ h2 = eht[p2]; b2 = ebase[p2];
+ h3 = eht[p3]; b3 = ebase[p3];
+ subsh = EM(Subbase, ps);
+ if (b1 > b2 + subsh) /* move little sub down */
+ subsh += b1;
+ eht[yyval] = max(subsh+b2-b1+h1, h2);
+ supsh = -Supshift*(h1-b1) - b3;
+ d2 = EM(Subbase, subps);
+ if (h3 < (1-Supshift)*(h1-b1))
+ supsh = -(h1-b1) + (h3-b3) - d2;
+ ebase[yyval] = subsh + b2 - b1;
+ eht[yyval] = h1 + subsh+b2-b1 + max(0, h3-(1-Supshift)*(h1-b1));
+ dprintf(".\tS%d <- %d sub %d sup %d, ps=%d, subps=%d, h=%g, b=%g\n",
+ yyval, p1, p2, p3, ps, subps, eht[yyval], ebase[yyval]);
+ if (rclass[p1] == ILETF)
+ sh2 = "\\|\\|";
+ else
+ sh2 = SS2space;
+ /*n = max(class[rclass[p1]][lclass[p2]], class[rclass[p1]][lclass[p3]]);
+ /*sh2 = pad(max(2, n));
+ */
+ printf(".ds %d %s\\*(%d\n", p2, SS1space, p2);
+ nrwid(p2, subps, p2);
+ printf(".ds %d %s\\*(%d\n", p3, sh2, p3);
+ nrwid(p3, subps, p3);
+ printf(".nr %d \\n(%d\n", treg, p3);
+ printf(".if \\n(%d>\\n(%d .nr %d \\n(%d\n", p2, treg, treg, p2);
+ printf(".as %d %s\\v'%gm'\\*(%d\\v'%gm'\\h'-\\n(%du'\\\n",
+ p1, DPS(ps,subps), REL(subsh,subps), p2, REL(-subsh,subps), p2);
+ printf("\\v'%gm'\\*(%d\\v'%gm'\\h'-\\n(%du+\\n(%du'%s%s\n",
+ REL(supsh,subps), p3, REL(-supsh,subps), p3, treg, DPS(subps,ps), Sub2space);
+ if (rfont[p2] == ITAL)
+ rfont[yyval] = 0; /* lie */
+ rclass[yyval] = rclass[p3]; /* was OTHER */
+ sfree(p2); sfree(p3); sfree(treg);
+}
diff --git a/src/cmd/eqn/size.c b/src/cmd/eqn/size.c
new file mode 100644
index 00000000..59bf3f20
--- /dev/null
+++ b/src/cmd/eqn/size.c
@@ -0,0 +1,70 @@
+#include "e.h"
+#include <ctype.h>
+
+void setsize(char *p) /* set size as found in p */
+{
+ nszstack++;
+ szstack[nszstack] = 0; /* assume relative */
+ if (*p == '+') {
+ ps += atoi(p+1);
+ if (szstack[nszstack-1] != 0) /* propagate absolute size */
+ szstack[nszstack] = ps;
+ } else if (*p == '-') {
+ ps -= atoi(p+1);
+ if (szstack[nszstack-1] != 0)
+ szstack[nszstack] = ps;
+ } else if (isdigit(*p)) {
+ if (szstack[nszstack-1] == 0)
+ printf(".nr %d \\n(.s\n", 99-nszstack);
+ else
+ printf(".nr %d %d\n", 99-nszstack, ps);
+ szstack[nszstack] = ps = atoi(p);
+ } else {
+ ERROR "illegal size %s ignored", p WARNING;
+ }
+ dprintf(".\tsetsize %s; ps = %d\n", p, ps);
+}
+
+void size(int p1, int p2)
+{
+ /* old size in p1, new in ps */
+ yyval = p2;
+ dprintf(".\tS%d <- \\s%d %d \\s%d; b=%g, h=%g\n",
+ yyval, ps, p2, p1, ebase[yyval], eht[yyval]);
+ if (szstack[nszstack] != 0) {
+ printf(".ds %d %s\\*(%d\\s\\n(%d\n", yyval, ABSPS(ps), p2, 99-nszstack);
+ } else
+ printf(".ds %d %s\\*(%d%s\n", yyval, DPS(p1,ps), p2, DPS(ps,p1));
+ nszstack--;
+ ps = p1;
+}
+
+void globsize(void)
+{
+ char temp[20];
+
+ getstr(temp, sizeof(temp));
+ if (temp[0] == '+') {
+ gsize += atoi(temp+1);
+ if (szstack[0] != 0)
+ szstack[0] = gsize;
+ } else if (temp[0] == '-') {
+ gsize -= atoi(temp+1);
+ if (szstack[0] != 0)
+ szstack[0] = gsize;
+ } else if (isdigit(temp[0])) {
+ gsize = atoi(temp);
+ szstack[0] = gsize;
+ printf(".nr 99 \\n(.s\n");
+ } else {
+ ERROR "illegal gsize %s ignored", temp WARNING;
+ }
+ yyval = eqnreg = 0;
+ ps = gsize;
+ if (gsize < 12 && !dps_set) /* sub and sup size change */
+ deltaps = gsize / 3;
+ else if (gsize < 20)
+ deltaps = gsize / 4;
+ else
+ deltaps = gsize / 5;
+}
diff --git a/src/cmd/eqn/sqrt.c b/src/cmd/eqn/sqrt.c
new file mode 100644
index 00000000..391d924e
--- /dev/null
+++ b/src/cmd/eqn/sqrt.c
@@ -0,0 +1,35 @@
+#include "e.h"
+
+void sqrt(int p2)
+{
+ static int af = 0;
+ int nps; /* point size for radical */
+ double radscale = 0.95;
+
+ if (ttype == DEVPOST)
+ radscale = 1.05;
+ nps = ps * radscale * eht[p2] / EM(1.0,ps) + 0.99; /* kludgy */
+ nps = max(EFFPS(nps), ps);
+ yyval = p2;
+ if (ttype == DEVCAT || ttype == DEVAPS)
+ eht[yyval] = EM(1.2, nps);
+ else if (ttype == DEVPOST)
+ eht[yyval] = EM(1.15, nps);
+ else /* DEV202, DEVPOST */
+ eht[yyval] = EM(1.15, nps);
+ dprintf(".\tS%d <- sqrt S%d;b=%g, h=%g, nps=%d\n",
+ yyval, p2, ebase[yyval], eht[yyval], nps);
+ printf(".as %d \\|\n", yyval);
+ nrwid(p2, ps, p2);
+ if (af++ == 0)
+ printf(".af 10 01\n"); /* make it two digits when it prints */
+ printf(".nr 10 %.3fu*\\n(.su/10\n", 9.2*eht[p2]); /* this nonsense */
+ /* guesses point size corresponding to height of stuff */
+ printf(".ds %d \\v'%gm'\\s(\\n(10", yyval, REL(ebase[p2],ps));
+ if (ttype == DEVCAT || ttype == DEVAPS)
+ printf("\\v'-.2m'\\(sr\\l'\\n(%du\\(rn'\\v'.2m'", p2);
+ else /* DEV202, DEVPOST so far */
+ printf("\\(sr\\l'\\n(%du\\(rn'", p2);
+ printf("\\s0\\v'%gm'\\h'-\\n(%du'\\^\\*(%d\n", REL(-ebase[p2],ps), p2, p2);
+ lfont[yyval] = rfont[yyval] = ROM;
+}
diff --git a/src/cmd/eqn/text.c b/src/cmd/eqn/text.c
new file mode 100644
index 00000000..261c7fbf
--- /dev/null
+++ b/src/cmd/eqn/text.c
@@ -0,0 +1,318 @@
+#include "e.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+#define CSSIZE 1000
+char cs[CSSIZE+20]; /* text string converted into this */
+char *csp; /* next spot in cs[] */
+char *psp; /* next character in input token */
+
+int lf, rf; /* temporary spots for left and right fonts */
+int lastft; /* last \f added */
+int nextft; /* next \f to be added */
+
+int pclass; /* class of previous character */
+int nclass; /* class of next character */
+
+int class[LAST][LAST] ={ /* guesswork, tuned to times roman postscript */
+
+ /*OT OL IL DG LP RP SL PL IF IJ VB */
+/*OT*/ { 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 0 }, /* OTHER */
+/*OL*/ { 1, 0, 1, 1, 1, 1, 1, 2, 2, 2, 0 }, /* OLET */
+/*IL*/ { 1, 1, 0, 1, 1, 1, 1, 3, 2, 1, 0 }, /* ILET */
+/*DG*/ { 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 0 }, /* DIG */
+/*LP*/ { 1, 1, 1, 1, 1, 2, 1, 2, 3, 3, 0 }, /* LPAR */
+/*RP*/ { 2, 2, 2, 1, 1, 1, 1, 2, 3, 3, 0 }, /* RPAR */
+/*SL*/ { 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 0 }, /* SLASH */
+/*PL*/ { 2, 2, 2, 2, 2, 2, 3, 2, 3, 2, 0 }, /* PLUS */
+/*IF*/ { 3, 3, 1, 2, 2, 3, 2, 3, 0, 1, 1 }, /* ILETF */
+/*IJ*/ { 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0 }, /* ILETJ */
+/*VB*/ { 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 1 }, /* VBAR */
+
+};
+
+extern void shim(int, int);
+extern void roman(int);
+extern void sadd(char *);
+extern void cadd(int);
+extern int trans(int, char *);
+
+int textc(void) /* read next UTF rune from psp */
+{
+ wchar_t r;
+ int w;
+
+ w = mbtowc(&r, psp, 3);
+ if(w == 0){
+ psp++;
+ return 0;
+ }
+ if(w < 0){
+ psp += 1;
+ return 0x80; /* Plan 9-ism */
+ }
+ psp += w;
+ return r;
+}
+
+void text(int t, char *p1) /* convert text string p1 of type t */
+{
+ int c;
+ char *p;
+ tbl *tp;
+
+ yyval = salloc();
+ ebase[yyval] = 0;
+ eht[yyval] = EM(1.0, ps); /* ht in ems of orig size */
+ lfont[yyval] = rfont[yyval] = ROM;
+ lclass[yyval] = rclass[yyval] = OTHER;
+ if (t == QTEXT) {
+ for (p = p1; *p; p++) /* scan for embedded \f's */
+ if (*p == '\\' && *(p+1) == 'f')
+ break;
+ if (*p) /* if found \f, leave it alone and hope */
+ p = p1;
+ else {
+ sprintf(cs, "\\f%s%s\\fP", ftp->name, p1);
+ p = cs;
+ }
+ } else if (t == SPACE)
+ p = "\\ ";
+ else if (t == THIN)
+ p = "\\|";
+ else if (t == TAB)
+ p = "\\t";
+ else if ((tp = lookup(restbl, p1)) != NULL) {
+ p = tp->cval;
+ } else {
+ lf = rf = 0;
+ lastft = 0;
+ nclass = NONE; /* get started with no class == no pad */
+ csp = cs;
+ for (psp = p1; (c = textc()) != '\0'; ) {
+ nextft = ft;
+ pclass = nclass;
+ rf = trans(c, p1);
+ if (lf == 0) {
+ lf = rf; /* left stuff is first found */
+ lclass[yyval] = nclass;
+ }
+ if (csp-cs > CSSIZE)
+ ERROR "converted token %.25s... too long", p1 FATAL ;
+ }
+ sadd("\\fP");
+ *csp = '\0';
+ p = cs;
+ lfont[yyval] = lf;
+ rfont[yyval] = rf;
+ rclass[yyval] = nclass;
+ }
+ dprintf(".\t%dtext: S%d <- %s; b=%g,h=%g,lf=%c,rf=%c,ps=%d\n",
+ t, yyval, p, ebase[yyval], eht[yyval], lfont[yyval], rfont[yyval], ps);
+ printf(".ds %d \"%s\n", yyval, p);
+}
+
+int isalpharune(int c)
+{
+ return ('a'<=c && c<='z') || ('A'<=c && c<='Z');
+}
+
+int isdigitrune(int c)
+{
+ return ('0'<=c && c<='9');
+}
+
+trans(int c, char *p1)
+{
+ int f;
+
+ if (isalpharune(c) && ft == ITAL && c != 'f' && c != 'j') { /* italic letter */
+ shim(pclass, nclass = ILET);
+ cadd(c);
+ return ITAL;
+ }
+ if (isalpharune(c) && ft != ITAL) { /* other letter */
+ shim(pclass, nclass = OLET);
+ cadd(c);
+ return ROM;
+ }
+ if (isdigitrune(c)) {
+ shim(pclass, nclass = DIG);
+ roman(c);
+ return ROM; /* this is the right side font of this object */
+ }
+ f = ROM;
+ nclass = OTHER;
+ switch (c) {
+ case ':': case ';': case '!': case '%': case '?':
+ shim(pclass, nclass);
+ roman(c);
+ return f;
+ case '(': case '[':
+ shim(pclass, nclass = LPAR);
+ roman(c);
+ return f;
+ case ')': case ']':
+ shim(pclass, nclass = RPAR);
+ roman(c);
+ return f;
+ case ',':
+ shim(pclass, nclass = OTHER);
+ roman(c);
+ return f;
+ case '.':
+ if (rf == ROM)
+ roman(c);
+ else
+ cadd(c);
+ return f;
+ case '|': /* postscript needs help with default width! */
+ shim(pclass, nclass = VBAR);
+ sadd("\\v'.17m'\\z|\\v'-.17m'\\|"); /* and height */
+ return f;
+ case '=':
+ shim(pclass, nclass = PLUS);
+ sadd("\\(eq");
+ return f;
+ case '+':
+ shim(pclass, nclass = PLUS);
+ sadd("\\(pl");
+ return f;
+ case '>':
+ case '<': /* >, >=, >>, <, <-, <=, << */
+ shim(pclass, nclass = PLUS);
+ if (*psp == '=') {
+ sadd(c == '<' ? "\\(<=" : "\\(>=");
+ psp++;
+ } else if (c == '<' && *psp == '-') { /* <- only */
+ sadd("\\(<-");
+ psp++;
+ } else if (*psp == c) { /* << or >> */
+ cadd(c);
+ cadd(c);
+ psp++;
+ } else {
+ cadd(c);
+ }
+ return f;
+ case '-':
+ shim(pclass, nclass = PLUS); /* probably too big for ->'s */
+ if (*psp == '>') {
+ sadd("\\(->");
+ psp++;
+ } else {
+ sadd("\\(mi");
+ }
+ return f;
+ case '/':
+ shim(pclass, nclass = SLASH);
+ cadd('/');
+ return f;
+ case '~':
+ case ' ':
+ sadd("\\|\\|");
+ return f;
+ case '^':
+ sadd("\\|");
+ return f;
+ case '\\': /* troff - pass only \(xx without comment */
+ shim(pclass, nclass);
+ cadd('\\');
+ cadd(c = *psp++);
+ if (c == '(' && *psp && *(psp+1)) {
+ cadd(*psp++);
+ cadd(*psp++);
+ } else
+ fprintf(stderr, "eqn warning: unquoted troff command \\%c, file %s:%d\n",
+ c, curfile->fname, curfile->lineno);
+ return f;
+ case '\'':
+ shim(pclass, nclass);
+ sadd("\\(fm");
+ return f;
+
+ case 'f':
+ if (ft == ITAL) {
+ shim(pclass, nclass = ILETF);
+ cadd('f');
+ f = ITAL;
+ } else
+ cadd('f');
+ return f;
+ case 'j':
+ if (ft == ITAL) {
+ shim(pclass, nclass = ILETJ);
+ cadd('j');
+ f = ITAL;
+ } else
+ cadd('j');
+ return f;
+ default:
+ shim(pclass, nclass);
+ cadd(c);
+ return ft==ITAL ? ITAL : ROM;
+ }
+}
+
+char *pad(int n) /* return the padding as a string */
+{
+ static char buf[20];
+
+ buf[0] = 0;
+ if (n < 0) {
+ sprintf(buf, "\\h'-%du*\\w'\\^'u'", -n);
+ return buf;
+ }
+ for ( ; n > 1; n -= 2)
+ strcat(buf, "\\|");
+ if (n > 0)
+ strcat(buf, "\\^");
+ return buf;
+}
+
+void shim(int lc, int rc) /* add padding space suitable to left and right classes */
+{
+ sadd(pad(class[lc][rc]));
+}
+
+void roman(int c) /* add char c in "roman" font */
+{
+ nextft = ROM;
+ cadd(c);
+}
+
+void sadd(char *s) /* add string s to cs */
+{
+ while (*s)
+ cadd(*s++);
+}
+
+void cadd(int c) /* add character c to end of cs */
+{
+ char *p;
+ int w;
+
+ if (lastft != nextft) {
+ if (lastft != 0) {
+ *csp++ = '\\';
+ *csp++ = 'f';
+ *csp++ = 'P';
+ }
+ *csp++ = '\\';
+ *csp++ = 'f';
+ if (ftp == ftstack) { /* bottom level */
+ if (ftp->ft == ITAL) /* usual case */
+ *csp++ = nextft;
+ else /* gfont set, use it */
+ for (p = ftp->name; *csp = *p++; )
+ csp++;
+ } else { /* inside some kind of font ... */
+ for (p = ftp->name; *csp = *p++; )
+ csp++;
+ }
+ lastft = nextft;
+ }
+ w = wctomb(csp, c);
+ if(w > 0) /* ignore bad characters */
+ csp += w;
+}
diff --git a/src/cmd/eqn/tuning.c b/src/cmd/eqn/tuning.c
new file mode 100644
index 00000000..99718db3
--- /dev/null
+++ b/src/cmd/eqn/tuning.c
@@ -0,0 +1,153 @@
+#include "e.h"
+
+/*
+
+This file contains parameter values for many of the
+tuning parameters in eqn. Names are defined words.
+
+Strings are plugged in verbatim.
+Floats are usually in ems.
+
+*/
+
+/* In main.c: */
+
+double BeforeSub = 1.2; /* line space before a subscript */
+double AfterSub = 0.2; /* line space after a subscript */
+
+/* diacrit.c: */
+
+double Dvshift = 0.25; /* vertical shift for diacriticals on tall letters */
+double Dhshift = 0.025; /* horizontal shift for tall letters */
+double Dh2shift = 0.05; /* horizontal shift for small letters */
+double Dheight = 0.25; /* increment to height for diacriticals */
+double Barv = 0.68; /* vertical shift for bar */
+double Barh = 0.05; /* 1/2 horizontal shrink for bar */
+double Ubarv = 0.1; /* shift underbar up this much ems */
+double Ubarh = 0.05; /* 1/2 horizontal shrink for underbar */
+
+/* Also:
+ Vec, Dyad, Hat, Tilde, Dot, Dotdot, Utilde */
+
+/* eqnbox.c: */
+
+char *IRspace = "\\^"; /* space between italic & roman boxes */
+
+/* fat.c: */
+
+double Fatshift = 0.05; /* fattening shifts by Fatshift ems */
+
+/* funny.c: */
+
+int Funnyps = 5; /* point size change (== 5 above) */
+double Funnyht = 0.2; /* height correction */
+double Funnybase = 0.3; /* base correction */
+
+/* integral.c: */
+
+int Intps = 4; /* point size change for integral (== 4 above) */
+double Intht = 1.15; /* ht of integral in ems */
+double Intbase = 0.3; /* base in ems */
+double Int1h = 0.4; /* lower limit left */
+double Int1v = 0.2; /* lower limit down */
+double Int2h = 0.05; /* upper limit right was 8 */
+double Int2v = 0.1; /* upper limit up */
+
+/* matrix.c: */
+
+char *Matspace = "\\ \\ "; /* space between matrix columns */
+
+/* over.c: */
+
+double Overgap = 0.3; /* gap between num and denom */
+double Overwid = 0.5; /* extra width of box */
+double Overline = 0.1; /* extra length of fraction bar */
+
+/* paren.c* */
+
+double Parenbase = 0.4; /* shift of base for even count */
+double Parenshift = 0.13; /* how much to shift parens down in left ... */
+ /* ignored unless postscript */
+double Parenheight = 0.3; /* extra height above builtups */
+
+/* pile.c: */
+
+double Pilegap = 0.4; /* gap between pile elems */
+double Pilebase = 0.5; /* shift base of even # of piled elems */
+
+/* shift.c: */
+
+double Subbase = 0.2; /* subscript base belowe main base */
+double Supshift = 0.4; /* superscript .4 up main box */
+char *Sub1space = "\\|"; /* italic sub roman space */
+char *Sup1space = "\\|"; /* italic sup roman space */
+char *Sub2space = "\\^"; /* space after subscripted thing */
+char *SS1space = "\\^"; /* space before sub in x sub i sup j */
+char *SS2space = "\\^"; /* space before sup */
+
+/* sqrt.c: */
+ /* sqrt is hard! punt for now. */
+ /* part of the problem is that every typesetter does it differently */
+ /* and we have several typesetters to run. */
+
+/* text.c: */
+ /* ought to be done by a table */
+
+struct tune {
+ char *name;
+ char *cval;
+} tune[] ={
+ /* diacrit.c */
+ "vec_def", "\\f1\\v'-.5m'\\s-3\\(->\\s0\\v'.5m'\\fP", /* was \s-2 & .45m */
+ "dyad_def", "\\f1\\v'-.5m'\\s-3\\z\\(<-\\|\\(->\\s0\\v'.5m'\\fP",
+ "hat_def", "\\f1\\v'-.05m'\\s+1^\\s0\\v'.05m'\\fP", /* was .1 */
+ "tilde_def", "\\f1\\v'-.05m'\\s+1~\\s0\\v'.05m'\\fP",
+ "dot_def", "\\f1\\v'-.67m'.\\v'.67m'\\fP",
+ "dotdot_def", "\\f1\\v'-.67m'..\\v'.67m'\\fP",
+ "utilde_def", "\\f1\\v'1.0m'\\s+2~\\s-2\\v'-1.0m'\\fP",
+ /* funny.c */
+ "sum_def", "\\|\\v'.3m'\\s+5\\(*S\\s-5\\v'-.3m'\\|",
+ "union_def", "\\|\\v'.3m'\\s+5\\(cu\\s-5\\v'-.3m'\\|",
+ "inter_def", "\\|\\v'.3m'\\s+5\\(ca\\s-5\\v'-.3m'\\|",
+ "prod_def", "\\|\\v'.3m'\\s+5\\(*P\\s-5\\v'-.3m'\\|",
+ /* integral.c */
+ "int_def", "\\v'.1m'\\s+4\\(is\\s-4\\v'-.1m'",
+ 0, 0
+};
+
+tbl *ftunetbl[TBLSIZE]; /* user-defined names */
+
+char *ftunes[] ={ /* this table intentionally left small */
+ "Subbase",
+ "Supshift",
+ 0
+};
+
+void init_tune(void)
+{
+ int i;
+
+ for (i = 0; tune[i].name != NULL; i++)
+ install(deftbl, tune[i].name, tune[i].cval, 0);
+ for (i = 0; ftunes[i] != NULL; i++)
+ install(ftunetbl, ftunes[i], (char *) 0, 0);
+}
+
+#define eq(s, t) (strcmp(s,t) == 0)
+
+void ftune(char *s, char *t) /* brute force for now */
+{
+ double f = atof(t);
+ double *target;
+
+ while (*t == ' ' || *t == '\t')
+ t++;
+ if (eq(s, "Subbase"))
+ target = &Subbase;
+ else if (eq(s, "Supshift"))
+ target = &Supshift;
+ if (t[0] == '+' || t[0] == '-')
+ *target += f;
+ else
+ *target = f;
+}
diff --git a/src/cmd/eqn/y.tab.h b/src/cmd/eqn/y.tab.h
new file mode 100644
index 00000000..1f6d3c71
--- /dev/null
+++ b/src/cmd/eqn/y.tab.h
@@ -0,0 +1,57 @@
+#define CONTIG 57346
+#define QTEXT 57347
+#define SPACE 57348
+#define THIN 57349
+#define TAB 57350
+#define MATRIX 57351
+#define LCOL 57352
+#define CCOL 57353
+#define RCOL 57354
+#define COL 57355
+#define ABOVE 57356
+#define MARK 57357
+#define LINEUP 57358
+#define SUM 57359
+#define INT 57360
+#define PROD 57361
+#define UNION 57362
+#define INTER 57363
+#define DEFINE 57364
+#define TDEFINE 57365
+#define NDEFINE 57366
+#define DELIM 57367
+#define GSIZE 57368
+#define GFONT 57369
+#define INCLUDE 57370
+#define IFDEF 57371
+#define DOTEQ 57372
+#define DOTEN 57373
+#define FROM 57374
+#define TO 57375
+#define OVER 57376
+#define SQRT 57377
+#define SUP 57378
+#define SUB 57379
+#define SIZE 57380
+#define FONT 57381
+#define ROMAN 57382
+#define ITALIC 57383
+#define BOLD 57384
+#define FAT 57385
+#define UP 57386
+#define DOWN 57387
+#define BACK 57388
+#define FWD 57389
+#define LEFT 57390
+#define RIGHT 57391
+#define DOT 57392
+#define DOTDOT 57393
+#define HAT 57394
+#define TILDE 57395
+#define BAR 57396
+#define LOWBAR 57397
+#define HIGHBAR 57398
+#define UNDER 57399
+#define VEC 57400
+#define DYAD 57401
+#define UTILDE 57402
diff --git a/src/cmd/grap/coord.c b/src/cmd/grap/coord.c
new file mode 100644
index 00000000..72f0fc3a
--- /dev/null
+++ b/src/cmd/grap/coord.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "grap.h"
+#include "y.tab.h"
+
+char *dflt_coord = "gg";
+char *curr_coord = "gg";
+int ncoord = 0; /* number of explicit coord's given */
+
+Point xcoord;
+Point ycoord;
+int xcflag = 0; /* 1 if xcoord set */
+int ycflag = 0;
+int logcoord = 0;
+
+void coord_x(Point pt) /* remember x coord */
+{
+ xcoord = pt;
+ xcflag = 1;
+ margin = 0; /* no extra space around picture if explicit coords */
+}
+
+void coord_y(Point pt)
+{
+ ycoord = pt;
+ ycflag = 1;
+ margin = 0; /* no extra space if explicit coords */
+}
+
+void coordlog(int n) /* remember log scaling */
+{
+ logcoord = n;
+}
+
+void coord(Obj *p) /* set coord range */
+{
+ static char buf[10];
+
+ ncoord++;
+ if (ncoord > 1 && strcmp(p->name, dflt_coord) == 0) {
+ /* resetting default coordinate by implication */
+ sprintf(buf, "gg%d", ncoord);
+ dflt_coord = buf;
+ p = lookup(dflt_coord, 1);
+ }
+ if (xcflag) {
+ p->coord |= XFLAG;
+ p->pt.x = min(xcoord.x,xcoord.y); /* "xcoord" is xmin, xmax */
+ p->pt1.x = max(xcoord.x,xcoord.y);
+ if ((logcoord&XFLAG) && p->pt.x <= 0.0)
+ ERROR "can't have log of x coord %g,%g", p->pt.x, p->pt1.x FATAL;
+ xcflag = 0;
+ }
+ if (ycflag) {
+ p->coord |= YFLAG;
+ p->pt.y = min(ycoord.x,ycoord.y); /* "ycoord" is ymin, ymax */
+ p->pt1.y = max(ycoord.x,ycoord.y);
+ if ((logcoord&YFLAG) && p->pt.y <= 0.0)
+ ERROR "can't have log of y coord %g,%g", p->pt.y, p->pt1.y FATAL;
+ ycflag = 0;
+ }
+ p->log = logcoord;
+ logcoord = 0;
+ auto_x = 0;
+}
+
+void resetcoord(Obj *p) /* reset current coordinate */
+{
+ curr_coord = p->name;
+}
diff --git a/src/cmd/grap/find b/src/cmd/grap/find
new file mode 100644
index 00000000..1c2e905b
--- /dev/null
+++ b/src/cmd/grap/find
@@ -0,0 +1 @@
+exec /usr/bin/egrep -n "$1" *.[chyl]
diff --git a/src/cmd/grap/for.c b/src/cmd/grap/for.c
new file mode 100644
index 00000000..84de388d
--- /dev/null
+++ b/src/cmd/grap/for.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "grap.h"
+#include "y.tab.h"
+
+typedef struct {
+ Obj *var; /* index variable */
+ double to; /* limit */
+ double by;
+ int op; /* operator */
+ char *str; /* string to push back */
+} For;
+
+#define MAXFOR 10
+
+For forstk[MAXFOR]; /* stack of for loops */
+For *forp = forstk; /* pointer to current top */
+
+void forloop(Obj *var, double from, double to, int op, double by, char *str) /* set up a for loop */
+{
+ fprintf(tfd, "# for %s from %g to %g by %c %g \n",
+ var->name, from, to, op, by);
+ if (++forp >= forstk+MAXFOR)
+ ERROR "for loop nested too deep" FATAL;
+ forp->var = var;
+ forp->to = to;
+ forp->op = op;
+ forp->by = by;
+ forp->str = str;
+ setvar(var, from);
+ nextfor();
+ unput('\n');
+}
+
+void nextfor(void) /* do one iteration of a for loop */
+{
+ /* BUG: this should depend on op and direction */
+ if (forp->var->fval > SLOP * forp->to) { /* loop is done */
+ free(forp->str);
+ if (--forp < forstk)
+ ERROR "forstk popped too far" FATAL;
+ } else { /* another iteration */
+ pushsrc(String, "\nEndfor\n");
+ pushsrc(String, forp->str);
+ }
+}
+
+void endfor(void) /* end one iteration of for loop */
+{
+ switch (forp->op) {
+ case '+':
+ case ' ':
+ forp->var->fval += forp->by;
+ break;
+ case '-':
+ forp->var->fval -= forp->by;
+ break;
+ case '*':
+ forp->var->fval *= forp->by;
+ break;
+ case '/':
+ forp->var->fval /= forp->by;
+ break;
+ }
+ nextfor();
+}
+
+char *ifstat(double expr, char *thenpart, char *elsepart)
+{
+ dprintf("if %g then <%s> else <%s>\n", expr, thenpart, elsepart? elsepart : "");
+ if (expr) {
+ unput('\n');
+ pushsrc(Free, thenpart);
+ pushsrc(String, thenpart);
+ unput('\n');
+ if (elsepart)
+ free(elsepart);
+ return thenpart; /* to be freed later */
+ } else {
+ free(thenpart);
+ if (elsepart) {
+ unput('\n');
+ pushsrc(Free, elsepart);
+ pushsrc(String, elsepart);
+ unput('\n');
+ }
+ return elsepart;
+ }
+}
diff --git a/src/cmd/grap/frame.c b/src/cmd/grap/frame.c
new file mode 100644
index 00000000..a00a9d8d
--- /dev/null
+++ b/src/cmd/grap/frame.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "grap.h"
+#include "y.tab.h"
+
+double frame_ht; /* default frame height */
+double frame_wid; /* and width */
+
+int nsides = 0; /* how many sides given on this frame */
+char *sides[] = {
+ "\tline from Frame.nw to Frame.ne",
+ "\tline from Frame.sw to Frame.se",
+ "\tline from Frame.sw to Frame.nw",
+ "\tline from Frame.se to Frame.ne"
+};
+char *newsides[4] = { 0, 0, 0, 0 }; /* filled in later */
+
+void frame(void) /* pump out frame definition, reset for next */
+{
+ int i;
+
+ fprintf(tfd, "\tframeht = %g\n", frame_ht);
+ fprintf(tfd, "\tframewid = %g\n", frame_wid);
+ fprintf(tfd, "Frame:\tbox ht frameht wid framewid with .sw at 0,0 ");
+ if (nsides == 0)
+ fprintf(tfd, "\n");
+ else {
+ fprintf(tfd, "invis\n");
+ for (i = 0; i < 4; i++) {
+ if (newsides[i]) {
+ fprintf(tfd, "%s\n", newsides[i]);
+ free(newsides[i]);
+ newsides[i] = 0;
+ } else
+ fprintf(tfd, "%s\n", sides[i]);
+ }
+ nsides = 0;
+ }
+}
+
+void frameht(double f) /* set height of frame */
+{
+ frame_ht = f;
+}
+
+void framewid(double f) /* set width of frame */
+{
+ frame_wid = f;
+}
+
+void frameside(int type, Attr *desc) /* create and remember sides */
+{
+ int n;
+ char buf[100];
+
+ nsides++;
+ switch (type) {
+ case 0: /* no side specified; kludge up all */
+ frameside(TOP, desc);
+ frameside(BOT, desc);
+ frameside(LEFT, desc);
+ frameside(RIGHT, desc);
+ return;
+ case TOP: n = 0; break;
+ case BOT: n = 1; break;
+ case LEFT: n = 2; break;
+ case RIGHT: n = 3; break;
+ }
+ sprintf(buf, "%s %s", sides[n], desc_str(desc));
+ newsides[n] = tostring(buf);
+}
diff --git a/src/cmd/grap/grap.h b/src/cmd/grap/grap.h
new file mode 100644
index 00000000..65c7f83f
--- /dev/null
+++ b/src/cmd/grap/grap.h
@@ -0,0 +1,236 @@
+extern char errbuf[200];
+#define ERROR sprintf(errbuf,
+#define FATAL ), yyerror(errbuf), exit(1)
+#define WARNING ), yyerror(errbuf)
+
+#define dprintf if(dbg)printf
+
+#define String 01
+#define Macro 02
+#define File 04
+#define Char 010
+#define Thru 020
+#define Free 040
+
+#define MARGIN 0.07 /* default margin around data */
+#define SLOP 1.001 /* slop for limits of for loops */
+#define FRAMEWID 3 /* default width for boxes and ellipses */
+#define FRAMEHT 2 /* default height and line length */
+#define TICKLEN 0.1
+
+#define MAXNUM 200
+
+#define XFLAG 01
+#define YFLAG 02
+
+#define INTICK 01
+#define OUTICK 02
+
+#define BOT 01
+#define TOP 02
+#define RIGHT 04
+#define LEFT 010
+
+#define RJUST 01
+#define LJUST 02
+#define ABOVE 04
+#define BELOW 010
+
+typedef struct infile {
+ FILE *fin;
+ char *fname;
+ int lineno;
+} Infile;
+
+typedef struct { /* input source */
+ int type; /* Macro, String, File */
+ char *sp; /* if String or Macro */
+} Src;
+
+extern Src src[], *srcp; /* input source stack */
+
+#define MAXARGS 100
+typedef struct { /* argument stack */
+ char *argstk[MAXARGS]; /* pointers to args */
+ char *argval; /* points to space containing args */
+} Arg;
+
+extern Infile infile[10];
+extern Infile *curfile;
+
+typedef struct {
+ struct obj *obj;
+ double x, y;
+} Point;
+
+typedef struct attr { /* e.g., DASH 1.1 or "..." rjust size *.5 */
+ int type;
+ double fval;
+ char *sval;
+ int just; /* justification, for STRING type */
+ int op; /* optional operator, ditto */
+ struct attr *next;
+} Attr;
+
+typedef struct obj { /* a name and its properties */
+ char *name;
+ char *val; /* body of define, etc. */
+ double fval; /* if a numeric variable */
+ Point pt; /* usually for max and min */
+ Point pt1;
+ int type; /* NAME, DEFNAME, ... */
+ int first; /* 1 after 1st item seen */
+ int coord; /* 1 if coord system specified for this name */
+ int log; /* x, y, or z (= x+y) */
+ Attr *attr; /* DASH, etc., for now */
+ struct obj *next;
+} Obj;
+
+typedef union { /* the yacc stack type */
+ int i;
+ char *p;
+ double f;
+ Point pt;
+ Obj *op;
+ Attr *ap;
+} YYSTYPE;
+
+extern YYSTYPE yylval, yyval;
+
+extern int dbg;
+
+extern int ntext;
+extern double num[MAXNUM];
+extern int nnum;
+extern int ntick, tside;
+
+extern char *tostring(char *);
+extern char *grow(char *, char *, int, int);
+
+extern int lineno;
+extern int synerr;
+extern int codegen;
+extern char tempfile[];
+extern FILE *tfd;
+
+extern Point ptmin, ptmax;
+
+extern char *dflt_coord;
+extern char *curr_coord;
+extern int ncoord;
+extern int auto_x;
+extern double margin;
+extern int autoticks;
+extern int pointsize, ps_set;
+
+
+#define logit(x) (x) = log10(x)
+#define Log10(x) errcheck(log10(x), "log")
+#define Exp(x) errcheck(exp(x), "exp")
+#define Sqrt(x) errcheck(sqrt(x), "sqrt")
+
+#define min(x,y) (((x) <= (y)) ? (x) : (y))
+#define max(x,y) (((x) >= (y)) ? (x) : (y))
+
+extern void yyerror(char *);
+extern void coord_x(Point);
+extern void coord_y(Point);
+extern void coordlog(int);
+extern void coord(Obj *);
+extern void resetcoord(Obj *);
+extern void savenum(int, double);
+extern void setjust(int);
+extern void setsize(int, double);
+extern void range(Point);
+extern void halfrange(Obj *, int, double);
+extern Obj *lookup(char *, int);
+extern double getvar(Obj *);
+extern double setvar(Obj *, double);
+extern Point makepoint(Obj *, double, double);
+extern Attr *makefattr(int, double);
+extern Attr *makesattr(char *);
+extern Attr *makeattr(int, double, char *, int, int);
+extern Attr *addattr(Attr *, Attr *);
+extern void freeattr(Attr *);
+extern char *slprint(Attr *);
+extern char *juststr(int);
+extern char *sprntf(char *, Attr *);
+extern void forloop(Obj *, double, double, int, double, char *);
+extern void nextfor(void);
+extern void endfor(void);
+extern char *ifstat(double, char *, char *);
+extern void frame(void);
+extern void frameht(double);
+extern void framewid(double);
+extern void frameside(int, Attr *);
+extern void pushsrc(int, char *);
+extern void popsrc(void);
+extern void definition(char *);
+extern char *delimstr(char *);
+extern int baldelim(int, char *);
+extern void dodef(Obj *);
+extern int getarg(char *);
+extern int input(void);
+extern int nextchar(void);
+extern void do_thru(void);
+extern int unput(int);
+extern void pbstr(char *);
+extern double errcheck(double, char *);
+extern void yyerror(char *);
+extern void eprint(void);
+extern int yywrap(void);
+extern void copyfile(char *);
+extern void copydef(Obj *);
+extern Obj *copythru(char *);
+extern char *addnewline(char *);
+extern void copyuntil(char *);
+extern void copy(void);
+extern void shell_init(void);
+extern void shell_text(char *);
+extern void shell_exec(void);
+extern void labelwid(double);
+extern void labelmove(int, double);
+extern void label(int, Attr *);
+extern void lab_adjust(void);
+extern char *sizeit(Attr *);
+extern void line(int, Point, Point, Attr *);
+extern void circle(double, Point);
+extern char *xyname(Point);
+extern void pic(char *);
+extern void numlist(void);
+extern void plot(Attr *, Point);
+extern void plotnum(double, char *, Point);
+extern void drawdesc(int, Obj *, Attr *, char *);
+extern void next(Obj *, Point, Attr *);
+extern void print(void);
+extern void endstat(void);
+extern void graph(char *);
+extern void setup(void);
+extern void do_first(void);
+extern void reset(void);
+extern void opentemp(void);
+extern void savetick(double, char *);
+extern void dflt_tick(double);
+extern void tickside(int);
+extern void tickoff(int);
+extern void gridtickoff(void);
+extern void setlist(void);
+extern void tickdir(int, double, int);
+extern void ticks(void);
+extern double modfloor(double, double);
+extern double modceil(double, double);
+extern void do_autoticks(Obj *);
+extern void logtick(double, double, double);
+extern Obj *setauto(void);
+extern void autoside(Obj *, int);
+extern void autolog(Obj *, int);
+extern void iterator(double, double, int, double, char *);
+extern void ticklist(Obj *, int);
+extern void print_ticks(int, int, Obj *, char *, char *);
+extern void maketick(int, char *, int, int, double, char *, char *, char *);
+extern void griddesc(Attr *);
+extern void gridlist(Obj *);
+extern char *desc_str(Attr *);
+extern int sidelog(int, int);
+
+extern Obj *objlist;
diff --git a/src/cmd/grap/grap.y b/src/cmd/grap/grap.y
new file mode 100644
index 00000000..6d96b2f1
--- /dev/null
+++ b/src/cmd/grap/grap.y
@@ -0,0 +1,396 @@
+%{
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "grap.h"
+
+//#define RAND_MAX 32767 /* if your rand() returns bigger, change this too */
+
+extern int yylex(void);
+extern int yyparse(void);
+
+%}
+
+%token <i> FRAME TICKS GRID LABEL COORD
+%token <i> LINE ARROW CIRCLE DRAW NEW PLOT NEXT
+%token <p> PIC
+%token <i> COPY THRU UNTIL
+%token <i> FOR FROM TO BY AT WITH
+%token <i> IF
+%token <p> GRAPH THEN ELSE DOSTR
+%token <i> DOT DASH INVIS SOLID
+%token <i> TEXT JUST SIZE
+%token <i> LOG EXP SIN COS ATAN2 SQRT RAND MAX MIN INT PRINT SPRINTF
+%token <i> X Y SIDE IN OUT OFF UP DOWN ACROSS
+%token <i> HEIGHT WIDTH RADIUS
+%token <f> NUMBER
+%token <op> NAME VARNAME DEFNAME
+%token <p> STRING
+%token <i> ST '(' ')' ','
+
+%right <f> '='
+%left <f> OR
+%left <f> AND
+%nonassoc <f> GT LT LE GE EQ NE
+%left <f> '+' '-'
+%left <f> '*' '/' '%'
+%right <f> UMINUS NOT
+%right <f> '^'
+
+%type <f> expr optexpr if_expr number assign
+%type <i> optop
+%type <p> optstring if
+%type <op> optname iterator name
+%type <pt> point
+%type <i> side optside numlist comma linetype drawtype
+%type <ap> linedesc optdesc stringlist string stringattr sattrlist exprlist
+%type <i> frameitem framelist coordlog
+%type <f> string_expr
+
+%%
+
+top:
+ graphseq { if (codegen && !synerr) graph((char *) 0); }
+ | /* empty */ { codegen = 0; }
+ | error { codegen = 0; ERROR "syntax error" WARNING; }
+ ;
+
+graphseq:
+ statlist
+ | graph statlist
+ | graphseq graph statlist
+ ;
+graph:
+ GRAPH { graph($1); endstat(); }
+ ;
+
+statlist:
+ ST
+ | stat ST { endstat(); }
+ | statlist stat ST { endstat(); }
+ ;
+
+stat:
+ FRAME framelist { codegen = 1; }
+ | ticks { codegen = 1; }
+ | grid { codegen = 1; }
+ | label { codegen = 1; }
+ | coord
+ | plot { codegen = 1; }
+ | line { codegen = 1; }
+ | circle { codegen = 1; }
+ | draw
+ | next { codegen = 1; }
+ | PIC { codegen = 1; pic($1); }
+ | for
+ | if
+ | copy
+ | numlist { codegen = 1; numlist(); }
+ | assign
+ | PRINT expr { fprintf(stderr, "\t%g\n", $2); }
+ | PRINT string { fprintf(stderr, "\t%s\n", $2->sval); freeattr($2); }
+ | /* empty */
+ ;
+
+numlist:
+ number { savenum(0, $1); $$ = 1; }
+ | numlist number { savenum($1, $2); $$ = $1+1; }
+ | numlist comma number { savenum($1, $3); $$ = $1+1; }
+ ;
+number:
+ NUMBER
+ | '-' NUMBER %prec UMINUS { $$ = -$2; }
+ | '+' NUMBER %prec UMINUS { $$ = $2; }
+ ;
+
+label:
+ LABEL optside stringlist lablist { label($2, $3); }
+ ;
+lablist:
+ labattr
+ | lablist labattr
+ | /* empty */
+ ;
+labattr:
+ UP expr { labelmove($1, $2); }
+ | DOWN expr { labelmove($1, $2); }
+ | SIDE expr { labelmove($1, $2); /* LEFT or RIGHT only */ }
+ | WIDTH expr { labelwid($2); }
+ ;
+
+framelist:
+ framelist frameitem
+ | /* empty */ { $$ = 0; }
+ ;
+frameitem:
+ HEIGHT expr { frameht($2); }
+ | WIDTH expr { framewid($2); }
+ | side linedesc { frameside($1, $2); }
+ | linedesc { frameside(0, $1); }
+ ;
+side:
+ SIDE
+ ;
+optside:
+ side
+ | /* empty */ { $$ = 0; }
+ ;
+
+linedesc:
+ linetype optexpr { $$ = makeattr($1, $2, (char *) 0, 0, 0); }
+ ;
+linetype:
+ DOT | DASH | SOLID | INVIS
+ ;
+optdesc:
+ linedesc
+ | /* empty */ { $$ = makeattr(0, 0.0, (char *) 0, 0, 0); }
+ ;
+
+ticks:
+ TICKS tickdesc { ticks(); }
+ ;
+tickdesc:
+ tickattr
+ | tickdesc tickattr
+ ;
+tickattr:
+ side { tickside($1); }
+ | IN expr { tickdir(IN, $2, 1); }
+ | OUT expr { tickdir(OUT, $2, 1); }
+ | IN { tickdir(IN, 0.0, 0); }
+ | OUT { tickdir(OUT, 0.0, 0); }
+ | AT optname ticklist { setlist(); ticklist($2, AT); }
+ | iterator { setlist(); ticklist($1, AT); }
+ | side OFF { tickoff($1); }
+ | OFF { tickoff(LEFT|RIGHT|TOP|BOT); }
+ | labattr
+ ;
+ticklist:
+ tickpoint
+ | ticklist comma tickpoint
+ ;
+tickpoint:
+ expr { savetick($1, (char *) 0); }
+ | expr string { savetick($1, $2->sval); }
+ ;
+iterator:
+ FROM optname expr TO optname expr BY optop expr optstring
+ { iterator($3, $6, $8, $9, $10); $$ = $2; }
+ | FROM optname expr TO optname expr optstring
+ { iterator($3, $6, '+', 1.0, $7); $$ = $2; }
+ ;
+optop:
+ '+' { $$ = '+'; }
+ | '-' { $$ = '-'; }
+ | '*' { $$ = '*'; }
+ | '/' { $$ = '/'; }
+ | /* empty */ { $$ = ' '; }
+ ;
+optstring:
+ string { $$ = $1->sval; }
+ | /* empty */ { $$ = (char *) 0; }
+ ;
+
+grid:
+ GRID griddesc { ticks(); }
+ ;
+griddesc:
+ gridattr
+ | griddesc gridattr
+ ;
+gridattr:
+ side { tickside($1); }
+ | X { tickside(BOT); }
+ | Y { tickside(LEFT); }
+ | linedesc { griddesc($1); }
+ | AT optname ticklist { setlist(); gridlist($2); }
+ | iterator { setlist(); gridlist($1); }
+ | TICKS OFF { gridtickoff(); }
+ | OFF { gridtickoff(); }
+ | labattr
+ ;
+
+line:
+ LINE FROM point TO point optdesc { line($1, $3, $5, $6); }
+ | LINE optdesc FROM point TO point { line($1, $4, $6, $2); }
+ ;
+circle:
+ CIRCLE RADIUS expr AT point { circle($3, $5); }
+ | CIRCLE AT point RADIUS expr { circle($5, $3); }
+ | CIRCLE AT point { circle(0.0, $3); }
+ ;
+
+stringlist:
+ string
+ | stringlist string { $$ = addattr($1, $2); }
+ ;
+string:
+ STRING sattrlist { $$ = makesattr($1); }
+ | SPRINTF '(' STRING ')' sattrlist
+ { $$ = makesattr(sprntf($3, (Attr*) 0)); }
+ | SPRINTF '(' STRING ',' exprlist ')' sattrlist
+ { $$ = makesattr(sprntf($3, $5)); }
+ ;
+exprlist:
+ expr { $$ = makefattr(NUMBER, $1); }
+ | exprlist ',' expr { $$ = addattr($1, makefattr(NUMBER, $3)); }
+ ;
+sattrlist:
+ stringattr
+ | sattrlist stringattr
+ | /* empty */ { $$ = (Attr *) 0; }
+ ;
+stringattr:
+ JUST { setjust($1); }
+ | SIZE optop expr { setsize($2, $3); }
+ ;
+
+coord:
+ COORD optname coordlist { coord($2); }
+ | COORD optname { resetcoord($2); }
+ ;
+coordlist:
+ coorditem
+ | coordlist coorditem
+ ;
+coorditem:
+ coordlog { coordlog($1); }
+ | X point { coord_x($2); }
+ | Y point { coord_y($2); }
+ | X optname expr TO expr { coord_x(makepoint($2, $3, $5)); }
+ | Y optname expr TO expr { coord_y(makepoint($2, $3, $5)); }
+ | X FROM optname expr TO expr { coord_x(makepoint($3, $4, $6)); }
+ | Y FROM optname expr TO expr { coord_y(makepoint($3, $4, $6)); }
+ ;
+coordlog:
+ LOG X { $$ = XFLAG; }
+ | LOG Y { $$ = YFLAG; }
+ | LOG X LOG Y { $$ = XFLAG|YFLAG; }
+ | LOG Y LOG X { $$ = XFLAG|YFLAG; }
+ | LOG LOG { $$ = XFLAG|YFLAG; }
+ ;
+
+plot:
+ stringlist AT point { plot($1, $3); }
+ | PLOT stringlist AT point { plot($2, $4); }
+ | PLOT expr optstring AT point { plotnum($2, $3, $5); }
+ ;
+
+draw:
+ drawtype optname linedesc { drawdesc($1, $2, $3, (char *) 0); }
+ | drawtype optname optdesc string { drawdesc($1, $2, $3, $4->sval); }
+ | drawtype optname string optdesc { drawdesc($1, $2, $4, $3->sval); }
+ ;
+drawtype:
+ DRAW
+ | NEW
+ ;
+
+next:
+ NEXT optname AT point optdesc { next($2, $4, $5); }
+
+copy:
+ COPY copylist { copy(); }
+ ;
+copylist:
+ copyattr
+ | copylist copyattr
+ ;
+copyattr:
+ string { copyfile($1->sval); }
+ | THRU DEFNAME { copydef($2); }
+ | UNTIL string { copyuntil($2->sval); }
+ ;
+
+for:
+ FOR name FROM expr TO expr BY optop expr DOSTR
+ { forloop($2, $4, $6, $8, $9, $10); }
+ | FOR name FROM expr TO expr DOSTR
+ { forloop($2, $4, $6, '+', 1.0, $7); }
+ | FOR name '=' expr TO expr BY optop expr DOSTR
+ { forloop($2, $4, $6, $8, $9, $10); }
+ | FOR name '=' expr TO expr DOSTR
+ { forloop($2, $4, $6, '+', 1.0, $7); }
+ ;
+
+if:
+ IF if_expr THEN ELSE { $$ = ifstat($2, $3, $4); }
+ | IF if_expr THEN { $$ = ifstat($2, $3, (char *) 0); }
+ ;
+if_expr:
+ expr
+ | string_expr
+ | if_expr AND string_expr { $$ = $1 && $3; }
+ | if_expr OR string_expr { $$ = $1 || $3; }
+ ;
+string_expr:
+ STRING EQ STRING { $$ = strcmp($1,$3) == 0; free($1); free($3); }
+ | STRING NE STRING { $$ = strcmp($1,$3) != 0; free($1); free($3); }
+ ;
+
+point:
+ optname expr comma expr { $$ = makepoint($1, $2, $4); }
+ | optname '(' expr comma expr ')' { $$ = makepoint($1, $3, $5); }
+ ;
+comma:
+ ',' { $$ = ','; }
+ ;
+
+optname:
+ NAME { $$ = $1; }
+ | /* empty */ { $$ = lookup(curr_coord, 1); }
+ ;
+
+expr:
+ NUMBER
+ | assign
+ | '(' string_expr ')' { $$ = $2; }
+ | VARNAME { $$ = getvar($1); }
+ | expr '+' expr { $$ = $1 + $3; }
+ | expr '-' expr { $$ = $1 - $3; }
+ | expr '*' expr { $$ = $1 * $3; }
+ | expr '/' expr { if ($3 == 0.0) {
+ ERROR "division by 0" WARNING; $3 = 1; }
+ $$ = $1 / $3; }
+ | expr '%' expr { if ((long)$3 == 0) {
+ ERROR "mod division by 0" WARNING; $3 = 1; }
+ $$ = (long)$1 % (long)$3; }
+ | '-' expr %prec UMINUS { $$ = -$2; }
+ | '+' expr %prec UMINUS { $$ = $2; }
+ | '(' expr ')' { $$ = $2; }
+ | LOG '(' expr ')' { $$ = Log10($3); }
+ | EXP '(' expr ')' { $$ = Exp($3 * log(10.0)); }
+ | expr '^' expr { $$ = pow($1, $3); }
+ | SIN '(' expr ')' { $$ = sin($3); }
+ | COS '(' expr ')' { $$ = cos($3); }
+ | ATAN2 '(' expr ',' expr ')' { $$ = atan2($3, $5); }
+ | SQRT '(' expr ')' { $$ = Sqrt($3); }
+ | RAND '(' ')' { $$ = (double)rand() / (double)RAND_MAX; }
+ | MAX '(' expr ',' expr ')' { $$ = $3 >= $5 ? $3 : $5; }
+ | MIN '(' expr ',' expr ')' { $$ = $3 <= $5 ? $3 : $5; }
+ | INT '(' expr ')' { $$ = (long) $3; }
+ | expr GT expr { $$ = $1 > $3; }
+ | expr LT expr { $$ = $1 < $3; }
+ | expr LE expr { $$ = $1 <= $3; }
+ | expr GE expr { $$ = $1 >= $3; }
+ | expr EQ expr { $$ = $1 == $3; }
+ | expr NE expr { $$ = $1 != $3; }
+ | expr AND expr { $$ = $1 && $3; }
+ | expr OR expr { $$ = $1 || $3; }
+ | NOT expr { $$ = !($2); }
+ ;
+assign:
+ name '=' expr { $$ = setvar($1, $3); }
+ ;
+
+name:
+ NAME
+ | VARNAME
+ ;
+
+optexpr:
+ expr
+ | /* empty */ { $$ = 0.0; }
+ ;
diff --git a/src/cmd/grap/grapl.lx b/src/cmd/grap/grapl.lx
new file mode 100644
index 00000000..0023aded
--- /dev/null
+++ b/src/cmd/grap/grapl.lx
@@ -0,0 +1,213 @@
+%Start A str def thru sh
+
+%{
+#undef input
+#undef unput
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "grap.h"
+#include "y.tab.h"
+
+extern struct symtab symtab[];
+
+int yyback(int *, int);
+int yylook(void);
+int yywrap(void);
+void shell_init(void), shell_exec(void), shell_text(char *);
+
+#define CADD cbuf[clen++] = yytext[0]; \
+ if (clen >= CBUFLEN-1) { \
+ ERROR "string too long", cbuf WARNING; BEGIN A; }
+#define CBUFLEN 1500
+char cbuf[CBUFLEN];
+int clen, cflag;
+int c, delim, shcnt;
+%}
+
+A [a-zA-Z_]
+B [a-zA-Z0-9_]
+D [0-9]
+WS [ \t]
+
+%%
+ if (yybgin-yysvec-1 == 0) { /* witchcraft */
+ BEGIN A;
+ }
+
+<A>{WS} ;
+<A>"\\"\n ;
+<A>\n return(ST);
+<A>";" return(ST);
+
+<A>line return(yylval.i = LINE);
+<A>arrow { yylval.i = ARROW; return(LINE); }
+<A>circle return(yylval.i = CIRCLE);
+<A>frame return(FRAME);
+<A>tick(s)? return(TICKS);
+<A>grid(line)?(s)? return(GRID);
+<A>coord(s)? return(COORD);
+<A>log return(LOG);
+<A>exp return(EXP);
+<A>sin return(SIN);
+<A>cos return(COS);
+<A>atan2 return(ATAN2);
+<A>sqrt return(SQRT);
+<A>rand return(RAND);
+<A>max return(MAX);
+<A>min return(MIN);
+<A>int return(INT);
+<A>print return(PRINT);
+<A>sprintf return(SPRINTF);
+<A>pic{WS}.* { yylval.p = tostring(yytext+3); return(PIC); }
+<A>graph{WS}.* { yylval.p = tostring(yytext+5); return(GRAPH); }
+
+<A>for return(FOR);
+<A>^Endfor\n { endfor(); }
+<A>do { yylval.p = delimstr("loop body"); BEGIN A; return(DOSTR); }
+
+<A>copy|include { return(COPY); }
+<A>thru|through { BEGIN thru; return(THRU); }
+<thru>{WS}+ ;
+<thru>{A}{B}*|. { yylval.op = copythru(yytext); BEGIN A; return(DEFNAME); }
+<A>until return(UNTIL);
+
+<A>if return(IF);
+<A>then { yylval.p = delimstr("then part"); BEGIN A; return(THEN); }
+<A>else { yylval.p = delimstr("else part"); BEGIN A; return(ELSE); }
+
+<A>next return(NEXT);
+<A>draw return(yylval.i = DRAW);
+<A>new return(yylval.i = NEW);
+<A>plot return(yylval.i = PLOT);
+<A>label(s)? return(LABEL);
+<A>x return(X);
+<A>y return(Y);
+
+<A>top { yylval.i = TOP; return SIDE; }
+<A>bot(tom)? { yylval.i = BOT; return SIDE; }
+<A>left { yylval.i = LEFT; return SIDE; }
+<A>right { yylval.i = RIGHT; return SIDE; }
+<A>up return(yylval.i = UP);
+<A>down return(yylval.i = DOWN);
+<A>across return(yylval.i = ACROSS);
+<A>height|ht return(yylval.i = HEIGHT);
+<A>wid(th)? return(yylval.i = WIDTH);
+<A>rad(ius)? return(yylval.i = RADIUS);
+<A>invis return(yylval.i = INVIS);
+<A>dot(ted) return(yylval.i = DOT);
+<A>dash(ed) return(yylval.i = DASH);
+<A>solid return(yylval.i = SOLID);
+
+<A>ljust { yylval.i = LJUST; return JUST; }
+<A>rjust { yylval.i = RJUST; return JUST; }
+<A>above { yylval.i = ABOVE; return JUST; }
+<A>below { yylval.i = BELOW; return JUST; }
+<A>size return(yylval.i = SIZE);
+
+<A>from return(yylval.i = FROM);
+<A>to return(yylval.i = TO);
+<A>by|step return(yylval.i = BY);
+<A>at return(yylval.i = AT);
+<A>with return(yylval.i = WITH);
+<A>in return(yylval.i = IN);
+<A>out return(yylval.i = OUT);
+<A>off return(yylval.i = OFF);
+
+<A>sh{WS}+ { BEGIN sh;
+ if ((delim = input()) == '{') {
+ shcnt = 1;
+ delim = '}';
+ }
+ shell_init();
+ }
+<sh>{A}{B}* {
+ int c;
+ Obj *p;
+ if (yytext[0] == delim) {
+ shell_exec();
+ BEGIN A;
+ } else {
+ p = lookup(yytext, 0);
+ if (p != NULL && p->type == DEFNAME) {
+ c = input();
+ unput(c);
+ if (c == '(')
+ dodef(p);
+ else
+ pbstr(p->val);
+ } else
+ shell_text(yytext);
+ }
+ }
+<sh>"{" { shcnt++; shell_text(yytext); }
+<sh>"}" { if (delim != '}' || --shcnt > 0)
+ shell_text(yytext);
+ else {
+ shell_exec();
+ BEGIN A;
+ }
+ }
+<sh>.|\n { if (yytext[0] == delim) {
+ shell_exec();
+ BEGIN A;
+ } else
+ shell_text(yytext);
+ }
+
+<A>define{WS}+ { BEGIN def; }
+<def>{A}{B}* { definition(yytext); BEGIN A; }
+
+<A>({D}+("."?){D}*|"."{D}+)((e|E)("+"|-)?{D}+)?i? {
+ yylval.f = atof(yytext); return(NUMBER); }
+
+<A>^"."[^0-9].* { if (yytext[1] == 'G' && yytext[2] == '2') {
+ yylval.i = yytext[2];
+ return(EOF);
+ } else {
+ yylval.p = tostring(yytext);
+ return(PIC);
+ }
+ }
+
+<A>{A}{B}* {
+ int c;
+ Obj *p;
+ p = lookup(yytext, 1);
+ if (p->type == DEFNAME) {
+ c = input();
+ unput(c);
+ if (c == '(') /* it's name(...) */
+ dodef(p);
+ else /* no argument list */
+ pbstr(p->val);
+ } else {
+ yylval.op = p;
+ return p->type; /* NAME or VARNAME */
+ }
+ }
+
+<A>"==" return(EQ);
+<A>">=" return(GE);
+<A>"<=" return(LE);
+<A>"!=" return(NE);
+<A>">" return(GT);
+<A>"<" return(LT);
+<A>"&&" return(AND);
+<A>"||" return(OR);
+<A>"!" return(NOT);
+
+<A>\" { BEGIN str; clen = 0; }
+
+<A>#.* ;
+
+<A>. { yylval.i = yytext[0]; return(yytext[0]); }
+
+<str>\" { BEGIN A; cbuf[clen] = 0;
+ yylval.p = tostring(cbuf); return(STRING); }
+<str>\n { ERROR "newline in string" WARNING; BEGIN A; return(ST); }
+<str>"\\\"" { cbuf[clen++] = '\\'; cbuf[clen++] = '"'; }
+<str>"\\\\" { cbuf[clen++] = '\\'; cbuf[clen++] = '\\'; }
+<str>. { CADD; }
+
+%%
diff --git a/src/cmd/grap/input.c b/src/cmd/grap/input.c
new file mode 100644
index 00000000..f558145e
--- /dev/null
+++ b/src/cmd/grap/input.c
@@ -0,0 +1,580 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include "grap.h"
+#include "y.tab.h"
+
+Infile infile[10];
+Infile *curfile = infile;
+
+#define MAXSRC 50
+Src src[MAXSRC]; /* input source stack */
+Src *srcp = src;
+
+void pushsrc(int type, char *ptr) /* new input source */
+{
+ if (++srcp >= src + MAXSRC)
+ ERROR "inputs nested too deep" FATAL;
+ srcp->type = type;
+ srcp->sp = ptr;
+ if (dbg) {
+ printf("\n%3d ", srcp - src);
+ switch (srcp->type) {
+ case File:
+ printf("push file %s\n", ((Infile *)ptr)->fname);
+ break;
+ case Macro:
+ printf("push macro <%s>\n", ptr);
+ break;
+ case Char:
+ printf("push char <%c>\n", *ptr);
+ break;
+ case Thru:
+ printf("push thru\n");
+ break;
+ case String:
+ printf("push string <%s>\n", ptr);
+ break;
+ case Free:
+ printf("push free <%s>\n", ptr);
+ break;
+ default:
+ ERROR "pushed bad type %d", srcp->type FATAL;
+ }
+ }
+}
+
+void popsrc(void) /* restore an old one */
+{
+ if (srcp <= src)
+ ERROR "too many inputs popped" FATAL;
+ if (dbg) {
+ printf("%3d ", srcp - src);
+ switch (srcp->type) {
+ case File:
+ printf("pop file\n");
+ break;
+ case Macro:
+ printf("pop macro\n");
+ break;
+ case Char:
+ printf("pop char <%c>\n", *srcp->sp);
+ break;
+ case Thru:
+ printf("pop thru\n");
+ break;
+ case String:
+ printf("pop string\n");
+ break;
+ case Free:
+ printf("pop free\n");
+ break;
+ default:
+ ERROR "pop weird input %d", srcp->type FATAL;
+ }
+ }
+ srcp--;
+}
+
+void definition(char *s) /* collect definition for s and install */
+ /* definitions picked up lexically */
+{
+ char *p;
+ Obj *stp;
+
+ p = delimstr("definition");
+ stp = lookup(s, 0);
+ if (stp != NULL) { /* it's there before */
+ if (stp->type != DEFNAME) {
+ ERROR "%s used as variable and definition", s WARNING;
+ return;
+ }
+ free(stp->val);
+ } else {
+ stp = lookup(s, 1);
+ stp->type = DEFNAME;
+ }
+ stp->val = p;
+ dprintf("installing %s as `%s'\n", s, p);
+}
+
+char *delimstr(char *s) /* get body of X ... X */
+ /* message if too big */
+{
+ int c, delim, rdelim, n, deep;
+ static char *buf = NULL;
+ static int nbuf = 0;
+ char *p;
+
+ if (buf == NULL)
+ buf = grow(buf, "buf", nbuf += 1000, sizeof(buf[0]));
+ while ((delim = input()) == ' ' || delim == '\t' || delim == '\n')
+ ;
+ rdelim = baldelim(delim, "{}"); /* could be "(){}[]`'" */
+ deep = 1;
+ for (p = buf; ; ) {
+ c = input();
+ if (c == rdelim)
+ if (--deep == 0)
+ break;
+ if (c == delim)
+ deep++;
+ if (p >= buf + nbuf) {
+ n = p - buf;
+ buf = grow(buf, "buf", nbuf += 1000, sizeof(buf[0]));
+ p = buf + n;
+ }
+ if (c == EOF)
+ ERROR "end of file in %s %c %.20s... %c", s, delim, buf, delim FATAL;
+ *p++ = c;
+ }
+ *p = '\0';
+ dprintf("delimstr %s %c <%s> %c\n", s, delim, buf, delim);
+ return tostring(buf);
+}
+
+baldelim(int c, char *s) /* replace c by balancing entry in s */
+{
+ for ( ; *s; s += 2)
+ if (*s == c)
+ return s[1];
+ return c;
+}
+
+Arg args[10]; /* argument frames */
+Arg *argfp = args; /* frame pointer */
+int argcnt; /* number of arguments seen so far */
+
+void dodef(Obj *stp) /* collect args and switch input to defn */
+{
+ int i, len;
+ char *p;
+ Arg *ap;
+
+ ap = argfp+1;
+ if (ap >= args+10)
+ ERROR "arguments too deep" FATAL;
+ argcnt = 0;
+ if (input() != '(')
+ ERROR "disaster in dodef" FATAL;
+ if (ap->argval == 0)
+ ap->argval = malloc(1000);
+ for (p = ap->argval; (len = getarg(p)) != -1; p += len) {
+ ap->argstk[argcnt++] = p;
+ if (input() == ')')
+ break;
+ }
+ for (i = argcnt; i < MAXARGS; i++)
+ ap->argstk[i] = "";
+ if (dbg)
+ for (i = 0; i < argcnt; i++)
+ printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
+ argfp = ap;
+ pushsrc(Macro, stp->val);
+}
+
+getarg(char *p) /* pick up single argument, store in p, return length */
+{
+ int n, c, npar;
+
+ n = npar = 0;
+ for ( ;; ) {
+ c = input();
+ if (c == EOF)
+ ERROR "end of file in getarg!" FATAL;
+ if (npar == 0 && (c == ',' || c == ')'))
+ break;
+ if (c == '"') /* copy quoted stuff intact */
+ do {
+ *p++ = c;
+ n++;
+ } while ((c = input()) != '"' && c != EOF);
+ else if (c == '(')
+ npar++;
+ else if (c == ')')
+ npar--;
+ n++;
+ *p++ = c;
+ }
+ *p = 0;
+ unput(c);
+ return(n + 1);
+}
+
+#define PBSIZE 2000
+char pbuf[PBSIZE]; /* pushback buffer */
+char *pb = pbuf-1; /* next pushed back character */
+
+char ebuf[200]; /* collect input here for error reporting */
+char *ep = ebuf;
+
+int begin = 0;
+extern int thru;
+extern Obj *thrudef;
+extern char *untilstr;
+
+input(void)
+{
+ register int c;
+
+ if (thru && begin) {
+ do_thru();
+ begin = 0;
+ }
+ c = nextchar();
+ dprintf(" <%c>", c);
+ if (ep >= ebuf + sizeof ebuf)
+ ep = ebuf;
+ return *ep++ = c;
+}
+
+nextchar(void)
+{
+ register int c;
+
+ loop:
+ switch (srcp->type) {
+ case Free: /* free string */
+ free(srcp->sp);
+ popsrc();
+ goto loop;
+ case Thru: /* end of pushed back line */
+ begin = 1;
+ popsrc();
+ c = '\n';
+ break;
+ case Char:
+ if (pb >= pbuf) {
+ c = *pb--;
+ popsrc();
+ break;
+ } else { /* can't happen? */
+ popsrc();
+ goto loop;
+ }
+ case String:
+ c = *srcp->sp++;
+ if (c == '\0') {
+ popsrc();
+ goto loop;
+ } else {
+ if (*srcp->sp == '\0') /* empty, so pop */
+ popsrc();
+ break;
+ }
+ case Macro:
+ c = *srcp->sp++;
+ if (c == '\0') {
+ if (--argfp < args)
+ ERROR "argfp underflow" FATAL;
+ popsrc();
+ goto loop;
+ } else if (c == '$' && isdigit(*srcp->sp)) { /* $3 */
+ int n = 0;
+ while (isdigit(*srcp->sp))
+ n = 10 * n + *srcp->sp++ - '0';
+ if (n > 0 && n <= MAXARGS)
+ pushsrc(String, argfp->argstk[n-1]);
+ goto loop;
+ }
+ break;
+ case File:
+ c = getc(curfile->fin);
+ if (c == EOF) {
+ if (curfile == infile)
+ ERROR "end of file inside .G1/.G2" FATAL;
+ if (curfile->fin != stdin) {
+ fclose(curfile->fin);
+ free(curfile->fname); /* assumes allocated */
+ }
+ curfile--;
+ printf(".lf %d %s\n", curfile->lineno, curfile->fname);
+ popsrc();
+ thru = 0; /* chicken out */
+ thrudef = 0;
+ if (untilstr) {
+ free(untilstr);
+ untilstr = 0;
+ }
+ goto loop;
+ }
+ if (c == '\n')
+ curfile->lineno++;
+ break;
+ }
+ return c;
+}
+
+void do_thru(void) /* read one line, make into a macro expansion */
+{
+ int c, i;
+ char *p;
+ Arg *ap;
+
+ ap = argfp+1;
+ if (ap >= args+10)
+ ERROR "arguments too deep" FATAL;
+ if (ap->argval == NULL)
+ ap->argval = malloc(1000);
+ p = ap->argval;
+ argcnt = 0;
+ c = nextchar();
+ if (thru == 0) { /* end of file was seen, so thru is done */
+ unput(c);
+ return;
+ }
+ for ( ; c != '\n' && c != EOF; ) {
+ if (c == ' ' || c == '\t') {
+ c = nextchar();
+ continue;
+ }
+ if (argcnt >= MAXARGS)
+ ERROR "too many fields on input line" FATAL;
+ ap->argstk[argcnt++] = p;
+ if (c == '"') {
+ do {
+ *p++ = c;
+ if ((c = nextchar()) == '\\') {
+ *p++ = c;
+ *p++ = nextchar();
+ c = nextchar();
+ }
+ } while (c != '"' && c != '\n' && c != EOF);
+ *p++ = '"';
+ if (c == '"')
+ c = nextchar();
+ } else {
+ do {
+ *p++ = c;
+ } while ((c = nextchar())!=' ' && c!='\t' && c!='\n' && c!=',' && c!=EOF);
+ if (c == ',')
+ c = nextchar();
+ }
+ *p++ = '\0';
+ }
+ if (c == EOF)
+ ERROR "unexpected end of file in do_thru" FATAL;
+ if (argcnt == 0) { /* ignore blank line */
+ pushsrc(Thru, (char *) 0);
+ return;
+ }
+ for (i = argcnt; i < MAXARGS; i++)
+ ap->argstk[i] = "";
+ if (dbg)
+ for (i = 0; i < argcnt; i++)
+ printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
+ if (strcmp(ap->argstk[0], ".G2") == 0) {
+ thru = 0;
+ thrudef = 0;
+ pushsrc(String, "\n.G2\n");
+ return;
+ }
+ if (untilstr && strcmp(ap->argstk[0], untilstr) == 0) {
+ thru = 0;
+ thrudef = 0;
+ free(untilstr);
+ untilstr = 0;
+ return;
+ }
+ pushsrc(Thru, (char *) 0);
+ dprintf("do_thru pushing back <%s>\n", thrudef->val);
+ argfp = ap;
+ pushsrc(Macro, thrudef->val);
+}
+
+unput(int c)
+{
+ if (++pb >= pbuf + sizeof pbuf)
+ ERROR "pushback overflow" FATAL;
+ if (--ep < ebuf)
+ ep = ebuf + sizeof(ebuf) - 1;
+ *pb = c;
+ pushsrc(Char, pb);
+ return c;
+}
+
+void pbstr(char *s)
+{
+ pushsrc(String, s);
+}
+
+double errcheck(double x, char *s)
+{
+ extern int errno;
+
+ if (errno == EDOM) {
+ errno = 0;
+ ERROR "%s argument out of domain", s WARNING;
+ } else if (errno == ERANGE) {
+ errno = 0;
+ ERROR "%s result out of range", s WARNING;
+ }
+ return x;
+}
+
+char errbuf[200];
+
+void yyerror(char *s)
+{
+ extern char *cmdname;
+ int ern = errno; /* cause some libraries clobber it */
+
+ if (synerr)
+ return;
+ fflush(stdout);
+ fprintf(stderr, "%s: %s", cmdname, s);
+ if (ern > 0) {
+ errno = ern;
+ perror("???");
+ }
+ fprintf(stderr, " near %s:%d\n",
+ curfile->fname, curfile->lineno+1);
+ eprint();
+ synerr = 1;
+ errno = 0;
+}
+
+void eprint(void) /* try to print context around error */
+{
+ char *p, *q;
+
+ p = ep - 1;
+ if (p > ebuf && *p == '\n')
+ p--;
+ for ( ; p >= ebuf && *p != '\n'; p--)
+ ;
+ while (*p == '\n')
+ p++;
+ fprintf(stderr, " context is\n\t");
+ for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
+ ;
+ for (; p < q; p++)
+ if (isprint(*p))
+ putc(*p, stderr);
+ fprintf(stderr, " >>> ");
+ for (; p < q; p++)
+ if (isprint(*p))
+ putc(*p, stderr);
+ fprintf(stderr, " <<< ");
+ while (pb >= pbuf)
+ putc(*pb--, stderr);
+ fgets(ebuf, sizeof ebuf, curfile->fin);
+ fprintf(stderr, "%s", ebuf);
+ pbstr("\n.G2\n"); /* safety first */
+ ep = ebuf;
+}
+
+int yywrap(void) {return 1;}
+
+char *newfile = 0; /* filename for file copy */
+char *untilstr = 0; /* string that terminates a thru */
+int thru = 0; /* 1 if copying thru macro */
+Obj *thrudef = 0; /* macro being used */
+
+void copyfile(char *s) /* remember file to start reading from */
+{
+ newfile = s;
+}
+
+void copydef(Obj *p) /* remember macro Obj */
+{
+ thrudef = p;
+}
+
+Obj *copythru(char *s) /* collect the macro name or body for thru */
+{
+ Obj *p;
+ char *q;
+
+ p = lookup(s, 0);
+ if (p != NULL) {
+ if (p->type == DEFNAME) {
+ p->val = addnewline(p->val);
+ return p;
+ } else
+ ERROR "%s used as define and name", s FATAL;
+ }
+ /* have to collect the definition */
+ pbstr(s); /* first char is the delimiter */
+ q = delimstr("thru body");
+ p = lookup("nameless", 1);
+ if (p != NULL)
+ if (p->val)
+ free(p->val);
+ p->type = DEFNAME;
+ p->val = q;
+ p->val = addnewline(p->val);
+ dprintf("installing nameless as `%s'\n", p->val);
+ return p;
+}
+
+char *addnewline(char *p) /* add newline to end of p */
+{
+ int n;
+
+ n = strlen(p);
+ if (p[n-1] != '\n') {
+ p = realloc(p, n+2);
+ p[n] = '\n';
+ p[n+1] = '\0';
+ }
+ return p;
+}
+
+void copyuntil(char *s) /* string that terminates a thru */
+{
+ untilstr = s;
+}
+
+void copy(void) /* begin input from file, etc. */
+{
+ FILE *fin;
+
+ if (newfile) {
+ if ((fin = fopen(unsharp(newfile), "r")) == NULL)
+ ERROR "can't open file %s", newfile FATAL;
+ curfile++;
+ curfile->fin = fin;
+ curfile->fname = tostring(newfile);
+ curfile->lineno = 0;
+ printf(".lf 1 %s\n", curfile->fname);
+ pushsrc(File, curfile->fname);
+ newfile = 0;
+ }
+ if (thrudef) {
+ thru = 1;
+ begin = 1; /* wrong place */
+ }
+}
+
+char shellbuf[1000], *shellp;
+
+void shell_init(void) /* set up to interpret a shell command */
+{
+ fprintf(tfd, "# shell cmd...\n");
+ sprintf(shellbuf, "rc -c '");
+ shellp = shellbuf + strlen(shellbuf);
+}
+
+void shell_text(char *s) /* add string to command being collected */
+{
+ /* fprintf(tfd, "#add <%s> to <%s>\n", s, shellbuf); */
+ while (*s) {
+ if (*s == '\'') { /* protect interior quotes */
+ *shellp++ = '\'';
+ *shellp++ = '\\';
+ *shellp++ = '\'';
+ }
+ *shellp++ = *s++;
+ }
+}
+
+void shell_exec(void) /* do it */
+{
+ /* fprintf(tfd, "# run <%s>\n", shellbuf); */
+ *shellp++ = '\'';
+ *shellp = '\0';
+ system(shellbuf);
+}
diff --git a/src/cmd/grap/label.c b/src/cmd/grap/label.c
new file mode 100644
index 00000000..e788dd9d
--- /dev/null
+++ b/src/cmd/grap/label.c
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include <string.h>
+#include "grap.h"
+#include "y.tab.h"
+
+int pointsize = 10; /* assumed pointsize to start */
+int ps_set = 0; /* someone has set pointsize explicitly */
+
+double textht = 1.0/6.0; /* 6 lines/inch */
+double textwid = 1; /* width of text box for vertical */
+
+double lab_up = 0.0; /* extra motion for label */
+double lab_rt = 0.0; /* extra motion for label */
+double lab_wid = 0.0; /* override default width computation */
+
+void labelwid(double amt)
+{
+ lab_wid = amt + .00001;
+}
+
+void labelmove(int dir, double amt) /* record direction & motion of position corr */
+{
+ switch (dir) {
+ case UP: lab_up += amt; break;
+ case DOWN: lab_up -= amt; break;
+ case LEFT: lab_rt -= amt; break;
+ case RIGHT: lab_rt += amt; break;
+ }
+}
+
+void label(int label_side, Attr *stringlist) /* stick label on label_side */
+{
+ int m;
+ Attr *ap;
+
+ fprintf(tfd, "\ttextht = %g\n", textht);
+ if (lab_wid != 0.0) {
+ fprintf(tfd, "\ttextwid = %g\n", lab_wid);
+ lab_wid = 0;
+ } else if (label_side == LEFT || label_side == RIGHT) {
+ textwid = 0;
+ for (ap = stringlist; ap != NULL; ap = ap->next)
+ if ((m = strlen(ap->sval)) > textwid)
+ textwid = m;
+ textwid /= 15; /* estimate width at 15 chars/inch */
+ fprintf(tfd, "\ttextwid = %g\n", textwid);
+ }
+ fprintf(tfd, "Label:\t%s", slprint(stringlist));
+ freeattr(stringlist);
+ switch (label_side) {
+ case BOT:
+ case 0:
+ fprintf(tfd, " with .n at Frame.s - (0,2 * textht)");
+ break;
+ case LEFT:
+ fprintf(tfd, " wid textwid with .e at Frame.w - (0.2,0)");
+ break;
+ case RIGHT:
+ fprintf(tfd, " wid textwid with .w at Frame.e + (0.2,0)");
+ break;
+ case TOP:
+ fprintf(tfd, " with .s at Frame.n + (0,2 * textht)");
+ break;
+ }
+ lab_adjust();
+ fprintf(tfd, "\n");
+ label_side = BOT;
+}
+
+void lab_adjust(void) /* add a string to adjust labels, ticks, etc. */
+{
+ if (lab_up != 0.0 || lab_rt != 0.0)
+ fprintf(tfd, " + (%g,%g)", lab_rt, lab_up);
+}
+
+char *sizeit(Attr *ap) /* add \s..\s to ap->sval */
+{
+ int n;
+ static char buf[1000];
+
+ if (!ap->op) { /* no explicit size command */
+ if (ps_set) {
+ sprintf(buf, "\\s%d%s\\s0", pointsize, ap->sval);
+ return buf;
+ } else
+ return ap->sval;
+ } else if (!ps_set) { /* explicit size but no global size */
+ n = (int) ap->fval;
+ switch (ap->op) {
+ case ' ': /* absolute size */
+ sprintf(buf, "\\s%d%s\\s0", n, ap->sval);
+ break;
+ case '+': /* better be only one digit! */
+ sprintf(buf, "\\s+%d%s\\s-%d", n, ap->sval, n);
+ break;
+ case '-':
+ sprintf(buf, "\\s-%d%s\\s+%d", n, ap->sval, n);
+ break;
+ case '*':
+ case '/':
+ return ap->sval; /* ignore for now */
+ }
+ return buf;
+ } else {
+ /* explicit size and a global background size */
+ n = (int) ap->fval;
+ switch (ap->op) {
+ case ' ': /* absolute size */
+ sprintf(buf, "\\s%d%s\\s0", n, ap->sval);
+ break;
+ case '+':
+ sprintf(buf, "\\s%d%s\\s0", pointsize+n, ap->sval);
+ break;
+ case '-':
+ sprintf(buf, "\\s%d%s\\s0", pointsize-n, ap->sval);
+ break;
+ case '*':
+ case '/':
+ return ap->sval; /* ignore for now */
+ }
+ return buf;
+ }
+}
diff --git a/src/cmd/grap/main.c b/src/cmd/grap/main.c
new file mode 100644
index 00000000..0aa67417
--- /dev/null
+++ b/src/cmd/grap/main.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <signal.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "grap.h"
+#include "y.tab.h"
+
+int dbg = 0;
+
+#ifndef GRAPDEFINES
+#define GRAPDEFINES "#9/sys/lib/grap.defines"
+#endif
+char *lib_defines = GRAPDEFINES;
+
+int lib = 1; /* 1 to include lib_defines */
+FILE *tfd = NULL;
+char tempfile[L_tmpnam];
+
+int synerr = 0;
+int codegen = 0; /* 1=>output for this picture; 0=>no output */
+char *cmdname;
+
+Obj *objlist = NULL; /* all names stored here */
+
+#define BIG 1e30
+Point ptmin = { NULL, -BIG, -BIG };
+Point ptmax = { NULL, BIG, BIG };
+
+char *version = "version Dec 30, 1995";
+
+extern int yyparse(void);
+extern void setdefaults(void);
+extern void getdata(void);
+extern int unlink(char *);
+
+main(int argc, char *argv[])
+{
+ extern void onintr(int), fpecatch(int);
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, onintr);
+ signal(SIGFPE, fpecatch);
+ cmdname = argv[0];
+ tmpnam(tempfile);
+ while (argc > 1 && *argv[1] == '-') {
+ switch (argv[1][1]) {
+ case 'd':
+ dbg = 1;
+ tfd = stdout;
+ strcpy(tempfile, "grap.temp");
+ unlink(tempfile);
+ fprintf(stderr, "%s\n", version);
+ break;
+ case 'l': /* turn off /usr/lib inclusion */
+ lib = 0;
+ break;
+ }
+ argc--;
+ argv++;
+ }
+ setdefaults();
+ curfile = infile;
+ if (argc <= 1) {
+ curfile->fin = stdin;
+ curfile->fname = tostring("-");
+ pushsrc(File, curfile->fname);
+ getdata();
+ } else
+ while (argc-- > 1) {
+ if ((curfile->fin = fopen(*++argv, "r")) == NULL) {
+ fprintf(stderr, "grap: can't open %s\n", *argv);
+ onintr(0);
+ }
+ curfile->fname = tostring(*argv);
+ pushsrc(File, curfile->fname);
+ getdata();
+ fclose(curfile->fin);
+ free(curfile->fname);
+ }
+ if (!dbg)
+ unlink(tempfile);
+ exit(0);
+}
+
+void onintr(int n)
+{
+ n;
+ if (!dbg)
+ unlink(tempfile);
+ exit(1);
+}
+
+void fpecatch(int n)
+{
+ ERROR "floating point exception" WARNING;
+ onintr(n);
+}
+
+char *grow(char *ptr, char *name, int num, int size) /* make array bigger */
+{
+ char *p;
+
+ if (ptr == NULL)
+ p = malloc(num * size);
+ else
+ p = realloc(ptr, num * size);
+ if (p == NULL)
+ ERROR "can't grow %s to %d", name, num * size FATAL;
+ return p;
+}
+
+static struct {
+ char *name;
+ double val;
+} defaults[] ={
+ "frameht", FRAMEHT,
+ "framewid", FRAMEWID,
+ "ticklen", TICKLEN,
+ "slop", SLOP,
+ NULL, 0
+};
+
+void setdefaults(void) /* set default sizes for variables */
+{
+ int i;
+ Obj *p;
+
+ for (i = 0; defaults[i].name != NULL; i++) {
+ p = lookup(defaults[i].name, 1);
+ setvar(p, defaults[i].val);
+ }
+}
+
+void getdata(void) /* read input */
+{
+ register FILE *fin;
+ char buf[1000], buf1[100];
+ int ln;
+
+ fin = curfile->fin;
+ curfile->lineno = 0;
+ printf(".lf 1 %s\n", curfile->fname);
+ while (fgets(buf, sizeof buf, fin) != NULL) {
+ curfile->lineno++;
+ if (*buf == '.' && *(buf+1) == 'G' && *(buf+2) == '1') {
+ setup();
+ fprintf(stdout, ".PS%s", &buf[3]); /* maps .G1 [w] to .PS w */
+ printf("scale = 1\n"); /* defends against cip users */
+ printf(".lf %d\n", curfile->lineno+1);
+ yyparse();
+ fprintf(stdout, ".PE\n");
+ printf(".lf %d\n", curfile->lineno+1);
+ fflush(stdout);
+ } else if (buf[0] == '.' && buf[1] == 'l' && buf[2] == 'f') {
+ if (sscanf(buf+3, "%d %s", &ln, buf1) == 2) {
+ free(curfile->fname);
+ printf(".lf %d %s\n", curfile->lineno = ln, curfile->fname = tostring(buf1));
+ } else
+ printf(".lf %d\n", curfile->lineno = ln);
+ } else
+ fputs(buf, stdout);
+ }
+}
diff --git a/src/cmd/grap/misc.c b/src/cmd/grap/misc.c
new file mode 100644
index 00000000..7a68a692
--- /dev/null
+++ b/src/cmd/grap/misc.c
@@ -0,0 +1,263 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "grap.h"
+#include "y.tab.h"
+
+int nnum = 0; /* number of saved numbers */
+double num[MAXNUM];
+
+int just; /* current justification mode (RJUST, etc.) */
+int sizeop; /* current optional operator for size change */
+double sizexpr; /* current size change expression */
+
+void savenum(int n, double f) /* save f in num[n] */
+{
+ num[n] = f;
+ nnum = n+1;
+ if (nnum >= MAXNUM)
+ ERROR "too many numbers" WARNING;
+}
+
+void setjust(int j)
+{
+ just |= j;
+}
+
+void setsize(int op, double expr)
+{
+ sizeop = op;
+ sizexpr = expr;
+}
+
+char *tostring(char *s)
+{
+ register char *p;
+
+ p = malloc(strlen(s)+1);
+ if (p == NULL)
+ ERROR "out of space in tostring on %s", s FATAL;
+ strcpy(p, s);
+ return(p);
+}
+
+void range(Point pt) /* update the range for point pt */
+{
+ Obj *p = pt.obj;
+
+ if (!(p->coord & XFLAG)) {
+ if (pt.x > p->pt1.x)
+ p->pt1.x = pt.x;
+ if (pt.x < p->pt.x)
+ p->pt.x = pt.x;
+ }
+ if (!(p->coord & YFLAG)) {
+ if (pt.y > p->pt1.y)
+ p->pt1.y = pt.y;
+ if (pt.y < p->pt.y)
+ p->pt.y = pt.y;
+ }
+}
+
+void halfrange(Obj *p, int side, double val) /* record max and min for one direction */
+{
+ if (!(p->coord&XFLAG) && (side == LEFT || side == RIGHT)) {
+ if (val < p->pt.y)
+ p->pt.y = val;
+ if (val > p->pt1.y)
+ p->pt1.y = val;
+ } else if (!(p->coord&YFLAG) && (side == TOP || side == BOT)) {
+ if (val < p->pt.x)
+ p->pt.x = val;
+ if (val > p->pt1.x)
+ p->pt1.x = val;
+ }
+}
+
+
+Obj *lookup(char *s, int inst) /* find s in objlist, install if inst */
+{
+ Obj *p;
+ int found = 0;
+
+ for (p = objlist; p; p = p->next){
+ if (strcmp(s, p->name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (p == NULL && inst != 0) {
+ p = (Obj *) calloc(1, sizeof(Obj));
+ if (p == NULL)
+ ERROR "out of space in lookup" FATAL;
+ p->name = tostring(s);
+ p->type = NAME;
+ p->pt = ptmax;
+ p->pt1 = ptmin;
+ p->fval = 0.0;
+ p->next = objlist;
+ objlist = p;
+ }
+ dprintf("lookup(%s,%d) = %d\n", s, inst, found);
+ return p;
+}
+
+double getvar(Obj *p) /* return value of variable */
+{
+ return p->fval;
+}
+
+double setvar(Obj *p, double f) /* set value of variable to f */
+{
+ if (strcmp(p->name, "pointsize") == 0) { /* kludge */
+ pointsize = f;
+ ps_set = 1;
+ }
+ p->type = VARNAME;
+ return p->fval = f;
+}
+
+Point makepoint(Obj *s, double x, double y) /* make a Point */
+{
+ Point p;
+
+ dprintf("makepoint: %s, %g,%g\n", s->name, x, y);
+ p.obj = s;
+ p.x = x;
+ p.y = y;
+ return p;
+}
+
+Attr *makefattr(int type, double fval) /* set double in attribute */
+{
+ return makeattr(type, fval, (char *) 0, 0, 0);
+}
+
+Attr *makesattr(char *s) /* make an Attr cell containing s */
+{
+ Attr *ap = makeattr(STRING, sizexpr, s, just, sizeop);
+ just = sizeop = 0;
+ sizexpr = 0.0;
+ return ap;
+}
+
+Attr *makeattr(int type, double fval, char *sval, int just, int op)
+{
+ Attr *a;
+
+ a = (Attr *) malloc(sizeof(Attr));
+ if (a == NULL)
+ ERROR "out of space in makeattr" FATAL;
+ a->type = type;
+ a->fval = fval;
+ a->sval = sval;
+ a->just = just;
+ a->op = op;
+ a->next = NULL;
+ return a;
+}
+
+Attr *addattr(Attr *a1, Attr *ap) /* add attr ap to end of list a1 */
+{
+ Attr *p;
+
+ if (a1 == 0)
+ return ap;
+ if (ap == 0)
+ return a1;
+ for (p = a1; p->next; p = p->next)
+ ;
+ p->next = ap;
+ return a1;
+}
+
+void freeattr(Attr *ap) /* free an attribute list */
+{
+ Attr *p;
+
+ while (ap) {
+ p = ap->next; /* save next */
+ if (ap->sval)
+ free(ap->sval);
+ free((char *) ap);
+ ap = p;
+ }
+}
+
+char *slprint(Attr *stringlist) /* print strings from stringlist */
+{
+ int ntext, n, last_op, last_just;
+ double last_fval;
+ static char buf[1000];
+ Attr *ap;
+
+ buf[0] = '\0';
+ last_op = last_just = 0;
+ last_fval = 0.0;
+ for (ntext = 0, ap = stringlist; ap != NULL; ap = ap->next)
+ ntext++;
+ sprintf(buf, "box invis wid 0 ht %d*textht", ntext);
+ n = strlen(buf);
+ for (ap = stringlist; ap != NULL; ap = ap->next) {
+ if (ap->op == 0) { /* propagate last value */
+ ap->op = last_op;
+ ap->fval = last_fval;
+ } else {
+ last_op = ap->op;
+ last_fval = ap->fval;
+ }
+ sprintf(buf+n, " \"%s\"", ps_set || ap->op ? sizeit(ap) : ap->sval);
+ if (ap->just)
+ last_just = ap->just;
+ if (last_just)
+ strcat(buf+n, juststr(last_just));
+ n = strlen(buf);
+ }
+ return buf; /* watch it: static */
+}
+
+char *juststr(int j) /* convert RJUST, etc., into string */
+{
+ static char buf[50];
+
+ buf[0] = '\0';
+ if (j & RJUST)
+ strcat(buf, " rjust");
+ if (j & LJUST)
+ strcat(buf, " ljust");
+ if (j & ABOVE)
+ strcat(buf, " above");
+ if (j & BELOW)
+ strcat(buf, " below");
+ return buf; /* watch it: static */
+}
+
+char *sprntf(char *s, Attr *ap) /* sprintf(s, attrlist ap) */
+{
+ char buf[500];
+ int n;
+ Attr *p;
+
+ for (n = 0, p = ap; p; p = p->next)
+ n++;
+ switch (n) {
+ case 0:
+ return s;
+ case 1:
+ sprintf(buf, s, ap->fval);
+ break;
+ case 2:
+ sprintf(buf, s, ap->fval, ap->next->fval);
+ break;
+ case 3:
+ sprintf(buf, s, ap->fval, ap->next->fval, ap->next->next->fval);
+ break;
+ case 5:
+ ERROR "too many expressions in sprintf" WARNING;
+ case 4:
+ sprintf(buf, s, ap->fval, ap->next->fval, ap->next->next->fval, ap->next->next->next->fval);
+ break;
+ }
+ free(s);
+ return tostring(buf);
+}
diff --git a/src/cmd/grap/mkfile b/src/cmd/grap/mkfile
new file mode 100644
index 00000000..872497d1
--- /dev/null
+++ b/src/cmd/grap/mkfile
@@ -0,0 +1,37 @@
+<$PLAN9/src/mkhdr
+
+TARG=grap
+OFILES=\
+ grap.$O\
+ grapl.$O\
+ main.$O\
+ input.$O\
+ print.$O\
+ frame.$O\
+ for.$O\
+ coord.$O\
+ ticks.$O\
+ plot.$O\
+ label.$O\
+ misc.$O\
+
+HFILES=grap.h\
+ y.tab.h\
+
+YFILES=grap.y\
+
+LFILES=grapl.lx\
+
+SHORTLIB=bio 9
+<$PLAN9/src/mkone
+YFLAGS = -d -S
+LEX=9lex
+
+grap.c: y.tab.c
+ mv $prereq $target
+
+grapl.c: $LFILES
+ $LEX -t $prereq > $target
+
+clean:V:
+ rm -f [$OS].out *.[$OS] y.tab.? lex.yy.c grapl.c grap.c grap
diff --git a/src/cmd/grap/plot.c b/src/cmd/grap/plot.c
new file mode 100644
index 00000000..ab375953
--- /dev/null
+++ b/src/cmd/grap/plot.c
@@ -0,0 +1,132 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "grap.h"
+#include "y.tab.h"
+
+void line(int type, Point p1, Point p2, Attr *desc) /* draw a line segment */
+{
+ fprintf(tfd, "%s %s from %s",
+ type==LINE ? "line" : "arrow", desc_str(desc), xyname(p1));
+ fprintf(tfd, " to %s", xyname(p2)); /* 'cause xyname is botched */
+ fprintf(tfd, "\n");
+ range(p1);
+ range(p2);
+}
+
+void circle(double r, Point pt) /* draw a circle */
+{
+ if (r > 0.0)
+ fprintf(tfd, "circle rad %g at %s\n", r, xyname(pt));
+ else
+ fprintf(tfd, "\"\\s-3\\(ob\\s0\" at %s\n", xyname(pt));
+ range(pt);
+}
+
+char *xyname(Point pt) /* generate xy name macro for point p */
+{
+ static char buf[200];
+ Obj *p;
+
+ p = pt.obj;
+ if (p->log & XFLAG) {
+ if (pt.x <= 0.0)
+ ERROR "can't take log of x coord %g", pt.x FATAL;
+ logit(pt.x);
+ }
+ if (p->log & YFLAG) {
+ if (pt.y <= 0.0)
+ ERROR "can't take log of y coord %g", pt.y FATAL;
+ logit(pt.y);
+ }
+ sprintf(buf, "xy_%s(%g,%g)", p->name, pt.x, pt.y);
+ return buf; /* WATCH IT: static */
+}
+
+void pic(char *s) /* fire out pic stuff directly */
+{
+ while (*s == ' ')
+ s++;
+ fprintf(tfd, "%s\n", s);
+}
+
+int auto_x = 0; /* counts abscissa if none provided */
+
+void numlist(void) /* print numbers in default way */
+{
+ Obj *p;
+ Point pt;
+ int i;
+ static char *spot = "\\(bu";
+ Attr *ap;
+
+ p = pt.obj = lookup(curr_coord, 1);
+ if (nnum == 1) {
+ nnum = 2;
+ num[1] = num[0];
+ num[0] = ++auto_x;
+ }
+ pt.x = num[0];
+ if (p->attr && p->attr->sval)
+ spot = p->attr->sval;
+ for (i = 1; i < nnum; i++) {
+ pt.y = num[i];
+ if (p->attr == 0 || p->attr->type == 0) {
+ ap = makesattr(tostring(spot));
+ plot(ap, pt);
+ } else
+ next(p, pt, p->attr);
+ }
+ nnum = 0;
+}
+
+void plot(Attr *sl, Point pt) /* put stringlist sl at point pt */
+{
+ fprintf(tfd, "%s at %s\n", slprint(sl), xyname(pt));
+ range(pt);
+ freeattr(sl);
+}
+
+void plotnum(double f, char *fmt, Point pt) /* plot value f at point */
+{
+ char buf[100];
+
+ if (fmt) {
+ sprintf(buf, fmt, f);
+ free(fmt);
+ } else if (f >= 0.0)
+ sprintf(buf, "%g", f);
+ else
+ sprintf(buf, "\\-%g", -f);
+ fprintf(tfd, "\"%s\" at %s\n", buf, xyname(pt));
+ range(pt);
+}
+
+void drawdesc(int type, Obj *p, Attr *desc, char *s) /* set line description for p */
+{
+ p->attr = desc;
+ p->attr->sval = s;
+ if (type == NEW) {
+ p->first = 0; /* so it really looks new */
+ auto_x = 0;
+ }
+}
+
+void next(Obj *p, Point pt, Attr *desc) /* add component to a path */
+{
+ char *s;
+
+ if (p->first == 0) {
+ p->first++;
+ fprintf(tfd, "L%s: %s\n", p->name, xyname(pt));
+ } else {
+ fprintf(tfd, "line %s from L%s to %s; L%s: Here\n",
+ desc_str(desc->type ? desc : p->attr),
+ p->name, xyname(pt), p->name);
+ }
+ if (p->attr && (s=p->attr->sval)) {
+ /* BUG: should fix size here */
+ fprintf(tfd, "\"%s\" at %s\n", s, xyname(pt));
+ }
+ range(pt);
+}
diff --git a/src/cmd/grap/print.c b/src/cmd/grap/print.c
new file mode 100644
index 00000000..8f553c8f
--- /dev/null
+++ b/src/cmd/grap/print.c
@@ -0,0 +1,236 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+#include "grap.h"
+#include "y.tab.h"
+
+double margin = MARGIN; /* extra space around edges */
+extern double frame_ht, frame_wid, ticklen;
+extern int just, sizeop, tick_dir;
+extern double sizexpr, lab_up, lab_rt;
+
+char graphname[50] = "Graph";
+char graphpos[200] = "";
+
+void print(void) /* arrange final output */
+{
+ FILE *fd;
+ Obj *p, *dfp;
+ int c;
+ double dx, dy, xfac, yfac;
+
+ if (tfd != NULL) {
+ fclose(tfd); /* end the temp file */
+ tfd = stdout;
+ }
+
+ if ((p=lookup("margin",0)) != NULL)
+ margin = p->fval;
+ if (frame_ht < 0) /* wasn't set explicitly, so use default */
+ frame_ht = getvar(lookup("frameht", 0));
+ if (frame_wid < 0)
+ frame_wid = getvar(lookup("framewid", 0));
+ dfp = NULL;
+ for (p = objlist; p; p = p->next) {
+ dprintf("print: name = <%s>, type = %d\n", p->name, p->type);
+ if (p->type == NAME) {
+ Point pt, pt1;
+ pt = p->pt;
+ pt1 = p->pt1;
+ fprintf(tfd, "\t# %s %g .. %g, %g .. %g\n",
+ p->name, pt.x, pt1.x, pt.y, pt1.y);
+ if (p->log & XFLAG) {
+ if (pt.x <= 0.0)
+ ERROR "can't take log of x coord %g", pt.x FATAL;
+ logit(pt.x);
+ logit(pt1.x);
+ }
+ if (p->log & YFLAG) {
+ if (pt.y <= 0.0)
+ ERROR "can't take log of y coord %g", pt.y FATAL;
+ logit(pt.y);
+ logit(pt1.y);
+ }
+ if (!(p->coord & XFLAG)) {
+ dx = pt1.x - pt.x;
+ pt.x -= margin * dx;
+ pt1.x += margin * dx;
+ }
+ if (!(p->coord & YFLAG)) {
+ dy = pt1.y - pt.y;
+ pt.y -= margin * dy;
+ pt1.y += margin * dy;
+ }
+ if (autoticks && strcmp(p->name, dflt_coord) == 0) {
+ p->pt = pt;
+ p->pt1 = pt1;
+ if (p->log & XFLAG) {
+ p->pt.x = pow(10.0, pt.x);
+ p->pt1.x = pow(10.0, pt1.x);
+ }
+ if (p->log & YFLAG) {
+ p->pt.y = pow(10.0, pt.y);
+ p->pt1.y = pow(10.0, pt1.y);
+ }
+ dfp = setauto();
+ }
+ dx = pt1.x - pt.x;
+ dy = pt1.y - pt.y;
+ xfac = dx > 0 ? frame_wid/dx : frame_wid/2;
+ yfac = dy > 0 ? frame_ht/dy : frame_ht/2;
+
+ fprintf(tfd, "define xy_%s @ ", p->name);
+ if (dx > 0)
+ fprintf(tfd, "\t(($1)-(%g))*%g", pt.x, xfac);
+ else
+ fprintf(tfd, "\t%g", xfac);
+ if (dy > 0)
+ fprintf(tfd, ", (($2)-(%g))*%g @\n", pt.y, yfac);
+ else
+ fprintf(tfd, ", %g @\n", yfac);
+ fprintf(tfd, "define x_%s @ ", p->name);
+ if (dx > 0)
+ fprintf(tfd, "\t(($1)-(%g))*%g @\n", pt.x, xfac);
+ else
+ fprintf(tfd, "\t%g @\n", xfac);
+ fprintf(tfd, "define y_%s @ ", p->name);
+ if (dy > 0)
+ fprintf(tfd, "\t(($1)-(%g))*%g @\n", pt.y, yfac);
+ else
+ fprintf(tfd, "\t%g @\n", yfac);
+ }
+ }
+ if (codegen)
+ frame();
+ if (codegen && autoticks && dfp)
+ do_autoticks(dfp);
+
+ if ((fd = fopen(tempfile, "r")) != NULL) {
+ while ((c = getc(fd)) != EOF)
+ putc(c, tfd);
+ fclose(fd);
+ }
+ tfd = NULL;
+}
+
+void endstat(void) /* clean up after each statement */
+{
+
+ just = sizeop = 0;
+ lab_up = lab_rt = 0.0;
+ sizexpr = 0.0;
+ nnum = 0;
+ ntick = 0;
+ tside = 0;
+ tick_dir = OUT;
+ ticklen = TICKLEN;
+}
+
+void graph(char *s) /* graph statement */
+{
+ char *p, *os;
+ int c;
+
+ if (codegen) {
+ fprintf(stdout, "%s: [\n", graphname);
+ print(); /* pump out previous graph */
+ fprintf(stdout, "\n] %s\n", graphpos);
+ reset();
+ }
+ if (s) {
+ dprintf("into graph with <%s>\n", s);
+ opentemp();
+ os = s;
+ while ((c = *s) == ' ' || c == '\t')
+ s++;
+ if (c == '\0')
+ ERROR "no name on graph statement" WARNING;
+ if (!isupper(s[0]))
+ ERROR "graph name %s must be capitalized", s WARNING;
+ for (p=graphname; (c = *s) != ' ' && c != '\t' && c != '\0'; )
+ *p++ = *s++;
+ *p = '\0';
+ strcpy(graphpos, s);
+ dprintf("graphname = <%s>, graphpos = <%s>\n", graphname, graphpos);
+ free(os);
+ }
+}
+
+void setup(void) /* done at each .G1 */
+{
+ static int firstG1 = 0;
+
+ reset();
+ opentemp();
+ frame_ht = frame_wid = -1; /* reset in frame() */
+ ticklen = getvar(lookup("ticklen", 0));
+ if (firstG1++ == 0)
+ do_first();
+ codegen = synerr = 0;
+ strcpy(graphname, "Graph");
+ strcpy(graphpos, "");
+}
+
+void do_first(void) /* done at first .G1: definitions, etc. */
+{
+ extern int lib;
+ extern char *lib_defines;
+ static char buf[50], buf1[50]; /* static because pbstr uses them */
+ FILE *fp;
+ extern int getpid(void);
+
+ sprintf(buf, "define pid /%d/\n", getpid());
+ pbstr(buf);
+ if (lib != 0) {
+ if ((fp = fopen(unsharp(lib_defines), "r")) != NULL) {
+ sprintf(buf1, "copy \"%s\"\n", lib_defines);
+ pbstr(buf1);
+ fclose(fp);
+ } else {
+ fprintf(stderr, "grap warning: can't open %s\n", lib_defines);
+ }
+ }
+}
+
+void reset(void) /* done at each "graph ..." statement */
+{
+ Obj *p, *np, *deflist;
+ extern int tlist, toffside, autodir;
+
+ curr_coord = dflt_coord;
+ ncoord = auto_x = 0;
+ autoticks = LEFT|BOT;
+ autodir = 0;
+ tside = tlist = toffside = 0;
+ tick_dir = OUT;
+ margin = MARGIN;
+ deflist = NULL;
+ for (p = objlist; p; p = np) {
+ np = p->next;
+ if (p->type == DEFNAME || p->type == VARNAME) {
+ p->next = deflist;
+ deflist = p;
+ } else {
+ free(p->name);
+ freeattr(p->attr);
+ free((char *) p);
+ }
+ }
+ objlist = deflist;
+}
+
+void opentemp(void)
+{
+ if (tfd != NULL)
+ fclose(tfd);
+ if (tfd != stdout) {
+// if (tfd != NULL)
+// fclose(tfd);
+ if ((tfd = fopen(tempfile, "w")) == NULL) {
+ fprintf(stderr, "grap: can't open %s\n", tempfile);
+ exit(1);
+ }
+ }
+}
diff --git a/src/cmd/grap/ticks.c b/src/cmd/grap/ticks.c
new file mode 100644
index 00000000..72fc06f5
--- /dev/null
+++ b/src/cmd/grap/ticks.c
@@ -0,0 +1,492 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "grap.h"
+#include "y.tab.h"
+
+#define MAXTICK 200
+int ntick = 0;
+double tickval[MAXTICK]; /* tick values (one axis at a time */
+char *tickstr[MAXTICK]; /* and labels */
+
+int tside = 0;
+int tlist = 0; /* 1 => explicit values given */
+int toffside = 0; /* no ticks on these sides */
+int goffside = 0; /* no ticks on grid on these sides */
+int tick_dir = OUT;
+double ticklen = TICKLEN; /* default tick length */
+int autoticks = LEFT|BOT;
+int autodir = 0; /* set LEFT, etc. if automatic ticks go in */
+
+void savetick(double f, char *s) /* remember tick location and label */
+{
+ if (ntick >= MAXTICK)
+ ERROR "too many ticks (%d)", MAXTICK FATAL;
+ tickval[ntick] = f;
+ tickstr[ntick] = s;
+ ntick++;
+}
+
+void dflt_tick(double f)
+{
+ if (f >= 0.0)
+ savetick(f, tostring("%g"));
+ else
+ savetick(f, tostring("\\%g"));
+}
+
+void tickside(int n) /* remember which side these ticks/gridlines go on */
+{
+ tside |= n;
+}
+
+void tickoff(int side) /* remember explicit sides */
+{
+ toffside |= side;
+}
+
+void gridtickoff(void) /* turn grid ticks off on the side previously specified (ugh) */
+{
+ goffside = tside;
+}
+
+void setlist(void) /* remember that there was an explicit list */
+{
+ tlist = 1;
+}
+
+void tickdir(int dir, double val, int explicit) /* remember in/out [expr] */
+{
+ tick_dir = dir;
+ if (explicit)
+ ticklen = val;
+}
+
+void ticks(void) /* set autoticks after ticks statement */
+{
+ /* was there an explicit "ticks [side] off"? */
+ if (toffside)
+ autoticks &= ~toffside;
+ /* was there an explicit list? (eg "ticks at ..." or "ticks from ...") */
+ if (tlist) {
+ if (tside & (BOT|TOP))
+ autoticks &= ~(BOT|TOP);
+ if (tside & (LEFT|RIGHT))
+ autoticks &= ~(LEFT|RIGHT);
+ }
+ /* was there a side without a list? (eg "ticks left in") */
+ if (tside && !tlist) {
+ if (tick_dir == IN)
+ autodir |= tside;
+ if (tside & (BOT|TOP))
+ autoticks = (autoticks & ~(BOT|TOP)) | (tside & (BOT|TOP));
+ if (tside & (LEFT|RIGHT))
+ autoticks = (autoticks & ~(LEFT|RIGHT)) | (tside & (LEFT|RIGHT));
+ }
+ tlist = tside = toffside = goffside = 0;
+ tick_dir = OUT;
+}
+
+double modfloor(double f, double t)
+{
+ t = fabs(t);
+ return floor(f/t) * t;
+}
+
+double modceil(double f, double t)
+{
+ t = fabs(t);
+ return ceil(f/t) * t;
+}
+
+double xtmin, xtmax; /* range of ticks */
+double ytmin, ytmax;
+double xquant, xmult; /* quantization & scale for auto x ticks */
+double yquant, ymult;
+double lograt = 5;
+
+void do_autoticks(Obj *p) /* make set of ticks for default coord only */
+{
+ double x, xl, xu, q;
+
+ if (p == NULL)
+ return;
+ fprintf(tfd, "Autoticks:\t# x %g..%g, y %g..%g",
+ p->pt.x, p->pt1.x, p->pt.y, p->pt1.y);
+ fprintf(tfd, "; xt %g,%g, yt %g,%g, xq,xm = %g,%g, yq,ym = %g,%g\n",
+ xtmin, xtmax, ytmin, ytmax, xquant, xmult, yquant, ymult);
+ if ((autoticks & (BOT|TOP)) && p->pt1.x >= p->pt.x) { /* make x ticks */
+ q = xquant;
+ xl = p->pt.x;
+ xu = p->pt1.x;
+ if (xl >= xu)
+ dflt_tick(xl);
+ else if ((p->log & XFLAG) && xu/xl >= lograt) {
+ for (x = q; x < xu; x *= 10) {
+ logtick(x, xl, xu);
+ if (xu/xl <= 100) {
+ logtick(2*x, xl, xu);
+ logtick(5*x, xl, xu);
+ }
+ }
+ } else {
+ xl = modceil(xtmin - q/100, q);
+ xu = modfloor(xtmax + q/100, q) + q/2;
+ for (x = xl; x <= xu; x += q)
+ dflt_tick(x);
+ }
+ tside = autoticks & (BOT|TOP);
+ ticklist(p, 0);
+ }
+ if ((autoticks & (LEFT|RIGHT)) && p->pt1.y >= p->pt.y) { /* make y ticks */
+ q = yquant;
+ xl = p->pt.y;
+ xu = p->pt1.y;
+ if (xl >= xu)
+ dflt_tick(xl);
+ else if ((p->log & YFLAG) && xu/xl >= lograt) {
+ for (x = q; x < xu; x *= 10) {
+ logtick(x, xl, xu);
+ if (xu/xl <= 100) {
+ logtick(2*x, xl, xu);
+ logtick(5*x, xl, xu);
+ }
+ }
+ } else {
+ xl = modceil(ytmin - q/100, q);
+ xu = modfloor(ytmax + q/100, q) + q/2;
+ for (x = xl; x <= xu; x += q)
+ dflt_tick(x);
+ }
+ tside = autoticks & (LEFT|RIGHT);
+ ticklist(p, 0);
+ }
+}
+
+void logtick(double v, double lb, double ub)
+{
+ float slop = 1.0; /* was 1.001 */
+
+ if (slop * lb <= v && ub >= slop * v)
+ dflt_tick(v);
+}
+
+Obj *setauto(void) /* compute new min,max, and quant & mult */
+{
+ Obj *p, *q;
+
+ if ((q = lookup("lograt",0)) != NULL)
+ lograt = q->fval;
+ for (p = objlist; p; p = p->next)
+ if (p->type == NAME && strcmp(p->name,dflt_coord) == 0)
+ break;
+ if (p) {
+ if ((p->log & XFLAG) && p->pt1.x/p->pt.x >= lograt)
+ autolog(p, 'x');
+ else
+ autoside(p, 'x');
+ if ((p->log & YFLAG) && p->pt1.y/p->pt.y >= lograt)
+ autolog(p, 'y');
+ else
+ autoside(p, 'y');
+ }
+ return p;
+}
+
+void autoside(Obj *p, int side)
+{
+ double r, s, d, ub, lb;
+
+ if (side == 'x') {
+ xtmin = lb = p->pt.x;
+ xtmax = ub = p->pt1.x;
+ } else {
+ ytmin = lb = p->pt.y;
+ ytmax = ub = p->pt1.y;
+ }
+ if (ub <= lb)
+ return; /* cop out on little ranges */
+ d = ub - lb;
+ r = s = 1;
+ while (d * s < 10)
+ s *= 10;
+ d *= s;
+ while (10 * r < d)
+ r *= 10;
+ if (r > d/3)
+ r /= 2;
+ else if (r <= d/6)
+ r *= 2;
+ if (side == 'x') {
+ xquant = r / s;
+ } else {
+ yquant = r / s;
+ }
+}
+
+void autolog(Obj *p, int side)
+{
+ double r, s, t, ub, lb;
+ int flg;
+
+ if (side == 'x') {
+ xtmin = lb = p->pt.x;
+ xtmax = ub = p->pt1.x;
+ flg = p->coord & XFLAG;
+ } else {
+ ytmin = lb = p->pt.y;
+ ytmax = ub = p->pt1.y;
+ flg = p->coord & YFLAG;
+ }
+ for (s = 1; lb * s < 1; s *= 10)
+ ;
+ lb *= s;
+ ub *= s;
+ for (r = 1; 10 * r < lb; r *= 10)
+ ;
+ for (t = 1; t < ub; t *= 10)
+ ;
+ if (side == 'x')
+ xquant = r / s;
+ else
+ yquant = r / s;
+ if (flg)
+ return;
+ if (ub / lb < 100) {
+ if (lb >= 5 * r)
+ r *= 5;
+ else if (lb >= 2 * r)
+ r *= 2;
+ if (ub * 5 <= t)
+ t /= 5;
+ else if (ub * 2 <= t)
+ t /= 2;
+ if (side == 'x') {
+ xtmin = r / s;
+ xtmax = t / s;
+ } else {
+ ytmin = r / s;
+ ytmax = t / s;
+ }
+ }
+}
+
+void iterator(double from, double to, int op, double by, char *fmt) /* create an iterator */
+{
+ double x;
+
+ /* should validate limits, etc. */
+ /* punt for now */
+
+ dprintf("iterate from %g to %g by %g, op = %c, fmt=%s\n",
+ from, to, by, op, fmt ? fmt : "");
+ switch (op) {
+ case '+':
+ case ' ':
+ for (x = from; x <= to + (SLOP-1) * by; x += by)
+ if (fmt)
+ savetick(x, tostring(fmt));
+ else
+ dflt_tick(x);
+ break;
+ case '-':
+ for (x = from; x >= to; x -= by)
+ if (fmt)
+ savetick(x, tostring(fmt));
+ else
+ dflt_tick(x);
+ break;
+ case '*':
+ for (x = from; x <= SLOP * to; x *= by)
+ if (fmt)
+ savetick(x, tostring(fmt));
+ else
+ dflt_tick(x);
+ break;
+ case '/':
+ for (x = from; x >= to; x /= by)
+ if (fmt)
+ savetick(x, tostring(fmt));
+ else
+ dflt_tick(x);
+ break;
+ }
+ if (fmt)
+ free(fmt);
+}
+
+void ticklist(Obj *p, int explicit) /* fire out the accumulated ticks */
+ /* 1 => list, 0 => auto */
+{
+ if (p == NULL)
+ return;
+ fprintf(tfd, "Ticks_%s:\n\tticklen = %g\n", p->name, ticklen);
+ print_ticks(TICKS, explicit, p, "ticklen", "");
+}
+
+void print_ticks(int type, int explicit, Obj *p, char *lenstr, char *descstr)
+{
+ int i, logflag, inside;
+ char buf[100];
+ double tv;
+
+ for (i = 0; i < ntick; i++) /* any ticks given explicitly? */
+ if (tickstr[i] != NULL)
+ break;
+ if (i >= ntick && type == TICKS) /* no, so use values */
+ for (i = 0; i < ntick; i++) {
+ if (tickval[i] >= 0.0)
+ sprintf(buf, "%g", tickval[i]);
+ else
+ sprintf(buf, "\\-%g", -tickval[i]);
+ tickstr[i] = tostring(buf);
+ }
+ else
+ for (i = 0; i < ntick; i++) {
+ if (tickstr[i] != NULL) {
+ sprintf(buf, tickstr[i], tickval[i]);
+ free(tickstr[i]);
+ tickstr[i] = tostring(buf);
+ }
+ }
+ logflag = sidelog(p->log, tside);
+ for (i = 0; i < ntick; i++) {
+ tv = tickval[i];
+ halfrange(p, tside, tv);
+ if (logflag) {
+ if (tv <= 0.0)
+ ERROR "can't take log of tick value %g", tv FATAL;
+ logit(tv);
+ }
+ if (type == GRID)
+ inside = LEFT|RIGHT|TOP|BOT;
+ else if (explicit)
+ inside = (tick_dir == IN) ? tside : 0;
+ else
+ inside = autodir;
+ if (tside & BOT)
+ maketick(type, p->name, BOT, inside, tv, tickstr[i], lenstr, descstr);
+ if (tside & TOP)
+ maketick(type, p->name, TOP, inside, tv, tickstr[i], lenstr, descstr);
+ if (tside & LEFT)
+ maketick(type, p->name, LEFT, inside, tv, tickstr[i], lenstr, descstr);
+ if (tside & RIGHT)
+ maketick(type, p->name, RIGHT, inside, tv, tickstr[i], lenstr, descstr);
+ if (tickstr[i]) {
+ free(tickstr[i]);
+ tickstr[i] = NULL;
+ }
+ }
+ ntick = 0;
+}
+
+void maketick(int type, char *name, int side, int inflag, double val, char *lab, char *lenstr, char *descstr)
+{
+ char *sidestr, *td;
+
+ fprintf(tfd, "\tline %s ", descstr);
+ inflag &= side;
+ switch (side) {
+ case BOT:
+ case 0:
+ td = inflag ? "up" : "down";
+ fprintf(tfd, "%s %s from (x_%s(%g),0)", td, lenstr, name, val);
+ break;
+ case TOP:
+ td = inflag ? "down" : "up";
+ fprintf(tfd, "%s %s from (x_%s(%g),frameht)", td, lenstr, name, val);
+ break;
+ case LEFT:
+ td = inflag ? "right" : "left";
+ fprintf(tfd, "%s %s from (0,y_%s(%g))", td, lenstr, name, val);
+ break;
+ case RIGHT:
+ td = inflag ? "left" : "right";
+ fprintf(tfd, "%s %s from (framewid,y_%s(%g))", td, lenstr, name, val);
+ break;
+ }
+ fprintf(tfd, "\n");
+ if (type == GRID && (side & goffside)) /* wanted no ticks on grid */
+ return;
+ sidestr = tick_dir == IN ? "start" : "end";
+ if (lab != NULL) {
+ /* BUG: should fix size of lab here */
+ double wid = strlen(lab)/7.5 + (tick_dir == IN ? 0 : 0.1); /* estimate width at 15 chars/inch */
+ switch (side) {
+ case BOT: case 0:
+ /* can drop "box invis" with new pic */
+ fprintf(tfd, "\tbox invis \"%s\" ht .25 wid 0 with .n at last line.%s",
+ lab, sidestr);
+ break;
+ case TOP:
+ fprintf(tfd, "\tbox invis \"%s\" ht .2 wid 0 with .s at last line.%s",
+ lab, sidestr);
+ break;
+ case LEFT:
+ fprintf(tfd, "\t\"%s \" wid %.2f rjust at last line.%s",
+ lab, wid, sidestr);
+ break;
+ case RIGHT:
+ fprintf(tfd, "\t\" %s\" wid %.2f ljust at last line.%s",
+ lab, wid, sidestr);
+ break;
+ }
+ /* BUG: works only if "down x" comes before "at wherever" */
+ lab_adjust();
+ fprintf(tfd, "\n");
+ }
+}
+
+Attr *grid_desc = 0;
+
+void griddesc(Attr *a)
+{
+ grid_desc = a;
+}
+
+void gridlist(Obj *p)
+{
+ char *framestr;
+
+ if ((tside & (BOT|TOP)) || tside == 0)
+ framestr = "frameht";
+ else
+ framestr = "framewid";
+ fprintf(tfd, "Grid_%s:\n", p->name);
+ tick_dir = IN;
+ print_ticks(GRID, 0, p, framestr, desc_str(grid_desc));
+ if (grid_desc) {
+ freeattr(grid_desc);
+ grid_desc = 0;
+ }
+}
+
+char *desc_str(Attr *a) /* convert DOT to "dotted", etc. */
+{
+ static char buf[50], *p;
+
+ if (a == NULL)
+ return p = "";
+ switch (a->type) {
+ case DOT: p = "dotted"; break;
+ case DASH: p = "dashed"; break;
+ case INVIS: p = "invis"; break;
+ default: p = "";
+ }
+ if (a->fval != 0.0) {
+ sprintf(buf, "%s %g", p, a->fval);
+ return buf;
+ } else
+ return p;
+}
+
+sidelog(int logflag, int side) /* figure out whether to scale a side */
+{
+ if ((logflag & XFLAG) && ((side & (BOT|TOP)) || side == 0))
+ return 1;
+ else if ((logflag & YFLAG) && (side & (LEFT|RIGHT)))
+ return 1;
+ else
+ return 0;
+}
diff --git a/src/cmd/mkfile b/src/cmd/mkfile
index e5ddf684..d08461e9 100644
--- a/src/cmd/mkfile
+++ b/src/cmd/mkfile
@@ -5,7 +5,7 @@ SHORTLIB=sec fs mux regexp9 thread bio 9
<$PLAN9/src/mkmany
-BUGGERED='CVS|faces|factotum|mailfs|scat|upas|vac|venti|lex|vncv'
+BUGGERED='CVS|faces|factotum|mailfs|scat|upas|vac|venti|lex|vncv|grap|eqn|troff|pic|tbl'
DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "^($BUGGERED)$"`
<$PLAN9/src/mkdirs
diff --git a/src/cmd/pic/arcgen.c b/src/cmd/pic/arcgen.c
new file mode 100644
index 00000000..d1b64224
--- /dev/null
+++ b/src/cmd/pic/arcgen.c
@@ -0,0 +1,221 @@
+#include <stdio.h>
+#include <math.h>
+#include "pic.h"
+#include "y.tab.h"
+
+void arc_extreme(double, double, double, double, double, double);
+int quadrant(double x, double y);
+
+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 };
+ 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;
+
+ prevrad = getfval("arcrad");
+ prevh = getfval("arrowht");
+ prevw = getfval("arrowwid");
+ fromx = curx;
+ fromy = cury;
+ head = to = at = cw = invis = ddtype = battr = 0;
+ for (i = 0; i < nattr; i++) {
+ ap = &attr[i];
+ switch (ap->a_type) {
+ case TEXTATTR:
+ savetext(ap->a_sub, ap->a_val.p);
+ break;
+ case HEAD:
+ head += ap->a_val.i;
+ break;
+ case INVIS:
+ invis = INVIS;
+ break;
+ case HEIGHT: /* length of arrowhead */
+ prevh = ap->a_val.f;
+ break;
+ case WIDTH: /* width of arrowhead */
+ prevw = ap->a_val.f;
+ break;
+ case RADIUS:
+ prevrad = ap->a_val.f;
+ break;
+ case DIAMETER:
+ prevrad = ap->a_val.f / 2;
+ break;
+ case CW:
+ cw = 1;
+ break;
+ case FROM: /* start point of arc */
+ ppos = ap->a_val.o;
+ fromx = ppos->o_x;
+ fromy = ppos->o_y;
+ break;
+ case TO: /* end point of arc */
+ ppos = ap->a_val.o;
+ tox = ppos->o_x;
+ toy = ppos->o_y;
+ to++;
+ break;
+ case AT: /* center of arc */
+ ppos = ap->a_val.o;
+ curx = ppos->o_x;
+ cury = ppos->o_y;
+ at = 1;
+ break;
+ case UP:
+ hvmode = U_DIR;
+ break;
+ case DOWN:
+ hvmode = D_DIR;
+ break;
+ case RIGHT:
+ hvmode = R_DIR;
+ break;
+ case LEFT:
+ hvmode = L_DIR;
+ break;
+ case FILL:
+ battr |= FILLBIT;
+ if (ap->a_sub == DEFAULT)
+ fillval = getfval("fillval");
+ else
+ fillval = ap->a_val.f;
+ break;
+ }
+ }
+ if (!at && !to) { /* the defaults are mostly OK */
+ curx = fromx + prevrad * dctrx[cw][hvmode];
+ cury = fromy + prevrad * dctry[cw][hvmode];
+ tox = fromx + prevrad * dtox[cw][hvmode];
+ toy = fromy + prevrad * dtoy[cw][hvmode];
+ hvmode = nexthv[cw][hvmode];
+ }
+ else if (!at) {
+ dx2 = (tox - fromx) / 2;
+ dy2 = (toy - fromy) / 2;
+ phi = atan2(dy2, dx2) + (cw ? -PI/2 : PI/2);
+ if (prevrad <= 0.0)
+ prevrad = dx2*dx2+dy2*dy2;
+ for (r=prevrad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2)
+ ; /* this kludge gets around too-small radii */
+ prevrad = r;
+ ht = sqrt(d);
+ curx = fromx + dx2 + ht * cos(phi);
+ cury = fromy + dy2 + ht * sin(phi);
+ dprintf("dx2,dy2=%g,%g, phi=%g, r,ht=%g,%g\n",
+ dx2, dy2, phi, r, ht);
+ }
+ else if (at && !to) { /* do we have all the cases??? */
+ tox = fromx + prevrad * dtox[cw][hvmode];
+ toy = fromy + prevrad * dtoy[cw][hvmode];
+ hvmode = nexthv[cw][hvmode];
+ }
+ if (cw) { /* interchange roles of from-to and heads */
+ double temp;
+ temp = fromx; fromx = tox; tox = temp;
+ temp = fromy; fromy = toy; toy = temp;
+ if (head == HEAD1)
+ head = HEAD2;
+ else if (head == HEAD2)
+ head = HEAD1;
+ }
+ p = makenode(type, 7);
+ arc_extreme(fromx, fromy, tox, toy, curx, cury);
+ p->o_val[0] = fromx;
+ p->o_val[1] = fromy;
+ p->o_val[2] = tox;
+ p->o_val[3] = toy;
+ if (cw) {
+ curx = fromx;
+ cury = fromy;
+ } else {
+ curx = tox;
+ cury = toy;
+ }
+ p->o_val[4] = prevw;
+ p->o_val[5] = prevh;
+ p->o_val[6] = prevrad;
+ p->o_attr = head | (cw ? CW_ARC : 0) | invis | ddtype | battr;
+ p->o_fillval = fillval;
+ if (head)
+ p->o_nhead = getfval("arrowhead");
+ dprintf("arc rad %g at %g %g from %g %g to %g %g head %g %g\n",
+ prevrad, p->o_x, p->o_y,
+ p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]);
+ 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.
+***********************************************************************/
+
+
+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, xmin, ymin, xmax, ymax;
+ int j, k;
+ 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;
+ xmax += xc; ymax += yc;
+ extreme(xmin, ymin);
+ extreme(xmax, ymax);
+}
+
+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 */
+}
+
diff --git a/src/cmd/pic/blockgen.c b/src/cmd/pic/blockgen.c
new file mode 100644
index 00000000..9015d56d
--- /dev/null
+++ b/src/cmd/pic/blockgen.c
@@ -0,0 +1,226 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "pic.h"
+#include "y.tab.h"
+
+#define NBRACK 20 /* depth of [...] */
+#define NBRACE 20 /* depth of {...} */
+
+struct pushstack stack[NBRACK];
+int nstack = 0;
+struct pushstack bracestack[NBRACE];
+int nbstack = 0;
+
+void blockadj(obj *);
+
+obj *leftthing(int c) /* called for {... or [... */
+ /* really ought to be separate functions */
+{
+ obj *p;
+
+ if (c == '[') {
+ if (nstack >= NBRACK)
+ ERROR "[...] nested too deep" FATAL;
+ stack[nstack].p_x = curx;
+ stack[nstack].p_y = cury;
+ stack[nstack].p_hvmode = hvmode;
+ curx = cury = 0;
+ stack[nstack].p_xmin = xmin;
+ stack[nstack].p_xmax = xmax;
+ stack[nstack].p_ymin = ymin;
+ stack[nstack].p_ymax = ymax;
+ nstack++;
+ xmin = ymin = 30000;
+ xmax = ymax = -30000;
+ p = makenode(BLOCK, 7);
+ p->o_val[4] = nobj; /* 1st item within [...] */
+ if (p->o_nobj != nobj-1)
+ fprintf(stderr, "nobjs wrong%d %d\n", p->o_nobj, nobj);
+ } else {
+ if (nbstack >= NBRACK)
+ ERROR "{...} nested too deep" FATAL;
+ bracestack[nbstack].p_x = curx;
+ bracestack[nbstack].p_y = cury;
+ bracestack[nbstack].p_hvmode = hvmode;
+ nbstack++;
+ p = NULL;
+ }
+ return(p);
+}
+
+obj *rightthing(obj *p, int c) /* called for ... ] or ... } */
+{
+ obj *q;
+
+ if (c == '}') {
+ nbstack--;
+ curx = bracestack[nbstack].p_x;
+ cury = bracestack[nbstack].p_y;
+ hvmode = bracestack[nbstack].p_hvmode;
+ q = makenode(MOVE, 0);
+ dprintf("M %g %g\n", curx, cury);
+ } else {
+ nstack--;
+ curx = stack[nstack].p_x;
+ cury = stack[nstack].p_y;
+ hvmode = stack[nstack].p_hvmode;
+ q = makenode(BLOCKEND, 7);
+ q->o_val[4] = p->o_nobj + 1; /* back pointer */
+ p->o_val[5] = q->o_nobj - 1; /* forward pointer */
+ if (xmin > xmax) /* nothing happened */
+ xmin = xmax;
+ if (ymin > ymax)
+ ymin = ymax;
+ p->o_val[0] = xmin; p->o_val[1] = ymin;
+ p->o_val[2] = xmax; p->o_val[3] = ymax;
+ p->o_symtab = q->o_symtab = stack[nstack+1].p_symtab;
+ xmin = stack[nstack].p_xmin;
+ ymin = stack[nstack].p_ymin;
+ xmax = stack[nstack].p_xmax;
+ ymax = stack[nstack].p_ymax;
+ }
+ return(q);
+}
+
+obj *blockgen(obj *p, obj *q) /* handles [...] */
+{
+ int i, invis, at, with;
+ double ddval, h, w, xwith, ywith;
+ double x0, y0, x1, y1, cx, cy;
+ obj *ppos;
+ Attr *ap;
+
+ invis = at = 0;
+ with = xwith = ywith = 0;
+ ddval = 0;
+ w = p->o_val[2] - p->o_val[0];
+ h = p->o_val[3] - p->o_val[1];
+ cx = (p->o_val[2] + p->o_val[0]) / 2; /* geom ctr of [] wrt local orogin */
+ cy = (p->o_val[3] + p->o_val[1]) / 2;
+ dprintf("cx,cy=%g,%g\n", cx, cy);
+ for (i = 0; i < nattr; i++) {
+ ap = &attr[i];
+ switch (ap->a_type) {
+ case HEIGHT:
+ h = ap->a_val.f;
+ break;
+ case WIDTH:
+ w = ap->a_val.f;
+ break;
+ case WITH:
+ with = ap->a_val.i; /* corner */
+ break;
+ case PLACE: /* actually with position ... */
+ ppos = ap->a_val.o;
+ xwith = cx - ppos->o_x;
+ ywith = cy - ppos->o_y;
+ with = PLACE;
+ break;
+ case AT:
+ case FROM:
+ ppos = ap->a_val.o;
+ curx = ppos->o_x;
+ cury = ppos->o_y;
+ at++;
+ break;
+ case INVIS:
+ invis = INVIS;
+ break;
+ case TEXTATTR:
+ savetext(ap->a_sub, ap->a_val.p);
+ break;
+ }
+ }
+ if (with) {
+ switch (with) {
+ case NORTH: ywith = -h / 2; break;
+ case SOUTH: ywith = h / 2; break;
+ case EAST: xwith = -w / 2; break;
+ case WEST: xwith = w / 2; break;
+ case NE: xwith = -w / 2; ywith = -h / 2; break;
+ case SE: xwith = -w / 2; ywith = h / 2; break;
+ case NW: xwith = w / 2; ywith = -h / 2; break;
+ case SW: xwith = w / 2; ywith = h / 2; break;
+ }
+ curx += xwith;
+ cury += ywith;
+ }
+ if (!at) {
+ if (isright(hvmode))
+ curx += w / 2;
+ else if (isleft(hvmode))
+ curx -= w / 2;
+ else if (isup(hvmode))
+ cury += h / 2;
+ else
+ cury -= h / 2;
+ }
+ x0 = curx - w / 2;
+ y0 = cury - h / 2;
+ x1 = curx + w / 2;
+ y1 = cury + h / 2;
+ extreme(x0, y0);
+ extreme(x1, y1);
+ p->o_x = curx;
+ p->o_y = cury;
+ p->o_nt1 = ntext1;
+ p->o_nt2 = ntext;
+ ntext1 = ntext;
+ p->o_val[0] = w;
+ p->o_val[1] = h;
+ p->o_val[2] = cx;
+ p->o_val[3] = cy;
+ p->o_val[5] = q->o_nobj - 1; /* last item in [...] */
+ p->o_ddval = ddval;
+ p->o_attr = invis;
+ dprintf("[] %g %g %g %g at %g %g, h=%g, w=%g\n", x0, y0, x1, y1, curx, cury, h, w);
+ if (isright(hvmode))
+ curx = x1;
+ else if (isleft(hvmode))
+ curx = x0;
+ else if (isup(hvmode))
+ cury = y1;
+ else
+ cury = y0;
+ for (i = 0; i <= 5; i++)
+ q->o_val[i] = p->o_val[i];
+ stack[nstack+1].p_symtab = NULL; /* so won't be found again */
+ blockadj(p); /* fix up coords for enclosed blocks */
+ return(p);
+}
+
+void blockadj(obj *p) /* adjust coords in block starting at p */
+{
+ double dx, dy;
+ int n, lev;
+
+ dx = p->o_x - p->o_val[2];
+ dy = p->o_y - p->o_val[3];
+ n = p->o_nobj + 1;
+ dprintf("into blockadj: dx,dy=%g,%g\n", dx, dy);
+ for (lev = 1; lev > 0; n++) {
+ p = objlist[n];
+ if (p->o_type == BLOCK)
+ lev++;
+ else if (p->o_type == BLOCKEND)
+ lev--;
+ dprintf("blockadj: type=%d o_x,y=%g,%g;", p->o_type, p->o_x, p->o_y);
+ p->o_x += dx;
+ p->o_y += dy;
+ dprintf(" becomes %g,%g\n", p->o_x, p->o_y);
+ switch (p->o_type) { /* other absolute coords */
+ case LINE:
+ case ARROW:
+ case SPLINE:
+ p->o_val[0] += dx;
+ p->o_val[1] += dy;
+ break;
+ case ARC:
+ p->o_val[0] += dx;
+ p->o_val[1] += dy;
+ p->o_val[2] += dx;
+ p->o_val[3] += dy;
+ break;
+ }
+ }
+}
diff --git a/src/cmd/pic/boxgen.c b/src/cmd/pic/boxgen.c
new file mode 100644
index 00000000..b575cd2f
--- /dev/null
+++ b/src/cmd/pic/boxgen.c
@@ -0,0 +1,115 @@
+#include <stdio.h>
+#include "pic.h"
+#include "y.tab.h"
+
+obj *boxgen(void)
+{
+ static double prevh = HT;
+ static double prevw = WID; /* golden mean, sort of */
+ int i, at, battr, with;
+ double ddval, fillval, xwith, ywith;
+ double h, w, x0, y0, x1, y1;
+ obj *p, *ppos;
+ Attr *ap;
+
+ h = getfval("boxht");
+ w = getfval("boxwid");
+ at = battr = with = 0;
+ ddval = fillval = xwith = ywith = 0;
+ for (i = 0; i < nattr; i++) {
+ ap = &attr[i];
+ switch (ap->a_type) {
+ case HEIGHT:
+ h = ap->a_val.f;
+ break;
+ case WIDTH:
+ w = ap->a_val.f;
+ break;
+ case SAME:
+ h = prevh;
+ w = prevw;
+ break;
+ case WITH:
+ with = ap->a_val.i; /* corner */
+ break;
+ case AT:
+ ppos = ap->a_val.o;
+ curx = ppos->o_x;
+ cury = ppos->o_y;
+ at++;
+ break;
+ case INVIS:
+ battr |= INVIS;
+ break;
+ case NOEDGE:
+ battr |= NOEDGEBIT;
+ break;
+ case DOT:
+ case DASH:
+ battr |= ap->a_type==DOT ? DOTBIT : DASHBIT;
+ if (ap->a_sub == DEFAULT)
+ ddval = getfval("dashwid");
+ else
+ ddval = ap->a_val.f;
+ break;
+ case FILL:
+ battr |= FILLBIT;
+ if (ap->a_sub == DEFAULT)
+ fillval = getfval("fillval");
+ else
+ fillval = ap->a_val.f;
+ break;
+ case TEXTATTR:
+ savetext(ap->a_sub, ap->a_val.p);
+ break;
+ }
+ }
+ if (with) {
+ switch (with) {
+ case NORTH: ywith = -h / 2; break;
+ case SOUTH: ywith = h / 2; break;
+ case EAST: xwith = -w / 2; break;
+ case WEST: xwith = w / 2; break;
+ case NE: xwith = -w / 2; ywith = -h / 2; break;
+ case SE: xwith = -w / 2; ywith = h / 2; break;
+ case NW: xwith = w / 2; ywith = -h / 2; break;
+ case SW: xwith = w / 2; ywith = h / 2; break;
+ }
+ curx += xwith;
+ cury += ywith;
+ }
+ if (!at) {
+ if (isright(hvmode))
+ curx += w / 2;
+ else if (isleft(hvmode))
+ curx -= w / 2;
+ else if (isup(hvmode))
+ cury += h / 2;
+ else
+ cury -= h / 2;
+ }
+ x0 = curx - w / 2;
+ y0 = cury - h / 2;
+ x1 = curx + w / 2;
+ y1 = cury + h / 2;
+ extreme(x0, y0);
+ extreme(x1, y1);
+ p = makenode(BOX, 2);
+ p->o_val[0] = w;
+ p->o_val[1] = h;
+ p->o_attr = battr;
+ p->o_ddval = ddval;
+ p->o_fillval = fillval;
+ dprintf("B %g %g %g %g at %g %g, h=%g, w=%g\n", x0, y0, x1, y1, curx, cury, h, w);
+ if (isright(hvmode))
+ curx = x1;
+ else if (isleft(hvmode))
+ curx = x0;
+ else if (isup(hvmode))
+ cury = y1;
+ else
+ cury = y0;
+ prevh = h;
+ prevw = w;
+ return(p);
+}
diff --git a/src/cmd/pic/circgen.c b/src/cmd/pic/circgen.c
new file mode 100644
index 00000000..f40bae9a
--- /dev/null
+++ b/src/cmd/pic/circgen.c
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include "pic.h"
+#include "y.tab.h"
+
+obj *circgen(int type)
+{
+ static double rad[2] = { HT2, WID2 };
+ static double rad2[2] = { HT2, HT2 };
+ int i, at, t, with, battr;
+ double xwith, ywith;
+ double r, r2, ddval, fillval;
+ obj *p, *ppos;
+ Attr *ap;
+
+ battr = at = 0;
+ with = xwith = ywith = fillval = ddval = 0;
+ t = (type == CIRCLE) ? 0 : 1;
+ if (type == CIRCLE)
+ r = r2 = getfval("circlerad");
+ else if (type == ELLIPSE) {
+ r = getfval("ellipsewid") / 2;
+ r2 = getfval("ellipseht") / 2;
+ }
+ for (i = 0; i < nattr; i++) {
+ ap = &attr[i];
+ switch (ap->a_type) {
+ case TEXTATTR:
+ savetext(ap->a_sub, ap->a_val.p);
+ break;
+ case RADIUS:
+ r = ap->a_val.f;
+ break;
+ case DIAMETER:
+ case WIDTH:
+ r = ap->a_val.f / 2;
+ break;
+ case HEIGHT:
+ r2 = ap->a_val.f / 2;
+ break;
+ case SAME:
+ r = rad[t];
+ r2 = rad2[t];
+ break;
+ case WITH:
+ with = ap->a_val.i;
+ break;
+ case AT:
+ ppos = ap->a_val.o;
+ curx = ppos->o_x;
+ cury = ppos->o_y;
+ at++;
+ break;
+ case INVIS:
+ battr |= INVIS;
+ break;
+ case NOEDGE:
+ battr |= NOEDGEBIT;
+ break;
+ case DOT:
+ case DASH:
+ battr |= ap->a_type==DOT ? DOTBIT : DASHBIT;
+ if (ap->a_sub == DEFAULT)
+ ddval = getfval("dashwid");
+ else
+ ddval = ap->a_val.f;
+ break;
+ case FILL:
+ battr |= FILLBIT;
+ if (ap->a_sub == DEFAULT)
+ fillval = getfval("fillval");
+ else
+ fillval = ap->a_val.f;
+ break;
+ }
+ }
+ if (type == CIRCLE)
+ r2 = r; /* probably superfluous */
+ if (with) {
+ switch (with) {
+ case NORTH: ywith = -r2; break;
+ case SOUTH: ywith = r2; break;
+ case EAST: xwith = -r; break;
+ case WEST: xwith = r; break;
+ case NE: xwith = -r * 0.707; ywith = -r2 * 0.707; break;
+ case SE: xwith = -r * 0.707; ywith = r2 * 0.707; break;
+ case NW: xwith = r * 0.707; ywith = -r2 * 0.707; break;
+ case SW: xwith = r * 0.707; ywith = r2 * 0.707; break;
+ }
+ curx += xwith;
+ cury += ywith;
+ }
+ if (!at) {
+ if (isright(hvmode))
+ curx += r;
+ else if (isleft(hvmode))
+ curx -= r;
+ else if (isup(hvmode))
+ cury += r2;
+ else
+ cury -= r2;
+ }
+ p = makenode(type, 2);
+ p->o_val[0] = rad[t] = r;
+ p->o_val[1] = rad2[t] = r2;
+ if (r <= 0 || r2 <= 0) {
+ ERROR "%s has invalid radius %g\n", (type==CIRCLE) ? "circle" : "ellipse", r<r2 ? r : r2 WARNING;
+ }
+ p->o_attr = battr;
+ p->o_ddval = ddval;
+ p->o_fillval = fillval;
+ extreme(curx+r, cury+r2);
+ extreme(curx-r, cury-r2);
+ if (type == CIRCLE)
+ dprintf("C %g %g %g\n", curx, cury, r);
+ if (type == ELLIPSE)
+ dprintf("E %g %g %g %g\n", curx, cury, r, r2);
+ if (isright(hvmode))
+ curx += r;
+ else if (isleft(hvmode))
+ curx -= r;
+ else if (isup(hvmode))
+ cury += r2;
+ else
+ cury -= r2;
+ return(p);
+}
diff --git a/src/cmd/pic/for.c b/src/cmd/pic/for.c
new file mode 100644
index 00000000..cece48b5
--- /dev/null
+++ b/src/cmd/pic/for.c
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "pic.h"
+#include "y.tab.h"
+
+#define SLOP 1.001
+
+typedef struct {
+ char *var; /* index variable */
+ double to; /* limit */
+ double by;
+ int op; /* operator */
+ char *str; /* string to push back */
+} For;
+
+For forstk[10]; /* stack of for loops */
+For *forp = forstk; /* pointer to current top */
+
+void setfval(char *, double);
+void nextfor(void);
+
+void forloop(char *var, double from, double to, int op,
+ double by, char *str) /* set up a for loop */
+{
+ dprintf("# for %s from %g to %g by %c %g \n",
+ var, from, to, op, by);
+ if (++forp >= forstk+10)
+ ERROR "for loop nested too deep" FATAL;
+ forp->var = var;
+ forp->to = to;
+ forp->op = op;
+ forp->by = by;
+ forp->str = str;
+ setfval(var, from);
+ nextfor();
+ unput('\n');
+}
+
+void nextfor(void) /* do one iteration of a for loop */
+{
+ /* BUG: this should depend on op and direction */
+ if (getfval(forp->var) > SLOP * forp->to) { /* loop is done */
+ free(forp->str);
+ if (--forp < forstk)
+ ERROR "forstk popped too far" FATAL;
+ } else { /* another iteration */
+ pushsrc(String, "\nEndfor\n");
+ pushsrc(String, forp->str);
+ }
+}
+
+void endfor(void) /* end one iteration of for loop */
+{
+ struct symtab *p = lookup(forp->var);
+
+ switch (forp->op) {
+ case '+':
+ case ' ':
+ p->s_val.f += forp->by;
+ break;
+ case '-':
+ p->s_val.f -= forp->by;
+ break;
+ case '*':
+ p->s_val.f *= forp->by;
+ break;
+ case '/':
+ p->s_val.f /= forp->by;
+ break;
+ }
+ nextfor();
+}
+
+char *ifstat(double expr, char *thenpart, char *elsepart)
+{
+ dprintf("if %g then <%s> else <%s>\n", expr, thenpart, elsepart? elsepart : "");
+ if (expr) {
+ unput('\n');
+ pushsrc(Free, thenpart);
+ pushsrc(String, thenpart);
+ unput('\n');
+ if (elsepart)
+ free(elsepart);
+ return thenpart; /* to be freed later */
+ } else {
+ free(thenpart);
+ if (elsepart) {
+ unput('\n');
+ pushsrc(Free, elsepart);
+ pushsrc(String, elsepart);
+ unput('\n');
+ }
+ return elsepart;
+ }
+}
diff --git a/src/cmd/pic/input.c b/src/cmd/pic/input.c
new file mode 100644
index 00000000..875b1868
--- /dev/null
+++ b/src/cmd/pic/input.c
@@ -0,0 +1,593 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include "pic.h"
+#include "y.tab.h"
+
+Infile infile[10];
+Infile *curfile = infile;
+
+#define MAXSRC 50
+Src src[MAXSRC]; /* input source stack */
+Src *srcp = src;
+
+void do_thru(void);
+int nextchar(void);
+int getarg(char *);
+void freedef(char *);
+int baldelim(int, char *);
+
+void pushsrc(int type, char *ptr) /* new input source */
+{
+ if (++srcp >= src + MAXSRC)
+ ERROR "inputs nested too deep" FATAL;
+ srcp->type = type;
+ srcp->sp = ptr;
+ if (dbg > 1) {
+ printf("\n%3d ", srcp - src);
+ switch (srcp->type) {
+ case File:
+ printf("push file %s\n", ((Infile *)ptr)->fname);
+ break;
+ case Macro:
+ printf("push macro <%s>\n", ptr);
+ break;
+ case Char:
+ printf("push char <%c>\n", *ptr);
+ break;
+ case Thru:
+ printf("push thru\n");
+ break;
+ case String:
+ printf("push string <%s>\n", ptr);
+ break;
+ case Free:
+ printf("push free <%s>\n", ptr);
+ break;
+ default:
+ ERROR "pushed bad type %d", srcp->type FATAL;
+ }
+ }
+}
+
+void popsrc(void) /* restore an old one */
+{
+ if (srcp <= src)
+ ERROR "too many inputs popped" FATAL;
+ if (dbg > 1) {
+ printf("%3d ", srcp - src);
+ switch (srcp->type) {
+ case File:
+ printf("pop file\n");
+ break;
+ case Macro:
+ printf("pop macro\n");
+ break;
+ case Char:
+ printf("pop char <%c>\n", *srcp->sp);
+ break;
+ case Thru:
+ printf("pop thru\n");
+ break;
+ case String:
+ printf("pop string\n");
+ break;
+ case Free:
+ printf("pop free\n");
+ break;
+ default:
+ ERROR "pop weird input %d", srcp->type FATAL;
+ }
+ }
+ srcp--;
+}
+
+void definition(char *s) /* collect definition for s and install */
+ /* definitions picked up lexically */
+{
+ char *p;
+ struct symtab *stp;
+
+ p = delimstr("definition");
+ stp = lookup(s);
+ if (stp != NULL) { /* it's there before */
+ if (stp->s_type != DEFNAME) {
+ ERROR "%s used as variable and definition", s WARNING;
+ return;
+ }
+ free(stp->s_val.p);
+ stp->s_val.p = p;
+ } else {
+ YYSTYPE u;
+ u.p = p;
+ makevar(tostring(s), DEFNAME, u);
+ }
+ dprintf("installing %s as `%s'\n", s, p);
+}
+
+char *delimstr(char *s) /* get body of X ... X */
+ /* message if too big */
+{
+ int c, delim, rdelim, n, deep;
+ static char *buf = NULL;
+ static int nbuf = 0;
+ char *p;
+
+ if (buf == NULL)
+ buf = grow(buf, "buf", nbuf += 1000, sizeof(buf[0]));
+ while ((delim = input()) == ' ' || delim == '\t' || delim == '\n')
+ ;
+ rdelim = baldelim(delim, "{}"); /* could be "(){}[]`'" */
+ deep = 1;
+ for (p = buf; ; ) {
+ c = input();
+ if (c == rdelim)
+ if (--deep == 0)
+ break;
+ if (c == delim)
+ deep++;
+ if (p >= buf + nbuf) {
+ n = p - buf;
+ buf = grow(buf, "buf", nbuf += 1000, sizeof(buf[0]));
+ p = buf + n;
+ }
+ if (c == EOF)
+ ERROR "end of file in %s %c %.20s... %c", s, delim, buf, delim FATAL;
+ *p++ = c;
+ }
+ *p = '\0';
+ dprintf("delimstr %s %c <%s> %c\n", s, delim, buf, delim);
+ return tostring(buf);
+}
+
+baldelim(int c, char *s) /* replace c by balancing entry in s */
+{
+ for ( ; *s; s += 2)
+ if (*s == c)
+ return s[1];
+ return c;
+}
+
+void undefine(char *s) /* undefine macro */
+{
+ while (*s != ' ' && *s != '\t') /* skip "undef..." */
+ s++;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ freedef(s);
+}
+
+
+Arg args[10]; /* argument frames */
+Arg *argfp = args; /* frame pointer */
+int argcnt; /* number of arguments seen so far */
+
+void dodef(struct symtab *stp) /* collect args and switch input to defn */
+{
+ int i, len;
+ char *p;
+ Arg *ap;
+
+ ap = argfp+1;
+ if (ap >= args+10)
+ ERROR "arguments too deep" FATAL;
+ argcnt = 0;
+ if (input() != '(')
+ ERROR "disaster in dodef" FATAL;
+ if (ap->argval == 0)
+ ap->argval = malloc(1000);
+ for (p = ap->argval; (len = getarg(p)) != -1; p += len) {
+ ap->argstk[argcnt++] = p;
+ if (input() == ')')
+ break;
+ }
+ for (i = argcnt; i < MAXARGS; i++)
+ ap->argstk[i] = "";
+ if (dbg)
+ for (i = 0; i < argcnt; i++)
+ printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
+ argfp = ap;
+ pushsrc(Macro, stp->s_val.p);
+}
+
+getarg(char *p) /* pick up single argument, store in p, return length */
+{
+ int n, c, npar;
+
+ n = npar = 0;
+ for ( ;; ) {
+ c = input();
+ if (c == EOF)
+ ERROR "end of file in getarg" FATAL;
+ if (npar == 0 && (c == ',' || c == ')'))
+ break;
+ if (c == '"') /* copy quoted stuff intact */
+ do {
+ *p++ = c;
+ n++;
+ } while ((c = input()) != '"' && c != EOF);
+ else if (c == '(')
+ npar++;
+ else if (c == ')')
+ npar--;
+ n++;
+ *p++ = c;
+ }
+ *p = 0;
+ unput(c);
+ return(n + 1);
+}
+
+#define PBSIZE 2000
+char pbuf[PBSIZE]; /* pushback buffer */
+char *pb = pbuf-1; /* next pushed back character */
+
+char ebuf[200]; /* collect input here for error reporting */
+char *ep = ebuf;
+
+int begin = 0;
+extern int thru;
+extern struct symtab *thrudef;
+extern char *untilstr;
+
+input(void)
+{
+ register int c;
+
+ if (thru && begin) {
+ do_thru();
+ begin = 0;
+ }
+ c = nextchar();
+ if (dbg > 1)
+ printf(" <%c>", c);
+ if (ep >= ebuf + sizeof ebuf)
+ ep = ebuf;
+ return *ep++ = c;
+}
+
+nextchar(void)
+{
+ register int c;
+
+ loop:
+ switch (srcp->type) {
+ case Free: /* free string */
+ free(srcp->sp);
+ popsrc();
+ goto loop;
+ case Thru: /* end of pushed back line */
+ begin = 1;
+ popsrc();
+ c = '\n';
+ break;
+ case Char:
+ if (pb >= pbuf) {
+ c = *pb--;
+ popsrc();
+ break;
+ } else { /* can't happen? */
+ popsrc();
+ goto loop;
+ }
+ case String:
+ c = *srcp->sp++;
+ if (c == '\0') {
+ popsrc();
+ goto loop;
+ } else {
+ if (*srcp->sp == '\0') /* empty, so pop */
+ popsrc();
+ break;
+ }
+ case Macro:
+ c = *srcp->sp++;
+ if (c == '\0') {
+ if (--argfp < args)
+ ERROR "argfp underflow" FATAL;
+ popsrc();
+ goto loop;
+ } else if (c == '$' && isdigit(*srcp->sp)) {
+ int n = 0;
+ while (isdigit(*srcp->sp))
+ n = 10 * n + *srcp->sp++ - '0';
+ if (n > 0 && n <= MAXARGS)
+ pushsrc(String, argfp->argstk[n-1]);
+ goto loop;
+ }
+ break;
+ case File:
+ c = getc(curfile->fin);
+ if (c == EOF) {
+ if (curfile == infile)
+ ERROR "end of file inside .PS/.PE" FATAL;
+ if (curfile->fin != stdin) {
+ fclose(curfile->fin);
+ free(curfile->fname); /* assumes allocated */
+ }
+ curfile--;
+ printlf(curfile->lineno, curfile->fname);
+ popsrc();
+ thru = 0; /* chicken out */
+ thrudef = 0;
+ if (untilstr) {
+ free(untilstr);
+ untilstr = 0;
+ }
+ goto loop;
+ }
+ if (c == '\n')
+ curfile->lineno++;
+ break;
+ }
+ return c;
+}
+
+void do_thru(void) /* read one line, make into a macro expansion */
+{
+ int c, i;
+ char *p;
+ Arg *ap;
+
+ ap = argfp+1;
+ if (ap >= args+10)
+ ERROR "arguments too deep" FATAL;
+ if (ap->argval == NULL)
+ ap->argval = malloc(1000);
+ p = ap->argval;
+ argcnt = 0;
+ c = nextchar();
+ if (thru == 0) { /* end of file was seen, so thru is done */
+ unput(c);
+ return;
+ }
+ for ( ; c != '\n' && c != EOF; ) {
+ if (c == ' ' || c == '\t') {
+ c = nextchar();
+ continue;
+ }
+ ap->argstk[argcnt++] = p;
+ if (c == '"') {
+ do {
+ *p++ = c;
+ if ((c = nextchar()) == '\\') {
+ *p++ = c;
+ *p++ = nextchar();
+ c = nextchar();
+ }
+ } while (c != '"' && c != '\n' && c != EOF);
+ *p++ = '"';
+ if (c == '"')
+ c = nextchar();
+ } else {
+ do {
+ *p++ = c;
+ } while ((c = nextchar())!=' ' && c!='\t' && c!='\n' && c!=',' && c!=EOF);
+ if (c == ',')
+ c = nextchar();
+ }
+ *p++ = '\0';
+ }
+ if (c == EOF)
+ ERROR "unexpected end of file in do_thru" FATAL;
+ if (argcnt == 0) { /* ignore blank line */
+ pushsrc(Thru, (char *) 0);
+ return;
+ }
+ for (i = argcnt; i < MAXARGS; i++)
+ ap->argstk[i] = "";
+ if (dbg)
+ for (i = 0; i < argcnt; i++)
+ printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
+ if (strcmp(ap->argstk[0], ".PE") == 0) {
+ thru = 0;
+ thrudef = 0;
+ pushsrc(String, "\n.PE\n");
+ return;
+ }
+ if (untilstr && strcmp(ap->argstk[0], untilstr) == 0) {
+ thru = 0;
+ thrudef = 0;
+ free(untilstr);
+ untilstr = 0;
+ return;
+ }
+ pushsrc(Thru, (char *) 0);
+ dprintf("do_thru pushing back <%s>\n", thrudef->s_val.p);
+ argfp = ap;
+ pushsrc(Macro, thrudef->s_val.p);
+}
+
+unput(int c)
+{
+ if (++pb >= pbuf + sizeof pbuf)
+ ERROR "pushback overflow" FATAL;
+ if (--ep < ebuf)
+ ep = ebuf + sizeof(ebuf) - 1;
+ *pb = c;
+ pushsrc(Char, pb);
+ return c;
+}
+
+void pbstr(char *s)
+{
+ pushsrc(String, s);
+}
+
+double errcheck(double x, char *s)
+{
+ extern int errno;
+
+ if (errno == EDOM) {
+ errno = 0;
+ ERROR "%s argument out of domain", s WARNING;
+ } else if (errno == ERANGE) {
+ errno = 0;
+ ERROR "%s result out of range", s WARNING;
+ }
+ return x;
+}
+
+char errbuf[200];
+
+void eprint(void);
+
+void yyerror(char *s)
+{
+ extern char *cmdname;
+ int ern = errno; /* cause some libraries clobber it */
+
+ if (synerr)
+ return;
+ fflush(stdout);
+ fprintf(stderr, "%s: %s", cmdname, s);
+ if (ern > 0) {
+ errno = ern;
+ perror("???");
+ }
+ fprintf(stderr, " near %s:%d\n",
+ curfile->fname, curfile->lineno+1);
+ eprint();
+ synerr = 1;
+ errno = 0;
+}
+
+void eprint(void) /* try to print context around error */
+{
+ char *p, *q;
+
+ p = ep - 1;
+ if (p > ebuf && *p == '\n')
+ p--;
+ for ( ; p >= ebuf && *p != '\n'; p--)
+ ;
+ while (*p == '\n')
+ p++;
+ fprintf(stderr, " context is\n\t");
+ for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
+ ;
+ while (p < q)
+ putc(*p++, stderr);
+ fprintf(stderr, " >>> ");
+ while (p < ep)
+ putc(*p++, stderr);
+ fprintf(stderr, " <<< ");
+ while (pb >= pbuf)
+ putc(*pb--, stderr);
+ fgets(ebuf, sizeof ebuf, curfile->fin);
+ fprintf(stderr, "%s", ebuf);
+ pbstr("\n.PE\n"); /* safety first */
+ ep = ebuf;
+}
+
+void yywrap(void) {}
+
+char *newfile = 0; /* filename for file copy */
+char *untilstr = 0; /* string that terminates a thru */
+int thru = 0; /* 1 if copying thru macro */
+struct symtab *thrudef = 0; /* macro being used */
+
+void copyfile(char *s) /* remember file to start reading from */
+{
+ newfile = s;
+}
+
+void copydef(struct symtab *p) /* remember macro symtab ptr */
+{
+ thrudef = p;
+}
+
+struct symtab *copythru(char *s) /* collect the macro name or body for thru */
+{
+ struct symtab *p;
+ char *q, *addnewline(char *);
+
+ p = lookup(s);
+ if (p != NULL) {
+ if (p->s_type == DEFNAME) {
+ p->s_val.p = addnewline(p->s_val.p);
+ return p;
+ } else
+ ERROR "%s used as define and name", s FATAL;
+ }
+ /* have to collect the definition */
+ pbstr(s); /* first char is the delimiter */
+ q = delimstr("thru body");
+ s = "nameless";
+ p = lookup(s);
+ if (p != NULL) {
+ if (p->s_val.p)
+ free(p->s_val.p);
+ p->s_val.p = q;
+ } else {
+ YYSTYPE u;
+ u.p = q;
+ p = makevar(tostring(s), DEFNAME, u);
+ }
+ p->s_val.p = addnewline(p->s_val.p);
+ dprintf("installing %s as `%s'\n", s, p->s_val.p);
+ return p;
+}
+
+char *addnewline(char *p) /* add newline to end of p */
+{
+ int n;
+
+ n = strlen(p);
+ if (p[n-1] != '\n') {
+ p = realloc(p, n+2);
+ p[n] = '\n';
+ p[n+1] = '\0';
+ }
+ return p;
+}
+
+void copyuntil(char *s) /* string that terminates a thru */
+{
+ untilstr = s;
+}
+
+void copy(void) /* begin input from file, etc. */
+{
+ FILE *fin;
+
+ if (newfile) {
+ if ((fin = fopen(newfile, "r")) == NULL)
+ ERROR "can't open file %s", newfile FATAL;
+ curfile++;
+ curfile->fin = fin;
+ curfile->fname = newfile;
+ curfile->lineno = 0;
+ printlf(1, curfile->fname);
+ pushsrc(File, curfile->fname);
+ newfile = 0;
+ }
+ if (thrudef) {
+ thru = 1;
+ begin = 1; /* wrong place */
+ }
+}
+
+char shellbuf[1000], *shellp;
+
+void shell_init(void) /* set up to interpret a shell command */
+{
+ sprintf(shellbuf, "rc -c '");
+ shellp = shellbuf + strlen(shellbuf);
+}
+
+void shell_text(char *s) /* add string to command being collected */
+{
+ while (*shellp++ = *s++)
+ ;
+ shellp--;
+}
+
+void shell_exec(void) /* do it */
+{
+ *shellp++ = '\'';
+ *shellp = '\0';
+ system(shellbuf);
+}
diff --git a/src/cmd/pic/linegen.c b/src/cmd/pic/linegen.c
new file mode 100644
index 00000000..eab268f5
--- /dev/null
+++ b/src/cmd/pic/linegen.c
@@ -0,0 +1,240 @@
+#include <stdio.h>
+#include <math.h>
+#include "pic.h"
+#include "y.tab.h"
+
+obj *linegen(int type)
+{
+ static double prevdx = HT;
+ static double prevdy = 0;
+ static double prevw = HT10;
+ static double prevh = HT5;
+ int i, j, some, head, ddtype, invis, chop, battr, with;
+ double ddval, chop1, chop2, x0, y0, x1, y1;
+ double fillval = 0;
+ double theta;
+ double defx, defy, xwith, ywith;
+ obj *p, *ppos;
+ static int xtab[] = { 1, 0, -1, 0 }; /* R=0, U=1, L=2, D=3 */
+ static int ytab[] = { 0, 1, 0, -1 };
+ double dx[500], dy[500];
+ int ndxy;
+ double nx, ny;
+ Attr *ap, *chop_ap[4];
+
+ nx = curx;
+ ny = cury;
+ defx = getfval("linewid");
+ defy = getfval("lineht");
+ prevh = getfval("arrowht");
+ prevw = getfval("arrowwid");
+ dx[0] = dy[0] = ndxy = some = head = invis = battr = with = 0;
+ chop = chop1 = chop2 = 0;
+ ddtype = ddval = xwith = ywith = 0;
+ for (i = 0; i < nattr; i++) {
+ ap = &attr[i];
+ switch (ap->a_type) {
+ case TEXTATTR:
+ savetext(ap->a_sub, ap->a_val.p);
+ break;
+ case HEAD:
+ head += ap->a_val.i;
+ break;
+ case INVIS:
+ invis = INVIS;
+ break;
+ case NOEDGE:
+ battr |= NOEDGEBIT;
+ break;
+ case DOT:
+ case DASH:
+ ddtype = ap->a_type==DOT ? DOTBIT : DASHBIT;
+ if (ap->a_sub == DEFAULT)
+ ddval = getfval("dashwid");
+ else
+ ddval = ap->a_val.f;
+ break;
+ case SAME:
+ dx[ndxy] = prevdx;
+ dy[ndxy] = prevdy;
+ some++;
+ break;
+ case LEFT:
+ dx[ndxy] -= (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
+ some++;
+ hvmode = L_DIR;
+ break;
+ case RIGHT:
+ dx[ndxy] += (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
+ some++;
+ hvmode = R_DIR;
+ break;
+ case UP:
+ dy[ndxy] += (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
+ some++;
+ hvmode = U_DIR;
+ break;
+ case DOWN:
+ dy[ndxy] -= (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
+ some++;
+ hvmode = D_DIR;
+ break;
+ case HEIGHT: /* length of arrowhead */
+ prevh = ap->a_val.f;
+ break;
+ case WIDTH: /* width of arrowhead */
+ prevw = ap->a_val.f;
+ break;
+ case TO:
+ if (some) {
+ nx += dx[ndxy];
+ ny += dy[ndxy];
+ ndxy++;
+ dx[ndxy] = dy[ndxy] = some = 0;
+ }
+ ppos = attr[i].a_val.o;
+ dx[ndxy] = ppos->o_x - nx;
+ dy[ndxy] = ppos->o_y - ny;
+ some++;
+ break;
+ case BY:
+ if (some) {
+ nx += dx[ndxy];
+ ny += dy[ndxy];
+ ndxy++;
+ dx[ndxy] = dy[ndxy] = some = 0;
+ }
+ ppos = ap->a_val.o;
+ dx[ndxy] = ppos->o_x;
+ dy[ndxy] = ppos->o_y;
+ some++;
+ break;
+ case THEN: /* turn off any previous accumulation */
+ if (some) {
+ nx += dx[ndxy];
+ ny += dy[ndxy];
+ ndxy++;
+ dx[ndxy] = dy[ndxy] = some = 0;
+ }
+ break;
+ case FROM:
+ case AT:
+ ppos = ap->a_val.o;
+ nx = curx = ppos->o_x;
+ ny = cury = ppos->o_y;
+ break;
+ case WITH:
+ with = ap->a_val.i;
+ break;
+ case CHOP:
+ if (ap->a_sub != PLACENAME) {
+ if( chop == 0)
+ chop1 = chop2 = ap->a_val.f;
+ else
+ chop2 = ap->a_val.f;
+ }
+ chop_ap[chop++] = ap;
+ break;
+ case FILL:
+ battr |= FILLBIT;
+ if (ap->a_sub == DEFAULT)
+ fillval = getfval("fillval");
+ else
+ fillval = ap->a_val.f;
+ break;
+ }
+ }
+ if (with) { /* this doesn't work at all */
+ switch (with) {
+ case CENTER:
+ xwith = (dx[1] - dx[0]) / 2; ywith = (dy[1] - dy[0]) / 2; break;
+ }
+ for (i = 0; i < ndxy; i++) {
+ dx[i] -= xwith;
+ dy[i] -= ywith;
+ }
+ curx += xwith;
+ cury += ywith;
+ }
+ if (some) {
+ nx += dx[ndxy];
+ ny += dy[ndxy];
+ ndxy++;
+ defx = dx[ndxy-1];
+ defy = dy[ndxy-1];
+ } else {
+ defx *= xtab[hvmode];
+ defy *= ytab[hvmode];
+ dx[ndxy] = defx;
+ dy[ndxy] = defy;
+ ndxy++;
+ nx += defx;
+ ny += defy;
+ }
+ prevdx = defx;
+ prevdy = defy;
+ if (chop) {
+ if (chop == 1 && chop1 == 0) /* just said "chop", so use default */
+ chop1 = chop2 = getfval("circlerad");
+ theta = atan2(dy[0], dx[0]);
+ x0 = chop1 * cos(theta);
+ y0 = chop1 * sin(theta);
+ curx += x0;
+ cury += y0;
+ dx[0] -= x0;
+ dy[0] -= y0;
+
+ theta = atan2(dy[ndxy-1], dx[ndxy-1]);
+ x1 = chop2 * cos(theta);
+ y1 = chop2 * sin(theta);
+ nx -= x1;
+ ny -= y1;
+ dx[ndxy-1] -= x1;
+ dy[ndxy-1] -= y1;
+ dprintf("chopping %g %g %g %g; cur=%g,%g end=%g,%g\n",
+ x0, y0, x1, y1, curx, cury, nx, ny);
+ }
+ p = makenode(type, 5 + 2 * ndxy);
+ curx = p->o_val[0] = nx;
+ cury = p->o_val[1] = ny;
+ if (head || type == ARROW) {
+ p->o_nhead = getfval("arrowhead");
+ p->o_val[2] = prevw;
+ p->o_val[3] = prevh;
+ if (head == 0)
+ head = HEAD2; /* default arrow head */
+ }
+ p->o_attr = head | invis | ddtype | battr;
+ p->o_fillval = fillval;
+ p->o_val[4] = ndxy;
+ nx = p->o_x;
+ ny = p->o_y;
+ for (i = 0, j = 5; i < ndxy; i++, j += 2) {
+ p->o_val[j] = dx[i];
+ p->o_val[j+1] = dy[i];
+ if (type == LINE || type == ARROW)
+ extreme(nx += dx[i], ny += dy[i]);
+ else if (type == SPLINE && i < ndxy-1) {
+ /* to compute approx extreme of spline at p,
+ /* compute midway between p-1 and p+1,
+ /* then go 3/4 from there to p */
+ double ex, ey, xi, yi, xi1, yi1;
+ xi = nx + dx[i]; yi = ny + dy[i]; /* p */
+ xi1 = xi + dx[i+1]; yi1 = yi + dy[i+1]; /* p+1 */
+ ex = (nx+xi1)/2; ey = (ny+yi1)/2; /* midway */
+ ex += 0.75*(xi-ex); ey += 0.75*(yi-ey);
+ extreme(ex, ey);
+ nx = xi; ny = yi;
+ }
+
+ }
+ p->o_ddval = ddval;
+ if (dbg) {
+ printf("S or L from %g %g to %g %g with %d elements:\n", p->o_x, p->o_y, curx, cury, ndxy);
+ for (i = 0, j = 5; i < ndxy; i++, j += 2)
+ printf("%g %g\n", p->o_val[j], p->o_val[j+1]);
+ }
+ extreme(p->o_x, p->o_y);
+ extreme(curx, cury);
+ return(p);
+}
diff --git a/src/cmd/pic/main.c b/src/cmd/pic/main.c
new file mode 100644
index 00000000..ab996650
--- /dev/null
+++ b/src/cmd/pic/main.c
@@ -0,0 +1,282 @@
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pic.h"
+#include "y.tab.h"
+
+char *version = "version July 5, 1993";
+
+obj **objlist = 0; /* store the elements here */
+int nobjlist = 0; /* size of objlist array */
+int nobj = 0;
+
+Attr *attr; /* attributes stored here as collected */
+int nattrlist = 0;
+int nattr = 0; /* number of entries in attr_list */
+
+Text *text = 0; /* text strings stored here as collected */
+int ntextlist = 0; /* size of text[] array */
+int ntext = 0;
+int ntext1 = 0; /* record ntext here on entry to each figure */
+
+double curx = 0;
+double cury = 0;
+
+int hvmode = R_DIR; /* R => join left to right, D => top to bottom, etc. */
+
+int codegen = 0; /* 1=>output for this picture; 0=>no output */
+char *PEstring; /* "PS" or "PE" picked up by lexer */
+
+double deltx = 6; /* max x value in output, for scaling */
+double delty = 6; /* max y value in output, for scaling */
+int dbg = 0;
+int lineno = 0;
+char *filename = "-";
+int synerr = 0;
+int anyerr = 0; /* becomes 1 if synerr ever 1 */
+char *cmdname;
+
+double xmin = 30000; /* min values found in actual data */
+double ymin = 30000;
+double xmax = -30000; /* max */
+double ymax = -30000;
+
+void fpecatch(int);
+void getdata(void), setdefaults(void);
+void setfval(char *, double);
+int getpid(void);
+
+main(int argc, char *argv[])
+{
+ char buf[20];
+
+ signal(SIGFPE, fpecatch);
+ cmdname = argv[0];
+ while (argc > 1 && *argv[1] == '-') {
+ switch (argv[1][1]) {
+ case 'd':
+ dbg = atoi(&argv[1][2]);
+ if (dbg == 0)
+ dbg = 1;
+ fprintf(stderr, "%s\n", version);
+ break;
+ case 'V':
+ fprintf(stderr, "%s\n", version);
+ return 0;
+ }
+ argc--;
+ argv++;
+ }
+ setdefaults();
+ objlist = (obj **) grow((char *)objlist, "objlist", nobjlist += 1000, sizeof(obj *));
+ text = (Text *) grow((char *)text, "text", ntextlist += 1000, sizeof(Text));
+ attr = (Attr *) grow((char *)attr, "attr", nattrlist += 100, sizeof(Attr));
+
+ sprintf(buf, "/%d/", getpid());
+ pushsrc(String, buf);
+ definition("pid");
+
+ curfile = infile;
+ pushsrc(File, curfile->fname);
+ if (argc <= 1) {
+ curfile->fin = stdin;
+ curfile->fname = tostring("-");
+ getdata();
+ } else
+ while (argc-- > 1) {
+ if ((curfile->fin = fopen(*++argv, "r")) == NULL) {
+ fprintf(stderr, "%s: can't open %s\n", cmdname, *argv);
+ exit(1);
+ }
+ curfile->fname = tostring(*argv);
+ getdata();
+ fclose(curfile->fin);
+ free(curfile->fname);
+ }
+ return anyerr;
+}
+
+void fpecatch(int n)
+{
+ ERROR "floating point exception %d", n FATAL;
+}
+
+char *grow(char *ptr, char *name, int num, int size) /* make array bigger */
+{
+ char *p;
+
+ if (ptr == NULL)
+ p = malloc(num * size);
+ else
+ p = realloc(ptr, num * size);
+ if (p == NULL)
+ ERROR "can't grow %s to %d", name, num * size FATAL;
+ return p;
+}
+
+static struct {
+ char *name;
+ 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
+};
+
+void setdefaults(void) /* set default sizes for variables like boxht */
+{
+ int i;
+ YYSTYPE v;
+
+ for (i = 0; defaults[i].name != NULL; i++) {
+ v.f = defaults[i].val;
+ makevar(tostring(defaults[i].name), VARNAME, v);
+ }
+}
+
+void resetvar(void) /* reset variables listed */
+{
+ int i, j;
+
+ if (nattr == 0) { /* none listed, so do all */
+ setdefaults();
+ return;
+ }
+ for (i = 0; i < nattr; i++) {
+ for (j = 0; defaults[j].name != NULL; j++)
+ if (strcmp(defaults[j].name, attr[i].a_val.p) == 0) {
+ setfval(defaults[j].name, defaults[j].val);
+ free(attr[i].a_val.p);
+ break;
+ }
+ }
+}
+
+void checkscale(char *s) /* if s is "scale", adjust default variables */
+{
+ int i;
+ double scale;
+
+ if (strcmp(s, "scale") == 0) {
+ scale = getfval("scale");
+ for (i = 1; defaults[i].name != NULL; i++)
+ if (defaults[i].scalable)
+ setfval(defaults[i].name, defaults[i].val * scale);
+ }
+}
+
+void getdata(void)
+{
+ char *p, buf[1000], buf1[100];
+ int ln;
+ void reset(void), openpl(char *), closepl(char *), print(void);
+ int yyparse(void);
+
+ curfile->lineno = 0;
+ printlf(1, curfile->fname);
+ while (fgets(buf, sizeof buf, curfile->fin) != NULL) {
+ curfile->lineno++;
+ if (*buf == '.' && *(buf+1) == 'P' && *(buf+2) == 'S') {
+ for (p = &buf[3]; *p == ' '; p++)
+ ;
+ if (*p++ == '<') {
+ Infile svfile;
+ svfile = *curfile;
+ sscanf(p, "%s", buf1);
+ if ((curfile->fin=fopen(buf1, "r")) == NULL)
+ ERROR "can't open %s", buf1 FATAL;
+ curfile->fname = tostring(buf1);
+ getdata();
+ fclose(curfile->fin);
+ free(curfile->fname);
+ *curfile = svfile;
+ printlf(curfile->lineno, curfile->fname);
+ continue;
+ }
+ reset();
+ yyparse();
+ anyerr += synerr;
+ deltx = (xmax - xmin) / getfval("scale");
+ delty = (ymax - ymin) / getfval("scale");
+ if (buf[3] == ' ') { /* next things are wid & ht */
+ if (sscanf(&buf[4],"%lf %lf", &deltx, &delty) < 2)
+ delty = deltx * (ymax-ymin) / (xmax-xmin);
+ /* else {
+ /* double xfac, yfac; */
+ /* xfac = deltx / (xmax-xmin);
+ /* yfac = delty / (ymax-ymin);
+ /* if (xfac <= yfac)
+ /* delty = xfac * (ymax-ymin);
+ /* else
+ /* deltx = yfac * (xmax-xmin);
+ /*}
+ */
+ }
+ dprintf("deltx = %g, delty = %g\n", deltx, delty);
+ if (codegen && !synerr) {
+ openpl(&buf[3]); /* puts out .PS, with ht & wid stuck in */
+ printlf(curfile->lineno+1, NULL);
+ print(); /* assumes \n at end */
+ closepl(PEstring); /* does the .PE/F */
+ free(PEstring);
+ }
+ printlf(curfile->lineno+1, NULL);
+ fflush(stdout);
+ } else if (buf[0] == '.' && buf[1] == 'l' && buf[2] == 'f') {
+ if (sscanf(buf+3, "%d %s", &ln, buf1) == 2) {
+ free(curfile->fname);
+ printlf(curfile->lineno = ln, curfile->fname = tostring(buf1));
+ } else
+ printlf(curfile->lineno = ln, NULL);
+ } else
+ fputs(buf, stdout);
+ }
+}
+
+void reset(void)
+{
+ obj *op;
+ int i;
+ extern int nstack;
+ extern void freesymtab(struct symtab *);
+
+ for (i = 0; i < nobj; i++) {
+ op = objlist[i];
+ if (op->o_type == BLOCK)
+ freesymtab(op->o_symtab);
+ free((char *)objlist[i]);
+ }
+ nobj = 0;
+ nattr = 0;
+ for (i = 0; i < ntext; i++)
+ if (text[i].t_val)
+ free(text[i].t_val);
+ ntext = ntext1 = 0;
+ codegen = synerr = 0;
+ nstack = 0;
+ curx = cury = 0;
+ PEstring = 0;
+ hvmode = R_DIR;
+ xmin = ymin = 30000;
+ xmax = ymax = -30000;
+}
diff --git a/src/cmd/pic/makefile b/src/cmd/pic/makefile
new file mode 100644
index 00000000..92d733a0
--- /dev/null
+++ b/src/cmd/pic/makefile
@@ -0,0 +1,39 @@
+CC = cc # the usual situation
+CFLAGS = # the usual situation
+
+CC = lcc # you will probably want to remove this
+CFLAGS = -g -N -I/usr/include/lcc -I/usr/include # and this
+
+YFLAGS = -d
+
+OFILES = picl.o main.o print.o misc.o symtab.o blockgen.o boxgen.o \
+ circgen.o arcgen.o linegen.o movegen.o textgen.o \
+ input.o for.o pltroff.o $(ALLOC)
+CFILES = main.c print.c misc.c symtab.c blockgen.c boxgen.c circgen.c \
+ arcgen.c linegen.c movegen.c textgen.c \
+ input.c for.c pltroff.c
+SRCFILES = picy.y picl.l pic.h $(CFILES) makefile FIXES README PS-PEmacros
+
+pic: picy.o $(OFILES) pic.h
+ $(CC) $(CFLAGS) picy.o $(OFILES) -lm
+
+$(OFILES): pic.h prevy.tab.h
+
+picy.o: pic.h
+
+prevy.tab.h: y.tab.h
+ -cmp -s y.tab.h prevy.tab.h || cp y.tab.h prevy.tab.h
+
+bundle:
+ @bundle $(SRCFILES)
+
+bowell: $(SRCFILES) pictest.a
+ push bowell $? /usr/src/cmd/pic
+ touch bowell
+
+clean:
+ rm *.o a.out *y.tab.h
+
+install:
+ cp a.out /usr/bin/pic
+ strip /usr/bin/pic
diff --git a/src/cmd/pic/misc.c b/src/cmd/pic/misc.c
new file mode 100644
index 00000000..0a33c672
--- /dev/null
+++ b/src/cmd/pic/misc.c
@@ -0,0 +1,436 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pic.h"
+#include "y.tab.h"
+
+int whatpos(obj *p, int corner, double *px, double *py);
+void makeattr(int type, int sub, YYSTYPE val);
+YYSTYPE getblk(obj *, char *);
+
+setdir(int n) /* set direction (hvmode) from LEFT, RIGHT, etc. */
+{
+ switch (n) {
+ case UP: hvmode = U_DIR; break;
+ case DOWN: hvmode = D_DIR; break;
+ case LEFT: hvmode = L_DIR; break;
+ case RIGHT: hvmode = R_DIR; break;
+ }
+ return(hvmode);
+}
+
+curdir(void) /* convert current dir (hvmode) to RIGHT, LEFT, etc. */
+{
+ switch (hvmode) {
+ case R_DIR: return RIGHT;
+ case L_DIR: return LEFT;
+ case U_DIR: return UP;
+ case D_DIR: return DOWN;
+ }
+ ERROR "can't happen curdir" FATAL;
+ return 0;
+}
+
+double getcomp(obj *p, int t) /* return component of a position */
+{
+ switch (t) {
+ case DOTX:
+ return p->o_x;
+ case DOTY:
+ return p->o_y;
+ case DOTWID:
+ switch (p->o_type) {
+ case BOX:
+ case BLOCK:
+ case TEXT:
+ return p->o_val[0];
+ case CIRCLE:
+ case ELLIPSE:
+ return 2 * p->o_val[0];
+ case LINE:
+ case ARROW:
+ return p->o_val[0] - p->o_x;
+ case PLACE:
+ return 0;
+ }
+ case DOTHT:
+ switch (p->o_type) {
+ case BOX:
+ case BLOCK:
+ case TEXT:
+ return p->o_val[1];
+ case CIRCLE:
+ case ELLIPSE:
+ return 2 * p->o_val[1];
+ case LINE:
+ case ARROW:
+ return p->o_val[1] - p->o_y;
+ case PLACE:
+ return 0;
+ }
+ case DOTRAD:
+ switch (p->o_type) {
+ case CIRCLE:
+ case ELLIPSE:
+ return p->o_val[0];
+ }
+ }
+ ERROR "you asked for a weird dimension or position" WARNING;
+ return 0;
+}
+
+double exprlist[100];
+int nexpr = 0;
+
+void exprsave(double f)
+{
+ exprlist[nexpr++] = f;
+}
+
+char *sprintgen(char *fmt)
+{
+ char buf[1000];
+
+ sprintf(buf, fmt, exprlist[0], exprlist[1], exprlist[2], exprlist[3], exprlist[4]);
+ nexpr = 0;
+ free(fmt);
+ return tostring(buf);
+}
+
+void makefattr(int type, int sub, double f) /* double attr */
+{
+ YYSTYPE val;
+ val.f = f;
+ makeattr(type, sub, val);
+}
+
+void makeoattr(int type, obj *o) /* obj* attr */
+{
+ YYSTYPE val;
+ val.o = o;
+ makeattr(type, 0, val);
+}
+
+void makeiattr(int type, int i) /* int attr */
+{
+ YYSTYPE val;
+ val.i = i;
+ makeattr(type, 0, val);
+}
+
+void maketattr(int sub, char *p) /* text attribute: takes two */
+{
+ YYSTYPE val;
+ val.p = p;
+ makeattr(TEXTATTR, sub, val);
+}
+
+void addtattr(int sub) /* add text attrib to existing item */
+{
+ attr[nattr-1].a_sub |= sub;
+}
+
+void makevattr(char *p) /* varname attribute */
+{
+ YYSTYPE val;
+ val.p = p;
+ makeattr(VARNAME, 0, val);
+}
+
+void makeattr(int type, int sub, YYSTYPE val) /* add attribute type and val */
+{
+ if (type == 0 && val.i == 0) { /* clear table for next stat */
+ nattr = 0;
+ return;
+ }
+ if (nattr >= nattrlist)
+ attr = (Attr *) grow((char *)attr, "attr", nattrlist += 100, sizeof(Attr));
+ dprintf("attr %d: %d %d %d\n", nattr, type, sub, val.i);
+ attr[nattr].a_type = type;
+ attr[nattr].a_sub = sub;
+ attr[nattr].a_val = val;
+ nattr++;
+}
+
+void printexpr(double f) /* print expression for debugging */
+{
+ printf("%g\n", f);
+}
+
+void printpos(obj *p) /* print position for debugging */
+{
+ printf("%g, %g\n", p->o_x, p->o_y);
+}
+
+char *tostring(char *s)
+{
+ register char *p;
+
+ p = malloc(strlen(s)+1);
+ if (p == NULL)
+ ERROR "out of space in tostring on %s", s FATAL;
+ strcpy(p, s);
+ return(p);
+}
+
+obj *makepos(double x, double y) /* make a position cell */
+{
+ obj *p;
+
+ p = makenode(PLACE, 0);
+ p->o_x = x;
+ p->o_y = y;
+ return(p);
+}
+
+obj *makebetween(double f, obj *p1, obj *p2) /* make position between p1 and p2 */
+{
+ obj *p;
+
+ dprintf("fraction = %.2f\n", f);
+ p = makenode(PLACE, 0);
+ p->o_x = p1->o_x + f * (p2->o_x - p1->o_x);
+ p->o_y = p1->o_y + f * (p2->o_y - p1->o_y);
+ return(p);
+}
+
+obj *getpos(obj *p, int corner) /* find position of point */
+{
+ double x, y;
+
+ whatpos(p, corner, &x, &y);
+ return makepos(x, y);
+}
+
+int whatpos(obj *p, int corner, double *px, double *py) /* what is the position (no side effect) */
+{
+ double x, y, x1, y1;
+
+ dprintf("whatpos %o %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) {
+ x1 = p->o_val[0];
+ y1 = p->o_val[1];
+ }
+ switch (p->o_type) {
+ case PLACE:
+ break;
+ case BOX:
+ case BLOCK:
+ case TEXT:
+ switch (corner) {
+ case NORTH: y += y1 / 2; break;
+ case SOUTH: y -= y1 / 2; break;
+ case EAST: x += x1 / 2; break;
+ case WEST: x -= x1 / 2; break;
+ case NE: x += x1 / 2; y += y1 / 2; break;
+ case SW: x -= x1 / 2; y -= y1 / 2; break;
+ case SE: x += x1 / 2; y -= y1 / 2; break;
+ case NW: x -= x1 / 2; y += y1 / 2; break;
+ case START:
+ if (p->o_type == BLOCK)
+ return whatpos(objlist[(int)p->o_val[2]], START, px, py);
+ case END:
+ if (p->o_type == BLOCK)
+ return whatpos(objlist[(int)p->o_val[3]], END, px, py);
+ }
+ break;
+ case ARC:
+ switch (corner) {
+ case START:
+ if (p->o_attr & CW_ARC) {
+ x = p->o_val[2]; y = p->o_val[3];
+ } else {
+ x = x1; y = y1;
+ }
+ break;
+ case END:
+ if (p->o_attr & CW_ARC) {
+ x = x1; y = y1;
+ } else {
+ x = p->o_val[2]; y = p->o_val[3];
+ }
+ break;
+ }
+ if (corner == START || corner == END)
+ break;
+ x1 = y1 = sqrt((x1-x)*(x1-x) + (y1-y)*(y1-y));
+ /* Fall Through! */
+ case CIRCLE:
+ case ELLIPSE:
+ switch (corner) {
+ case NORTH: y += y1; break;
+ case SOUTH: y -= y1; break;
+ case EAST: x += x1; break;
+ case WEST: x -= x1; break;
+ case NE: x += 0.707 * x1; y += 0.707 * y1; break;
+ case SE: x += 0.707 * x1; y -= 0.707 * y1; break;
+ case NW: x -= 0.707 * x1; y += 0.707 * y1; break;
+ case SW: x -= 0.707 * x1; y -= 0.707 * y1; break;
+ }
+ break;
+ case LINE:
+ case SPLINE:
+ case ARROW:
+ switch (corner) {
+ case START: break; /* already in place */
+ case END: x = x1; y = y1; break;
+ default: /* change! */
+ case CENTER: x = (x+x1)/2; y = (y+y1)/2; break;
+ case NORTH: if (y1 > y) { x = x1; y = y1; } break;
+ case SOUTH: if (y1 < y) { x = x1; y = y1; } break;
+ case EAST: if (x1 > x) { x = x1; y = y1; } break;
+ case WEST: if (x1 < x) { x = x1; y = y1; } break;
+ }
+ break;
+ case MOVE:
+ /* really ought to be same as line... */
+ break;
+ }
+ dprintf("whatpos returns %g %g\n", x, y);
+ *px = x;
+ *py = y;
+ return 1;
+}
+
+obj *gethere(void) /* make a place for curx,cury */
+{
+ dprintf("gethere %g %g\n", curx, cury);
+ return(makepos(curx, cury));
+}
+
+obj *getlast(int n, int t) /* find n-th previous occurrence of type t */
+{
+ int i, k;
+ obj *p;
+
+ k = n;
+ for (i = nobj-1; i >= 0; i--) {
+ p = objlist[i];
+ if (p->o_type == BLOCKEND) {
+ i = p->o_val[4];
+ continue;
+ }
+ if (p->o_type != t)
+ continue;
+ if (--k > 0)
+ continue; /* not there yet */
+ 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;
+ return(NULL);
+}
+
+obj *getfirst(int n, int t) /* find n-th occurrence of type t */
+{
+ int i, k;
+ obj *p;
+
+ k = n;
+ for (i = 0; i < nobj; i++) {
+ p = objlist[i];
+ if (p->o_type == BLOCK && t != BLOCK) { /* skip whole block */
+ i = p->o_val[5] + 1;
+ continue;
+ }
+ if (p->o_type != t)
+ continue;
+ if (--k > 0)
+ continue; /* not there yet */
+ 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;
+ return(NULL);
+}
+
+double getblkvar(obj *p, char *s) /* find variable s2 in block p */
+{
+ YYSTYPE y;
+
+ y = getblk(p, s);
+ return y.f;
+}
+
+obj *getblock(obj *p, char *s) /* find variable s in block p */
+{
+ YYSTYPE y;
+
+ y = getblk(p, s);
+ return y.o;
+}
+
+YYSTYPE getblk(obj *p, char *s) /* find union type for s in p */
+{
+ static YYSTYPE bug;
+ struct symtab *stp;
+
+ if (p->o_type != BLOCK) {
+ ERROR ".%s is not in that block", s WARNING;
+ return(bug);
+ }
+ for (stp = p->o_symtab; stp != NULL; stp = stp->s_next)
+ if (strcmp(s, stp->s_name) == 0) {
+ dprintf("getblk %s found x,y= %g,%g\n",
+ s, (stp->s_val.o)->o_x, (stp->s_val.o)->o_y);
+ return(stp->s_val);
+ }
+ ERROR "there is no .%s in that []", s WARNING;
+ return(bug);
+}
+
+obj *fixpos(obj *p, double x, double y)
+{
+ dprintf("fixpos returns %g %g\n", p->o_x + x, p->o_y + y);
+ return makepos(p->o_x + x, p->o_y + y);
+}
+
+obj *addpos(obj *p, obj *q)
+{
+ dprintf("addpos returns %g %g\n", p->o_x+q->o_x, p->o_y+q->o_y);
+ return makepos(p->o_x+q->o_x, p->o_y+q->o_y);
+}
+
+obj *subpos(obj *p, obj *q)
+{
+ dprintf("subpos returns %g %g\n", p->o_x-q->o_x, p->o_y-q->o_y);
+ return makepos(p->o_x-q->o_x, p->o_y-q->o_y);
+}
+
+obj *makenode(int type, int n)
+{
+ obj *p;
+
+ p = (obj *) calloc(1, sizeof(obj) + (n-1)*sizeof(ofloat));
+ if (p == NULL)
+ ERROR "out of space in makenode" FATAL;
+ p->o_type = type;
+ p->o_count = n;
+ p->o_nobj = nobj;
+ p->o_mode = hvmode;
+ p->o_x = curx;
+ p->o_y = cury;
+ p->o_nt1 = ntext1;
+ p->o_nt2 = ntext;
+ ntext1 = ntext; /* ready for next caller */
+ if (nobj >= nobjlist)
+ objlist = (obj **) grow((char *) objlist, "objlist",
+ nobjlist *= 2, sizeof(obj *));
+ objlist[nobj++] = p;
+ return(p);
+}
+
+void extreme(double x, double y) /* record max and min x and y values */
+{
+ if (x > xmax)
+ xmax = x;
+ if (y > ymax)
+ ymax = y;
+ if (x < xmin)
+ xmin = x;
+ if (y < ymin)
+ ymin = y;
+}
diff --git a/src/cmd/pic/mkfile b/src/cmd/pic/mkfile
new file mode 100644
index 00000000..b27e07db
--- /dev/null
+++ b/src/cmd/pic/mkfile
@@ -0,0 +1,38 @@
+<$PLAN9/src/mkhdr
+
+TARG=pic
+OFILES=picy.$O\
+ picl.$O\
+ main.$O\
+ print.$O\
+ misc.$O\
+ symtab.$O\
+ blockgen.$O\
+ boxgen.$O\
+ circgen.$O\
+ arcgen.$O\
+ linegen.$O\
+ movegen.$O\
+ textgen.$O\
+ input.$O\
+ for.$O\
+ pltroff.$O\
+
+HFILES=pic.h\
+ y.tab.h\
+
+YFILES=picy.y\
+
+SHORTLIB=bio 9
+<$PLAN9/src/mkone
+YFLAGS=-S -d
+LEX=9lex
+
+picy.c: y.tab.c
+ mv $prereq $target
+
+picl.c: picl.lx
+ $LEX -t $prereq > $target
+
+clean:V:
+ rm -f *.[$OS] [$OS].out y.tab.? y.debug $TARG picy.c picl.c
diff --git a/src/cmd/pic/movegen.c b/src/cmd/pic/movegen.c
new file mode 100644
index 00000000..5ff44d46
--- /dev/null
+++ b/src/cmd/pic/movegen.c
@@ -0,0 +1,86 @@
+#include <stdio.h>
+#include "pic.h"
+#include "y.tab.h"
+
+obj *movegen(void)
+{
+ static double prevdx, prevdy;
+ int i, some;
+ double defx, defy, dx, dy;
+ obj *p;
+ obj *ppos;
+ static int xtab[] = { 1, 0, -1, 0 }; /* R=0, U=1, L=2, D=3 */
+ static int ytab[] = { 0, 1, 0, -1 };
+ Attr *ap;
+
+ defx = getfval("movewid");
+ defy = getfval("moveht");
+ dx = dy = some = 0;
+ for (i = 0; i < nattr; i++) {
+ ap = &attr[i];
+ switch (ap->a_type) {
+ case TEXTATTR:
+ savetext(ap->a_sub, ap->a_val.p);
+ break;
+ case SAME:
+ dx = prevdx;
+ dy = prevdy;
+ some++;
+ break;
+ case LEFT:
+ dx -= (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
+ some++;
+ hvmode = L_DIR;
+ break;
+ case RIGHT:
+ dx += (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
+ some++;
+ hvmode = R_DIR;
+ break;
+ case UP:
+ dy += (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
+ some++;
+ hvmode = U_DIR;
+ break;
+ case DOWN:
+ dy -= (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
+ some++;
+ hvmode = D_DIR;
+ break;
+ case TO:
+ ppos = ap->a_val.o;
+ dx = ppos->o_x - curx;
+ dy = ppos->o_y - cury;
+ some++;
+ break;
+ case BY:
+ ppos = ap->a_val.o;
+ dx = ppos->o_x;
+ dy = ppos->o_y;
+ some++;
+ break;
+ case FROM:
+ case AT:
+ ppos = ap->a_val.o;
+ curx = ppos->o_x;
+ cury = ppos->o_y;
+ break;
+ }
+ }
+ if (some) {
+ defx = dx;
+ defy = dy;
+ } else {
+ defx *= xtab[hvmode];
+ defy *= ytab[hvmode];
+ }
+ prevdx = defx;
+ prevdy = defy;
+ extreme(curx, cury);
+ curx += defx;
+ cury += defy;
+ extreme(curx, cury);
+ p = makenode(MOVE, 0);
+ dprintf("M %g %g\n", curx, cury);
+ return(p);
+}
diff --git a/src/cmd/pic/pic.h b/src/cmd/pic/pic.h
new file mode 100644
index 00000000..a4f7a6a4
--- /dev/null
+++ b/src/cmd/pic/pic.h
@@ -0,0 +1,219 @@
+#ifndef PI
+#define PI 3.1415926535897932384626433832795028841971693993751
+#endif
+
+#define MAXWID 8.5 /* default limits max picture to 8.5 x 11; */
+#define MAXHT 11 /* change to taste without peril */
+
+#define dprintf if(dbg)printf
+
+extern void yyerror(char *);
+
+extern char errbuf[200];
+#define ERROR sprintf(errbuf,
+#define FATAL ), yyerror(errbuf), exit(1)
+#define WARNING ), yyerror(errbuf)
+
+#define DEFAULT 0
+
+#define HEAD1 1
+#define HEAD2 2
+#define HEAD12 (HEAD1+HEAD2)
+#define INVIS 4
+#define CW_ARC 8 /* clockwise arc */
+#define DOTBIT 16 /* line styles */
+#define DASHBIT 32
+#define FILLBIT 64 /* gray-fill on boxes, etc. */
+#define NOEDGEBIT 128 /* no edge on filled object */
+
+#define CENTER 01 /* text attributes */
+#define LJUST 02
+#define RJUST 04
+#define ABOVE 010
+#define BELOW 020
+#define SPREAD 040
+
+#define SCALE 1.0 /* default scale: units/inch */
+#define WID 0.75 /* default width for boxes and ellipses */
+#define WID2 0.375
+#define HT 0.5 /* default height and line length */
+#define HT2 (HT/2)
+#define HT5 (HT/5)
+#define HT10 (HT/10)
+
+/* these have to be like so, so that we can write */
+/* things like R & V, etc. */
+#define H 0
+#define V 1
+#define R_DIR 0
+#define U_DIR 1
+#define L_DIR 2
+#define D_DIR 3
+#define ishor(n) (((n) & V) == 0)
+#define isvert(n) (((n) & V) != 0)
+#define isright(n) ((n) == R_DIR)
+#define isleft(n) ((n) == L_DIR)
+#define isdown(n) ((n) == D_DIR)
+#define isup(n) ((n) == U_DIR)
+
+typedef float ofloat; /* for o_val[] in obj; could be double */
+
+typedef struct obj { /* stores various things in variable length */
+ int o_type;
+ int o_count; /* number of things */
+ int o_nobj; /* index in objlist */
+ int o_mode; /* hor or vert */
+ float o_x; /* coord of "center" */
+ float o_y;
+ int o_nt1; /* 1st index in text[] for this object */
+ int o_nt2; /* 2nd; difference is #text strings */
+ int o_attr; /* HEAD, CW, INVIS, etc., go here */
+ int o_size; /* linesize */
+ int o_nhead; /* arrowhead style */
+ struct symtab *o_symtab; /* symtab for [...] */
+ float o_ddval; /* value of dot/dash expression */
+ float o_fillval; /* gray scale value */
+ ofloat o_val[1]; /* actually this will be > 1 in general */
+ /* type is not always FLOAT!!!! */
+} obj;
+
+typedef union { /* the yacc stack type */
+ int i;
+ char *p;
+ obj *o;
+ double f;
+ struct symtab *st;
+} YYSTYPE;
+
+extern YYSTYPE yylval, yyval;
+
+struct symtab {
+ char *s_name;
+ int s_type;
+ YYSTYPE s_val;
+ struct symtab *s_next;
+};
+
+typedef struct { /* attribute of an object */
+ int a_type;
+ int a_sub;
+ YYSTYPE a_val;
+} Attr;
+
+typedef struct {
+ int t_type; /* CENTER, LJUST, etc. */
+ char t_op; /* optional sign for size changes */
+ char t_size; /* size, abs or rel */
+ char *t_val;
+} Text;
+
+#define String 01
+#define Macro 02
+#define File 04
+#define Char 010
+#define Thru 020
+#define Free 040
+
+typedef struct { /* input source */
+ int type; /* Macro, String, File */
+ char *sp; /* if String or Macro */
+} Src;
+
+extern Src src[], *srcp; /* input source stack */
+
+typedef struct {
+ FILE *fin;
+ char *fname;
+ int lineno;
+} Infile;
+
+extern Infile infile[], *curfile;
+
+#define MAXARGS 20
+typedef struct { /* argument stack */
+ char *argstk[MAXARGS]; /* pointers to args */
+ char *argval; /* points to space containing args */
+} Arg;
+
+extern int dbg;
+extern obj **objlist;
+extern int nobj, nobjlist;
+extern Attr *attr;
+extern int nattr, nattrlist;
+extern Text *text;
+extern int ntextlist;
+extern int ntext;
+extern int ntext1;
+extern double curx, cury;
+extern int hvmode;
+extern int codegen;
+extern char *PEstring;
+
+char *tostring(char *);
+char *grow(char *, char *, int, int);
+double getfval(char *), getcomp(obj *, int), getblkvar(obj *, char *);
+YYSTYPE getvar(char *);
+struct symtab *lookup(char *), *makevar(char *, int, YYSTYPE);
+char *ifstat(double, char *, char *), *delimstr(char *), *sprintgen(char *);
+void forloop(char *var, double from, double to, int op, double by, char *_str);
+int setdir(int), curdir(void);
+void resetvar(void);
+void checkscale(char *);
+void pushsrc(int, char *);
+void copy(void);
+void copyuntil(char *);
+void copyfile(char *);
+void copydef(struct symtab *);
+void definition(char *);
+struct symtab *copythru(char *);
+int input(void);
+int unput(int);
+void extreme(double, double);
+
+extern double deltx, delty;
+extern int lineno;
+extern int synerr;
+
+extern double xmin, ymin, xmax, ymax;
+
+obj *leftthing(int), *boxgen(void), *circgen(int), *arcgen(int);
+obj *linegen(int), *splinegen(void), *movegen(void);
+obj *textgen(void), *plotgen(void);
+obj *troffgen(char *), *rightthing(obj *, int), *blockgen(obj *, obj *);
+obj *makenode(int, int), *makepos(double, double);
+obj *fixpos(obj *, double, double);
+obj *addpos(obj *, obj *), *subpos(obj *, obj *);
+obj *makebetween(double, obj *, obj *);
+obj *getpos(obj *, int), *gethere(void), *getfirst(int, int);
+obj *getlast(int, int), *getblock(obj *, char *);
+void savetext(int, char *);
+void makeiattr(int, int);
+void makevattr(char *);
+void makefattr(int type, int sub, double f);
+void maketattr(int, char *);
+void makeoattr(int, obj *);
+void makeattr(int type, int sub, YYSTYPE val);
+void printexpr(double);
+void printpos(obj *);
+void exprsave(double);
+void addtattr(int);
+void printlf(int, char *);
+
+struct pushstack {
+ double p_x;
+ double p_y;
+ int p_hvmode;
+ double p_xmin;
+ double p_ymin;
+ double p_xmax;
+ double p_ymax;
+ struct symtab *p_symtab;
+};
+extern struct pushstack stack[];
+extern int nstack;
+extern int cw;
+
+extern double errcheck(double, char *);
+#define Log10(x) errcheck(log10(x), "log")
+#define Exp(x) errcheck(exp(x), "exp")
+#define Sqrt(x) errcheck(sqrt(x), "sqrt")
diff --git a/src/cmd/pic/picl.lx b/src/cmd/pic/picl.lx
new file mode 100644
index 00000000..dbe2a3d5
--- /dev/null
+++ b/src/cmd/pic/picl.lx
@@ -0,0 +1,273 @@
+%Start A str def sc br thru sh
+%e 1700
+%k 120
+%a 1800
+%o 1500
+%p 5000
+%n 700
+
+%{
+#undef input
+#undef unput
+/* #include <stdio.h> lex puts one out for us */
+#include <ctype.h>
+#include <stdlib.h>
+#include "pic.h"
+#include "y.tab.h"
+
+extern char *filename;
+extern struct symtab symtab[];
+
+void pbstr(char *);
+void dodef(struct symtab *stp);
+void undefine(char *s);
+void shell_init(void), shell_exec(void), shell_text(char *);
+void endfor(void);
+
+int yyback(int *, int);
+int yylook(void);
+int yywrap(void);
+
+#define CADD cbuf[clen++]=yytext[0]; \
+ if (clen>=CBUFLEN-1) { ERROR "string too long", cbuf WARNING; BEGIN A; }
+#define CBUFLEN 500
+char cbuf[CBUFLEN];
+int c, clen, cflag, delim;
+int ifsw = 0; /* 1 if if statement in progress */
+%}
+
+A [a-zA-Z_]
+B [a-zA-Z0-9_]
+D [0-9]
+WS [ \t]
+
+%%
+ switch (yybgin-yysvec-1) { /* witchcraft */
+ case 0:
+ BEGIN A;
+ break;
+ case sc:
+ BEGIN A;
+ return('}');
+ case br:
+ BEGIN A;
+ return(']');
+ }
+
+<A>{WS} ;
+<A>"\\"\n ;
+<A>\n { return(ST); }
+<A>";" { return(ST); }
+<A>"}" { BEGIN sc; return(ST); }
+<A>"]" { BEGIN br; return(ST); }
+<A>"{"{WS}*(#.*)?\n+ { return(yylval.i = yytext[0]); }
+
+<A>^".PS".* { if (curfile == infile) ERROR ".PS found inside .PS/.PE" WARNING; }
+<A>^".PE".* { if (curfile == infile) {
+ yylval.p = PEstring = tostring(yytext);
+ return(EOF);
+ }
+ }
+<A>^".".* { yylval.p = tostring(yytext); return(TROFF); }
+
+<A>print return(yylval.i = PRINT);
+<A>box return(yylval.i = BOX);
+<A>circle return(yylval.i = CIRCLE);
+<A>arc return(yylval.i = ARC);
+<A>ellipse return(yylval.i = ELLIPSE);
+<A>arrow return(yylval.i = ARROW);
+<A>spline return(yylval.i = SPLINE);
+<A>line return(yylval.i = LINE);
+<A>move return(yylval.i = MOVE);
+<A>"[]" return(yylval.i = BLOCK);
+<A>reset return(RESET);
+<A>sprintf return(SPRINTF);
+
+<A>same return(SAME);
+<A>between return(BETWEEN);
+<A>and return(AND);
+
+<A>of ;
+<A>the ;
+<A>way ;
+
+<A>"."(e|east) { yylval.i = EAST; return(CORNER); }
+<A>"."(r|right) { yylval.i = EAST; return(CORNER); }
+<A>"."(w|west) { yylval.i = WEST; return(CORNER); }
+<A>"."(l|left) { yylval.i = WEST; return(CORNER); }
+<A>"."(n|north) { yylval.i = NORTH; return(CORNER); }
+<A>"."(t|top) { yylval.i = NORTH; return(CORNER); }
+<A>"."(s|south) { yylval.i = SOUTH; return(CORNER); }
+<A>"."(b|bot|bottom) { yylval.i = SOUTH; return(CORNER); }
+<A>"."(c|center) { yylval.i = CENTER; return(CORNER); }
+<A>".start" { yylval.i = START; return(CORNER); }
+<A>".end" { yylval.i = END; return(CORNER); }
+<A>".ne" { yylval.i = NE; return(CORNER); }
+<A>".se" { yylval.i = SE; return(CORNER); }
+<A>".nw" { yylval.i = NW; return(CORNER); }
+<A>".sw" { yylval.i = SW; return(CORNER); }
+
+<A>top" "+of { yylval.i = NORTH; return(CORNER); }
+<A>north" "+of { yylval.i = NORTH; return(CORNER); }
+<A>bottom" "+of { yylval.i = SOUTH; return(CORNER); }
+<A>south" "+of { yylval.i = SOUTH; return(CORNER); }
+<A>left" "+of { yylval.i = WEST; return(CORNER); }
+<A>west" "+of { yylval.i = WEST; return(CORNER); }
+<A>right" "+of { yylval.i = EAST; return(CORNER); }
+<A>east" "+of { yylval.i = EAST; return(CORNER); }
+<A>center" "+of { yylval.i = CENTER; return(CORNER); }
+<A>start" "+of { yylval.i = START; return(CORNER); }
+<A>end" "+of { yylval.i = END; return(CORNER); }
+
+<A>height|ht { yylval.i = HEIGHT; return(ATTR); }
+<A>width|wid { yylval.i = WIDTH; return(ATTR); }
+<A>radius|rad { yylval.i = RADIUS; return(ATTR); }
+<A>diameter|diam { yylval.i = DIAMETER; return(ATTR); }
+<A>size { yylval.i = SIZE; return(ATTR); }
+<A>left { yylval.i = LEFT; return(DIR); }
+<A>right { yylval.i = RIGHT; return(DIR); }
+<A>up { yylval.i = UP; return(DIR); }
+<A>down { yylval.i = DOWN; return(DIR); }
+<A>cw { yylval.i = CW; return(ATTR); }
+<A>clockwise { yylval.i = CW; return(ATTR); }
+<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>solid ;
+<A>dot(ted)? return(yylval.i = DOT);
+<A>dash(ed)? return(yylval.i = DASH);
+<A>chop return(yylval.i = CHOP);
+
+<A>spread { yylval.i = SPREAD; return TEXTATTR; }
+<A>ljust { yylval.i = LJUST; return TEXTATTR; }
+<A>rjust { yylval.i = RJUST; return TEXTATTR; }
+<A>above { yylval.i = ABOVE; return TEXTATTR; }
+<A>below { yylval.i = BELOW; return TEXTATTR; }
+<A>center { yylval.i = CENTER; return TEXTATTR; }
+
+<A>"<-" { yylval.i = HEAD1; return(HEAD); }
+<A>"->" { yylval.i = HEAD2; return(HEAD); }
+<A>"<->" { yylval.i = HEAD12; return(HEAD); }
+
+<A>".x" return(yylval.i = DOTX);
+<A>".y" return(yylval.i = DOTY);
+<A>"."(ht|height) return(yylval.i = DOTHT);
+<A>"."(wid|width) return(yylval.i = DOTWID);
+<A>"."(rad|radius) return(yylval.i = DOTRAD);
+
+<A>from return(yylval.i = FROM);
+<A>to return(yylval.i = TO);
+<A>at return(yylval.i = AT);
+<A>by return(yylval.i = BY);
+<A>with return(yylval.i = WITH);
+<A>last return(yylval.i = LAST);
+
+<A>log return(LOG);
+<A>exp return(EXP);
+<A>sin return(SIN);
+<A>cos return(COS);
+<A>atan2 return(ATAN2);
+<A>sqrt return(SQRT);
+<A>rand return(RAND);
+<A>max return(MAX);
+<A>min return(MIN);
+<A>int return(INT);
+
+<A>"==" return(EQ);
+<A>">=" return(GE);
+<A>"<=" return(LE);
+<A>"!=" return(NEQ);
+<A>">" return(GT);
+<A>"<" return(LT);
+<A>"&&" return(ANDAND);
+<A>"||" return(OROR);
+<A>"!" return(NOT);
+
+<A>Here return(yylval.i = HERE);
+
+<A>for return(FOR);
+<A>^Endfor\n { endfor(); }
+<A>do { yylval.p = delimstr("loop body"); return(DOSTR); }
+
+<A>copy|include return(COPY);
+<A>(thru|through){WS}+ { BEGIN thru; return(THRU); }
+<thru>{A}{B}*|. { yylval.st = copythru(yytext); BEGIN A; return(DEFNAME); }
+<A>until return(UNTIL);
+
+<A>if { ifsw = 1; return(IF); }
+<A>then { if (!ifsw) { yylval.i = THEN; return(ATTR); }
+ yylval.p = delimstr("then part"); ifsw = 0;
+ return(THENSTR); }
+<A>else { yylval.p = delimstr("else part"); return(ELSESTR); }
+
+<A>sh{WS}+ { BEGIN sh;
+ if ((delim = input()) == '{') delim = '}'; /* no nested {} */
+ shell_init(); }
+<sh>{A}{B}* { struct symtab *p;
+ if (yytext[0] == delim) {
+ shell_exec();
+ BEGIN A;
+ } else {
+ p = lookup(yytext);
+ if (p != NULL && p->s_type == DEFNAME) {
+ c = input();
+ unput(c);
+ if (c == '(')
+ dodef(p);
+ else
+ pbstr(p->s_val.p);
+ } else
+ shell_text(yytext);
+ }
+ }
+<sh>.|\n { if (yytext[0] == delim) {
+ shell_exec();
+ BEGIN A;
+ } else
+ shell_text(yytext);
+ }
+
+<A>define{WS}+ { BEGIN def; }
+<def>{A}{B}* { definition(yytext); BEGIN A; }
+<A>undef(ine)?{WS}+{A}{B}* { undefine(yytext); }
+
+<A>first { yylval.i = 1; return(NTH); }
+<A>{D}+(th|nd|rd|st) { yylval.i = atoi(yytext); return(NTH); }
+<A>({D}+("."?){D}*|"."{D}+)((e|E)("+"|-)?{D}+)?i? {
+ yylval.f = atof(yytext); return(NUMBER); }
+
+<A>{A}{B}* { struct symtab *p;
+ p = lookup(yytext);
+ if (p != NULL && p->s_type == DEFNAME) {
+ c = input();
+ unput(c);
+ if (c == '(') /* it's name(...) */
+ dodef(p);
+ else { /* no argument list */
+ pbstr(p->s_val.p);
+ dprintf("pushing back `%s'\n", p->s_val.p);
+ }
+ } else if (islower(yytext[0])) {
+ yylval.p = tostring(yytext);
+ return(VARNAME);
+ } else {
+ yylval.p = tostring(yytext);
+ return(PLACENAME);
+ }
+ }
+
+<A>\" { BEGIN str; clen=0; }
+<str>\" { cbuf[clen]=0; yylval.p = tostring(cbuf); BEGIN A; return(TEXT); }
+<str>\n { cbuf[clen]=0; ERROR "missing quote in string \"%s\"", cbuf WARNING;
+ BEGIN A; return(ST); }
+<str>"\\\"" { cbuf[clen++]='"'; }
+<str>"\\"t { cbuf[clen++]='\t'; }
+<str>"\\\\" { cbuf[clen++]='\\'; }
+<str>. { CADD; }
+
+<A>#.* ;
+
+<A>. return(yylval.i = yytext[0]);
+
+%%
diff --git a/src/cmd/pic/picy.y b/src/cmd/pic/picy.y
new file mode 100644
index 00000000..b534b5a5
--- /dev/null
+++ b/src/cmd/pic/picy.y
@@ -0,0 +1,328 @@
+%{
+#include <stdio.h>
+#include "pic.h"
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+YYSTYPE y;
+
+extern void yyerror(char *);
+extern int yylex(void);
+%}
+
+%token <i> BOX 1 /* DON'T CHANGE THESE! */
+%token <i> LINE 2
+%token <i> ARROW 3
+%token <i> CIRCLE 4
+%token <i> ELLIPSE 5
+%token <i> ARC 6
+%token <i> SPLINE 7
+%token <i> BLOCK 8
+%token <p> TEXT 9
+%token <p> TROFF 10
+%token <i> MOVE 11
+%token <i> BLOCKEND 12
+%token <i> PLACE 13
+%token <i> PRINT RESET THRU UNTIL
+%token <o> FOR IF COPY
+%token <p> THENSTR ELSESTR DOSTR PLACENAME VARNAME SPRINTF
+%token <st> DEFNAME
+%token <i> ATTR TEXTATTR
+%token <i> LEFT RIGHT UP DOWN FROM TO AT BY WITH HEAD CW CCW THEN
+%token <i> HEIGHT WIDTH RADIUS DIAMETER LENGTH SIZE
+%token <i> CORNER HERE LAST NTH SAME BETWEEN AND
+%token <i> EAST WEST NORTH SOUTH NE NW SE SW START END
+%token <i> DOTX DOTY DOTHT DOTWID DOTRAD
+%token <f> NUMBER
+%token <f> LOG EXP SIN COS ATAN2 SQRT RAND MIN MAX INT
+%token <i> DIR
+%token <i> DOT DASH CHOP FILL NOEDGE
+%token <o> ST /* statement terminator */
+
+%right <f> '='
+%left <f> OROR
+%left <f> ANDAND
+%nonassoc <f> GT LT LE GE EQ NEQ
+%left <f> '+' '-'
+%left <f> '*' '/' '%'
+%right <f> UMINUS NOT
+%right <f> '^'
+
+%type <f> expr if_expr asgn
+%type <p> name text
+%type <i> optop exprlist
+%type <o> if for copy
+
+/* this is a lie: picture and position are really the whole union */
+%type <o> leftbrace picture piclist position lbracket
+%type <o> prim place blockname
+%type <i> textlist textattr /* not a sensible value */
+%type <i> last type
+
+%%
+
+top:
+ piclist
+ | /* empty */
+ | error { ERROR "syntax error" WARNING; }
+ ;
+
+piclist:
+ picture
+ | piclist picture
+ ;
+
+picture:
+ prim ST { codegen = 1; makeiattr(0, 0); }
+ | leftbrace piclist '}' { rightthing($1, '}'); $$ = $2; }
+ | PLACENAME ':' picture { y.o=$3; makevar($1,PLACENAME,y); $$ = $3; }
+ | PLACENAME ':' ST picture { y.o=$4; makevar($1,PLACENAME,y); $$ = $4; }
+ | PLACENAME ':' position ST { y.o=$3; makevar($1,PLACENAME,y); $$ = $3; }
+ | asgn ST { y.f = $1; $$ = y.o; $$ = makenode(PLACE, 0); }
+ | DIR { setdir($1); $$ = makenode(PLACE, 0); }
+ | PRINT expr ST { printexpr($2); $$ = makenode(PLACE, 0); }
+ | PRINT position ST { printpos($2); $$ = makenode(PLACE, 0); }
+ | PRINT text ST { printf("%s\n", $2); free($2); $$ = makenode(PLACE, 0); }
+ | RESET varlist ST { resetvar(); makeiattr(0, 0); $$ = makenode(PLACE, 0); }
+ | copy
+ | for
+ | if
+ | ST
+ ;
+
+varlist:
+ /* empty */
+ | VARNAME { makevattr($1); }
+ | varlist VARNAME { makevattr($2); }
+ | varlist ',' VARNAME { makevattr($3); }
+ ;
+
+asgn:
+ VARNAME '=' expr { $$=y.f=$3; makevar($1,VARNAME,y); checkscale($1); }
+ ;
+
+copy:
+ COPY copylist { copy(); }
+ ;
+copylist:
+ copyattr
+ | copylist copyattr
+ ;
+copyattr:
+ text { copyfile($1); }
+ | THRU DEFNAME { copydef($2); }
+ | UNTIL text { copyuntil($2); }
+ ;
+
+for:
+ FOR name FROM expr TO expr BY optop expr DOSTR
+ { forloop($2, $4, $6, $8, $9, $10); }
+ | FOR name FROM expr TO expr DOSTR
+ { forloop($2, $4, $6, '+', 1.0, $7); }
+ | FOR name '=' expr TO expr BY optop expr DOSTR
+ { forloop($2, $4, $6, $8, $9, $10); }
+ | FOR name '=' expr TO expr DOSTR
+ { forloop($2, $4, $6, '+', 1.0, $7); }
+ ;
+
+if:
+ IF if_expr THENSTR ELSESTR { ifstat($2, $3, $4); }
+ | IF if_expr THENSTR { ifstat($2, $3, (char *) 0); }
+ ;
+if_expr:
+ expr
+ | text EQ text { $$ = strcmp($1,$3) == 0; free($1); free($3); }
+ | text NEQ text { $$ = strcmp($1,$3) != 0; free($1); free($3); }
+ ;
+
+name:
+ VARNAME { y.f = 0; makevar($1, VARNAME, y); }
+ ;
+optop:
+ '+' { $$ = '+'; }
+ | '-' { $$ = '-'; }
+ | '*' { $$ = '*'; }
+ | '/' { $$ = '/'; }
+ | /* empty */ { $$ = ' '; }
+ ;
+
+
+leftbrace:
+ '{' { $$ = leftthing('{'); }
+ ;
+
+prim:
+ BOX attrlist { $$ = boxgen(); }
+ | CIRCLE attrlist { $$ = circgen($1); }
+ | ELLIPSE attrlist { $$ = circgen($1); }
+ | ARC attrlist { $$ = arcgen($1); }
+ | LINE attrlist { $$ = linegen($1); }
+ | ARROW attrlist { $$ = linegen($1); }
+ | SPLINE attrlist { $$ = linegen($1); }
+ | MOVE attrlist { $$ = movegen(); }
+ | textlist attrlist { $$ = textgen(); }
+ | TROFF { $$ = troffgen($1); }
+ | lbracket piclist ']' { $<o>$=rightthing($1,']'); } attrlist
+ { $$ = blockgen($1, $<o>4); }
+ ;
+
+lbracket:
+ '[' { $$ = leftthing('['); }
+ ;
+
+attrlist:
+ attrlist attr
+ | /* empty */
+ ;
+
+attr:
+ ATTR expr { makefattr($1, !DEFAULT, $2); }
+ | ATTR { makefattr($1, DEFAULT, 0.0); }
+ | expr { makefattr(curdir(), !DEFAULT, $1); }
+ | DIR expr { makefattr($1, !DEFAULT, $2); }
+ | DIR { makefattr($1, DEFAULT, 0.0); }
+ | FROM position { makeoattr($1, $2); }
+ | TO position { makeoattr($1, $2); }
+ | AT position { makeoattr($1, $2); }
+ | BY position { makeoattr($1, $2); }
+ | WITH CORNER { makeiattr(WITH, $2); }
+ | WITH '.' PLACENAME { makeoattr(PLACE, getblock(getlast(1,BLOCK), $3)); }
+ | WITH '.' PLACENAME CORNER
+ { makeoattr(PLACE, getpos(getblock(getlast(1,BLOCK), $3), $4)); }
+ | WITH position { makeoattr(PLACE, $2); }
+ | SAME { makeiattr(SAME, $1); }
+ | TEXTATTR { maketattr($1, (char *) 0); }
+ | HEAD { makeiattr(HEAD, $1); }
+ | DOT expr { makefattr(DOT, !DEFAULT, $2); }
+ | DOT { makefattr(DOT, DEFAULT, 0.0); }
+ | DASH expr { makefattr(DASH, !DEFAULT, $2); }
+ | DASH { makefattr(DASH, DEFAULT, 0.0); }
+ | CHOP expr { makefattr(CHOP, !DEFAULT, $2); }
+ | CHOP { makefattr(CHOP, DEFAULT, 0.0); }
+ | CHOP PLACENAME { makeattr(CHOP, PLACENAME, getvar($2)); }
+ | FILL expr { makefattr(FILL, !DEFAULT, $2); }
+ | FILL { makefattr(FILL, DEFAULT, 0.0); }
+ | NOEDGE { makeiattr(NOEDGE, 0); }
+ | textlist
+ ;
+
+textlist:
+ textattr
+ | textlist textattr
+ ;
+textattr:
+ text { maketattr(CENTER, $1); }
+ | text TEXTATTR { maketattr($2, $1); }
+ | textattr TEXTATTR { addtattr($2); }
+ ;
+text:
+ TEXT
+ | SPRINTF '(' text ')' { $$ = sprintgen($3); }
+ | SPRINTF '(' text ',' exprlist ')' { $$ = sprintgen($3); }
+ ;
+
+exprlist:
+ expr { exprsave($1); $$ = 0; }
+ | exprlist ',' expr { exprsave($3); }
+ ;
+
+position: /* absolute, not relative */
+ place
+ | '(' position ')' { $$ = $2; }
+ | expr ',' expr { $$ = makepos($1, $3); }
+ | position '+' expr ',' expr { $$ = fixpos($1, $3, $5); }
+ | position '-' expr ',' expr { $$ = fixpos($1, -$3, -$5); }
+ | position '+' '(' expr ',' expr ')' { $$ = fixpos($1, $4, $6); }
+ | position '-' '(' expr ',' expr ')' { $$ = fixpos($1, -$4, -$6); }
+ | position '+' place { $$ = addpos($1, $3); }
+ | position '-' place { $$ = subpos($1, $3); }
+ | '(' place ',' place ')' { $$ = makepos(getcomp($2,DOTX), getcomp($4,DOTY)); }
+ | expr LT position ',' position GT { $$ = makebetween($1, $3, $5); }
+ | expr BETWEEN position AND position { $$ = makebetween($1, $3, $5); }
+ ;
+
+place:
+ PLACENAME { y = getvar($1); $$ = y.o; }
+ | PLACENAME CORNER { y = getvar($1); $$ = getpos(y.o, $2); }
+ | CORNER PLACENAME { y = getvar($2); $$ = getpos(y.o, $1); }
+ | HERE { $$ = gethere(); }
+ | last type { $$ = getlast($1, $2); }
+ | last type CORNER { $$ = getpos(getlast($1, $2), $3); }
+ | CORNER last type { $$ = getpos(getlast($2, $3), $1); }
+ | NTH type { $$ = getfirst($1, $2); }
+ | NTH type CORNER { $$ = getpos(getfirst($1, $2), $3); }
+ | CORNER NTH type { $$ = getpos(getfirst($2, $3), $1); }
+ | blockname
+ | blockname CORNER { $$ = getpos($1, $2); }
+ | CORNER blockname { $$ = getpos($2, $1); }
+ ;
+
+blockname:
+ last BLOCK '.' PLACENAME { $$ = getblock(getlast($1,$2), $4); }
+ | NTH BLOCK '.' PLACENAME { $$ = getblock(getfirst($1,$2), $4); }
+ | PLACENAME '.' PLACENAME { y = getvar($1); $$ = getblock(y.o, $3); }
+ ;
+
+last:
+ last LAST { $$ = $1 + 1; }
+ | NTH LAST { $$ = $1; }
+ | LAST { $$ = 1; }
+ ;
+
+type:
+ BOX
+ | CIRCLE
+ | ELLIPSE
+ | ARC
+ | LINE
+ | ARROW
+ | SPLINE
+ | BLOCK
+ ;
+
+expr:
+ NUMBER
+ | VARNAME { $$ = getfval($1); }
+ | asgn
+ | expr '+' expr { $$ = $1 + $3; }
+ | expr '-' expr { $$ = $1 - $3; }
+ | expr '*' expr { $$ = $1 * $3; }
+ | expr '/' expr { if ($3 == 0.0) {
+ ERROR "division by 0" WARNING; $3 = 1; }
+ $$ = $1 / $3; }
+ | expr '%' expr { if ((long)$3 == 0) {
+ ERROR "mod division by 0" WARNING; $3 = 1; }
+ $$ = (long)$1 % (long)$3; }
+ | '-' expr %prec UMINUS { $$ = -$2; }
+ | '+' expr %prec UMINUS { $$ = $2; }
+ | '(' expr ')' { $$ = $2; }
+ | place DOTX { $$ = getcomp($1, $2); }
+ | place DOTY { $$ = getcomp($1, $2); }
+ | place DOTHT { $$ = getcomp($1, $2); }
+ | place DOTWID { $$ = getcomp($1, $2); }
+ | place DOTRAD { $$ = getcomp($1, $2); }
+ | PLACENAME '.' VARNAME { y = getvar($1); $$ = getblkvar(y.o, $3); }
+ | last BLOCK '.' VARNAME { $$ = getblkvar(getlast($1,$2), $4); }
+ | NTH BLOCK '.' VARNAME { $$ = getblkvar(getfirst($1,$2), $4); }
+ | expr GT expr { $$ = $1 > $3; }
+ | expr LT expr { $$ = $1 < $3; }
+ | expr LE expr { $$ = $1 <= $3; }
+ | expr GE expr { $$ = $1 >= $3; }
+ | expr EQ expr { $$ = $1 == $3; }
+ | expr NEQ expr { $$ = $1 != $3; }
+ | expr ANDAND expr { $$ = $1 && $3; }
+ | expr OROR expr { $$ = $1 || $3; }
+ | NOT expr { $$ = !($2); }
+ | LOG '(' expr ')' { $$ = Log10($3); }
+ | EXP '(' expr ')' { $$ = Exp($3 * log(10.0)); }
+ | expr '^' expr { $$ = pow($1, $3); }
+ | SIN '(' expr ')' { $$ = sin($3); }
+ | COS '(' expr ')' { $$ = cos($3); }
+ | ATAN2 '(' expr ',' expr ')' { $$ = atan2($3, $5); }
+ | SQRT '(' expr ')' { $$ = Sqrt($3); }
+ | RAND '(' ')' { $$ = (float)rand() / 32767.0; /* might be 2^31-1 */ }
+ | MAX '(' expr ',' expr ')' { $$ = $3 >= $5 ? $3 : $5; }
+ | MIN '(' expr ',' expr ')' { $$ = $3 <= $5 ? $3 : $5; }
+ | INT '(' expr ')' { $$ = (long) $3; }
+ ;
diff --git a/src/cmd/pic/pltroff.c b/src/cmd/pic/pltroff.c
new file mode 100644
index 00000000..d3e3151b
--- /dev/null
+++ b/src/cmd/pic/pltroff.c
@@ -0,0 +1,357 @@
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "pic.h"
+extern int dbg;
+
+#define abs(n) (n >= 0 ? n : -(n))
+#define max(x,y) ((x)>(y) ? (x) : (y))
+
+char *textshift = "\\v'.2m'"; /* move text this far down */
+
+/* scaling stuff defined by s command as X0,Y0 to X1,Y1 */
+/* output dimensions set by -l,-w options to 0,0 to hmax, vmax */
+/* default output is 6x6 inches */
+
+
+double xscale;
+double yscale;
+
+double hpos = 0; /* current horizontal position in output coordinate system */
+double vpos = 0; /* current vertical position; 0 is top of page */
+
+double htrue = 0; /* where we really are */
+double vtrue = 0;
+
+double X0, Y0; /* left bottom of input */
+double X1, Y1; /* right top of input */
+
+double hmax; /* right end of output */
+double vmax; /* top of output (down is positive) */
+
+extern double deltx;
+extern double delty;
+extern double xmin, ymin, xmax, ymax;
+
+double xconv(double), yconv(double), xsc(double), ysc(double);
+void space(double, double, double, double);
+void hgoto(double), vgoto(double), hmot(double), vmot(double);
+void move(double, double), movehv(double, double);
+void cont(double, double);
+
+void openpl(char *s) /* initialize device; s is residue of .PS invocation line */
+{
+ double maxw, maxh, ratio = 1;
+ double odeltx = deltx, odelty = delty;
+
+ hpos = vpos = 0;
+ maxw = getfval("maxpswid");
+ maxh = getfval("maxpsht");
+ if (deltx > maxw) { /* shrink horizontal */
+ ratio = maxw / deltx;
+ deltx *= ratio;
+ delty *= ratio;
+ }
+ if (delty > maxh) { /* shrink vertical */
+ ratio = maxh / delty;
+ deltx *= ratio;
+ delty *= ratio;
+ }
+ if (ratio != 1) {
+ fprintf(stderr, "pic: %g X %g picture shrunk to", odeltx, odelty);
+ fprintf(stderr, " %g X %g\n", deltx, delty);
+ }
+ space(xmin, ymin, xmax, ymax);
+ printf("... %g %g %g %g\n", xmin, ymin, xmax, ymax);
+ printf("... %.3fi %.3fi %.3fi %.3fi\n",
+ xconv(xmin), yconv(ymin), xconv(xmax), yconv(ymax));
+ printf(".nr 00 \\n(.u\n");
+ printf(".nf\n");
+ printf(".PS %.3fi %.3fi %s", yconv(ymin), xconv(xmax), s);
+ /* assumes \n comes as part of s */
+}
+
+void space(double x0, double y0, double x1, double y1) /* set limits of page */
+{
+ X0 = x0;
+ Y0 = y0;
+ X1 = x1;
+ Y1 = y1;
+ xscale = deltx == 0.0 ? 1.0 : deltx / (X1-X0);
+ yscale = delty == 0.0 ? 1.0 : delty / (Y1-Y0);
+}
+
+double xconv(double x) /* convert x from external to internal form */
+{
+ return (x-X0) * xscale;
+}
+
+double xsc(double x) /* convert x from external to internal form, scaling only */
+{
+
+ return (x) * xscale;
+}
+
+double yconv(double y) /* convert y from external to internal form */
+{
+ return (Y1-y) * yscale;
+}
+
+double ysc(double y) /* convert y from external to internal form, scaling only */
+{
+ return (y) * yscale;
+}
+
+void closepl(char *PEline) /* clean up after finished */
+{
+ movehv(0.0, 0.0); /* get back to where we started */
+ if (strchr(PEline, 'F') == NULL) {
+ printf(".sp 1+%.3fi\n", yconv(ymin));
+ }
+ printf("%s\n", PEline);
+ printf(".if \\n(00 .fi\n");
+}
+
+void move(double x, double y) /* go to position x, y in external coords */
+{
+ hgoto(xconv(x));
+ vgoto(yconv(y));
+}
+
+void movehv(double h, double v) /* go to internal position h, v */
+{
+ hgoto(h);
+ vgoto(v);
+}
+
+void hmot(double n) /* generate n units of horizontal motion */
+{
+ hpos += n;
+}
+
+void vmot(double n) /* generate n units of vertical motion */
+{
+ vpos += n;
+}
+
+void hgoto(double n)
+{
+ hpos = n;
+}
+
+void vgoto(double n)
+{
+ vpos = n;
+}
+
+double fabs(double x)
+{
+ return x < 0 ? -x : x;
+}
+
+void hvflush(void) /* get to proper point for output */
+{
+ if (fabs(hpos-htrue) >= 0.0005) {
+ printf("\\h'%.3fi'", hpos - htrue);
+ htrue = hpos;
+ }
+ if (fabs(vpos-vtrue) >= 0.0005) {
+ printf("\\v'%.3fi'", vpos - vtrue);
+ vtrue = vpos;
+ }
+}
+
+void flyback(void) /* return to upper left corner (entry point) */
+{
+ printf(".sp -1\n");
+ htrue = vtrue = 0;
+}
+
+void printlf(int n, char *f)
+{
+ if (f)
+ printf(".lf %d %s\n", n, f);
+ else
+ printf(".lf %d\n", n);
+}
+
+void troff(char *s) /* output troff right here */
+{
+ printf("%s\n", s);
+}
+
+void label(char *s, int t, int nh) /* text s of type t nh half-lines up */
+{
+ int q;
+ char *p;
+
+ if (!s)
+ return;
+ hvflush();
+ dprintf("label: %s %o %d\n", s, t, nh);
+ printf("%s", textshift); /* shift down and left */
+ if (t & ABOVE)
+ nh++;
+ else if (t & BELOW)
+ nh--;
+ if (nh)
+ printf("\\v'%du*\\n(.vu/2u'", -nh);
+ /* just in case the text contains a quote: */
+ q = 0;
+ for (p = s; *p; p++)
+ if (*p == '\'') {
+ q = 1;
+ break;
+ }
+ t &= ~(ABOVE|BELOW);
+ if (t & LJUST) {
+ printf("%s", s);
+ } else if (t & RJUST) {
+ if (q)
+ printf("\\h\\(ts-\\w\\(ts%s\\(tsu\\(ts%s", s, s);
+ else
+ printf("\\h'-\\w'%s'u'%s", s, s);
+ } else { /* CENTER */
+ if (q)
+ printf("\\h\\(ts-\\w\\(ts%s\\(tsu/2u\\(ts%s", s, s);
+ else
+ printf("\\h'-\\w'%s'u/2u'%s", s, s);
+ }
+ printf("\n");
+ flyback();
+}
+
+void line(double x0, double y0, double x1, double y1) /* draw line from x0,y0 to x1,y1 */
+{
+ move(x0, y0);
+ cont(x1, y1);
+}
+
+void arrow(double x0, double y0, double x1, double y1, double w, double h,
+ double ang, int nhead) /* draw arrow (without shaft) */
+{
+ double alpha, rot, drot, hyp;
+ double dx, dy;
+ int i;
+
+ rot = atan2(w / 2, h);
+ hyp = sqrt(w/2 * w/2 + h * h);
+ alpha = atan2(y1-y0, x1-x0) + ang;
+ if (nhead < 2)
+ nhead = 2;
+ dprintf("rot=%g, hyp=%g, alpha=%g\n", rot, hyp, alpha);
+ for (i = nhead-1; i >= 0; i--) {
+ drot = 2 * rot / (double) (nhead-1) * (double) i;
+ dx = hyp * cos(alpha + PI - rot + drot);
+ dy = hyp * sin(alpha + PI - rot + drot);
+ dprintf("dx,dy = %g,%g\n", dx, dy);
+ line(x1+dx, y1+dy, x1, y1);
+ }
+}
+
+double lastgray = 0;
+
+void fillstart(double v) /* this works only for postscript, obviously. */
+{ /* uses drechsler's dpost conventions... */
+ hvflush();
+ printf("\\X'BeginObject %g setgray'\n", v);
+ lastgray = v;
+ flyback();
+}
+
+void fillend(int vis, int fill)
+{
+ hvflush();
+ printf("\\X'EndObject gsave eofill grestore %g setgray %s'\n",
+ !vis ? lastgray : 0.0,
+ vis ? "stroke" : "");
+ /* for dashed: [50] 0 setdash just before stroke. */
+ lastgray = 0;
+ flyback();
+}
+
+void box(double x0, double y0, double x1, double y1)
+{
+ move(x0, y0);
+ cont(x0, y1);
+ cont(x1, y1);
+ cont(x1, y0);
+ cont(x0, y0);
+}
+
+void cont(double x, double y) /* continue line from here to x,y */
+{
+ double h1, v1;
+ double dh, dv;
+
+ h1 = xconv(x);
+ v1 = yconv(y);
+ dh = h1 - hpos;
+ dv = v1 - vpos;
+ hvflush();
+ printf("\\D'l%.3fi %.3fi'\n", dh, dv);
+ flyback(); /* expensive */
+ hpos = h1;
+ vpos = v1;
+}
+
+void circle(double x, double y, double r)
+{
+ move(x-r, y);
+ hvflush();
+ printf("\\D'c%.3fi'\n", xsc(2 * r));
+ flyback();
+}
+
+void spline(double x, double y, double n, ofloat *p, int dashed, double ddval)
+{
+ int i;
+ double dx, dy;
+ double xerr, yerr;
+
+ move(x, y);
+ hvflush();
+ xerr = yerr = 0.0;
+ printf("\\D'~");
+ for (i = 0; i < 2 * n; i += 2) {
+ dx = xsc(xerr += p[i]);
+ xerr -= dx/xscale;
+ dy = ysc(yerr += p[i+1]);
+ yerr -= dy/yscale;
+ printf(" %.3fi %.3fi", dx, -dy); /* WATCH SIGN */
+ }
+ printf("'\n");
+ flyback();
+}
+
+void ellipse(double x, double y, double r1, double r2)
+{
+ double ir1, ir2;
+
+ move(x-r1, y);
+ hvflush();
+ ir1 = xsc(r1);
+ ir2 = ysc(r2);
+ printf("\\D'e%.3fi %.3fi'\n", 2 * ir1, 2 * abs(ir2));
+ flyback();
+}
+
+void arc(double x, double y, double x0, double y0, double x1, double y1) /* draw arc with center x,y */
+{
+
+ move(x0, y0);
+ hvflush();
+ printf("\\D'a%.3fi %.3fi %.3fi %.3fi'\n",
+ xsc(x-x0), -ysc(y-y0), xsc(x1-x), -ysc(y1-y)); /* WATCH SIGNS */
+ flyback();
+}
+
+void dot(void) {
+ hvflush();
+ /* what character to draw here depends on what's available. */
+ /* on the 202, l. is good but small. */
+ /* in general, use a smaller, shifted period and hope */
+
+ printf("\\&\\f1\\h'-.1m'\\v'.03m'\\s-3.\\s+3\\fP\n");
+ flyback();
+}
diff --git a/src/cmd/pic/prevy.tab.h b/src/cmd/pic/prevy.tab.h
new file mode 100644
index 00000000..4782af9b
--- /dev/null
+++ b/src/cmd/pic/prevy.tab.h
@@ -0,0 +1,97 @@
+# define BOX 1
+# define LINE 2
+# define ARROW 3
+# define CIRCLE 4
+# define ELLIPSE 5
+# define ARC 6
+# define SPLINE 7
+# define BLOCK 8
+# define TEXT 9
+# define TROFF 10
+# define MOVE 11
+# define BLOCKEND 12
+# define PLACE 13
+# define PRINT 270
+# define RESET 271
+# define THRU 272
+# define UNTIL 273
+# define FOR 274
+# define IF 275
+# define COPY 276
+# define THENSTR 277
+# define ELSESTR 278
+# define DOSTR 279
+# define PLACENAME 280
+# define VARNAME 281
+# define SPRINTF 282
+# define DEFNAME 283
+# define ATTR 284
+# define TEXTATTR 285
+# define LEFT 286
+# define RIGHT 287
+# define UP 288
+# define DOWN 289
+# define FROM 290
+# define TO 291
+# define AT 292
+# define BY 293
+# define WITH 294
+# define HEAD 295
+# define CW 296
+# define CCW 297
+# define THEN 298
+# define HEIGHT 299
+# define WIDTH 300
+# define RADIUS 301
+# define DIAMETER 302
+# define LENGTH 303
+# define SIZE 304
+# define CORNER 305
+# define HERE 306
+# define LAST 307
+# define NTH 308
+# define SAME 309
+# define BETWEEN 310
+# define AND 311
+# define EAST 312
+# define WEST 313
+# define NORTH 314
+# define SOUTH 315
+# define NE 316
+# define NW 317
+# define SE 318
+# define SW 319
+# define START 320
+# define END 321
+# define DOTX 322
+# define DOTY 323
+# define DOTHT 324
+# define DOTWID 325
+# define DOTRAD 326
+# define NUMBER 327
+# define LOG 328
+# define EXP 329
+# define SIN 330
+# define COS 331
+# define ATAN2 332
+# define SQRT 333
+# define RAND 334
+# define MIN 335
+# define MAX 336
+# define INT 337
+# define DIR 338
+# define DOT 339
+# define DASH 340
+# define CHOP 341
+# define FILL 342
+# define ST 343
+# define OROR 344
+# define ANDAND 345
+# define GT 346
+# define LT 347
+# define LE 348
+# define GE 349
+# define EQ 350
+# define NEQ 351
+# define UMINUS 352
+# define NOT 353
diff --git a/src/cmd/pic/print.c b/src/cmd/pic/print.c
new file mode 100644
index 00000000..ad21e0d7
--- /dev/null
+++ b/src/cmd/pic/print.c
@@ -0,0 +1,238 @@
+#include <stdio.h>
+#include <math.h>
+#include "pic.h"
+#include "y.tab.h"
+
+void dotext(obj *);
+void dotline(double, double, double, double, int, double);
+void dotbox(double, double, double, double, int, double);
+void ellipse(double, double, double, double);
+void circle(double, double, double);
+void arc(double, double, double, double, double, double);
+void arrow(double, double, double, double, double, double, double, int);
+void line(double, double, double, double);
+void box(double, double, double, double);
+void spline(double x, double y, double n, ofloat *p, int dashed, double ddval);
+void move(double, double);
+void troff(char *);
+void dot(void);
+void fillstart(double), fillend(int vis, int noedge);
+
+void print(void)
+{
+ obj *p;
+ int i, j, k, m;
+ int fill, vis, invis;
+ double x0, y0, x1, y1, ox, oy, dx, dy, ndx, ndy;
+
+ for (i = 0; i < nobj; i++) {
+ p = objlist[i];
+ ox = p->o_x;
+ oy = p->o_y;
+ if (p->o_count >= 1)
+ x1 = p->o_val[0];
+ if (p->o_count >= 2)
+ y1 = p->o_val[1];
+ m = p->o_mode;
+ fill = p->o_attr & FILLBIT;
+ invis = p->o_attr & INVIS;
+ vis = !invis;
+ switch (p->o_type) {
+ case TROFF:
+ troff(text[p->o_nt1].t_val);
+ break;
+ case BOX:
+ case BLOCK:
+ x0 = ox - x1 / 2;
+ y0 = oy - y1 / 2;
+ x1 = ox + x1 / 2;
+ y1 = oy + y1 / 2;
+ if (fill) {
+ move(x0, y0);
+ fillstart(p->o_fillval);
+ }
+ if (p->o_type == BLOCK)
+ ; /* nothing at all */
+ else if (invis && !fill)
+ ; /* nothing at all */
+ else if (p->o_attr & (DOTBIT|DASHBIT))
+ dotbox(x0, y0, x1, y1, p->o_attr, p->o_ddval);
+ else
+ box(x0, y0, x1, y1);
+ if (fill)
+ fillend(vis, fill);
+ move(ox, oy);
+ dotext(p); /* if there are any text strings */
+ if (ishor(m))
+ move(isright(m) ? x1 : x0, oy); /* right side */
+ else
+ move(ox, isdown(m) ? y0 : y1); /* bottom */
+ break;
+ case BLOCKEND:
+ break;
+ case CIRCLE:
+ if (fill)
+ fillstart(p->o_fillval);
+ if (vis || fill)
+ circle(ox, oy, x1);
+ if (fill)
+ fillend(vis, fill);
+ move(ox, oy);
+ dotext(p);
+ if (ishor(m))
+ move(ox + isright(m) ? x1 : -x1, oy);
+ else
+ move(ox, oy + isup(m) ? x1 : -x1);
+ break;
+ case ELLIPSE:
+ if (fill)
+ fillstart(p->o_fillval);
+ if (vis || fill)
+ ellipse(ox, oy, x1, y1);
+ if (fill)
+ fillend(vis, fill);
+ move(ox, oy);
+ dotext(p);
+ if (ishor(m))
+ move(ox + isright(m) ? x1 : -x1, oy);
+ else
+ move(ox, oy - isdown(m) ? y1 : -y1);
+ break;
+ case ARC:
+ if (fill) {
+ move(ox, oy);
+ fillstart(p->o_fillval);
+ }
+ if (p->o_attr & HEAD1)
+ arrow(x1 - (y1 - oy), y1 + (x1 - ox),
+ x1, y1, p->o_val[4], p->o_val[5], p->o_val[5]/p->o_val[6]/2, p->o_nhead);
+ if (invis && !fill)
+ /* probably wrong when it's cw */
+ move(x1, y1);
+ else
+ arc(ox, oy, x1, y1, p->o_val[2], p->o_val[3]);
+ if (p->o_attr & HEAD2)
+ arrow(p->o_val[2] + p->o_val[3] - oy, p->o_val[3] - (p->o_val[2] - ox),
+ p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5], -p->o_val[5]/p->o_val[6]/2, p->o_nhead);
+ if (fill)
+ fillend(vis, fill);
+ if (p->o_attr & CW_ARC)
+ move(x1, y1); /* because drawn backwards */
+ move(ox, oy);
+ dotext(p);
+ break;
+ case LINE:
+ case ARROW:
+ case SPLINE:
+ if (fill) {
+ move(ox, oy);
+ fillstart(p->o_fillval);
+ }
+ if (vis && p->o_attr & HEAD1)
+ arrow(ox + p->o_val[5], oy + p->o_val[6], ox, oy, p->o_val[2], p->o_val[3], 0.0, p->o_nhead);
+ if (invis && !fill)
+ move(x1, y1);
+ else if (p->o_type == SPLINE)
+ spline(ox, oy, p->o_val[4], &p->o_val[5], p->o_attr & (DOTBIT|DASHBIT), p->o_ddval);
+ else {
+ dx = ox;
+ dy = oy;
+ for (k=0, j=5; k < p->o_val[4]; k++, j += 2) {
+ ndx = dx + p->o_val[j];
+ ndy = dy + p->o_val[j+1];
+ if (p->o_attr & (DOTBIT|DASHBIT))
+ dotline(dx, dy, ndx, ndy, p->o_attr, p->o_ddval);
+ else
+ line(dx, dy, ndx, ndy);
+ dx = ndx;
+ dy = ndy;
+ }
+ }
+ if (vis && p->o_attr & HEAD2) {
+ dx = ox;
+ dy = oy;
+ for (k = 0, j = 5; k < p->o_val[4] - 1; k++, j += 2) {
+ dx += p->o_val[j];
+ dy += p->o_val[j+1];
+ }
+ arrow(dx, dy, x1, y1, p->o_val[2], p->o_val[3], 0.0, p->o_nhead);
+ }
+ if (fill)
+ fillend(vis, fill);
+ move((ox + x1)/2, (oy + y1)/2); /* center */
+ dotext(p);
+ break;
+ case MOVE:
+ move(ox, oy);
+ break;
+ case TEXT:
+ move(ox, oy);
+ if (vis)
+ dotext(p);
+ break;
+ }
+ }
+}
+
+void dotline(double x0, double y0, double x1, double y1, int ddtype, double ddval) /* dotted line */
+{
+ static double prevval = 0.05; /* 20 per inch by default */
+ int i, numdots;
+ double a, b, dx, dy;
+
+ if (ddval == 0)
+ ddval = prevval;
+ prevval = ddval;
+ /* don't save dot/dash value */
+ dx = x1 - x0;
+ dy = y1 - y0;
+ if (ddtype & DOTBIT) {
+ numdots = sqrt(dx*dx + dy*dy) / prevval + 0.5;
+ if (numdots > 0)
+ for (i = 0; i <= numdots; i++) {
+ a = (double) i / (double) numdots;
+ move(x0 + (a * dx), y0 + (a * dy));
+ dot();
+ }
+ } else if (ddtype & DASHBIT) {
+ double d, dashsize, spacesize;
+ d = sqrt(dx*dx + dy*dy);
+ if (d <= 2 * prevval) {
+ line(x0, y0, x1, y1);
+ return;
+ }
+ numdots = d / (2 * prevval) + 1; /* ceiling */
+ dashsize = prevval;
+ spacesize = (d - numdots * dashsize) / (numdots - 1);
+ for (i = 0; i < numdots-1; i++) {
+ a = i * (dashsize + spacesize) / d;
+ b = a + dashsize / d;
+ line(x0 + (a*dx), y0 + (a*dy), x0 + (b*dx), y0 + (b*dy));
+ a = b;
+ b = a + spacesize / d;
+ move(x0 + (a*dx), y0 + (a*dy));
+ }
+ line(x0 + (b * dx), y0 + (b * dy), x1, y1);
+ }
+ prevval = 0.05;
+}
+
+void dotbox(double x0, double y0, double x1, double y1, int ddtype, double ddval) /* dotted or dashed box */
+{
+ dotline(x0, y0, x1, y0, ddtype, ddval);
+ dotline(x1, y0, x1, y1, ddtype, ddval);
+ dotline(x1, y1, x0, y1, ddtype, ddval);
+ dotline(x0, y1, x0, y0, ddtype, ddval);
+}
+
+void dotext(obj *p) /* print text strings of p in proper vertical spacing */
+{
+ int i, nhalf;
+ void label(char *, int, int);
+
+ nhalf = p->o_nt2 - p->o_nt1 - 1;
+ for (i = p->o_nt1; i < p->o_nt2; i++) {
+ label(text[i].t_val, text[i].t_type, nhalf);
+ nhalf -= 2;
+ }
+}
diff --git a/src/cmd/pic/symtab.c b/src/cmd/pic/symtab.c
new file mode 100644
index 00000000..879075c6
--- /dev/null
+++ b/src/cmd/pic/symtab.c
@@ -0,0 +1,104 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include "pic.h"
+#include "y.tab.h"
+
+YYSTYPE getvar(char *s) /* return value of variable s (usually pointer) */
+{
+ struct symtab *p;
+ static YYSTYPE bug;
+
+ p = lookup(s);
+ if (p == NULL) {
+ if (islower(s[0]))
+ ERROR "no such variable as %s", s WARNING;
+ else
+ ERROR "no such place as %s", s WARNING;
+ return(bug);
+ }
+ return(p->s_val);
+}
+
+double getfval(char *s) /* return float value of variable s */
+{
+ YYSTYPE y;
+
+ y = getvar(s);
+ return y.f;
+}
+
+void setfval(char *s, double f) /* set variable s to f */
+{
+ struct symtab *p;
+
+ if ((p = lookup(s)) != NULL)
+ p->s_val.f = f;
+}
+
+struct symtab *makevar(char *s, int t, YYSTYPE v) /* make variable named s in table */
+ /* assumes s is static or from tostring */
+{
+ struct symtab *p;
+
+ for (p = stack[nstack].p_symtab; p != NULL; p = p->s_next)
+ if (strcmp(s, p->s_name) == 0)
+ break;
+ if (p == NULL) { /* it's a new one */
+ p = (struct symtab *) malloc(sizeof(struct symtab));
+ if (p == NULL)
+ ERROR "out of symtab space with %s", s FATAL;
+ p->s_next = stack[nstack].p_symtab;
+ stack[nstack].p_symtab = p; /* stick it at front */
+ }
+ p->s_name = s;
+ p->s_type = t;
+ p->s_val = v;
+ return(p);
+}
+
+struct symtab *lookup(char *s) /* find s in symtab */
+{
+ int i;
+ struct symtab *p;
+
+ for (i = nstack; i >= 0; i--) /* look in each active symtab */
+ for (p = stack[i].p_symtab; p != NULL; p = p->s_next)
+ if (strcmp(s, p->s_name) == 0)
+ return(p);
+ return(NULL);
+}
+
+void freesymtab(struct symtab *p) /* free space used by symtab at p */
+{
+ struct symtab *q;
+
+ for ( ; p != NULL; p = q) {
+ q = p->s_next;
+ free(p->s_name); /* assumes done with tostring */
+ free((char *)p);
+ }
+}
+
+void freedef(char *s) /* free definition for string s */
+{
+ struct symtab *p, *q, *op;
+
+ for (p = op = q = stack[nstack].p_symtab; p != NULL; p = p->s_next) {
+ if (strcmp(s, p->s_name) == 0) { /* got it */
+ if (p->s_type != DEFNAME)
+ break;
+ if (p == op) /* 1st elem */
+ stack[nstack].p_symtab = p->s_next;
+ else
+ q->s_next = p->s_next;
+ free(p->s_name);
+ free(p->s_val.p);
+ free((char *)p);
+ return;
+ }
+ q = p;
+ }
+ /* ERROR "%s is not defined at this point", s WARNING; */
+}
diff --git a/src/cmd/pic/textgen.c b/src/cmd/pic/textgen.c
new file mode 100644
index 00000000..98dfbb57
--- /dev/null
+++ b/src/cmd/pic/textgen.c
@@ -0,0 +1,115 @@
+#include <stdio.h>
+#include "pic.h"
+#include "y.tab.h"
+
+obj *textgen(void)
+{
+ int i, sub, nstr, at, with, hset, invis;
+ double xwith, ywith, h, w, x0, y0, x1, y1;
+ obj *p, *ppos;
+ static double prevh = 0;
+ static double prevw = 0;
+ Attr *ap;
+
+ at = with = nstr = hset = invis = 0;
+ h = getfval("textht");
+ w = getfval("textwid");
+ for (i = 0; i < nattr; i++) {
+ ap = &attr[i];
+ switch (ap->a_type) {
+ case HEIGHT:
+ h = ap->a_val.f;
+ hset++;
+ break;
+ case WIDTH:
+ w = ap->a_val.f;
+ break;
+ case WITH:
+ with = ap->a_val.i;
+ break;
+ case INVIS:
+ invis = INVIS;
+ break;
+ case AT:
+ ppos = ap->a_val.o;
+ curx = ppos->o_x;
+ cury = ppos->o_y;
+ at++;
+ break;
+ case TEXTATTR:
+ sub = ap->a_sub;
+ if (ap->a_val.p == NULL) /* an isolated modifier */
+ text[ntext-1].t_type = sub;
+ else {
+ savetext(sub, ap->a_val.p);
+ nstr++;
+ }
+ break;
+ }
+ }
+ if (hset == 0) /* no explicit ht cmd */
+ h *= nstr;
+ if (with) {
+ xwith = ywith = 0.0;
+ switch (with) {
+ case NORTH: ywith = -h / 2; break;
+ case SOUTH: ywith = h / 2; break;
+ case EAST: xwith = -w / 2; break;
+ case WEST: xwith = w / 2; break;
+ case NE: xwith = -w / 2; ywith = -h / 2; break;
+ case SE: xwith = -w / 2; ywith = h / 2; break;
+ case NW: xwith = w / 2; ywith = -h / 2; break;
+ case SW: xwith = w / 2; ywith = h / 2; break;
+ }
+ curx += xwith;
+ cury += ywith;
+ }
+ if (!at) {
+ if (isright(hvmode))
+ curx += w / 2;
+ else if (isleft(hvmode))
+ curx -= w / 2;
+ else if (isup(hvmode))
+ cury += h / 2;
+ else
+ cury -= h / 2;
+ }
+ x0 = curx - w / 2;
+ y0 = cury - h / 2;
+ x1 = curx + w / 2;
+ y1 = cury + h / 2;
+ extreme(x0, y0);
+ extreme(x1, y1);
+ dprintf("Text h %g w %g at %g,%g\n", h, w, curx, cury);
+ p = makenode(TEXT, 2);
+ p->o_attr = invis;
+ p->o_val[0] = w;
+ p->o_val[1] = h;
+ if (isright(hvmode))
+ curx = x1;
+ else if (isleft(hvmode))
+ curx = x0;
+ else if (isup(hvmode))
+ cury = y1;
+ else
+ cury = y0;
+ prevh = h;
+ prevw = w;
+ return(p);
+}
+
+obj *troffgen(char *s) /* save away a string of troff commands */
+{
+ savetext(CENTER, s); /* use the existing text mechanism */
+ return makenode(TROFF, 0);
+}
+
+void savetext(int t, char *s) /* record text elements for current object */
+{
+ if (ntext >= ntextlist)
+ text = (Text *) grow((char *) text, "text", ntextlist += 200, sizeof(Text));
+ text[ntext].t_type = t;
+ text[ntext].t_val = s;
+ dprintf("saving %d text %s at %d\n", t, s, ntext);
+ ntext++;
+}
diff --git a/src/cmd/tbl/mkfile b/src/cmd/tbl/mkfile
new file mode 100644
index 00000000..cdaf4a7e
--- /dev/null
+++ b/src/cmd/tbl/mkfile
@@ -0,0 +1,32 @@
+<$PLAN9/src/mkhdr
+
+TARG=tbl
+OFILES=\
+ t8.$O\
+ t4.$O\
+ t6.$O\
+ tu.$O\
+ t5.$O\
+ t7.$O\
+ tv.$O\
+ tg.$O\
+ t3.$O\
+ tb.$O\
+ tt.$O\
+ t9.$O\
+ t1.$O\
+ tf.$O\
+ tc.$O\
+ ti.$O\
+ tm.$O\
+ t0.$O\
+ tr.$O\
+ te.$O\
+ ts.$O\
+ t2.$O\
+
+HFILES=\
+ t.h\
+
+SHORTLIB=bio 9
+<$PLAN9/src/mkone
diff --git a/src/cmd/tbl/t.h b/src/cmd/tbl/t.h
new file mode 100644
index 00000000..efcab373
--- /dev/null
+++ b/src/cmd/tbl/t.h
@@ -0,0 +1,192 @@
+/* t..c : external declarations */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+# include <ctype.h>
+
+# define MAXLIN 250
+# define MAXHEAD 44
+# define MAXCOL 30
+ /* Do NOT make MAXCOL bigger with adjusting nregs[] in tr.c */
+# define MAXCHS 2000
+#define MAXLINLEN 300
+# define MAXRPT 100
+# define CLLEN 10
+# define SHORTLINE 4
+extern int nlin, ncol, iline, nclin, nslin;
+
+extern int (*style)[MAXHEAD];
+extern char (*font)[MAXHEAD][2];
+extern char (*csize)[MAXHEAD][4];
+extern char (*vsize)[MAXHEAD][4];
+extern char (*cll)[CLLEN];
+extern int (*flags)[MAXHEAD];
+# define ZEROW 001
+# define HALFUP 002
+# define CTOP 004
+# define CDOWN 010
+extern int stynum[];
+extern int qcol;
+extern int *doubled, *acase, *topat;
+extern int F1, F2;
+extern int (*lefline)[MAXHEAD];
+extern int fullbot[];
+extern char *instead[];
+extern int expflg;
+extern int ctrflg;
+extern int evenflg;
+extern int *evenup;
+extern int boxflg;
+extern int dboxflg;
+extern int linsize;
+extern int tab;
+extern int pr1403;
+extern int linsize, delim1, delim2;
+extern int allflg;
+extern int textflg;
+extern int left1flg;
+extern int rightl;
+struct colstr {char *col, *rcol;};
+extern struct colstr *table[];
+extern char *cspace, *cstore;
+extern char *exstore, *exlim, *exspace;
+extern int *sep;
+extern int *used, *lused, *rused;
+extern int linestop[];
+extern char *leftover;
+extern char *last, *ifile;
+extern int texname;
+extern int texct, texmax;
+extern char texstr[];
+extern int linstart;
+
+
+extern Biobuf *tabin, tabout;
+# define CRIGHT 2
+# define CLEFT 0
+# define CMID 1
+# define S1 31
+# define S2 32
+# define S3 33
+# define TMP 38
+#define S9 39
+# define SF 35
+# define SL 34
+# define LSIZE 33
+# define SIND 37
+# define SVS 36
+/* this refers to the relative position of lines */
+# define LEFT 1
+# define RIGHT 2
+# define THRU 3
+# define TOP 1
+# define BOT 2
+
+int tbl(int argc,char *argv[]); /*t1.c*/
+void setinp(int, char **);
+int swapin(void);
+
+void tableput(void); /*t2.c*/
+
+void getcomm(void); /*t3.c*/
+void backrest(char *);
+
+void getspec(void); /*t4.c*/
+void readspec(void);
+int findcol(void);
+void garray(int);
+char *getcore(int, int);
+void freearr(void);
+
+void gettbl(void); /*t5.c*/
+int nodata(int);
+int oneh(int);
+int vspand(int, int, int);
+int vspen(char *);
+void permute(void);
+
+void maktab(void); /*t6.c*/
+void wide(char *, char *, char *);
+int filler(char *);
+
+void runout(void); /*t7.c*/
+void runtabs(int, int);
+int ifline(char *);
+void need(void);
+void deftail(void);
+
+void putline(int, int); /*t8.c*/
+void puttext(char *, char *, char *);
+void funnies(int, int);
+void putfont(char *);
+void putsize(char *);
+
+void yetmore(void); /*t9.c*/
+int domore(char *);
+
+void checkuse(void); /*tb.c*/
+int real(char *);
+char *chspace(void);
+int *alocv(int);
+void release(void);
+
+void choochar(void); /*tc.c*/
+int point(char *);
+
+void error(char *); /*te.c*/
+char *gets1(char *, int);
+void un1getc(int);
+int get1char(void);
+
+void savefill(void); /*tf.c*/
+void rstofill(void);
+void endoff(void);
+void freearr(void);
+void saveline(void);
+void ifdivert(void);
+void restline(void);
+void cleanfc(void);
+
+int gettext(char *, int, int, char *, char *); /*tg.c*/
+void untext(void);
+
+int interv(int, int); /*ti.c*/
+int interh(int, int);
+int up1(int);
+
+char *maknew(char *); /*tm.c*/
+int ineqn (char *, char *);
+
+char *reg(int, int); /*tr.c*/
+
+int match (char *, char *); /*ts.c*/
+int prefix(char *, char *);
+int letter (int);
+int numb(char *);
+int digit(int);
+int max(int, int);
+void tcopy (char *, char *);
+
+int ctype(int, int); /*tt.c*/
+int min(int, int);
+int fspan(int, int);
+int lspan(int, int);
+int ctspan(int, int);
+void tohcol(int);
+int allh(int);
+int thish(int, int);
+
+void makeline(int, int, int); /*tu.c*/
+void fullwide(int, int);
+void drawline(int, int, int, int, int, int);
+void getstop(void);
+int left(int, int, int *);
+int lefdata(int, int);
+int next(int);
+int prev(int);
+
+void drawvert(int, int, int, int); /*tv.c*/
+int midbar(int, int);
+int midbcol(int, int);
+int barent(char *);
diff --git a/src/cmd/tbl/t0.c b/src/cmd/tbl/t0.c
new file mode 100644
index 00000000..0c0895bd
--- /dev/null
+++ b/src/cmd/tbl/t0.c
@@ -0,0 +1,49 @@
+ /* t0.c: storage allocation */
+#
+# include "t.h"
+int expflg = 0;
+int ctrflg = 0;
+int boxflg = 0;
+int dboxflg = 0;
+int tab = '\t';
+int linsize;
+int pr1403;
+int delim1, delim2;
+int evenflg;
+int *evenup;
+int F1 = 0;
+int F2 = 0;
+int allflg = 0;
+char *leftover = 0;
+int textflg = 0;
+int left1flg = 0;
+int rightl = 0;
+char *cstore, *cspace;
+char *last;
+struct colstr *table[MAXLIN];
+int stynum[MAXLIN+1];
+int fullbot[MAXLIN];
+char *instead[MAXLIN];
+int linestop[MAXLIN];
+int (*style)[MAXHEAD];
+char (*font)[MAXHEAD][2];
+char (*csize)[MAXHEAD][4];
+char (*vsize)[MAXHEAD][4];
+int (*lefline)[MAXHEAD];
+char (*cll)[CLLEN];
+int (*flags)[MAXHEAD];
+int qcol;
+int *doubled, *acase, *topat;
+int nslin, nclin;
+int *sep;
+int *used, *lused, *rused;
+int nlin, ncol;
+int iline = 1;
+char *ifile = "Input";
+int texname = 'a';
+int texct = 0;
+char texstr[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWYXZ0123456789";
+int linstart;
+char *exstore, *exlim, *exspace;
+Biobuf *tabin /*= stdin */;
+Biobuf tabout /* = stdout */;
diff --git a/src/cmd/tbl/t1.c b/src/cmd/tbl/t1.c
new file mode 100644
index 00000000..1e6cbf11
--- /dev/null
+++ b/src/cmd/tbl/t1.c
@@ -0,0 +1,95 @@
+/* t1.c: main control and input switching */
+#
+# include "t.h"
+
+# define MACROS "/usr/lib/tmac.s"
+# define PYMACS "/usr/lib/tmac.m"
+
+
+# define ever (;;)
+
+void
+main(int argc, char *argv[])
+{
+ exits(tbl(argc, argv)? "error" : 0);
+}
+
+
+int
+tbl(int argc, char *argv[])
+{
+ char line[5120];
+ /*int x;*/
+ /*x=malloc((char *)0); uncomment when allocation breaks*/
+ Binit(&tabout, 1, OWRITE);
+ setinp(argc, argv);
+ while (gets1(line, sizeof(line))) {
+ Bprint(&tabout, "%s\n", line);
+ if (prefix(".TS", line))
+ tableput();
+ }
+ Bterm(tabin);
+ return(0);
+}
+
+
+int sargc;
+char **sargv;
+
+void
+setinp(int argc, char **argv)
+{
+ sargc = argc;
+ sargv = argv;
+ sargc--;
+ sargv++;
+ if (sargc > 0)
+ swapin();
+ else {
+ tabin = (Biobuf*)getcore(sizeof(Biobuf), 1);
+ Binit(tabin, 0, OREAD);
+ }
+}
+
+
+int
+swapin(void)
+{
+ char *name;
+ while (sargc > 0 && **sargv == '-') {
+ if (match("-ms", *sargv)) {
+ *sargv = MACROS;
+ break;
+ }
+ if (match("-mm", *sargv)) {
+ *sargv = PYMACS;
+ break;
+ }
+ if (match("-TX", *sargv))
+ pr1403 = 1;
+ if (match("-", *sargv))
+ break;
+ sargc--;
+ sargv++;
+ }
+ if (sargc <= 0)
+ return(0);
+ /* file closing is done by GCOS troff preprocessor */
+ if(tabin)
+ Bterm(tabin);
+ ifile = *sargv;
+ name = ifile;
+ if (match(ifile, "-")) {
+ tabin = (Biobuf*)getcore(sizeof(Biobuf), 1);
+ Binit(tabin, 0, OREAD);
+ } else
+ tabin = Bopen(ifile, OREAD);
+ iline = 1;
+ Bprint(&tabout, ".ds f. %s\n", ifile);
+ Bprint(&tabout, ".lf %d %s\n", iline, name);
+ if (tabin == 0)
+ error("Can't open file");
+ sargc--;
+ sargv++;
+ return(1);
+}
diff --git a/src/cmd/tbl/t2.c b/src/cmd/tbl/t2.c
new file mode 100644
index 00000000..6d2d7414
--- /dev/null
+++ b/src/cmd/tbl/t2.c
@@ -0,0 +1,25 @@
+/* t2.c: subroutine sequencing for one table */
+# include "t.h"
+void
+tableput(void)
+{
+ saveline();
+ savefill();
+ ifdivert();
+ cleanfc();
+ getcomm();
+ getspec();
+ gettbl();
+ getstop();
+ checkuse();
+ choochar();
+ maktab();
+ runout();
+ release();
+ rstofill();
+ endoff();
+ freearr();
+ restline();
+}
+
+
diff --git a/src/cmd/tbl/t3.c b/src/cmd/tbl/t3.c
new file mode 100644
index 00000000..a4dc9f9f
--- /dev/null
+++ b/src/cmd/tbl/t3.c
@@ -0,0 +1,104 @@
+/* t3.c: interpret commands affecting whole table */
+# include "t.h"
+struct optstr {
+ char *optnam;
+ int *optadd;
+} options [] = {
+ "expand", &expflg,
+ "EXPAND", &expflg,
+ "center", &ctrflg,
+ "CENTER", &ctrflg,
+ "box", &boxflg,
+ "BOX", &boxflg,
+ "allbox", &allflg,
+ "ALLBOX", &allflg,
+ "doublebox", &dboxflg,
+ "DOUBLEBOX", &dboxflg,
+ "frame", &boxflg,
+ "FRAME", &boxflg,
+ "doubleframe", &dboxflg,
+ "DOUBLEFRAME", &dboxflg,
+ "tab", &tab,
+ "TAB", &tab,
+ "linesize", &linsize,
+ "LINESIZE", &linsize,
+ "delim", &delim1,
+ "DELIM", &delim1,
+ 0, 0};
+
+
+void
+getcomm(void)
+{
+ char line[200], *cp, nb[25], *t;
+ struct optstr *lp;
+ int c, ci, found;
+
+ for (lp = options; lp->optnam; lp++)
+ *(lp->optadd) = 0;
+ texname = texstr[texct=0];
+ tab = '\t';
+ Bprint(&tabout, ".nr %d \\n(.s\n", LSIZE);
+ gets1(line, sizeof(line));
+ /* see if this is a command line */
+ if (strchr(line, ';') == 0) {
+ backrest(line);
+ return;
+ }
+ for (cp = line; (c = *cp) != ';'; cp++) {
+ if (!letter(c))
+ continue;
+ found = 0;
+ for (lp = options; lp->optadd; lp++) {
+ if (prefix(lp->optnam, cp)) {
+ *(lp->optadd) = 1;
+ cp += strlen(lp->optnam);
+ if (letter(*cp))
+ error("Misspelled global option");
+ while (*cp == ' ')
+ cp++;
+ t = nb;
+ if ( *cp == '(')
+ while ((ci = *++cp) != ')')
+ *t++ = ci;
+ else
+ cp--;
+ *t++ = 0;
+ *t = 0;
+ if (lp->optadd == &tab) {
+ if (nb[0])
+ *(lp->optadd) = nb[0];
+ }
+ if (lp->optadd == &linsize)
+ Bprint(&tabout, ".nr %d %s\n", LSIZE, nb);
+ if (lp->optadd == &delim1) {
+ delim1 = nb[0];
+ delim2 = nb[1];
+ }
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ error("Illegal option");
+ }
+ cp++;
+ backrest(cp);
+ return;
+}
+
+
+void
+backrest(char *cp)
+{
+ char *s;
+
+ for (s = cp; *s; s++)
+ ;
+ un1getc('\n');
+ while (s > cp)
+ un1getc(*--s);
+ return;
+}
+
+
diff --git a/src/cmd/tbl/t4.c b/src/cmd/tbl/t4.c
new file mode 100644
index 00000000..558d3ba3
--- /dev/null
+++ b/src/cmd/tbl/t4.c
@@ -0,0 +1,405 @@
+/* t4.c: read table specification */
+# include "t.h"
+int oncol;
+
+void
+getspec(void)
+{
+ int icol, i;
+
+ qcol = findcol() + 1;/* must allow one extra for line at right */
+ garray(qcol);
+ sep[-1] = -1;
+ for (icol = 0; icol < qcol; icol++) {
+ sep[icol] = -1;
+ evenup[icol] = 0;
+ cll[icol][0] = 0;
+ for (i = 0; i < MAXHEAD; i++) {
+ csize[icol][i][0] = 0;
+ vsize[icol][i][0] = 0;
+ font[icol][i][0] = lefline[icol][i] = 0;
+ flags[icol][i] = 0;
+ style[icol][i] = 'l';
+ }
+ }
+ for (i = 0; i < MAXHEAD; i++)
+ lefline[qcol][i] = 0; /* fixes sample55 looping */
+ nclin = ncol = 0;
+ oncol = 0;
+ left1flg = rightl = 0;
+ readspec();
+ Bprint(&tabout, ".rm");
+ for (i = 0; i < ncol; i++)
+ Bprint(&tabout, " %2s", reg(i, CRIGHT));
+ Bprint(&tabout, "\n");
+}
+
+
+void
+readspec(void)
+{
+ int icol, c, sawchar, stopc, i;
+ char sn[10], *snp, *temp;
+
+ sawchar = icol = 0;
+ while (c = get1char()) {
+ switch (c) {
+ default:
+ if (c != tab) {
+ char buf[64];
+ sprint(buf, "bad table specification character %c", c);
+ error(buf);
+ }
+ case ' ': /* note this is also case tab */
+ continue;
+ case '\n':
+ if (sawchar == 0)
+ continue;
+ case ',':
+ case '.': /* end of table specification */
+ ncol = max(ncol, icol);
+ if (lefline[ncol][nclin] > 0) {
+ ncol++;
+ rightl++;
+ };
+ if (sawchar)
+ nclin++;
+ if (nclin >= MAXHEAD)
+ error("too many lines in specification");
+ icol = 0;
+ if (ncol == 0 || nclin == 0)
+ error("no specification");
+ if (c == '.') {
+ while ((c = get1char()) && c != '\n')
+ if (c != ' ' && c != '\t')
+ error("dot not last character on format line");
+ /* fix up sep - default is 3 except at edge */
+ for (icol = 0; icol < ncol; icol++)
+ if (sep[icol] < 0)
+ sep[icol] = icol + 1 < ncol ? 3 : 2;
+ if (oncol == 0)
+ oncol = ncol;
+ else if (oncol + 2 < ncol)
+ error("tried to widen table in T&, not allowed");
+ return;
+ }
+ sawchar = 0;
+ continue;
+ case 'C':
+ case 'S':
+ case 'R':
+ case 'N':
+ case 'L':
+ case 'A':
+ c += ('a' - 'A');
+ case '_':
+ if (c == '_')
+ c = '-';
+ case '=':
+ case '-':
+ case '^':
+ case 'c':
+ case 's':
+ case 'n':
+ case 'r':
+ case 'l':
+ case 'a':
+ style[icol][nclin] = c;
+ if (c == 's' && icol <= 0)
+ error("first column can not be S-type");
+ if (c == 's' && style[icol-1][nclin] == 'a') {
+ Bprint(&tabout, ".tm warning: can't span a-type cols, changed to l\n");
+ style[icol-1][nclin] = 'l';
+ }
+ if (c == 's' && style[icol-1][nclin] == 'n') {
+ Bprint(&tabout, ".tm warning: can't span n-type cols, changed to c\n");
+ style[icol-1][nclin] = 'c';
+ }
+ icol++;
+ if (c == '^' && nclin <= 0)
+ error("first row can not contain vertical span");
+ if (icol > qcol)
+ error("too many columns in table");
+ sawchar = 1;
+ continue;
+ case 'b':
+ case 'i':
+ c += 'A' - 'a';
+ case 'B':
+ case 'I':
+ if (icol == 0)
+ continue;
+ snp = font[icol-1][nclin];
+ snp[0] = (c == 'I' ? '2' : '3');
+ snp[1] = 0;
+ continue;
+ case 't':
+ case 'T':
+ if (icol > 0)
+ flags[icol-1][nclin] |= CTOP;
+ continue;
+ case 'd':
+ case 'D':
+ if (icol > 0)
+ flags[icol-1][nclin] |= CDOWN;
+ continue;
+ case 'f':
+ case 'F':
+ if (icol == 0)
+ continue;
+ snp = font[icol-1][nclin];
+ snp[0] = snp[1] = stopc = 0;
+ for (i = 0; i < 2; i++) {
+ c = get1char();
+ if (i == 0 && c == '(') {
+ stopc = ')';
+ c = get1char();
+ }
+ if (c == 0)
+ break;
+ if (c == stopc) {
+ stopc = 0;
+ break;
+ }
+ if (stopc == 0)
+ if (c == ' ' || c == tab )
+ break;
+ if (c == '\n' || c == '|') {
+ un1getc(c);
+ break;
+ }
+ snp[i] = c;
+ if (c >= '0' && c <= '9')
+ break;
+ }
+ if (stopc)
+ if (get1char() != stopc)
+ error("Nonterminated font name");
+ continue;
+ case 'P':
+ case 'p':
+ if (icol <= 0)
+ continue;
+ temp = snp = csize[icol-1][nclin];
+ while (c = get1char()) {
+ if (c == ' ' || c == tab || c == '\n')
+ break;
+ if (c == '-' || c == '+')
+ if (snp > temp)
+ break;
+ else
+ *snp++ = c;
+ else if (digit(c))
+ *snp++ = c;
+ else
+ break;
+ if (snp - temp > 4)
+ error("point size too large");
+ }
+ *snp = 0;
+ if (atoi(temp) > 36)
+ error("point size unreasonable");
+ un1getc (c);
+ continue;
+ case 'V':
+ case 'v':
+ if (icol <= 0)
+ continue;
+ temp = snp = vsize[icol-1][nclin];
+ while (c = get1char()) {
+ if (c == ' ' || c == tab || c == '\n')
+ break;
+ if (c == '-' || c == '+')
+ if (snp > temp)
+ break;
+ else
+ *snp++ = c;
+ else if (digit(c))
+ *snp++ = c;
+ else
+ break;
+ if (snp - temp > 4)
+ error("vertical spacing value too large");
+ }
+ *snp = 0;
+ un1getc(c);
+ continue;
+ case 'w':
+ case 'W':
+ snp = cll [icol-1];
+ /* Dale Smith didn't like this check - possible to have two text blocks
+ of different widths now ....
+ if (*snp)
+ {
+ Bprint(&tabout, "Ignored second width specification");
+ continue;
+ }
+ /* end commented out code ... */
+ stopc = 0;
+ while (c = get1char()) {
+ if (snp == cll[icol-1] && c == '(') {
+ stopc = ')';
+ continue;
+ }
+ if ( !stopc && (c > '9' || c < '0'))
+ break;
+ if (stopc && c == stopc)
+ break;
+ *snp++ = c;
+ }
+ *snp = 0;
+ if (snp - cll[icol-1] > CLLEN)
+ error ("column width too long");
+ if (!stopc)
+ un1getc(c);
+ continue;
+ case 'e':
+ case 'E':
+ if (icol < 1)
+ continue;
+ evenup[icol-1] = 1;
+ evenflg = 1;
+ continue;
+ case 'z':
+ case 'Z': /* zero width-ignre width this item */
+ if (icol < 1)
+ continue;
+ flags[icol-1][nclin] |= ZEROW;
+ continue;
+ case 'u':
+ case 'U': /* half line up */
+ if (icol < 1)
+ continue;
+ flags[icol-1][nclin] |= HALFUP;
+ continue;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ sn[0] = c;
+ snp = sn + 1;
+ while (digit(*snp++ = c = get1char()))
+ ;
+ un1getc(c);
+ sep[icol-1] = max(sep[icol-1], numb(sn));
+ continue;
+ case '|':
+ lefline[icol][nclin]++;
+ if (icol == 0)
+ left1flg = 1;
+ continue;
+ }
+ }
+ error("EOF reading table specification");
+}
+
+
+int
+findcol(void)
+{
+# define FLNLIM 200
+ /* this counts the number of columns and then puts the line back*/
+ char *s, line[FLNLIM+2], *p;
+ int c, n = 0, inpar = 0;
+
+ while ((c = get1char()) != 0 && c == ' ')
+ ;
+ if (c != '\n')
+ un1getc(c);
+ for (s = line; *s = c = get1char(); s++) {
+ if (c == ')')
+ inpar = 0;
+ if (inpar)
+ continue;
+ if (c == '\n' || c == 0 || c == '.' || c == ',')
+ break;
+ else if (c == '(')
+ inpar = 1;
+ else if (s >= line + FLNLIM)
+ error("too long spec line");
+ }
+ for (p = line; p < s; p++)
+ switch (*p) {
+ case 'l':
+ case 'r':
+ case 'c':
+ case 'n':
+ case 'a':
+ case 's':
+ case 'L':
+ case 'R':
+ case 'C':
+ case 'N':
+ case 'A':
+ case 'S':
+ case '-':
+ case '=':
+ case '_':
+ n++;
+ }
+ while (p >= line)
+ un1getc(*p--);
+ return(n);
+}
+
+
+void
+garray(int qcol)
+{
+ style = (int (*)[]) getcore(MAXHEAD * qcol, sizeof(int));
+ evenup = (int *) getcore(qcol, sizeof(int));
+ lefline = (int (*)[]) getcore(MAXHEAD * (qcol + 1), sizeof (int)); /*+1 for sample55 loop - others may need it too*/
+ font = (char (*)[][2]) getcore(MAXHEAD * qcol, 2);
+ csize = (char (*)[MAXHEAD][4]) getcore(MAXHEAD * qcol, 4);
+ vsize = (char (*)[MAXHEAD][4]) getcore(MAXHEAD * qcol, 4);
+ flags = (int (*)[]) getcore(MAXHEAD * qcol, sizeof(int));
+ cll = (char (*)[])getcore(qcol, CLLEN);
+ sep = (int *) getcore(qcol + 1, sizeof(int));
+ sep++; /* sep[-1] must be legal */
+ used = (int *) getcore(qcol + 1, sizeof(int));
+ lused = (int *) getcore(qcol + 1, sizeof(int));
+ rused = (int *) getcore(qcol + 1, sizeof(int));
+ doubled = (int *) getcore(qcol + 1, sizeof(int));
+ acase = (int *) getcore(qcol + 1, sizeof(int));
+ topat = (int *) getcore(qcol + 1, sizeof(int));
+}
+
+
+char *
+getcore(int a, int b)
+{
+ char *x;
+ x = calloc(a, b);
+ if (x == 0)
+ error("Couldn't get memory");
+ return(x);
+}
+
+
+void
+freearr(void)
+{
+ free(style);
+ free(evenup);
+ free(lefline);
+ free(flags);
+ free(font);
+ free(csize);
+ free(vsize);
+ free(cll);
+ free(--sep); /* netnews says this should be --sep because incremented earlier! */
+ free(used);
+ free(lused);
+ free(rused);
+ free(doubled);
+ free(acase);
+ free(topat);
+}
+
+
diff --git a/src/cmd/tbl/t5.c b/src/cmd/tbl/t5.c
new file mode 100644
index 00000000..8b7a65d8
--- /dev/null
+++ b/src/cmd/tbl/t5.c
@@ -0,0 +1,198 @@
+/* t5.c: read data for table */
+# include "t.h"
+
+void
+gettbl(void)
+{
+ int icol, ch;
+
+ cstore = cspace = chspace();
+ textflg = 0;
+ for (nlin = nslin = 0; gets1(cstore, MAXCHS - (cstore - cspace)); nlin++) {
+ stynum[nlin] = nslin;
+ if (prefix(".TE", cstore)) {
+ leftover = 0;
+ break;
+ }
+ if (prefix(".TC", cstore) || prefix(".T&", cstore)) {
+ readspec();
+ nslin++;
+ }
+ if (nlin >= MAXLIN) {
+ leftover = cstore;
+ break;
+ }
+ fullbot[nlin] = 0;
+ if (cstore[0] == '.' && !isdigit(cstore[1])) {
+ instead[nlin] = cstore;
+ while (*cstore++)
+ ;
+ continue;
+ } else
+ instead[nlin] = 0;
+ if (nodata(nlin)) {
+ if (ch = oneh(nlin))
+ fullbot[nlin] = ch;
+ table[nlin] = (struct colstr *) alocv((ncol + 2) * sizeof(table[0][0]));
+ for (icol = 0; icol < ncol; icol++) {
+ table[nlin][icol].rcol = "";
+ table[nlin][icol].col = "";
+ }
+ nlin++;
+ nslin++;
+ fullbot[nlin] = 0;
+ instead[nlin] = (char *) 0;
+ }
+ table[nlin] = (struct colstr *) alocv((ncol + 2) * sizeof(table[0][0]));
+ if (cstore[1] == 0)
+ switch (cstore[0]) {
+ case '_':
+ fullbot[nlin] = '-';
+ continue;
+ case '=':
+ fullbot[nlin] = '=';
+ continue;
+ }
+ stynum[nlin] = nslin;
+ nslin = min(nslin + 1, nclin - 1);
+ for (icol = 0; icol < ncol; icol++) {
+ table[nlin][icol].col = cstore;
+ table[nlin][icol].rcol = 0;
+ ch = 1;
+ if (match(cstore, "T{")) { /* text follows */
+ table[nlin][icol].col =
+ (char *)gettext(cstore, nlin, icol,
+ font[icol][stynum[nlin]],
+ csize[icol][stynum[nlin]]);
+ } else
+ {
+ for (; (ch = *cstore) != '\0' && ch != tab; cstore++)
+ ;
+ *cstore++ = '\0';
+ switch (ctype(nlin, icol)) /* numerical or alpha, subcol */ {
+ case 'n':
+ table[nlin][icol].rcol = maknew(table[nlin][icol].col);
+ break;
+ case 'a':
+ table[nlin][icol].rcol = table[nlin][icol].col;
+ table[nlin][icol].col = "";
+ break;
+ }
+ }
+ while (ctype(nlin, icol + 1) == 's') /* spanning */
+ table[nlin][++icol].col = "";
+ if (ch == '\0')
+ break;
+ }
+ while (++icol < ncol + 2) {
+ table[nlin][icol].col = "";
+ table [nlin][icol].rcol = 0;
+ }
+ while (*cstore != '\0')
+ cstore++;
+ if (cstore - cspace + MAXLINLEN > MAXCHS)
+ cstore = cspace = chspace();
+ }
+ last = cstore;
+ permute();
+ if (textflg)
+ untext();
+ return;
+}
+
+
+int
+nodata(int il)
+{
+ int c;
+
+ for (c = 0; c < ncol; c++) {
+ switch (ctype(il, c)) {
+ case 'c':
+ case 'n':
+ case 'r':
+ case 'l':
+ case 's':
+ case 'a':
+ return(0);
+ }
+ }
+ return(1);
+}
+
+
+int
+oneh(int lin)
+{
+ int k, icol;
+
+ k = ctype(lin, 0);
+ for (icol = 1; icol < ncol; icol++) {
+ if (k != ctype(lin, icol))
+ return(0);
+ }
+ return(k);
+}
+
+
+# define SPAN "\\^"
+
+void
+permute(void)
+{
+ int irow, jcol, is;
+ char *start, *strig;
+
+ for (jcol = 0; jcol < ncol; jcol++) {
+ for (irow = 1; irow < nlin; irow++) {
+ if (vspand(irow, jcol, 0)) {
+ is = prev(irow);
+ if (is < 0)
+ error("Vertical spanning in first row not allowed");
+ start = table[is][jcol].col;
+ strig = table[is][jcol].rcol;
+ while (irow < nlin && vspand(irow, jcol, 0))
+ irow++;
+ table[--irow][jcol].col = start;
+ table[irow][jcol].rcol = strig;
+ while (is < irow) {
+ table[is][jcol].rcol = 0;
+ table[is][jcol].col = SPAN;
+ is = next(is);
+ }
+ }
+ }
+ }
+}
+
+
+int
+vspand(int ir, int ij, int ifform)
+{
+ if (ir < 0)
+ return(0);
+ if (ir >= nlin)
+ return(0);
+ if (instead[ir])
+ return(0);
+ if (ifform == 0 && ctype(ir, ij) == '^')
+ return(1);
+ if (table[ir][ij].rcol != 0)
+ return(0);
+ if (fullbot[ir])
+ return(0);
+ return(vspen(table[ir][ij].col));
+}
+
+
+int
+vspen(char *s)
+{
+ if (s == 0)
+ return(0);
+ if (!point(s))
+ return(0);
+ return(match(s, SPAN));
+}
+
+
diff --git a/src/cmd/tbl/t6.c b/src/cmd/tbl/t6.c
new file mode 100644
index 00000000..a78368fc
--- /dev/null
+++ b/src/cmd/tbl/t6.c
@@ -0,0 +1,223 @@
+/* t6.c: compute tab stops */
+# define tx(a) (a>0 && a<128)
+# include "t.h"
+# define FN(i,c) font[c][stynum[i]]
+# define SZ(i,c) csize[c][stynum[i]]
+# define TMP1 S1
+# define TMP2 S2
+
+void
+maktab(void) /* define the tab stops of the table */
+{
+ int icol, ilin, tsep, k, ik, vforml, il, text;
+ char *s;
+
+ for (icol = 0; icol < ncol; icol++) {
+ doubled[icol] = acase[icol] = 0;
+ Bprint(&tabout, ".nr %2s 0\n", reg(icol, CRIGHT));
+ for (text = 0; text < 2; text++) {
+ if (text)
+ Bprint(&tabout, ".%2s\n.rm %2s\n", reg(icol, CRIGHT),
+ reg(icol, CRIGHT));
+ for (ilin = 0; ilin < nlin; ilin++) {
+ if (instead[ilin] || fullbot[ilin])
+ continue;
+ vforml = ilin;
+ for (il = prev(ilin); il >= 0 && vspen(table[il][icol].col); il = prev(il))
+ vforml = il;
+ if (fspan(vforml, icol))
+ continue;
+ if (filler(table[ilin][icol].col))
+ continue;
+ if ((flags[icol][stynum[ilin]] & ZEROW) != 0)
+ continue;
+ switch (ctype(vforml, icol)) {
+ case 'a':
+ acase[icol] = 1;
+ s = table[ilin][icol].col;
+ if ((int)s > 0 && (int)s < 128 && text) {
+ if (doubled[icol] == 0)
+ Bprint(&tabout, ".nr %d 0\n.nr %d 0\n",
+ S1, S2);
+ doubled[icol] = 1;
+ Bprint(&tabout, ".if \\n(%c->\\n(%d .nr %d \\n(%c-\n",
+ (int)s, S2, S2, (int)s);
+ }
+ case 'n':
+ if (table[ilin][icol].rcol != 0) {
+ if (doubled[icol] == 0 && text == 0)
+ Bprint(&tabout, ".nr %d 0\n.nr %d 0\n",
+ S1, S2);
+ doubled[icol] = 1;
+ if (real(s = table[ilin][icol].col) && !vspen(s)) {
+ if (tx((int)s) != text)
+ continue;
+ Bprint(&tabout, ".nr %d ", TMP);
+ wide(s, FN(vforml, icol), SZ(vforml, icol));
+ Bprint(&tabout, "\n");
+ Bprint(&tabout, ".if \\n(%d<\\n(%d .nr %d \\n(%d\n",
+ S1, TMP, S1, TMP);
+ }
+ if (text == 0 && real(s = table[ilin][icol].rcol) && !vspen(s) && !barent(s)) {
+ Bprint(&tabout, ".nr %d \\w%c%s%c\n",
+ TMP, F1, s, F1);
+ Bprint(&tabout, ".if \\n(%d<\\n(%d .nr %d \\n(%d\n", S2, TMP, S2,
+ TMP);
+ }
+ continue;
+ }
+ case 'r':
+ case 'c':
+ case 'l':
+ if (real(s = table[ilin][icol].col) && !vspen(s)) {
+ if (tx((int)s) != text)
+ continue;
+ Bprint(&tabout, ".nr %d ", TMP);
+ wide(s, FN(vforml, icol), SZ(vforml, icol));
+ Bprint(&tabout, "\n");
+ Bprint(&tabout, ".if \\n(%2s<\\n(%d .nr %2s \\n(%d\n",
+ reg(icol, CRIGHT), TMP, reg(icol, CRIGHT), TMP);
+ }
+ }
+ }
+ }
+ if (acase[icol]) {
+ Bprint(&tabout, ".if \\n(%d>=\\n(%2s .nr %2s \\n(%du+2n\n",
+ S2, reg(icol, CRIGHT), reg(icol, CRIGHT), S2);
+ }
+ if (doubled[icol]) {
+ Bprint(&tabout, ".nr %2s \\n(%d\n", reg(icol, CMID), S1);
+ Bprint(&tabout, ".nr %d \\n(%2s+\\n(%d\n", TMP, reg(icol, CMID), S2);
+ Bprint(&tabout, ".if \\n(%d>\\n(%2s .nr %2s \\n(%d\n", TMP,
+ reg(icol, CRIGHT), reg(icol, CRIGHT), TMP);
+ Bprint(&tabout, ".if \\n(%d<\\n(%2s .nr %2s +(\\n(%2s-\\n(%d)/2\n",
+ TMP, reg(icol, CRIGHT), reg(icol, CMID), reg(icol, CRIGHT), TMP);
+ }
+ if (cll[icol][0]) {
+ Bprint(&tabout, ".nr %d %sn\n", TMP, cll[icol]);
+ Bprint(&tabout, ".if \\n(%2s<\\n(%d .nr %2s \\n(%d\n",
+ reg(icol, CRIGHT), TMP, reg(icol, CRIGHT), TMP);
+ }
+ for (ilin = 0; ilin < nlin; ilin++)
+ if (k = lspan(ilin, icol)) {
+ s = table[ilin][icol-k].col;
+ if (!real(s) || barent(s) || vspen(s) )
+ continue;
+ Bprint(&tabout, ".nr %d ", TMP);
+ wide(table[ilin][icol-k].col, FN(ilin, icol - k), SZ(ilin, icol - k));
+ for (ik = k; ik >= 0; ik--) {
+ Bprint(&tabout, "-\\n(%2s", reg(icol - ik, CRIGHT));
+ if (!expflg && ik > 0)
+ Bprint(&tabout, "-%dn", sep[icol-ik]);
+ }
+ Bprint(&tabout, "\n");
+ Bprint(&tabout, ".if \\n(%d>0 .nr %d \\n(%d/%d\n", TMP,
+ TMP, TMP, k);
+ Bprint(&tabout, ".if \\n(%d<0 .nr %d 0\n", TMP, TMP);
+ for (ik = 1; ik <= k; ik++) {
+ if (doubled[icol-k+ik])
+ Bprint(&tabout, ".nr %2s +\\n(%d/2\n",
+ reg(icol - k + ik, CMID), TMP);
+ Bprint(&tabout, ".nr %2s +\\n(%d\n",
+ reg(icol - k + ik, CRIGHT), TMP);
+ }
+ }
+ }
+ if (textflg)
+ untext();
+ /* if even requested, make all columns widest width */
+ if (evenflg) {
+ Bprint(&tabout, ".nr %d 0\n", TMP);
+ for (icol = 0; icol < ncol; icol++) {
+ if (evenup[icol] == 0)
+ continue;
+ Bprint(&tabout, ".if \\n(%2s>\\n(%d .nr %d \\n(%2s\n",
+ reg(icol, CRIGHT), TMP, TMP, reg(icol, CRIGHT));
+ }
+ for (icol = 0; icol < ncol; icol++) {
+ if (evenup[icol] == 0)
+ /* if column not evened just retain old interval */
+ continue;
+ if (doubled[icol])
+ Bprint(&tabout, ".nr %2s (100*\\n(%2s/\\n(%2s)*\\n(%d/100\n",
+ reg(icol, CMID), reg(icol, CMID), reg(icol, CRIGHT), TMP);
+ /* that nonsense with the 100's and parens tries
+ to avoid overflow while proportionally shifting
+ the middle of the number */
+ Bprint(&tabout, ".nr %2s \\n(%d\n", reg(icol, CRIGHT), TMP);
+ }
+ }
+ /* now adjust for total table width */
+ for (tsep = icol = 0; icol < ncol; icol++)
+ tsep += sep[icol];
+ if (expflg) {
+ Bprint(&tabout, ".nr %d 0", TMP);
+ for (icol = 0; icol < ncol; icol++)
+ Bprint(&tabout, "+\\n(%2s", reg(icol, CRIGHT));
+ Bprint(&tabout, "\n");
+ Bprint(&tabout, ".nr %d \\n(.l-\\n(%d\n", TMP, TMP);
+ if (boxflg || dboxflg || allflg)
+ /* tsep += 1; */ {}
+ else
+ tsep -= sep[ncol-1];
+ Bprint(&tabout, ".nr %d \\n(%d/%d\n", TMP, TMP, tsep);
+ Bprint(&tabout, ".if \\n(%d<0 .nr %d 0\n", TMP, TMP);
+ } else
+ Bprint(&tabout, ".nr %d 1n\n", TMP);
+ Bprint(&tabout, ".nr %2s 0\n", reg(-1, CRIGHT));
+ tsep = (boxflg || allflg || dboxflg || left1flg) ? 2 : 0;
+ if (sep[-1] >= 0)
+ tsep = sep[-1];
+ for (icol = 0; icol < ncol; icol++) {
+ Bprint(&tabout, ".nr %2s \\n(%2s+((%d*\\n(%d)/2)\n", reg(icol, CLEFT),
+ reg(icol - 1, CRIGHT), tsep, TMP);
+ Bprint(&tabout, ".nr %2s +\\n(%2s\n", reg(icol, CRIGHT), reg(icol, CLEFT));
+ if (doubled[icol]) {
+ /* the next line is last-ditch effort to avoid zero field width */
+ /*Bprint(&tabout, ".if \\n(%2s=0 .nr %2s 1\n",reg(icol,CMID), reg(icol,CMID));*/
+ Bprint(&tabout, ".nr %2s +\\n(%2s\n", reg(icol, CMID),
+ reg(icol, CLEFT));
+ /* Bprint(&tabout, ".if n .if \\n(%s%%24>0 .nr %s +12u\n",reg(icol,CMID), reg(icol,CMID)); */
+ }
+ tsep = sep[icol] * 2;
+ }
+ if (rightl)
+ Bprint(&tabout, ".nr %s (\\n(%s+\\n(%s)/2\n", reg(ncol - 1, CRIGHT),
+ reg(ncol - 1, CLEFT), reg(ncol - 2, CRIGHT));
+ Bprint(&tabout, ".nr TW \\n(%2s\n", reg(ncol - 1, CRIGHT));
+ tsep = sep[ncol-1];
+ if (boxflg || allflg || dboxflg)
+ Bprint(&tabout, ".nr TW +((%d*\\n(%d)/2)\n", tsep, TMP);
+ Bprint(&tabout,
+ ".if t .if (\\n(TW+\\n(.o)>7.65i .tm Table at line %d file %s is too wide - \\n(TW units\n", iline - 1, ifile);
+ return;
+}
+
+
+void
+wide(char *s, char *fn, char *size)
+{
+ if (point(s)) {
+ Bprint(&tabout, "\\w%c", F1);
+ if (*fn > 0)
+ putfont(fn);
+ if (*size)
+ putsize(size);
+ Bprint(&tabout, "%s", s);
+ if (*fn > 0)
+ putfont("P");
+ if (*size)
+ putsize("0");
+ Bprint(&tabout, "%c", F1);
+ } else
+ Bprint(&tabout, "\\n(%c-", (int)s);
+}
+
+
+int
+filler(char *s)
+{
+ return (point(s) && s[0] == '\\' && s[1] == 'R');
+}
+
+
diff --git a/src/cmd/tbl/t7.c b/src/cmd/tbl/t7.c
new file mode 100644
index 00000000..2fa9de53
--- /dev/null
+++ b/src/cmd/tbl/t7.c
@@ -0,0 +1,150 @@
+/* t7.c: control to write table entries */
+# include "t.h"
+# define realsplit ((ct=='a'||ct=='n') && table[ldata][c].rcol)
+
+void
+runout(void)
+{
+ int i;
+
+ if (boxflg || allflg || dboxflg)
+ need();
+ if (ctrflg) {
+ Bprint(&tabout, ".nr #I \\n(.i\n");
+ Bprint(&tabout, ".in +(\\n(.lu-\\n(TWu-\\n(.iu)/2u\n");
+ }
+ Bprint(&tabout, ".fc %c %c\n", F1, F2);
+ Bprint(&tabout, ".nr #T 0-1\n");
+ deftail();
+ for (i = 0; i < nlin; i++)
+ putline(i, i);
+ if (leftover)
+ yetmore();
+ Bprint(&tabout, ".fc\n");
+ Bprint(&tabout, ".nr T. 1\n");
+ Bprint(&tabout, ".T# 1\n");
+ if (ctrflg)
+ Bprint(&tabout, ".in \\n(#Iu\n");
+}
+
+
+void
+runtabs(int lform, int ldata)
+{
+ int c, ct, vforml, lf;
+
+ Bprint(&tabout, ".ta ");
+ for (c = 0; c < ncol; c++) {
+ vforml = lform;
+ for (lf = prev(lform); lf >= 0 && vspen(table[lf][c].col); lf = prev(lf))
+ vforml = lf;
+ if (fspan(vforml, c))
+ continue;
+ switch (ct = ctype(vforml, c)) {
+ case 'n':
+ case 'a':
+ if (table[ldata][c].rcol)
+ if (lused[c]) /*Zero field width*/
+ Bprint(&tabout, "\\n(%2su ", reg(c, CMID));
+ case 'c':
+ case 'l':
+ case 'r':
+ if (realsplit ? rused[c] : (used[c] + lused[c]))
+ Bprint(&tabout, "\\n(%2su ", reg(c, CRIGHT));
+ continue;
+ case 's':
+ if (lspan(lform, c))
+ Bprint(&tabout, "\\n(%2su ", reg(c, CRIGHT));
+ continue;
+ }
+ }
+ Bprint(&tabout, "\n");
+}
+
+
+int
+ifline(char *s)
+{
+ if (!point(s))
+ return(0);
+ if (s[0] == '\\')
+ s++;
+ if (s[1] )
+ return(0);
+ if (s[0] == '_')
+ return('-');
+ if (s[0] == '=')
+ return('=');
+ return(0);
+}
+
+
+void
+need(void)
+{
+ int texlin, horlin, i;
+
+ for (texlin = horlin = i = 0; i < nlin; i++) {
+ if (fullbot[i] != 0)
+ horlin++;
+ else if (instead[i] != 0)
+ continue;
+ else
+ texlin++;
+ }
+ Bprint(&tabout, ".ne %dv+%dp\n", texlin, 2 * horlin);
+}
+
+
+void
+deftail(void)
+{
+ int i, c, lf, lwid;
+
+ for (i = 0; i < MAXHEAD; i++)
+ if (linestop[i])
+ Bprint(&tabout, ".nr #%c 0-1\n", linestop[i] + 'a' - 1);
+ Bprint(&tabout, ".nr #a 0-1\n");
+ Bprint(&tabout, ".eo\n");
+ Bprint(&tabout, ".de T#\n");
+ Bprint(&tabout, ".nr 35 1m\n");
+ Bprint(&tabout, ".ds #d .d\n");
+ Bprint(&tabout, ".if \\(ts\\n(.z\\(ts\\(ts .ds #d nl\n");
+ Bprint(&tabout, ".mk ##\n");
+ Bprint(&tabout, ".nr ## -1v\n");
+ Bprint(&tabout, ".ls 1\n");
+ for (i = 0; i < MAXHEAD; i++)
+ if (linestop[i])
+ Bprint(&tabout, ".if \\n(#T>=0 .nr #%c \\n(#T\n",
+ linestop[i] + 'a' - 1);
+ if (boxflg || allflg || dboxflg) /* bottom of table line */
+ if (fullbot[nlin-1] == 0) {
+ if (!pr1403)
+ Bprint(&tabout, ".if \\n(T. .vs \\n(.vu-\\n(.sp\n");
+ Bprint(&tabout, ".if \\n(T. ");
+ drawline(nlin, 0, ncol, dboxflg ? '=' : '-', 1, 0);
+ Bprint(&tabout, "\n.if \\n(T. .vs\n");
+ /* T. is really an argument to a macro but because of
+ eqn we don't dare pass it as an argument and reference by $1 */
+ }
+ for (c = 0; c < ncol; c++) {
+ if ((lf = left(nlin - 1, c, &lwid)) >= 0) {
+ Bprint(&tabout, ".if \\n(#%c>=0 .sp -1\n", linestop[lf] + 'a' - 1);
+ Bprint(&tabout, ".if \\n(#%c>=0 ", linestop[lf] + 'a' - 1);
+ tohcol(c);
+ drawvert(lf, nlin - 1, c, lwid);
+ Bprint(&tabout, "\\h'|\\n(TWu'\n");
+ }
+ }
+ if (boxflg || allflg || dboxflg) /* right hand line */ {
+ Bprint(&tabout, ".if \\n(#a>=0 .sp -1\n");
+ Bprint(&tabout, ".if \\n(#a>=0 \\h'|\\n(TWu'");
+ drawvert (0, nlin - 1, ncol, dboxflg ? 2 : 1);
+ Bprint(&tabout, "\n");
+ }
+ Bprint(&tabout, ".ls\n");
+ Bprint(&tabout, "..\n");
+ Bprint(&tabout, ".ec\n");
+}
+
+
diff --git a/src/cmd/tbl/t8.c b/src/cmd/tbl/t8.c
new file mode 100644
index 00000000..92277e2a
--- /dev/null
+++ b/src/cmd/tbl/t8.c
@@ -0,0 +1,367 @@
+/* t8.c: write out one line of output table */
+# include "t.h"
+# define realsplit ((ct=='a'||ct=='n') && table[nl][c].rcol)
+int watchout;
+int once;
+
+void
+putline(int i, int nl)
+ /* i is line number for deciding format */
+ /* nl is line number for finding data usually identical */
+{
+ int c, lf, ct, form, lwid, vspf, ip, cmidx, exvspen, vforml;
+ int vct, chfont, uphalf;
+ char *s, *size, *fn, *rct;
+
+ cmidx = watchout = vspf = exvspen = 0;
+ if (i == 0)
+ once = 0;
+ if (i == 0 && ( allflg || boxflg || dboxflg))
+ fullwide(0, dboxflg ? '=' : '-');
+ if (instead[nl] == 0 && fullbot[nl] == 0)
+ for (c = 0; c < ncol; c++) {
+ s = table[nl][c].col;
+ if (s == 0)
+ continue;
+ if (vspen(s)) {
+ for (ip = nl; ip < nlin; ip = next(ip))
+ if (!vspen(s = table[ip][c].col))
+ break;
+ if ((int)s > 0 && (int)s < 128)
+ Bprint(&tabout, ".ne \\n(%c|u+\\n(.Vu\n", (int)s);
+ continue;
+ }
+ if (point(s))
+ continue;
+ Bprint(&tabout, ".ne \\n(%c|u+\\n(.Vu\n", (int)s);
+ watchout = 1;
+ }
+ if (linestop[nl])
+ Bprint(&tabout, ".mk #%c\n", linestop[nl] + 'a' - 1);
+ lf = prev(nl);
+ if (instead[nl]) {
+ Bprint(&tabout, "%s\n", instead[nl]);
+ return;
+ }
+ if (fullbot[nl]) {
+ switch (ct = fullbot[nl]) {
+ case '=':
+ case '-':
+ fullwide(nl, ct);
+ }
+ return;
+ }
+ for (c = 0; c < ncol; c++) {
+ if (instead[nl] == 0 && fullbot[nl] == 0)
+ if (vspen(table[nl][c].col))
+ vspf = 1;
+ if (lf >= 0)
+ if (vspen(table[lf][c].col))
+ vspf = 1;
+ }
+ if (vspf) {
+ Bprint(&tabout, ".nr #^ \\n(\\*(#du\n");
+ Bprint(&tabout, ".nr #- \\n(#^\n"); /* current line position relative to bottom */
+ }
+ vspf = 0;
+ chfont = 0;
+ for (c = 0; c < ncol; c++) {
+ s = table[nl][c].col;
+ if (s == 0)
+ continue;
+ chfont |= (int)(font[c][stynum[nl]]);
+ if (point(s) )
+ continue;
+ lf = prev(nl);
+ if (lf >= 0 && vspen(table[lf][c].col))
+ Bprint(&tabout,
+ ".if (\\n(%c|+\\n(^%c-1v)>\\n(#- .nr #- +(\\n(%c|+\\n(^%c-\\n(#--1v)\n",
+ (int)s, 'a' + c, (int)s, 'a' + c);
+ else
+ Bprint(&tabout,
+ ".if (\\n(%c|+\\n(#^-1v)>\\n(#- .nr #- +(\\n(%c|+\\n(#^-\\n(#--1v)\n",
+ (int)s, (int)s);
+ }
+ if (allflg && once > 0 )
+ fullwide(i, '-');
+ once = 1;
+ runtabs(i, nl);
+ if (allh(i) && !pr1403) {
+ Bprint(&tabout, ".nr %d \\n(.v\n", SVS);
+ Bprint(&tabout, ".vs \\n(.vu-\\n(.sp\n");
+ Bprint(&tabout, ".nr 35 \\n(.vu\n");
+ } else
+ Bprint(&tabout, ".nr 35 1m\n");
+ if (chfont)
+ Bprint(&tabout, ".nr %2d \\n(.f\n", S1);
+ Bprint(&tabout, "\\&");
+ vct = 0;
+ for (c = 0; c < ncol; c++) {
+ uphalf = 0;
+ if (watchout == 0 && i + 1 < nlin && (lf = left(i, c, &lwid)) >= 0) {
+ tohcol(c);
+ drawvert(lf, i, c, lwid);
+ vct += 2;
+ }
+ if (rightl && c + 1 == ncol)
+ continue;
+ vforml = i;
+ for (lf = prev(nl); lf >= 0 && vspen(table[lf][c].col); lf = prev(lf))
+ vforml = lf;
+ form = ctype(vforml, c);
+ if (form != 's') {
+ rct = reg(c, CLEFT);
+ if (form == 'a')
+ rct = reg(c, CMID);
+ if (form == 'n' && table[nl][c].rcol && lused[c] == 0)
+ rct = reg(c, CMID);
+ Bprint(&tabout, "\\h'|\\n(%2su'", rct);
+ }
+ s = table[nl][c].col;
+ fn = font[c][stynum[vforml]];
+ size = csize[c][stynum[vforml]];
+ if (*size == 0)
+ size = 0;
+ if ((flags[c][stynum[nl]] & HALFUP) != 0 && pr1403 == 0)
+ uphalf = 1;
+ switch (ct = ctype(vforml, c)) {
+ case 'n':
+ case 'a':
+ if (table[nl][c].rcol) {
+ if (lused[c]) /*Zero field width*/ {
+ ip = prev(nl);
+ if (ip >= 0)
+ if (vspen(table[ip][c].col)) {
+ if (exvspen == 0) {
+ Bprint(&tabout, "\\v'-(\\n(\\*(#du-\\n(^%cu", c + 'a');
+ if (cmidx)
+/* code folded from here */
+ Bprint(&tabout, "-((\\n(#-u-\\n(^%cu)/2u)", c + 'a');
+/* unfolding */
+ vct++;
+ if (pr1403) /* must round to whole lines */
+/* code folded from here */
+ Bprint(&tabout, "/1v*1v");
+/* unfolding */
+ Bprint(&tabout, "'");
+ exvspen = 1;
+ }
+ }
+ Bprint(&tabout, "%c%c", F1, F2);
+ if (uphalf)
+ Bprint(&tabout, "\\u");
+ puttext(s, fn, size);
+ if (uphalf)
+ Bprint(&tabout, "\\d");
+ Bprint(&tabout, "%c", F1);
+ }
+ s = table[nl][c].rcol;
+ form = 1;
+ break;
+ }
+ case 'c':
+ form = 3;
+ break;
+ case 'r':
+ form = 2;
+ break;
+ case 'l':
+ form = 1;
+ break;
+ case '-':
+ case '=':
+ if (real(table[nl][c].col))
+ fprint(2, "%s: line %d: Data ignored on table line %d\n", ifile, iline - 1, i + 1);
+ makeline(i, c, ct);
+ continue;
+ default:
+ continue;
+ }
+ if (realsplit ? rused[c] : used[c]) /*Zero field width*/ {
+ /* form: 1 left, 2 right, 3 center adjust */
+ if (ifline(s)) {
+ makeline(i, c, ifline(s));
+ continue;
+ }
+ if (filler(s)) {
+ Bprint(&tabout, "\\l'|\\n(%2su\\&%s'", reg(c, CRIGHT), s + 2);
+ continue;
+ }
+ ip = prev(nl);
+ cmidx = (flags[c][stynum[nl]] & (CTOP | CDOWN)) == 0;
+ if (ip >= 0)
+ if (vspen(table[ip][c].col)) {
+ if (exvspen == 0) {
+ Bprint(&tabout, "\\v'-(\\n(\\*(#du-\\n(^%cu", c + 'a');
+ if (cmidx)
+ Bprint(&tabout, "-((\\n(#-u-\\n(^%cu)/2u)", c + 'a');
+ vct++;
+ if (pr1403) /* round to whole lines */
+ Bprint(&tabout, "/1v*1v");
+ Bprint(&tabout, "'");
+ }
+ }
+ Bprint(&tabout, "%c", F1);
+ if (form != 1)
+ Bprint(&tabout, "%c", F2);
+ if (vspen(s))
+ vspf = 1;
+ else
+ {
+ if (uphalf)
+ Bprint(&tabout, "\\u");
+ puttext(s, fn, size);
+ if (uphalf)
+ Bprint(&tabout, "\\d");
+ }
+ if (form != 2)
+ Bprint(&tabout, "%c", F2);
+ Bprint(&tabout, "%c", F1);
+ }
+ ip = prev(nl);
+ if (ip >= 0)
+ if (vspen(table[ip][c].col)) {
+ exvspen = (c + 1 < ncol) && vspen(table[ip][c+1].col) &&
+ (topat[c] == topat[c+1]) &&
+ (cmidx == (flags[c+1] [stynum[nl]] & (CTOP | CDOWN) == 0))
+ && (left(i, c + 1, &lwid) < 0);
+ if (exvspen == 0) {
+ Bprint(&tabout, "\\v'(\\n(\\*(#du-\\n(^%cu", c + 'a');
+ if (cmidx)
+ Bprint(&tabout, "-((\\n(#-u-\\n(^%cu)/2u)", c + 'a');
+ vct++;
+ if (pr1403) /* round to whole lines */
+ Bprint(&tabout, "/1v*1v");
+ Bprint(&tabout, "'");
+ }
+ }
+ else
+ exvspen = 0;
+ /* if lines need to be split for gcos here is the place for a backslash */
+ if (vct > 7 && c < ncol) {
+ Bprint(&tabout, "\n.sp-1\n\\&");
+ vct = 0;
+ }
+ }
+ Bprint(&tabout, "\n");
+ if (allh(i) && !pr1403)
+ Bprint(&tabout, ".vs \\n(%du\n", SVS);
+ if (watchout)
+ funnies(i, nl);
+ if (vspf) {
+ for (c = 0; c < ncol; c++)
+ if (vspen(table[nl][c].col) && (nl == 0 || (lf = prev(nl)) < 0 ||
+ !vspen(table[lf][c].col))) {
+ Bprint(&tabout, ".nr ^%c \\n(#^u\n", 'a' + c);
+ topat[c] = nl;
+ }
+ }
+}
+
+
+void
+puttext(char *s, char *fn, char *size)
+{
+ if (point(s)) {
+ putfont(fn);
+ putsize(size);
+ Bprint(&tabout, "%s", s);
+ if (*fn > 0)
+ Bprint(&tabout, "\\f\\n(%2d", S1);
+ if (size != 0)
+ putsize("0");
+ }
+}
+
+
+void
+funnies(int stl, int lin)
+{
+ /* write out funny diverted things */
+ int c, s, pl, lwid, dv, lf, ct;
+ char *fn, *ss;
+
+ Bprint(&tabout, ".mk ##\n"); /* rmember current vertical position */
+ Bprint(&tabout, ".nr %d \\n(##\n", S1); /* bottom position */
+ for (c = 0; c < ncol; c++) {
+ ss = table[lin][c].col;
+ if (point(ss))
+ continue;
+ if (ss == 0)
+ continue;
+ s = (int)ss;
+ Bprint(&tabout, ".sp |\\n(##u-1v\n");
+ Bprint(&tabout, ".nr %d ", SIND);
+ ct = 0;
+ for (pl = stl; pl >= 0 && !isalpha(ct = ctype(pl, c)); pl = prev(pl))
+ ;
+ switch (ct) {
+ case 'n':
+ case 'c':
+ Bprint(&tabout, "(\\n(%2su+\\n(%2su-\\n(%c-u)/2u\n", reg(c, CLEFT),
+ reg(c - 1 + ctspan(lin, c), CRIGHT),
+ s);
+ break;
+ case 'l':
+ Bprint(&tabout, "\\n(%2su\n", reg(c, CLEFT));
+ break;
+ case 'a':
+ Bprint(&tabout, "\\n(%2su\n", reg(c, CMID));
+ break;
+ case 'r':
+ Bprint(&tabout, "\\n(%2su-\\n(%c-u\n", reg(c, CRIGHT), s);
+ break;
+ }
+ Bprint(&tabout, ".in +\\n(%du\n", SIND);
+ fn = font[c][stynum[stl]];
+ putfont(fn);
+ pl = prev(stl);
+ if (stl > 0 && pl >= 0 && vspen(table[pl][c].col)) {
+ Bprint(&tabout, ".sp |\\n(^%cu\n", 'a' + c);
+ if ((flags[c][stynum[stl]] & (CTOP | CDOWN)) == 0) {
+ Bprint(&tabout, ".nr %d \\n(#-u-\\n(^%c-\\n(%c|+1v\n",
+ TMP, 'a' + c, s);
+ Bprint(&tabout, ".if \\n(%d>0 .sp \\n(%du/2u", TMP, TMP);
+ if (pr1403) /* round */
+ Bprint(&tabout, "/1v*1v");
+ Bprint(&tabout, "\n");
+ }
+ }
+ Bprint(&tabout, ".%c+\n", s);
+ Bprint(&tabout, ".in -\\n(%du\n", SIND);
+ if (*fn > 0)
+ putfont("P");
+ Bprint(&tabout, ".mk %d\n", S2);
+ Bprint(&tabout, ".if \\n(%d>\\n(%d .nr %d \\n(%d\n", S2, S1, S1, S2);
+ }
+ Bprint(&tabout, ".sp |\\n(%du\n", S1);
+ for (c = dv = 0; c < ncol; c++) {
+ if (stl + 1 < nlin && (lf = left(stl, c, &lwid)) >= 0) {
+ if (dv++ == 0)
+ Bprint(&tabout, ".sp -1\n");
+ tohcol(c);
+ dv++;
+ drawvert(lf, stl, c, lwid);
+ }
+ }
+ if (dv)
+ Bprint(&tabout, "\n");
+}
+
+
+void
+putfont(char *fn)
+{
+ if (fn && *fn)
+ Bprint(&tabout, fn[1] ? "\\f(%.2s" : "\\f%.2s", fn);
+}
+
+
+void
+putsize(char *s)
+{
+ if (s && *s)
+ Bprint(&tabout, "\\s%s", s);
+}
+
+
diff --git a/src/cmd/tbl/t9.c b/src/cmd/tbl/t9.c
new file mode 100644
index 00000000..bf1978a9
--- /dev/null
+++ b/src/cmd/tbl/t9.c
@@ -0,0 +1,76 @@
+/* t9.c: write lines for tables over 200 lines */
+# include "t.h"
+static useln;
+
+void
+yetmore(void)
+{
+ for (useln = 0; useln < MAXLIN && table[useln] == 0; useln++)
+ ;
+ if (useln >= MAXLIN)
+ error("Wierd. No data in table.");
+ table[0] = table[useln];
+ for (useln = nlin - 1; useln >= 0 && (fullbot[useln] || instead[useln]); useln--)
+ ;
+ if (useln < 0)
+ error("Wierd. No real lines in table.");
+ domore(leftover);
+ while (gets1(cstore = cspace, MAXCHS) && domore(cstore))
+ ;
+ last = cstore;
+ return;
+}
+
+
+int
+domore(char *dataln)
+{
+ int icol, ch;
+
+ if (prefix(".TE", dataln))
+ return(0);
+ if (dataln[0] == '.' && !isdigit(dataln[1])) {
+ Bprint(&tabout, "%s\n", dataln);
+ return(1);
+ }
+ fullbot[0] = 0;
+ instead[0] = (char *)0;
+ if (dataln[1] == 0)
+ switch (dataln[0]) {
+ case '_':
+ fullbot[0] = '-';
+ putline(useln, 0);
+ return(1);
+ case '=':
+ fullbot[0] = '=';
+ putline(useln, 0);
+ return(1);
+ }
+ for (icol = 0; icol < ncol; icol++) {
+ table[0][icol].col = dataln;
+ table[0][icol].rcol = 0;
+ for (; (ch = *dataln) != '\0' && ch != tab; dataln++)
+ ;
+ *dataln++ = '\0';
+ switch (ctype(useln, icol)) {
+ case 'n':
+ table[0][icol].rcol = maknew(table[0][icol].col);
+ break;
+ case 'a':
+ table[0][icol].rcol = table[0][icol].col;
+ table[0][icol].col = "";
+ break;
+ }
+ while (ctype(useln, icol + 1) == 's') /* spanning */
+ table[0][++icol].col = "";
+ if (ch == '\0')
+ break;
+ }
+ while (++icol < ncol)
+ table[0][icol].col = "";
+ putline(useln, 0);
+ exstore = exspace; /* reuse space for numerical items */
+ return(1);
+}
+
+
diff --git a/src/cmd/tbl/tb.c b/src/cmd/tbl/tb.c
new file mode 100644
index 00000000..5cc59880
--- /dev/null
+++ b/src/cmd/tbl/tb.c
@@ -0,0 +1,101 @@
+/* tb.c: check which entries exist, also storage allocation */
+# include "t.h"
+
+void
+checkuse(void)
+{
+ int i, c, k;
+
+ for (c = 0; c < ncol; c++) {
+ used[c] = lused[c] = rused[c] = 0;
+ for (i = 0; i < nlin; i++) {
+ if (instead[i] || fullbot[i])
+ continue;
+ k = ctype(i, c);
+ if (k == '-' || k == '=')
+ continue;
+ if ((k == 'n' || k == 'a')) {
+ rused[c] |= real(table[i][c].rcol);
+ if ( !real(table[i][c].rcol))
+ used[c] |= real(table[i][c].col);
+ if (table[i][c].rcol)
+ lused[c] |= real(table[i][c].col);
+ } else
+ used[c] |= real(table[i][c].col);
+ }
+ }
+}
+
+
+int
+real(char *s)
+{
+ if (s == 0)
+ return(0);
+ if (!point(s))
+ return(1);
+ if (*s == 0)
+ return(0);
+ return(1);
+}
+
+
+int spcount = 0;
+# define MAXVEC 20
+char *spvecs[MAXVEC];
+
+char *
+chspace(void)
+{
+ char *pp;
+
+ if (spvecs[spcount])
+ return(spvecs[spcount++]);
+ if (spcount >= MAXVEC)
+ error("Too many characters in table");
+ spvecs[spcount++] = pp = calloc(MAXCHS + MAXLINLEN, 1);
+ if (pp == (char *) - 1 || pp == (char *)0)
+ error("no space for characters");
+ return(pp);
+}
+
+
+# define MAXPC 50
+char *thisvec;
+int tpcount = -1;
+char *tpvecs[MAXPC];
+
+int *
+alocv(int n)
+{
+ int *tp, *q;
+
+ if (tpcount < 0 || thisvec + n > tpvecs[tpcount] + MAXCHS) {
+ tpcount++;
+ if (tpvecs[tpcount] == 0) {
+ tpvecs[tpcount] = calloc(MAXCHS, 1);
+ }
+ thisvec = tpvecs[tpcount];
+ if (thisvec == (char *)0)
+ error("no space for vectors");
+ }
+ tp = (int *)thisvec;
+ thisvec += n;
+ for (q = tp; q < (int *)thisvec; q++)
+ *q = 0;
+ return(tp);
+}
+
+
+void
+release(void)
+{
+ /* give back unwanted space in some vectors */
+ /* this should call free; it does not because
+ alloc() is so buggy */
+ spcount = 0;
+ tpcount = -1;
+ exstore = 0;
+}
+
+
diff --git a/src/cmd/tbl/tc.c b/src/cmd/tbl/tc.c
new file mode 100644
index 00000000..635dbf8a
--- /dev/null
+++ b/src/cmd/tbl/tc.c
@@ -0,0 +1,65 @@
+/* tc.c: find character not in table to delimit fields */
+# include "t.h"
+
+void
+choochar(void)
+{
+ /* choose funny characters to delimit fields */
+ int had[128], ilin, icol, k;
+ char *s;
+
+ for (icol = 0; icol < 128; icol++)
+ had[icol] = 0;
+ F1 = F2 = 0;
+ for (ilin = 0; ilin < nlin; ilin++) {
+ if (instead[ilin])
+ continue;
+ if (fullbot[ilin])
+ continue;
+ for (icol = 0; icol < ncol; icol++) {
+ k = ctype(ilin, icol);
+ if (k == 0 || k == '-' || k == '=')
+ continue;
+ s = table[ilin][icol].col;
+ if (point(s))
+ while (*s)
+ had[*s++] = 1;
+ s = table[ilin][icol].rcol;
+ if (point(s))
+ while (*s)
+ had[*s++] = 1;
+ }
+ }
+ /* choose first funny character */
+ for (
+ s = "\002\003\005\006\007!%&#/?,:;<=>@`^~_{}+-*ABCDEFGHIJKMNOPQRSTUVWXYZabcdefgjkoqrstwxyz";
+ *s; s++) {
+ if (had[*s] == 0) {
+ F1 = *s;
+ had[F1] = 1;
+ break;
+ }
+ }
+ /* choose second funny character */
+ for (
+ s = "\002\003\005\006\007:_~^`@;,<=>#%&!/?{}+-*ABCDEFGHIJKMNOPQRSTUVWXZabcdefgjkoqrstuwxyz";
+ *s; s++) {
+ if (had[*s] == 0) {
+ F2 = *s;
+ break;
+ }
+ }
+ if (F1 == 0 || F2 == 0)
+ error("couldn't find characters to use for delimiters");
+ return;
+}
+
+
+int
+point(char *s)
+{
+ int ss = (int)s;
+ return(ss >= 128 || ss < 0);
+}
+
+
diff --git a/src/cmd/tbl/te.c b/src/cmd/tbl/te.c
new file mode 100644
index 00000000..3df8d445
--- /dev/null
+++ b/src/cmd/tbl/te.c
@@ -0,0 +1,75 @@
+/* te.c: error message control, input line count */
+# include "t.h"
+
+void
+error(char *s)
+{
+ fprint(2, "\n%s:%d: %s\n", ifile, iline, s);
+ fprint(2, "tbl quits\n");
+ exits(s);
+}
+
+
+char *
+gets1(char *s, int size)
+{
+ char *p, *ns;
+ int nbl;
+
+ iline++;
+ ns = s;
+ p = Brdline(tabin, '\n');
+ while (p == 0) {
+ if (swapin() == 0)
+ return(0);
+ p = Brdline(tabin, '\n');
+ }
+ nbl = Blinelen(tabin)-1;
+ if(nbl >= size)
+ error("input buffer too small");
+ p[nbl] = 0;
+ strcpy(s, p);
+ s += nbl;
+ for (nbl = 0; *s == '\\' && s > ns; s--)
+ nbl++;
+ if (linstart && nbl % 2) /* fold escaped nl if in table */
+ gets1(s + 1, size - (s-ns));
+
+ return(p);
+}
+
+
+# define BACKMAX 500
+char backup[BACKMAX];
+char *backp = backup;
+
+void
+un1getc(int c)
+{
+ if (c == '\n')
+ iline--;
+ *backp++ = c;
+ if (backp >= backup + BACKMAX)
+ error("too much backup");
+}
+
+
+int
+get1char(void)
+{
+ int c;
+ if (backp > backup)
+ c = *--backp;
+ else
+ c = Bgetc(tabin);
+ if (c == 0) /* EOF */ {
+ if (swapin() == 0)
+ error("unexpected EOF");
+ c = Bgetc(tabin);
+ }
+ if (c == '\n')
+ iline++;
+ return(c);
+}
+
+
diff --git a/src/cmd/tbl/tf.c b/src/cmd/tbl/tf.c
new file mode 100644
index 00000000..3791c32f
--- /dev/null
+++ b/src/cmd/tbl/tf.c
@@ -0,0 +1,74 @@
+/* tf.c: save and restore fill mode around table */
+# include "t.h"
+
+void
+savefill(void)
+{
+ /* remembers various things: fill mode, vs, ps in mac 35 (SF) */
+ Bprint(&tabout, ".de %d\n", SF);
+ Bprint(&tabout, ".ps \\n(.s\n");
+ Bprint(&tabout, ".vs \\n(.vu\n");
+ Bprint(&tabout, ".in \\n(.iu\n");
+ Bprint(&tabout, ".if \\n(.u .fi\n");
+ Bprint(&tabout, ".if \\n(.j .ad\n");
+ Bprint(&tabout, ".if \\n(.j=0 .na\n");
+ Bprint(&tabout, "..\n");
+ Bprint(&tabout, ".nf\n");
+ /* set obx offset if useful */
+ Bprint(&tabout, ".nr #~ 0\n");
+ Bprint(&tabout, ".if \\n(.T .if n .nr #~ 0.6n\n");
+}
+
+
+void
+rstofill(void)
+{
+ Bprint(&tabout, ".%d\n", SF);
+}
+
+
+void
+endoff(void)
+{
+ int i;
+
+ for (i = 0; i < MAXHEAD; i++)
+ if (linestop[i])
+ Bprint(&tabout, ".nr #%c 0\n", linestop[i] + 'a' - 1);
+ for (i = 0; i < texct; i++)
+ Bprint(&tabout, ".rm %c+\n", texstr[i]);
+ Bprint(&tabout, "%s\n", last);
+}
+
+
+void
+ifdivert(void)
+{
+ Bprint(&tabout, ".ds #d .d\n");
+ Bprint(&tabout, ".if \\(ts\\n(.z\\(ts\\(ts .ds #d nl\n");
+}
+
+
+void
+saveline(void)
+{
+ Bprint(&tabout, ".if \\n+(b.=1 .nr d. \\n(.c-\\n(c.-1\n");
+ linstart = iline;
+}
+
+
+void
+restline(void)
+{
+ Bprint(&tabout, ".if \\n-(b.=0 .nr c. \\n(.c-\\n(d.-%d\n", iline - linstart);
+ linstart = 0;
+}
+
+
+void
+cleanfc(void)
+{
+ Bprint(&tabout, ".fc\n");
+}
+
+
diff --git a/src/cmd/tbl/tg.c b/src/cmd/tbl/tg.c
new file mode 100644
index 00000000..6abb1490
--- /dev/null
+++ b/src/cmd/tbl/tg.c
@@ -0,0 +1,81 @@
+/* tg.c: process included text blocks */
+# include "t.h"
+
+int
+gettext(char *sp, int ilin, int icol, char *fn, char *sz)
+{
+ /* get a section of text */
+ char line[4096];
+ int oname;
+ char *vs;
+
+ if (texname == 0)
+ error("Too many text block diversions");
+ if (textflg == 0) {
+ Bprint(&tabout, ".nr %d \\n(.lu\n", SL); /* remember old line length */
+ textflg = 1;
+ }
+ Bprint(&tabout, ".eo\n");
+ Bprint(&tabout, ".am %s\n", reg(icol, CRIGHT));
+ Bprint(&tabout, ".br\n");
+ Bprint(&tabout, ".di %c+\n", texname);
+ rstofill();
+ if (fn && *fn)
+ Bprint(&tabout, ".nr %d \\n(.f\n.ft %s\n", S1, fn);
+ Bprint(&tabout, ".ft \\n(.f\n"); /* protect font */
+ vs = vsize[icol][stynum[ilin]];
+ if ((sz && *sz) || (vs && *vs)) {
+ Bprint(&tabout, ".nr %d \\n(.v\n", S9);
+ if (vs == 0 || *vs == 0)
+ vs = "\\n(.s+2";
+ if (sz && *sz)
+ Bprint(&tabout, ".ps %s\n", sz);
+ Bprint(&tabout, ".vs %s\n", vs);
+ Bprint(&tabout, ".if \\n(%du>\\n(.vu .sp \\n(%du-\\n(.vu\n", S9, S9);
+ }
+ if (cll[icol][0])
+ Bprint(&tabout, ".ll %sn\n", cll[icol]);
+ else
+ Bprint(&tabout, ".ll \\n(%du*%du/%du\n", SL, ctspan(ilin, icol), ncol + 1);
+ Bprint(&tabout, ".if \\n(.l<\\n(%2s .ll \\n(%2su\n", reg(icol, CRIGHT),
+ reg(icol, CRIGHT));
+ if (ctype(ilin, icol) == 'a')
+ Bprint(&tabout, ".ll -2n\n");
+ Bprint(&tabout, ".in 0\n");
+ while (gets1(line, sizeof(line))) {
+ if (line[0] == 'T' && line[1] == '}' && line[2] == tab)
+ break;
+ if (match("T}", line))
+ break;
+ Bprint(&tabout, "%s\n", line);
+ }
+ if (fn && *fn)
+ Bprint(&tabout, ".ft \\n(%d\n", S1);
+ if (sz && *sz)
+ Bprint(&tabout, ".br\n.ps\n.vs\n");
+ Bprint(&tabout, ".br\n");
+ Bprint(&tabout, ".di\n");
+ Bprint(&tabout, ".nr %c| \\n(dn\n", texname);
+ Bprint(&tabout, ".nr %c- \\n(dl\n", texname);
+ Bprint(&tabout, "..\n");
+ Bprint(&tabout, ".ec \\\n");
+ /* copy remainder of line */
+ if (line[2])
+ tcopy (sp, line + 3);
+ else
+ *sp = 0;
+ oname = texname;
+ texname = texstr[++texct];
+ return(oname);
+}
+
+
+void
+untext(void)
+{
+ rstofill();
+ Bprint(&tabout, ".nf\n");
+ Bprint(&tabout, ".ll \\n(%du\n", SL);
+}
+
+
diff --git a/src/cmd/tbl/ti.c b/src/cmd/tbl/ti.c
new file mode 100644
index 00000000..ef995bbb
--- /dev/null
+++ b/src/cmd/tbl/ti.c
@@ -0,0 +1,75 @@
+/* ti.c: classify line intersections */
+# include "t.h"
+/* determine local environment for intersections */
+
+int
+interv(int i, int c)
+{
+ int ku, kl;
+
+ if (c >= ncol || c == 0) {
+ if (dboxflg) {
+ if (i == 0)
+ return(BOT);
+ if (i >= nlin)
+ return(TOP);
+ return(THRU);
+ }
+ if (c >= ncol)
+ return(0);
+ }
+ ku = i > 0 ? lefdata(i - 1, c) : 0;
+ if (i + 1 >= nlin && allh(i))
+ kl = 0;
+ else
+ kl = lefdata(allh(i) ? i + 1 : i, c);
+ if (ku == 2 && kl == 2)
+ return(THRU);
+ if (ku == 2)
+ return(TOP);
+ if (kl == BOT)
+ return(2);
+ return(0);
+}
+
+
+int
+interh(int i, int c)
+{
+ int kl, kr;
+
+ if (fullbot[i] == '=' || (dboxflg && (i == 0 || i >= nlin - 1))) {
+ if (c == ncol)
+ return(LEFT);
+ if (c == 0)
+ return(RIGHT);
+ return(THRU);
+ }
+ if (i >= nlin)
+ return(0);
+ kl = c > 0 ? thish (i, c - 1) : 0;
+ if (kl <= 1 && i > 0 && allh(up1(i)))
+ kl = c > 0 ? thish(up1(i), c - 1) : 0;
+ kr = thish(i, c);
+ if (kr <= 1 && i > 0 && allh(up1(i)))
+ kr = c > 0 ? thish(up1(i), c) : 0;
+ if (kl == '=' && kr == '=')
+ return(THRU);
+ if (kl == '=')
+ return(LEFT);
+ if (kr == '=')
+ return(RIGHT);
+ return(0);
+}
+
+
+int
+up1(int i)
+{
+ i--;
+ while (instead[i] && i > 0)
+ i--;
+ return(i);
+}
+
+
diff --git a/src/cmd/tbl/tm.c b/src/cmd/tbl/tm.c
new file mode 100644
index 00000000..8fa4e497
--- /dev/null
+++ b/src/cmd/tbl/tm.c
@@ -0,0 +1,65 @@
+/* tm.c: split numerical fields */
+# include "t.h"
+
+char *
+maknew(char *str)
+{
+ /* make two numerical fields */
+ int dpoint, c;
+ char *p, *q, *ba;
+
+ p = str;
+ for (ba = 0; c = *str; str++)
+ if (c == '\\' && *(str + 1) == '&')
+ ba = str;
+ str = p;
+ if (ba == 0) {
+ for (dpoint = 0; *str; str++) {
+ if (*str == '.' && !ineqn(str, p) &&
+ (str > p && digit(*(str - 1)) ||
+ digit(*(str + 1))))
+ dpoint = (int)str;
+ }
+ if (dpoint == 0)
+ for (; str > p; str--) {
+ if (digit( *(str - 1) ) && !ineqn(str, p))
+ break;
+ }
+ if (!dpoint && p == str) /* not numerical, don't split */
+ return(0);
+ if (dpoint)
+ str = (char *)dpoint;
+ } else
+ str = ba;
+ p = str;
+ if (exstore == 0 || exstore > exlim) {
+ exstore = exspace = chspace();
+ exlim = exstore + MAXCHS;
+ }
+ q = exstore;
+ while (*exstore++ = *str++)
+ ;
+ *p = 0;
+ return(q);
+}
+
+
+int
+ineqn (char *s, char *p)
+{
+ /* true if s is in a eqn within p */
+ int ineq = 0, c;
+
+ while (c = *p) {
+ if (s == p)
+ return(ineq);
+ p++;
+ if ((ineq == 0) && (c == delim1))
+ ineq = 1;
+ else if ((ineq == 1) && (c == delim2))
+ ineq = 0;
+ }
+ return(0);
+}
+
+
diff --git a/src/cmd/tbl/tr.c b/src/cmd/tbl/tr.c
new file mode 100644
index 00000000..7d586e8b
--- /dev/null
+++ b/src/cmd/tbl/tr.c
@@ -0,0 +1,28 @@
+# include "t.h"
+/* tr.c: number register allocation */
+char *nregs[] = {
+ /* this array must have at least 3*qcol entries
+ or illegal register names will result */
+ "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
+ "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+ "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
+ "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+ "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
+ "90", "91", "92", "93", "94", "95", "96", "97", "4q", "4r",
+ "4s", "4t", "4u", "4v", "4w", "4x", "4y", "4z", "4;", "4.",
+ "4a", "4b", "4c", "4d", "4e", "4f", "4g", "4h", "4i", "4j",
+ "4k", "4l", "4m", "4n", "4o", "4p", "5a", "5b", "5c", "5d",
+ "5e", "5f", "5g", "5h", "5i", "5j", "5k", "5l", "5m", "5n",
+ "5o", "5p", "5q", "5r", "5s", "5t", "5u", "5v", "5w", "5x",
+ 0};
+
+
+char *
+reg(int col, int place)
+{
+ if (sizeof(nregs) < 2 * 3 * qcol)
+ error("Too many columns for registers");
+ return (nregs[qcol*place+col]);
+}
+
+
diff --git a/src/cmd/tbl/ts.c b/src/cmd/tbl/ts.c
new file mode 100644
index 00000000..43cc84ec
--- /dev/null
+++ b/src/cmd/tbl/ts.c
@@ -0,0 +1,71 @@
+/* ts.c: minor string processing subroutines */
+#include "t.h"
+
+int
+match (char *s1, char *s2)
+{
+ while (*s1 == *s2)
+ if (*s1++ == '\0')
+ return(1);
+ else
+ s2++;
+ return(0);
+}
+
+
+int
+prefix(char *small, char *big)
+{
+ int c;
+
+ while ((c = *small++) == *big++)
+ if (c == 0)
+ return(1);
+ return(c == 0);
+}
+
+
+int
+letter (int ch)
+{
+ if (ch >= 'a' && ch <= 'z')
+ return(1);
+ if (ch >= 'A' && ch <= 'Z')
+ return(1);
+ return(0);
+}
+
+
+int
+numb(char *str)
+{
+ /* convert to integer */
+ int k;
+ for (k = 0; *str >= '0' && *str <= '9'; str++)
+ k = k * 10 + *str - '0';
+ return(k);
+}
+
+
+int
+digit(int x)
+{
+ return(x >= '0' && x <= '9');
+}
+
+
+int
+max(int a, int b)
+{
+ return( a > b ? a : b);
+}
+
+
+void
+tcopy (char *s, char *t)
+{
+ while (*s++ = *t++)
+ ;
+}
+
+
diff --git a/src/cmd/tbl/tt.c b/src/cmd/tbl/tt.c
new file mode 100644
index 00000000..96270b75
--- /dev/null
+++ b/src/cmd/tbl/tt.c
@@ -0,0 +1,127 @@
+/* tt.c: subroutines for drawing horizontal lines */
+# include "t.h"
+
+int
+ctype(int il, int ic)
+{
+ if (instead[il])
+ return(0);
+ if (fullbot[il])
+ return(0);
+ il = stynum[il];
+ return(style[ic][il]);
+}
+
+
+int
+min(int a, int b)
+{
+ return(a < b ? a : b);
+}
+
+
+int
+fspan(int i, int c)
+{
+ c++;
+ return(c < ncol && ctype(i, c) == 's');
+}
+
+
+int
+lspan(int i, int c)
+{
+ int k;
+
+ if (ctype(i, c) != 's')
+ return(0);
+ c++;
+ if (c < ncol && ctype(i, c) == 's')
+ return(0);
+ for (k = 0; ctype(i, --c) == 's'; k++)
+ ;
+ return(k);
+}
+
+
+int
+ctspan(int i, int c)
+{
+ int k;
+ c++;
+ for (k = 1; c < ncol && ctype(i, c) == 's'; k++)
+ c++;
+ return(k);
+}
+
+
+void
+tohcol(int ic)
+{
+ if (ic == 0)
+ Bprint(&tabout, "\\h'|0'");
+ else
+ Bprint(&tabout, "\\h'(|\\n(%2su+|\\n(%2su)/2u'", reg(ic, CLEFT),
+ reg(ic - 1, CRIGHT));
+}
+
+
+int
+allh(int i)
+{
+ /* return true if every element in line i is horizontal */
+ /* also at least one must be horizontl */
+ int c, one, k;
+
+ if (fullbot[i])
+ return(1);
+ if (i >= nlin)
+ return(dboxflg || boxflg);
+ for (one = c = 0; c < ncol; c++) {
+ k = thish(i, c);
+ if (k == 0)
+ return(0);
+ if (k == 1)
+ continue;
+ one = 1;
+ }
+ return(one);
+}
+
+
+int
+thish(int i, int c)
+{
+ int t;
+ char *s;
+ struct colstr *pc;
+
+ if (c < 0)
+ return(0);
+ if (i < 0)
+ return(0);
+ t = ctype(i, c);
+ if (t == '_' || t == '-')
+ return('-');
+ if (t == '=')
+ return('=');
+ if (t == '^')
+ return(1);
+ if (fullbot[i] )
+ return(fullbot[i]);
+ if (t == 's')
+ return(thish(i, c - 1));
+ if (t == 0)
+ return(1);
+ pc = &table[i][c];
+ s = (t == 'a' ? pc->rcol : pc->col);
+ if (s == 0 || (point(s) && *s == 0))
+ return(1);
+ if (vspen(s))
+ return(1);
+ if (t = barent( s))
+ return(t);
+ return(0);
+}
+
+
diff --git a/src/cmd/tbl/tu.c b/src/cmd/tbl/tu.c
new file mode 100644
index 00000000..217869e1
--- /dev/null
+++ b/src/cmd/tbl/tu.c
@@ -0,0 +1,257 @@
+/* tu.c: draws horizontal lines */
+# include "t.h"
+
+void
+makeline(int i, int c, int lintype)
+{
+ int cr, type, shortl;
+
+ type = thish(i, c);
+ if (type == 0)
+ return;
+ shortl = (table[i][c].col[0] == '\\');
+ if (c > 0 && !shortl && thish(i, c - 1) == type)
+ return;
+ if (shortl == 0)
+ for (cr = c; cr < ncol && (ctype(i, cr) == 's' || type == thish(i, cr)); cr++)
+ ;
+ else
+ for (cr = c + 1; cr < ncol && ctype(i, cr) == 's'; cr++)
+ ;
+ drawline(i, c, cr - 1, lintype, 0, shortl);
+}
+
+
+void
+fullwide(int i, int lintype)
+{
+ int cr, cl;
+
+ if (!pr1403)
+ Bprint(&tabout, ".nr %d \\n(.v\n.vs \\n(.vu-\\n(.sp\n", SVS);
+ cr = 0;
+ while (cr < ncol) {
+ cl = cr;
+ while (i > 0 && vspand(prev(i), cl, 1))
+ cl++;
+ for (cr = cl; cr < ncol; cr++)
+ if (i > 0 && vspand(prev(i), cr, 1))
+ break;
+ if (cl < ncol)
+ drawline(i, cl, (cr < ncol ? cr - 1 : cr), lintype, 1, 0);
+ }
+ Bprint(&tabout, "\n");
+ if (!pr1403)
+ Bprint(&tabout, ".vs \\n(%du\n", SVS);
+}
+
+
+void
+drawline(int i, int cl, int cr, int lintype, int noheight, int shortl)
+{
+ char *exhr, *exhl, *lnch;
+ int lcount, ln, linpos, oldpos, nodata;
+
+ lcount = 0;
+ exhr = exhl = "";
+ switch (lintype) {
+ case '-':
+ lcount = 1;
+ break;
+ case '=':
+ lcount = pr1403 ? 1 : 2;
+ break;
+ case SHORTLINE:
+ lcount = 1;
+ break;
+ }
+ if (lcount <= 0)
+ return;
+ nodata = cr - cl >= ncol || noheight || allh(i);
+ if (!nodata)
+ Bprint(&tabout, "\\v'-.5m'");
+ for (ln = oldpos = 0; ln < lcount; ln++) {
+ linpos = 2 * ln - lcount + 1;
+ if (linpos != oldpos)
+ Bprint(&tabout, "\\v'%dp'", linpos - oldpos);
+ oldpos = linpos;
+ if (shortl == 0) {
+ tohcol(cl);
+ if (lcount > 1) {
+ switch (interv(i, cl)) {
+ case TOP:
+ exhl = ln == 0 ? "1p" : "-1p";
+ break;
+ case BOT:
+ exhl = ln == 1 ? "1p" : "-1p";
+ break;
+ case THRU:
+ exhl = "1p";
+ break;
+ }
+ if (exhl[0])
+ Bprint(&tabout, "\\h'%s'", exhl);
+ } else if (lcount == 1) {
+ switch (interv(i, cl)) {
+ case TOP:
+ case BOT:
+ exhl = "-1p";
+ break;
+ case THRU:
+ exhl = "1p";
+ break;
+ }
+ if (exhl[0])
+ Bprint(&tabout, "\\h'%s'", exhl);
+ }
+ if (lcount > 1) {
+ switch (interv(i, cr + 1)) {
+ case TOP:
+ exhr = ln == 0 ? "-1p" : "+1p";
+ break;
+ case BOT:
+ exhr = ln == 1 ? "-1p" : "+1p";
+ break;
+ case THRU:
+ exhr = "-1p";
+ break;
+ }
+ } else if (lcount == 1) {
+ switch (interv(i, cr + 1)) {
+ case TOP:
+ case BOT:
+ exhr = "+1p";
+ break;
+ case THRU:
+ exhr = "-1p";
+ break;
+ }
+ }
+ } else
+ Bprint(&tabout, "\\h'|\\n(%2su'", reg(cl, CLEFT));
+ Bprint(&tabout, "\\s\\n(%d", LSIZE);
+ if (linsize)
+ Bprint(&tabout, "\\v'-\\n(%dp/6u'", LSIZE);
+ if (shortl)
+ Bprint(&tabout, "\\l'|\\n(%2su'", reg(cr, CRIGHT));
+ else
+ {
+ lnch = "\\(ul";
+ if (pr1403)
+ lnch = lintype == 2 ? "=" : "\\(ru";
+ if (cr + 1 >= ncol)
+ Bprint(&tabout, "\\l'|\\n(TWu%s%s'", exhr, lnch);
+ else
+ Bprint(&tabout, "\\l'(|\\n(%2su+|\\n(%2su)/2u%s%s'", reg(cr, CRIGHT),
+ reg(cr + 1, CLEFT), exhr, lnch);
+ }
+ if (linsize)
+ Bprint(&tabout, "\\v'\\n(%dp/6u'", LSIZE);
+ Bprint(&tabout, "\\s0");
+ }
+ if (oldpos != 0)
+ Bprint(&tabout, "\\v'%dp'", -oldpos);
+ if (!nodata)
+ Bprint(&tabout, "\\v'+.5m'");
+}
+
+
+void
+getstop(void)
+{
+ int i, c, k, junk, stopp;
+
+ stopp = 1;
+ for (i = 0; i < MAXLIN; i++)
+ linestop[i] = 0;
+ for (i = 0; i < nlin; i++)
+ for (c = 0; c < ncol; c++) {
+ k = left(i, c, &junk);
+ if (k >= 0 && linestop[k] == 0)
+ linestop[k] = ++stopp;
+ }
+ if (boxflg || allflg || dboxflg)
+ linestop[0] = 1;
+}
+
+
+int
+left(int i, int c, int *lwidp)
+{
+ int kind, li, lj;
+ /* returns -1 if no line to left */
+ /* returns number of line where it starts */
+ /* stores into lwid the kind of line */
+ *lwidp = 0;
+ if (i < 0)
+ return(-1);
+ kind = lefdata(i, c);
+ if (kind == 0)
+ return(-1);
+ if (i + 1 < nlin)
+ if (lefdata(next(i), c) == kind)
+ return(-1);
+ li = i;
+ while (i >= 0 && lefdata(i, c) == kind)
+ i = prev(li = i);
+ if (prev(li) == -1)
+ li = 0;
+ *lwidp = kind;
+ for (lj = i + 1; lj < li; lj++)
+ if (instead[lj] && strcmp(instead[lj], ".TH") == 0)
+ return(li);
+ for (i = i + 1; i < li; i++)
+ if (fullbot[i])
+ li = i;
+ return(li);
+}
+
+
+int
+lefdata(int i, int c)
+{
+ int ck;
+
+ if (i >= nlin)
+ i = nlin - 1;
+ if (ctype(i, c) == 's') {
+ for (ck = c; ctype(i, ck) == 's'; ck--)
+ ;
+ if (thish(i, ck) == 0)
+ return(0);
+ }
+ i = stynum[i];
+ i = lefline[c][i];
+ if (i > 0)
+ return(i);
+ if (dboxflg && c == 0)
+ return(2);
+ if (allflg)
+ return(1);
+ if (boxflg && c == 0)
+ return(1);
+ return(0);
+}
+
+
+int
+next(int i)
+{
+ while (i + 1 < nlin) {
+ i++;
+ if (!fullbot[i] && !instead[i])
+ break;
+ }
+ return(i);
+}
+
+
+int
+prev(int i)
+{
+ while (--i >= 0 && (fullbot[i] || instead[i]))
+ ;
+ return(i);
+}
+
+
diff --git a/src/cmd/tbl/tv.c b/src/cmd/tbl/tv.c
new file mode 100644
index 00000000..19a4b03f
--- /dev/null
+++ b/src/cmd/tbl/tv.c
@@ -0,0 +1,183 @@
+/* tv.c: draw vertical lines */
+# include "t.h"
+
+void
+drawvert(int start, int end, int c, int lwid)
+{
+ char *exb = 0, *ext = 0;
+ int tp = 0, sl, ln, pos, epb, ept, vm;
+
+ end++;
+ vm = 'v';
+ /* note: nr 35 has value of 1m outside of linesize */
+ while (instead[end])
+ end++;
+ for (ln = 0; ln < lwid; ln++) {
+ epb = ept = 0;
+ pos = 2 * ln - lwid + 1;
+ if (pos != tp)
+ Bprint(&tabout, "\\h'%dp'", pos - tp);
+ tp = pos;
+ if (end < nlin) {
+ if (fullbot[end] || (!instead[end] && allh(end)))
+ epb = 2;
+ else
+ switch (midbar(end, c)) {
+ case '-':
+ exb = "1v-.5m";
+ break;
+ case '=':
+ exb = "1v-.5m";
+ epb = 1;
+ break;
+ }
+ }
+ if (lwid > 1)
+ switch (interh(end, c)) {
+ case THRU:
+ epb -= 1;
+ break;
+ case RIGHT:
+ epb += (ln == 0 ? 1 : -1);
+ break;
+ case LEFT:
+ epb += (ln == 1 ? 1 : -1);
+ break;
+ }
+ if (lwid == 1)
+ switch (interh(end, c)) {
+ case THRU:
+ epb -= 1;
+ break;
+ case RIGHT:
+ case LEFT:
+ epb += 1;
+ break;
+ }
+ if (start > 0) {
+ sl = start - 1;
+ while (sl >= 0 && instead[sl])
+ sl--;
+ if (sl >= 0 && (fullbot[sl] || allh(sl)))
+ ept = 0;
+ else if (sl >= 0)
+ switch (midbar(sl, c)) {
+ case '-':
+ ext = ".5m";
+ break;
+ case '=':
+ ext = ".5m";
+ ept = -1;
+ break;
+ default:
+ vm = 'm';
+ break;
+ }
+ else
+ ept = -4;
+ } else if (start == 0 && allh(0)) {
+ ept = 0;
+ vm = 'm';
+ }
+ if (lwid > 1)
+ switch (interh(start, c)) {
+ case THRU:
+ ept += 1;
+ break;
+ case LEFT:
+ ept += (ln == 0 ? 1 : -1);
+ break;
+ case RIGHT:
+ ept += (ln == 1 ? 1 : -1);
+ break;
+ }
+ else if (lwid == 1)
+ switch (interh(start, c)) {
+ case THRU:
+ ept += 1;
+ break;
+ case LEFT:
+ case RIGHT:
+ ept -= 1;
+ break;
+ }
+ if (exb)
+ Bprint(&tabout, "\\v'%s'", exb);
+ if (epb)
+ Bprint(&tabout, "\\v'%dp'", epb);
+ Bprint(&tabout, "\\s\\n(%d", LSIZE);
+ if (linsize)
+ Bprint(&tabout, "\\v'-\\n(%dp/6u'", LSIZE);
+ Bprint(&tabout, "\\h'-\\n(#~u'"); /* adjustment for T450 nroff boxes */
+ Bprint(&tabout, "\\L'|\\n(#%cu-%s", linestop[start] + 'a' - 1,
+ vm == 'v' ? "1v" : "\\n(35u");
+ if (ext)
+ Bprint(&tabout, "-(%s)", ext);
+ if (exb)
+ Bprint(&tabout, "-(%s)", exb);
+ pos = ept - epb;
+ if (pos)
+ Bprint(&tabout, "%s%dp", pos >= 0 ? "+" : "", pos);
+ /* the string #d is either "nl" or ".d" depending
+ on diversions; on GCOS not the same */
+ Bprint(&tabout, "'\\s0\\v'\\n(\\*(#du-\\n(#%cu+%s",
+ linestop[start] + 'a' - 1, vm == 'v' ? "1v" : "\\n(35u");
+ if (ext)
+ Bprint(&tabout, "+%s", ext);
+ if (ept)
+ Bprint(&tabout, "%s%dp", (-ept) > 0 ? "+" : "", (-ept));
+ Bprint(&tabout, "'");
+ if (linsize)
+ Bprint(&tabout, "\\v'\\n(%dp/6u'", LSIZE);
+ }
+}
+
+
+int
+midbar(int i, int c)
+{
+ int k;
+
+ k = midbcol(i, c);
+ if (k == 0 && c > 0)
+ k = midbcol(i, c - 1);
+ return(k);
+}
+
+
+int
+midbcol(int i, int c)
+{
+ int ct;
+
+ while ( (ct = ctype(i, c)) == 's')
+ c--;
+ if (ct == '-' || ct == '=')
+ return(ct);
+ if (ct = barent(table[i][c].col))
+ return(ct);
+ return(0);
+}
+
+
+int
+barent(char *s)
+{
+ if (s == 0)
+ return (1);
+ if (!point(s))
+ return(0);
+ if (s[0] == '\\')
+ s++;
+ if (s[1] != 0)
+ return(0);
+ switch (s[0]) {
+ case '_':
+ return('-');
+ case '=':
+ return('=');
+ }
+ return(0);
+}
+
+
diff --git a/src/cmd/troff/FIXES b/src/cmd/troff/FIXES
new file mode 100644
index 00000000..5765832c
--- /dev/null
+++ b/src/cmd/troff/FIXES
@@ -0,0 +1,821 @@
+March 11, 1994
+
+ If we are just plain old nroff (and not doing UNICODE) we should
+ only Lookup characters, not Install when we don't know them.
+ If we are troff, we Install them anyway
+
+March 8, 1994
+
+ Nroff had problems with parsing quoted white space as options or
+ character code in some terminals tables. Changed by having scanf
+ include white space when necessary as suggested by Rich.
+
+March 1, 1994
+
+ Made sanity check for terminal type depending on the trace level;
+ trace level set with -tn flag at start up
+
+22 Feb, 1994
+
+ More pointer shuffling fixes.
+
+18 Feb, 1994
+
+ More disabling of multibyte stuff. Fixed bug in n5.c: casetm didn'
+ know about the new format in the fontables.
+
+Feb 17, 1994
+
+ Removed extra include <setlocale> from n1.c
+
+ Fixed dubious pointer shuffling in n7.c, t10.c & n8.c. Thanks Rich!
+
+Feb 10, 1994
+
+ Disabled the multybyte stuff; only plan 9 will get it.
+
+Jan 24, 1994
+
+ Fixed nasty bug discovered by td, which caused core dumps on
+ \D'l-0.002775i 0i' and apparently all numbers closer to 0
+ than -.002775. Fixed in storeline() and storeword() (n7.c).
+
+Dec 16, 1993
+
+ nroff & troff -N were looking for the TYPESETTER variable, causing
+
+ troff: cannot open /sys/lib/troff/term/tab.202; line 1, file stdin
+
+ fixed my moving getenv("TYPESETTER") to t10.c in t_ptinit(void).
+
+Dec 3, 1993:
+
+ The sequence \s+2\H'+10' came sometimes out in the wrong order
+ (x H before s), so there wasn't a difference bewteen \s+2\H'+10'
+ and \H'+10'\s+2. Now the fonts bits of the CHARHT are used to
+ register the current pontsize, so we can issue a s10 in t10.c
+ if needed. A bit sneaky.
+
+ Try to prevent double slashes in path names. Especially under
+ plan9 things started to look ugly.
+
+ Exception word list now grows dynamic.
+
+Nov 30, 1993:
+
+ Allow multiple calls to .pi, requested by Rob.
+ .pi cat
+ .pi dogs
+ is now equivalent with
+ .pi cat | dogs
+
+
+ .ab now takes also optional error code:
+ .ab [n] [string]
+ If n and string, n is exit code, string is message
+ If n, n is exit code, ``User Abort, exit code n" is message
+ If !n and string, standard exit code, string is message
+ If !n and ! string, standard exit code, "User Abort" is message
+
+Nov 24, 1993:
+
+ Reordered code to keep the UNASNI scripts happy.
+
+ Nroff dumped core reading terminal tables: apparenty under plan 9,
+ scanf includes the '\n'; added test for '\0' in parse in n10.c.
+
+ Relative tab settings (.ta +1C +2C) didn't work; anding the
+ previous value with TABMASK fixes this (caseta).
+
+Nov 23, 1993:
+
+ Included code, originally done by bwk for plan 9, to handle
+ multi-byte characters.
+
+Nov 3, 1993:
+
+ ``pair internal'' two char names by shifting 16 bits. Will allow
+ the use of 16 bit characters sets (Unicode in plan9 etc.) for
+ macro's etc.
+
+Oct 20, 1993:
+
+ Word & line buffers are now dynamic: No more word or line overflow
+ unless when we run out of memory.
+
+Oct 11, 1993:
+
+ lost diversion warning pops up regularly with man macro's. Due
+ to a possible macro coding problem. Triggered by something like
+ troff -man:
+ .TP
+ .TP
+ foo
+ .ex
+ Minimal code:
+ .di aa
+ throw away this diversion (aa) while being defined.
+ .rm aa
+ .br
+ .di
+
+ Fixed by disallowing .rm to throw away current diversion. The
+ rn request will complain with:
+
+ cannot remove diversion aa during definition; etc.
+
+Sep 29, 1993:
+
+ Some long standing fixes which never went back in the source.
+ Thanks to Janet & Rich.
+
+Sep 28, 1993:
+
+ Changed getach() (n1.c), so it does't consider truncated
+ special characters as (8-bit) ascii. STX ETX ENQ ACK and BELL
+ are still allowed for the ultimate backwards compatibility.
+
+ Some code changes, so real ANSI compilers like the SGI version
+ (acc from Sun is a poor excuse for an ANSI compiler) don't
+ barf. Some compromises (static Tchar wbuf in n9.c) allowed so
+ the unansified stuff for non-ansi compilers (cc on Sun's) will
+ work as well.
+
+Sep 9, 1993:
+
+ Be nice to Gerard. Now also word spaces in .tl and after
+ tabs/fleids etc.
+
+Aug 12, 1993:
+
+ Tabs setting can now be humongous. We also allow 99 tabs to
+ accomodate tbl. As a side effect, NTM buffers are now 1K
+
+Aug 11, 1993:
+
+ .R register, now contains maximum number of addessable
+ registers minus the number actually used.
+
+ Small esthetic changes in error messages; removed a statement
+ which wasn't reached anyway.
+
+Aug 10, 1993:
+
+ Some more speed hacks: be smarter doing the linear table
+ lookups in alloc() and finds().
+
+ The real name of the det diversion size macro is now gd.
+
+Aug 9, 1993:
+
+ A much faster way to find the end of a string/macro, by
+ remembering that when defined.
+
+Aug 6, 1993:
+
+ Slightly more eficient way of skipping to the end of a
+ string/macro
+
+Aug 5, 1993:
+
+ Prevent character sign extension for 8-bit charnames diversions
+ etc. by unpair
+
+Aug 4, 1993:
+
+ Growing the dynamical macro/strings name space and registers
+ space (See the experiment of 21 July) now with bigger
+ increments. Casts added to satisfy non-ANSI compilers.
+
+Aug 3, 1993:
+
+ Should check return value in alloc (n3.c), to prevent core dump
+ when memory gets tight.
+
+July 28, 1993:
+
+ New request: .sg <div> sets the dn and dl registers to the size
+ of the diversion named in the argument. Doesn't do anything
+ when the named diversion doesn't exist. The name sg is
+ temporary until we find a better one.
+
+July 21, 1993:
+
+ Experiment: Macro space & registers name allocated
+ dynamically. Note that current reallocation occurs in
+ increments of 1, to force the code to be executed a lot; a kind
+ of stress testing. Also, eight bit characters allowed in
+ macro/string names.
+
+July 21, 1993:
+
+ Turn on the escape mode if the end macro is called.
+
+July 20, 1993:
+
+ Tracing mode now default off
+
+ Don't print s stackdump either when a file specfied on the
+ command line argument cannot be opened
+
+July 15, 1993:
+
+ Don't print useless line & current file informations when a
+ file specfied on the command line argument cannot be opened.
+
+ Sun ansi compiler doesn't default adhere to standards. Undid
+ the kludge in tdef.h
+
+July 14, 1993:
+
+ Coding error made the tab type R not function properly
+
+July 12, 1993:
+
+ Fixed a typo in the version stuff, noticed by Rich
+
+July 9, 1993:
+
+ Added the dwb home configuration stuff, thanks RIch. Also,
+ NCHARS is big enough. Added a fflush to casetm, so .fm <file>
+ will be up to date.
+
+June 25, 1993 (Rich):
+
+ -t option
+
+ reinstated for the sake of compatibility. Some old
+ shells scripts and man(1) from SunOs want this, sigh
+
+ Compiler and system dependencies
+
+ Some systems pull in sys/types.h via #include <time.h> and then
+ the compiler complains about two ushort typedefs. Therefore,
+ ushort is now Ushort (and uchar Uchar).
+
+ The SVID specifies a strdup, POSIX doesn't, anyway, troff
+ provides its own version, slightly different then the standard
+ one. A To prevent name clashes with that definion, renamed to
+ strdupl.
+
+June 24, 1993 (Rich):
+
+ -V option added for DWB3.4 (rich)
+
+May 18, 1993:
+
+ Trivial fix (.cf) request for troff -a
+
+ issuing
+
+ .cf /dev/null
+
+ with troff -a gives some spurious output:
+
+ H720
+ H720
+ s10
+ f1
+
+ fixed by checking for ascii mode it ptesc(), ptps() and
+ ptfont() in t10.c
+
+
+ Enhancement
+
+ Added a .tm request to roff. Works just like .tm, but now
+ it will do it to file. The name is coined by Carmela. Great
+ for creating indeces & toc's (we hope).
+
+May 18 1993:
+
+ Compatibilty change
+
+ Somebody complained that his favorite macro didn't work:
+ it had a BELL (^G) in the name. This was a non-documented
+ feature of earlier versions of troff (although the
+ documentation actually doesn't say that you can. (They can
+ only be used for delimiters or with the tr request), so it
+ isn't that important).
+
+ But the sake of eternal backward compatibilaty I allowed
+ some control characters like, STX, ACK, etc. also be part
+ of a macro/string name.
+
+ While at it, I made it also possible to have eight bit
+ characters be part of the name. It might be that this screws
+ up the way users think about these things. For UNICODE
+ versions, they probably want to do that as well, and that
+ won't work as easy, (because these characters are 16-bits
+ wide), so it is dubious whether we actually want this.
+
+ BTW. Now
+
+ .de \(ts\ts
+ .tm terminal sigma macro
+ ..
+ .\(ts\(ts
+
+ also works, as long the internal cookie for ts isn't more then
+ eight bits.
+
+May 12, 1993:
+
+ Syntax change
+
+ Some requests accept tabs as a separator, some don't and
+ this can be a nuisance. Now a tab is also recognized as
+ an argument separator for requests, this makes
+
+ .so /dev/null
+
+ works.
+
+ To be more precise, any motion character is allowed, so
+
+ .so\h'5i'/dev/null
+
+ will work as well, if one really wants that.
+
+ It will be a problem for users who really relied on this as in
+
+ .ds x string
+
+ and expect the tab to become part of the string a, but I haven't
+ seen any use of that (obscure trick).
+
+May 6, 1993:
+
+ Eileen count fixed
+
+ Troff sometimes went in a loop, and exited with: ``job
+ looping; check abuse of macros'' (also known as the Eileen's
+ loop). It can be forced with the next trivial programme:
+
+ .de ff
+ .di xx
+ ..
+ .wh -1 ff
+ .bp
+
+ Basically what happens is that a page transition now will
+ happen in a diversion, which doesn't make sense. Wat really
+ happens is that eject() (in n7.c) doesn't eject the frame
+ because we are in a diversion. This cause the loop in n1.c
+ (because now always stack->pname <= ejl). Adding check on
+ whether we are not in a diversion takes care of the problem.
+
+March 30, 1993:
+
+ Need request, .ne
+
+ When there is a begin of page trap set, and the first thing
+ in the file is a .ne request, the trap gets fired, but,
+ the x font R etc. cookies doen't come out, because the
+ troff thinks that the first page pseudo transition already
+ took place. Fixed by forcing the start of the first page
+ in the casene request with the same code as in casetl (which
+ caused a similar problem quite some time ago).
+
+ Change to .cf request ``Here document''
+
+ If the argument of .cf starts with a <<, the rest of it is taken
+ as an EOF token. It will reat the rest of the input until it hits
+ the EOF token and copies it to the output. This is similar as
+ the shell's ``here document'' mechanisme and put in place to
+ improve the kludgy way picasso, picpack etc. now include
+ postscript.
+
+ Using troff -TLatin1 (DWB version) and \N'...' caused core dump
+
+ In t11, in chadd, it should test on NCHARS - ALPHABET to see
+ whether we run out of table space (and we probably should beaf
+ up NCHARS for the DWB version).
+
+March 16, 1993:
+
+ Diversion rename bug fix
+
+ It is possible to get troff in an infinite loop by renaming a
+ diversion in progress, and calling it later with the
+ new name (as in .di xx, .rn xx yy, .yy). The effect depends on
+ whether troff already put stuff in the diversion or not.
+
+ Fix by having .rn also rename the current diversion (if
+ there is any and when appropriate). If the diversion calls
+ itself by the new name and given the fix made on 11 nov
+ 1992, this will now result in an error. (BTW, the fix from
+ 11 nov is improved: diversions nest, so we have to account
+ for that).
+
+December 18, 1992:
+ Some people have complete novels as comments, so we need
+ to skip comments while checking the legality of font files.
+ thaks Rixh
+
+December 16, 1992
+
+ Some people rely on the order that -r arguments are given,
+ so that troff -rC1 -rC3 ends up setting register C to 3.
+ Because cpushback() pushes things in a LIFO order back, we
+ have to do the same to get -r args in a FIFO order.
+
+Nov 17, 1992:
+
+ Giving a -rL8 option cuased the string .nr L 8 to be printed
+ on the output, using the wonderful 3b2. Some garbage was
+ left in buf[100] in main(). Fixed by setting buf[0] explicitly
+ to 0 (because some C-compilers complain about ``no automatic
+ aggregate initialization'').
+
+Nov 11, 1992:
+
+ Diversion bug fix
+
+ If a diversion was being read and the input is faulty so
+ the diversion was reading in itself, it caused troff to
+ loop undefinitely. This was easily fixed by a test in
+ control(a,b) in n1.c.
+
+ Something similar things might happen with macros causing
+ the ``eileenct problem'', but I didn't look for that. We
+ have to wait until it happens.
+
+Oct 26, 1992:
+
+ Numeric arguments:
+
+ Illegal argments are treated as missing arguments. This
+ changed the semantics of .ll, .ls, .in, .lg, .ul, .cu .lt
+ (which acted as if the argument was 0) and .ps which was
+ simply ignored with an illegal argument.
+
+ Tidied up number parsing in atoi1(). This prevents arguments
+ like .x or 1.2.3.4 being interpret as a legal number (nonumb = 0)
+
+ Numeric arguments error reporting:
+
+ Controlled by .pt, illegal numbers are now reported (default
+ trace mode is 1). This is also true for the escapes:
+ \h'..', \v'..' \H'..', \S'..', \N'..', \D'..', \l'.., \L'..
+ and \x'..'.
+
+ \D'c' is the only drawing request which doesn't take a pair
+ of numbers as arguments, so a special case is put here in
+ setdraw() (This code actually could use an overhaul to get
+ better parsing. As long as the \D'..' cookies are machine
+ generated it is low on the priority list).
+
+ Don't generate an error if the illegal argument to a request
+ is a \}. It is too painful to do right (although it can be
+ done, but it would clutter getch() and getcho() even more).
+
+ Input line numbers (.c register) bug fixes:
+
+ In not taken branches of .if or .ie, the input line #
+ (numtab[CD].val) should be raised when necessary (in eatblk()).
+
+ For concealed newlines, we still should count the line for input.
+
+ Setfield (n9.c) sometimes pushes the rest of the line back to
+ the input (including \n), without adjusting numtab[CD].val
+
+ Because .c (and so numtab[CD].val) is the number of lines read
+ and the error might actually happen in the current line
+ (before seeing the '\n), we need to apply correction in
+ errprint when nlflg set. (This correction needs to be undone
+ when inside a macro because the nlflg is set by reading the
+ args to the macro).
+
+ Line number setting (.lf) request bug fixes:
+
+ I interpret that the .c register will contain the number of
+ read lines, not including the current one.
+
+ Also, don't change the input line number when the first
+ argument of .lf is not a number.
+
+ As a net effect, the next input
+
+ .EQ
+ .EN
+ .ab
+
+ will generate the same output whether eqn has been used or not.
+
+ If request bug fix:
+
+ A ``.if page .tm foo'' caused the next line being ignored;
+ This bcause when the 2nd delimiter of a string couldn't be
+ found in cmpstr, the next line was always eaten. Solution:
+ in caseif1, if the condition is false, we should check
+ nlflg before eating a block. (Note: We might have eaten
+ \{\ as well. We could disallow the \{\ in a string to be
+ compared to prevent that but that might break other things).
+
+ Enhancement to .pt:
+
+ The .pt now pops the previous values when no argument is
+ specified. Turned out to be handy when chasing for problems.
+ Just ``bracked'' the code with .pt 7 and .pt and you get
+ a trace of only that block. The meaning of the arguments
+ is now:
+ 01 trace numeric arguments (default on)
+ 02 trace requests
+ 04 trace macros
+
+ Abort request (.ab) beautification:
+
+ Don't print the extra carriage return when .ab is called
+ without an argument.
+
+Oct 12, 1992:
+
+ (Comments & spelling errors from this day on by jaap)
+
+ replaced 32767 by INT_MAX in several places to allow for very
+ long pages (on 32-but machines).
+
+ The ``.fp 1 R \"COMMENT'' complains about ``./troff: Can't
+ open font file /usr/lib/font/devpost/h'' on some systems. It
+ sees the tab as part of the optional font file. Apparently it
+ is system dependent whether isgraph() includes the tab
+ character. Fixed by using getach() in getname() in n1.c
+ instead.
+
+Aug 28, 1992:
+ removed call to popi from rdtty(); it was eating up the
+ rest of the macro if it was used from within one. (thanks, jaap)
+
+
+Jul 21, 1992:
+ added extra test in nextfile() to pop current input file
+ only if not in .nx command. thanks to jaap.
+
+ added test in getword() to avoid hyphenating after \z character,
+ which prevents any hyphenation inside \X'...'. thanks to jaap.
+
+ added, then removed, code in getword() to prevent hyphenating
+ anything shorter than 6 characters. looks like it changed a
+ lot more than i thought.
+
+Jul 12, 1992:
+ added .pt request to trace macros and requests (from jaap).
+ .pt N Print trace of macros (N=1), requests (N=2) or both (N=3)
+
+Jun 5, 1992:
+ added tests to t.twrest and t.twinit to avoid 0 deref in
+ n2 and n10, for nroff -t xxxxx. thanks to Rich Drechsler.
+
+May 22, 1992:
+ added extern decls to e.g., void Tchar (*hmot)(void) in tdef.h
+ and added definition to ni.c, so pointers are defined explicitly.
+ makes it work on turbo c++ and probably others.
+
+ changed a couple of isdigit's and isgraph(getch()) to avoid
+ multiple evaluation (even though it shouldn't happen).
+
+ Made /usr/bin/nroff a shell script.
+
+May 12, 1992:
+ n1.c: need p++ after strrchr to skip / in program name.
+ thanks to Rich Drechsler.
+
+Apr 17, 1992:
+ casefi(), n5.c: .u register should be 0 or 1, not incremented
+ with each .fi.
+
+Apr 5, 1992:
+ fiddled n7.c and added _nmwid to the environment, to add a
+ 5th argument to .nm: the maximum number of digits in any
+ line number. default is 3, which was previously hardwired in.
+
+ added jaap's code for yet another register which actually delivers
+ a string, called .S (so it can easily go in the switch in setn()
+ in n4.c); it delivers the current tabstop and alignment modes in
+ a format suitable for a subsequent .ta \n(.S command:
+ .ds T \n(.S
+ ...
+ .ta \*T
+
+Mar 30, 1992:
+ added test in getword to avoid hyphenating things with motions
+ (and avoid a core dump sometimes too).
+
+Mar 13, 1992:
+ \n(sb initialized wrong in setwd().
+
+ TYPESETTER=foo troff -Tpost used foo instead of post.
+
+Mar 12, 1992:
+ rearranged tests in popf so that .so is closed properly before
+ moving on to the next macro package.
+
+Mar 1, 1992:
+ input mechanism rearranged to use getc() instead of stack of
+ explicit input buffers. 5-10% slowdown.
+
+Jan 28, 1992:
+ fixed .tm \(mi to print something sensible. thanks to jaap.
+
+Jan 2, 1992:
+ fiddle setfp so doesn't put out font stuff if -a turned on.
+
+Dec 17, 1991:
+ copy 3rd argument in .fp commands to x font ... lines when it contains
+ a /, for testing fonts locally.
+
+Dec 13, 1991:
+ parameterize the font directories, etc., so can be set in makefiles.
+ added -N argument to run as nroff.
+
+Nov 8, 1991:
+ add a maplow(towlower...) in n8.c to handle brain-damaged libraries.
+
+Nov 2, 1991:
+ merged nroff into troff, based on Ken's plan 9 version.
+ merged nii.c into ni.c, removed tw.h, etc. more work needed
+ to make this stuff cleaner.
+
+July 27, 1991:
+ added test in setn in n4 to fix bug that permitted things like
+ \n (ab to work "properly". thanks to jaap for finding and fixing.
+
+ added paranoid testing in t11 to make sure font files look ok.
+
+May 13, 1991:
+ moved evaluation of \(xx from copy mode to non-copy mode, so that
+ weird character names wouldn't get reevaluated in argument parsing.
+ installed july 27.
+
+May 6, 1991:
+ increased size of hyphenation exception buffer to 512 from 128
+
+Apr 14, 1991:
+ added an extra redundant call of ptfont in setfp, since it appears
+ that some versions of adobe transcript assume that an "x font" command
+ means to change the actual font as well. the fix preserves the current font.
+ thanks to david brailsford and friends for spotting the problem.
+
+ fixed up tests in alpha() in n8 to defend isalpha() against too-big inputs.
+ punct() argument had wrong type too. thanks to rich drexler and peter nelson.
+
+Mar 19, 1991:
+ fixed bug that prevented .rd from working with new corebuf organization.
+
+ fixed bug that caused .ig inside diversions to give bad storage
+ allocation. thanks to arthur david olson, whose fix was on netnews
+ 3 years earlier.
+
+Mar 5, 1991:
+ huge table sizes for kanji.
+
+Feb ??, 1991:
+ working on dealing with large alphabets, notably kanji.
+ added "defaultwidth" to font descriptions, for characters
+ not given an explicit width.
+
+Jan, 1991:
+ added tex hyphenation, using standard tex data files, but not the
+ elaborate compressed trie, which is a lot of trouble to save maybe
+ 40k bytes. this appears to run at exactly the same speed as before.
+
+ so far this stuff reads into a fixed size array; that should change.
+ it should also be possible to deal with multiple languages.
+
+ the command .ha sets the algorithm. .ha 1 => tex, with troff rules
+ if tex doesn't hyphenate; .ha 0 gives troff rules, and .ha resets
+ to the default, which is tex. the hyphenation algorithm is part of
+ the environment, a nod to a future in which i handle more than one
+ language.
+
+ replaced the fixed size corebuf array for string/macro storage by
+ a dynamic structure that can grow.
+
+ this appears to slow things down by maybe 3%. the code is about
+ the same complexity.
+
+Dec 27, 1990:
+ converted to ansi c, based on some work by ken thompson, but not
+ as thoroughly as he did. there is a shell script unansi and an awk
+ program cvt that will help you step back in time if you do not have
+ an ansi c compiler.
+
+ moved the special-name characters up to 256 instead of 128, although
+ done in terms of ALPHABET, so one can pass 8 bit characters through.
+ removed lots of 0177's and similar numbers. input is now not filtered,
+ and if a character with the 8th bit on comes in, it will go out again.
+
+ fixed t11.c to read character names in hex or octal as well as
+ single-character ascii.
+
+ unknown characters are now carried through with width = spacewidth.
+ needs a way to set widths.
+
+ removed all signal handling from troff. you signal, you die.
+
+ added -d option to print version number.
+
+Dec 7, 1990:
+ .fp 3 V VERYLONGNAME used to truncate the name to 10 chars; fixed.
+
+ increased the limit on FBUFSZ for tables with very long fields.
+
+ changed atoi1() to use double to avoid intermediate overflow.
+
+ moved filenames like /usr/lib/font into tdef.h for easy change.
+ removed some dreggish definitions.
+
+ cleaned up non-portable error printing stuff; fixed up some messages.
+
+Dec 12, 1989:
+ Removed the .! command, an undocumented synonym for .sy.
+
+Dec 4, 1989:
+ Another wart to the \X code, to try to preserve blanks in all situations.
+
+Nov 17, 1989:
+ A number of small changes preparatory to getting rid of nroff.
+ The argument -Tnroff or -Tnroff-12 changes some internal values
+ so that the predicate .if n is true and certain arithmetic operations
+ are done as if nroff. This design is not yet final.
+
+Nov 7, 1989:
+ Fixed hyphenation for nov-ice, ad-vice, de-vice, ser-vice, *-vice.
+
+Oct 11, 1989:
+ It is now permitted to do an explicit change to font S.
+ It is not clear what will break (though nothing seems to have).
+
+Oct 10, 1989:
+ Modified flush code to always put out \nH instead of sometimes h.
+ This makes it easier to parse the output for positioning.
+
+Sep 9, 1989:
+ Fixed internal representation of \D'~...' so that it
+ is immune to .tr ~ and variations. No external change.
+
+Aug 9, 1989:
+ Changed .tm so it outputs \e, \%, \-, \&, \(blank).
+ This might break indexing code.
+ Only in the new version, as are all subsequent fixes.
+
+July, 1989:
+ A major internal change: font information is read in ascii
+ instead of the weird binary format of makedev (which is now dead).
+ character names need not all appear in DESC; new names that
+ appear when a font is used become part of the set of known names.
+
+ There are some flaky bits here (it's conceivable that some \N
+ number will collide with a real name), and it's probably 10-15%
+ slower. Tant pis.
+
+ As a by-product, nroff no longer compiles. I'll probably get
+ back to this, but an alternative is to bag it once and for all.
+
+May 25, 1989:
+ Another bug in \l, this time when width is 0. Not installed,
+ since it's in the new font version.
+
+Apr 23, 1989:
+ Fixed bug in n9 that caused core dump with unterminated
+ \l command, like \l'1.5i
+
+ ptflush no longer called when -a is on.
+
+Apr 12, 1989:
+ fixed bug in n2 that failed to suppress printing of \!
+ output when a -o was in effect.
+
+Apr 5, 1989:
+ .fl and \X now cause output of size, font, hpos and vpos.
+ this is necesary for postprocessors that intend to insert
+ independent material, such as postscript.
+
+Feb 1, 1989:
+ wait for .pi pipe to empty before exiting
+
+Oct 2, 1988:
+ default is now -Tpost
+
+Sep 19, 1988:
+ added abortive code to handle built-up characters by
+ passing something through as \D'b...'. never used.
+
+Jul 4, 1988:
+ replaced the sbrk nonsense in n3.c by calls to malloc.
+
+ \N now tests against proper font size.
+
+ installed Jaap Akkerhuis's code (mutatis mutandis) for
+ permitting up to 99 fonts, swapping them into font pos 0
+ as needed. fixes the long-standing problem of having
+ multiple font changes on a single output line.
+
+Jul 2, 1988:
+ \X now preserves spaces even when contents are diverted.
+
+ \N code safer -- NTRTAB and NWIDCACHE enlarged.
+
+Jul 14, 1987:
+ Fixed obscure bug causing incorrect indentation of .mc output.
diff --git a/src/cmd/troff/README b/src/cmd/troff/README
new file mode 100644
index 00000000..6dc8fa09
--- /dev/null
+++ b/src/cmd/troff/README
@@ -0,0 +1,31 @@
+To make troff (actually a.out):
+
+ make
+
+You will also need to write a driver for your favorite output device.
+d202.c provides a model, although it is specialized to a machine no
+one has. There are also a variety of postscript drivers that are the
+best thing to use if you have a postscript device.
+
+You will also have to make a DESC file for your typesetter and some
+font description files; see dev202 for examples. These describe the
+named characters, widths, kerning information, and output codes.
+
+Nroff is the same program as troff, so you should
+
+ cp a.out /usr/bin/troff
+ ln /usr/bin/troff /usr/bin/nroff
+
+or the equivalent.
+
+You will also need terminal description files for your terminals; see
+tab.37, tab.450 and tab.lp for examples.
+
+Troff uses files that are normally stored in /usr/lib/font;
+macro packages are in /usr/lib/tmac; and nroff tables are in
+/usr/lib/term. You can edit tdef.h to change these assumptions.
+
+There have been a few features since the last version, and a number of
+significant internal changes. Not all are improvements, of course.
+Most of the more recent changes, including bug fixes, are in FIXES,
+which you should read also.
diff --git a/src/cmd/troff/cvt b/src/cmd/troff/cvt
new file mode 100644
index 00000000..3426d626
--- /dev/null
+++ b/src/cmd/troff/cvt
@@ -0,0 +1,45 @@
+
+awk '
+
+/^{/ {
+ if (prev != "") {
+ # comments can be trouble (e.g. ffree())
+ if ( (c = match(prev, /\/\*.*\*\/$/)) != 0 ) {
+ comment = substr(prev, c)
+ sub(/\/\*.*\*\/$/, "", prev)
+ } else comment = ""
+
+ x = prev
+
+ # isolate argument list
+ sub(/^[^(]*\(/, "", x)
+ sub(/\)[^)]*$/, "", x)
+
+ # find the names in it
+ n = split(x, args)
+ arglist = ""
+ for (i = 2; i <= n; i += 2)
+ arglist = arglist args[i]
+ gsub(/\(\*f\)\(Tchar\)/, "f", arglist) # special case for n4.c
+ gsub(/\[[0-9]+\]/, "", arglist) # for n8.c
+ gsub(/[*()\[\]]/, "", arglist) # discard noise characters *()[]
+ gsub(/,/, ", ", arglist) # space nicely
+ sub(/\(.*\)/, "(" arglist ")", prev) # reconstruct
+ print prev comment
+
+ # argument declarations
+ gsub(/,/, ";", x)
+ gsub(/\(\*f\)\(Tchar\)/, "(*f)()", x) # special case for n4.c
+ if (x != "")
+ print "\t" x ";"
+ }
+ prev = $0
+ next
+}
+
+{ print prev
+ prev = $0
+}
+
+END { print prev }
+' $*
diff --git a/src/cmd/troff/dwbinit.c b/src/cmd/troff/dwbinit.c
new file mode 100644
index 00000000..a63e5fde
--- /dev/null
+++ b/src/cmd/troff/dwbinit.c
@@ -0,0 +1,313 @@
+/*
+ *
+ * Pathname management routines for DWB C programs.
+ *
+ * Applications should initialize a dwbinit array with the string
+ * pointers and arrays that need to be updated, and then hand that
+ * array to DWBinit before much else happens in their main program.
+ * DWBinit calls DWBhome to get the current home directory. DWBhome
+ * uses the last definition of DWBENV (usually "DWBHOME") in file
+ * DWBCONFIG (e.g., /usr/lib/dwb3.4) or the value assigned to that
+ * variable in the environment if the DWBCONFIG file doesn't exist,
+ * can't be read, or doesn't define DWBENV.
+ *
+ * DWBCONFIG must be a simple shell script - comments, a definition
+ * of DWBHOME, and perhaps an export or echo is about all that's
+ * allowed. The parsing in DWBhome is simple and makes no attempt
+ * to duplicate the shell. It only looks for DWBHOME= as the first
+ * non-white space string on a line, so
+ *
+ * #
+ * # A sample DWBCONFIG shell script
+ * #
+ *
+ * DWBHOME=/usr/add-on/dwb3.4
+ * export DWBHOME
+ *
+ * means DWBhome would return "/usr/add-on/dwb3.4" for the DWB home
+ * directory. A DWBCONFIG file means there can only be one working
+ * copy of a DWB release on a system, which seems like a good idea.
+ * Using DWBCONFIG also means programs will always include correct
+ * versions of files (e.g., prologues or macro packages).
+ *
+ * Relying on an environment variable guarantees nothing. You could
+ * execute a version of dpost, but your environment might point at
+ * incorrect font tables or prologues. Despite the obvious problems
+ * we've also implemented an environment variable approach, but it's
+ * only used if there's no DWBCONFIG file.
+ *
+ * DWBinit calls DWBhome to get the DWB home directory prefix and
+ * then marches through its dwbinit argument, removing the default
+ * home directory and prepending the new home. DWBinit stops when
+ * it reaches an element that has NULL for its address and value
+ * fields. Pointers in a dwbinit array are reallocated and properly
+ * initialized; arrays are simply reinitialized if there's room.
+ * All pathnames that are to be adjusted should be relative. For
+ * example,
+ *
+ * char *fontdir = "lib/font";
+ * char xyzzy[25] = "etc/xyzzy";
+ *
+ * would be represented in a dwbinit array as,
+ *
+ * dwbinit allpaths[] = {
+ * &fontdir, NULL, 0,
+ * NULL, xyzzy, sizeof(xyzzy),
+ * NULL, NULL, 0
+ * };
+ *
+ * The last element must have NULL entries for the address and
+ * value fields. The main() routine would then do,
+ *
+ * #include "dwbinit.h"
+ *
+ * main() {
+ *
+ * DWBinit("program name", allpaths);
+ * ...
+ * }
+ *
+ * Debugging is enabled if DWBDEBUG is in the environment and has
+ * the value ON. Output is occasionally useful and probably should
+ * be documented.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "dwbinit.h"
+
+#ifndef DWBCONFIG
+#define DWBCONFIG "/dev/null"
+#endif
+
+#ifndef DWBENV
+#define DWBENV "DWBHOME"
+#endif
+
+#ifndef DWBHOME
+#define DWBHOME ""
+#endif
+
+#ifndef DWBDEBUG
+#define DWBDEBUG "DWBDEBUG"
+#endif
+
+#ifndef DWBPREFIX
+#define DWBPREFIX "\\*(.P"
+#endif
+
+/*****************************************************************************/
+
+void DWBdebug(dwbinit *ptr, int level)
+{
+
+ char *path;
+ char *home;
+ static char *debug = NULL;
+
+/*
+ *
+ * Debugging output, but only if DWBDEBUG is defined to be ON in the
+ * environment. Dumps general info the first time through.
+ *
+ */
+
+ if ( debug == NULL && (debug = getenv(DWBDEBUG)) == NULL )
+ debug = "OFF";
+
+ if ( strcmp(debug, "ON") == 0 ) {
+ if ( level == 0 ) {
+ fprintf(stderr, "Environment variable: %s\n", DWBENV);
+ fprintf(stderr, "Configuration file: %s\n", DWBCONFIG);
+ fprintf(stderr, "Default home: %s\n", DWBHOME);
+ if ( (home = DWBhome()) != NULL )
+ fprintf(stderr, "Current home: %s\n", home);
+ } /* End if */
+
+ fprintf(stderr, "\n%s pathnames:\n", level == 0 ? "Original" : "Final");
+ for ( ; ptr->value != NULL || ptr->address != NULL; ptr++ ) {
+ if ( (path = ptr->value) == NULL ) {
+ path = *ptr->address;
+ fprintf(stderr, " pointer: %s\n", path);
+ } else fprintf(stderr, " array[%d]: %s\n", ptr->length, path);
+ if ( level == 0 && *path == '/' )
+ fprintf(stderr, " WARNING - absolute path\n");
+ } /* End for */
+ } /* End if */
+
+} /* End of DWBdebug */
+
+/*****************************************************************************/
+
+char *DWBhome(void)
+{
+
+ FILE *fp;
+ char *ptr;
+ char *path;
+ int len;
+ char buf[200];
+ char *home = NULL;
+
+/*
+ *
+ * Return the DWB home directory. Uses the last definition of DWBENV
+ * (usually "DWBHOME") in file DWBCONFIG (perhaps /usr/lib/dwb3.4) or
+ * the value assigned to the variable named by the DWBENV string in
+ * the environment if DWBCONFIG doesn't exist or doesn't define DWBENV.
+ * Skips the file lookup if DWBCONFIG can't be read. Returns NULL if
+ * there's no home directory.
+ *
+ */
+
+ if ( (fp = fopen(DWBCONFIG, "r")) != NULL ) {
+ len = strlen(DWBENV);
+ while ( fgets(buf, sizeof(buf), fp) != NULL ) {
+ for ( ptr = buf; isspace(*ptr); ptr++ ) ;
+ if ( strncmp(ptr, DWBENV, len) == 0 && *(ptr+len) == '=' ) {
+ path = ptr + len + 1;
+ for ( ptr = path; !isspace(*ptr) && *ptr != ';'; ptr++ ) ;
+ *ptr = '\0';
+ if ( home != NULL )
+ free(home);
+ if ( (home = malloc(strlen(path)+1)) != NULL )
+ strcpy(home, path);
+ } /* End if */
+ } /* End while */
+ fclose(fp);
+ } /* End if */
+
+ if ( home == NULL ) {
+ if ( (home = getenv(DWBENV)) == NULL ) {
+ if ( (home = DWBHOME) == NULL || *home == '\0' || *home == ' ' )
+ home = NULL;
+ } /* End if */
+ } /* End if */
+
+ while (home && *home == '/' && *(home +1) == '/') /* remove extra slashes */
+ home++;
+ return(home);
+
+} /* End of DWBhome */
+
+/*****************************************************************************/
+
+void DWBinit(char *prog, dwbinit *paths)
+{
+
+ char *prefix;
+ char *value;
+ char *path;
+ int plen;
+ int length;
+ dwbinit *opaths = paths;
+
+/*
+ *
+ * Adjust the pathnames listed in paths, using the home directory
+ * returned by DWBhome(). Stops when it reaches an element that has
+ * NULL address and value fields. Assumes pathnames are relative,
+ * but changes everything. DWBdebug issues a warning if an original
+ * path begins with a /.
+ *
+ * A non-NULL address refers to a pointer, which is reallocated and
+ * then reinitialized. A NULL address implies a non-NULL value field
+ * and describes a character array that we only reinitialize. The
+ * length field for an array is the size of that array. The length
+ * field of a pointer is an increment that's added to the length
+ * required to store the new pathname string - should help when we
+ * want to change character arrays to pointers in applications like
+ * troff.
+ *
+ */
+
+ if ( (prefix = DWBhome()) == NULL ) {
+ fprintf(stderr, "%s: no DWB home directory\n", prog);
+ exit(1);
+ } /* End if */
+
+ DWBdebug(opaths, 0);
+ plen = strlen(prefix);
+
+ for ( ; paths->value != NULL || paths->address != NULL; paths++ ) {
+ if ( paths->address == NULL ) {
+ length = 0;
+ value = paths->value;
+ } else {
+ length = paths->length;
+ value = *paths->address;
+ } /* End else */
+
+ length += plen + 1 + strlen(value); /* +1 is for the '/' */
+
+ if ( (path = malloc(length+1)) == NULL ) {
+ fprintf(stderr, "%s: can't allocate pathname memory\n", prog);
+ exit(1);
+ } /* End if */
+
+ if ( *value != '\0' ) {
+ char *eop = prefix;
+ while(*eop++)
+ ;
+ eop -= 2;
+ if (*value != '/' && *eop != '/') {
+ sprintf(path, "%s/%s", prefix, value);
+ } else if (*value == '/' && *eop == '/') {
+ value++;
+ sprintf(path, "%s%s", prefix, value);
+ } else
+ sprintf(path, "%s%s", prefix, value);
+ } else
+ sprintf(path, "%s", prefix);
+
+ if ( paths->address == NULL ) {
+ if ( strlen(path) >= paths->length ) {
+ fprintf(stderr, "%s: no room for %s\n", prog, path);
+ exit(1);
+ } /* End if */
+ strcpy(paths->value, path);
+ free(path);
+ } else *paths->address = path;
+ } /* End for */
+
+ DWBdebug(opaths, 1);
+
+} /* End of DWBinit */
+
+/*****************************************************************************/
+
+void DWBprefix( char *prog, char *path, int length)
+{
+
+ char *home;
+ char buf[512];
+ int len = strlen(DWBPREFIX);
+
+/*
+ *
+ * Replace a leading DWBPREFIX string in path by the current DWBhome().
+ * Used by programs that pretend to handle .so requests. Assumes path
+ * is an array with room for length characters. The implementation is
+ * not great, but should be good enough for now. Also probably should
+ * have DWBhome() only do the lookup once, and remember the value if
+ * called again.
+ *
+ */
+
+ if ( strncmp(path, DWBPREFIX, len) == 0 ) {
+ if ( (home = DWBhome()) != NULL ) {
+ if ( strlen(home) + strlen(path+len) < length ) {
+ sprintf(buf, "%s%s", home, path+len);
+ strcpy(path, buf); /* assuming there's room in path */
+ } else fprintf(stderr, "%s: no room to grow path %s", prog, path);
+ } /* End if */
+ } /* End if */
+
+} /* End of DWBprefix */
+
+/*****************************************************************************/
+
diff --git a/src/cmd/troff/dwbinit.h b/src/cmd/troff/dwbinit.h
new file mode 100644
index 00000000..acb1476c
--- /dev/null
+++ b/src/cmd/troff/dwbinit.h
@@ -0,0 +1,19 @@
+/*
+ *
+ * A structure used to adjust pathnames in DWB C code. Pointers
+ * set the address field, arrays use the value field and must
+ * also set length to the number elements in the array. Pointers
+ * are always reallocated and then reinitialized; arrays are only
+ * reinitialized, if there's room.
+ *
+ */
+
+typedef struct {
+ char **address;
+ char *value;
+ int length;
+} dwbinit;
+
+extern void DWBinit(char *, dwbinit *);
+extern char* DWBhome(void);
+extern void DWBprefix(char *, char *, int);
diff --git a/src/cmd/troff/ext.h b/src/cmd/troff/ext.h
new file mode 100644
index 00000000..42147880
--- /dev/null
+++ b/src/cmd/troff/ext.h
@@ -0,0 +1,184 @@
+extern int TROFF;
+
+extern int alphabet;
+extern char **argp;
+extern char *eibuf;
+extern char *ibufp;
+extern char *obufp;
+extern char *unlkp;
+extern char *xbufp;
+extern char *xeibuf;
+extern char cfname[NSO+1][NS];
+extern int trace;
+extern char devname[];
+extern char ibuf[IBUFSZ];
+extern char mfiles[NMF][NS];
+extern char nextf[];
+extern char obuf[];
+extern char termtab[];
+extern char fontdir[];
+extern Font fonts[MAXFONTS+1];
+extern char xbuf[IBUFSZ];
+extern Offset apptr;
+extern Offset ip;
+extern Offset nextb;
+extern Offset offset;
+extern Offset woff;
+extern Numerr numerr;
+extern int *pnp;
+extern int pstab[];
+extern int nsizes;
+extern int app;
+extern int ascii;
+extern int bd;
+extern int bdtab[];
+extern int ccs;
+extern char *chnames[]; /* chnames[n-ALPHABET] -> name of char n */
+extern int copyf;
+extern int cs;
+extern int dfact;
+extern int dfactd;
+extern int diflg;
+extern int dilev;
+extern int donef;
+extern int dotT;
+extern int dpn;
+extern int ds;
+extern int ejf;
+extern int em;
+extern int eqflg;
+extern int error;
+extern int esc;
+extern int eschar;
+extern int ev;
+extern int evi;
+extern int evlist[EVLSZ];
+extern int fc;
+extern int flss;
+extern int fontlab[];
+extern int hflg;
+extern int ibf;
+extern int ifi;
+extern int iflg;
+extern int init;
+extern int lead;
+extern int lg;
+extern int lgf;
+extern int macerr;
+extern int mflg;
+extern int mfont;
+extern int mlist[NTRAP];
+extern int mpts;
+extern int nchnames;
+extern int ndone;
+extern int newmn;
+extern int nflush;
+extern int nfo;
+extern int nfonts;
+extern int nform;
+extern int nhyp;
+extern int nlflg;
+extern int nlist[NTRAP];
+extern int nmfi;
+extern int nonumb;
+extern int noscale;
+extern int npn;
+extern int npnflg;
+extern int nx;
+extern int oldbits;
+extern int oldmn;
+extern int over;
+extern int padc;
+extern int pfont;
+extern int pfrom;
+extern int pipeflg;
+extern int pl;
+extern int pnlist[];
+extern int po1;
+extern int po;
+extern int ppts;
+extern int print;
+extern FILE *ptid;
+extern int pto;
+extern int quiet;
+extern int ralss;
+extern int rargc;
+extern int raw;
+extern int res;
+extern int sbold;
+extern int setwdf;
+extern int sfont;
+extern int smnt;
+extern int stdi;
+extern int stop;
+extern int sv;
+extern int tabch, ldrch;
+extern int tflg;
+extern int totout;
+extern int trap;
+extern Ushort trtab[];
+extern int tty;
+extern int ulfont;
+extern int vflag;
+extern int whichroff;
+extern int widthp;
+extern int xfont;
+extern int xpts;
+extern Stack *ejl;
+extern Stack *frame;
+extern Stack *stk;
+extern Stack *nxf;
+extern Tchar **hyp;
+extern Tchar *olinep;
+extern Tchar pbbuf[NC];
+extern Tchar *pbp;
+extern Tchar *lastpbp;
+extern Tchar ch;
+extern Tchar nrbits;
+extern Tbuf _oline;
+extern Wcache widcache[];
+extern char gchtab[];
+extern Diver d[NDI];
+extern Diver *dip;
+
+
+extern char xchname[];
+extern short xchtab[];
+extern char *codestr;
+extern char *chnamep;
+extern short *chtab;
+extern int nchtab;
+
+extern Numtab *numtabp;
+
+/* these characters are used as various signals or values
+/* in miscellaneous places.
+/* values are set in specnames in t10.c
+*/
+
+extern int c_hyphen;
+extern int c_emdash;
+extern int c_rule;
+extern int c_minus;
+extern int c_fi;
+extern int c_fl;
+extern int c_ff;
+extern int c_ffi;
+extern int c_ffl;
+extern int c_acute;
+extern int c_grave;
+extern int c_under;
+extern int c_rooten;
+extern int c_boxrule;
+extern int c_lefthand;
+extern int c_dagger;
+extern int c_isalnum;
+
+/*
+ * String pointers for DWB pathname management.
+ */
+
+extern char *DWBfontdir;
+extern char *DWBntermdir;
+extern char *DWBalthyphens;
+
diff --git a/src/cmd/troff/find b/src/cmd/troff/find
new file mode 100644
index 00000000..eccbfbdb
--- /dev/null
+++ b/src/cmd/troff/find
@@ -0,0 +1 @@
+grep $1 *.[ch]
diff --git a/src/cmd/troff/fns.h b/src/cmd/troff/fns.h
new file mode 100644
index 00000000..6bd94ada
--- /dev/null
+++ b/src/cmd/troff/fns.h
@@ -0,0 +1,384 @@
+/*
+ * other
+ */
+int pclose(FILE*);
+long filesize(int fd);
+int open(char *, int);
+int read(int, char *, int);
+int lseek(int, long, int);
+int close(int);
+int getpid(void);
+
+/*
+ * c1.c
+ */
+void init0(void);
+void init2(void);
+void cvtime(void);
+void errprint(void);
+int control(int a, int b);
+void casept(void);
+int getrq(void);
+Tchar getch(void);
+void setxon(void);
+Tchar getch0(void);
+Tchar get1ch(FILE *);
+void pushback(Tchar *b);
+void cpushback(char *b);
+int nextfile(void);
+int popf(void);
+void flushi(void);
+int getach(void);
+void casenx(void);
+int getname(void);
+void caseso(void);
+void caself(void);
+void casecf(void);
+void getline(char *s, int n);
+void casesy(void);
+void getpn(char *a);
+void setrpt(void);
+
+/*
+ * n2.c
+ */
+int pchar(Tchar i);
+void pchar1(Tchar i);
+int pchar2(Tchar i);
+int flusho(void);
+void casedone(void);
+void caseex(void);
+void done(int x);
+void done1(int x);
+void done2(int x);
+void done3(int x);
+void edone(int x);
+void casepi(void);
+
+/*
+ * c3.c
+ */
+void blockinit(void);
+char* grow(char *, int, int);
+void mnspace(void);
+void caseig(void);
+void casern(void);
+void maddhash(Contab *rp);
+void munhash(Contab *mp);
+void mrehash(void);
+void caserm(void);
+void caseas(void);
+void caseds(void);
+void caseam(void);
+void casede(void);
+int findmn(int i);
+void clrmn(int i);
+Offset finds(int mn);
+int skip(void);
+int copyb(void);
+void copys(void);
+Offset alloc(void);
+void ffree(Offset i);
+void wbf(Tchar i);
+Tchar rbf(void);
+Tchar popi(void);
+Offset pushi(Offset newip, int mname);
+void* setbrk(int x);
+int getsn(void);
+Offset setstr(void);
+void collect(void);
+void seta(void);
+void caseda(void);
+void casegd(void);
+void casedi(void);
+void casedt(void);
+void casetl(void);
+void casepc(void);
+void casepm(void);
+void stackdump(void);
+
+/*
+ * c4.c
+ */
+void setn(void);
+int wrc(Tchar i);
+void setn1(int i, int form, Tchar bits);
+void nnspace(void);
+void nrehash(void);
+void nunhash(Numtab *rp);
+int findr(int i);
+int usedr(int i);
+int fnumb(int i, int (*f)(Tchar));
+int decml(int i, int (*f)(Tchar));
+int roman(int i, int (*f)(Tchar));
+int roman0(int i, int (*f)(Tchar), char *onesp, char *fivesp);
+int abc(int i, int (*f)(Tchar));
+int abc0(int i, int (*f)(Tchar));
+long atoi0(void);
+long ckph(void);
+long atoi1(Tchar ii);
+void caserr(void);
+void casenr(void);
+void caseaf(void);
+void setaf(void);
+int vnumb(int *i);
+int hnumb(int *i);
+int inumb(int *n);
+int quant(int n, int m);
+
+/*
+ * c5.c
+ */
+void casead(void);
+void casena(void);
+void casefi(void);
+void casenf(void);
+void casers(void);
+void casens(void);
+int chget(int c);
+void casecc(void);
+void casec2(void);
+void casehc(void);
+void casetc(void);
+void caselc(void);
+void casehy(void);
+int max(int aa, int bb);
+void casenh(void);
+void casece(void);
+void casein(void);
+void casell(void);
+void caselt(void);
+void caseti(void);
+void casels(void);
+void casepo(void);
+void casepl(void);
+void casewh(void);
+void casech(void);
+int findn(int i);
+void casepn(void);
+void casebp(void);
+void casextm(void);
+void casetm(void);
+void casefm(void);
+void casetm1(int ab, FILE *out);
+void casesp(void);
+void casesp1(int a);
+void casert(void);
+void caseem(void);
+void casefl(void);
+void caseev(void);
+void envcopy(Env *e1, Env *e2);
+void caseel(void);
+void caseie(void);
+void casexif(void);
+void caseif(void);
+void caseif1(int);
+void eatblk(int inblk);
+int cmpstr(Tchar c);
+void caserd(void);
+int rdtty(void);
+void caseec(void);
+void caseeo(void);
+void caseta(void);
+void casene(void);
+void casetr(void);
+void casecu(void);
+void caseul(void);
+void caseuf(void);
+void caseit(void);
+void casemc(void);
+void casemk(void);
+void casesv(void);
+void caseos(void);
+void casenm(void);
+void getnm(int *p, int min);
+void casenn(void);
+void caseab(void);
+void save_tty(void);
+void restore_tty(void);
+void set_tty(void);
+void echo_off(void);
+void echo_on(void);
+
+/*
+ * t6.c
+ */
+int t_width(Tchar j);
+void zapwcache(int s);
+int onfont(int n, int f);
+int getcw(int i);
+void xbits(Tchar i, int bitf);
+Tchar t_setch(int c);
+Tchar t_setabs(void);
+int t_findft(int i);
+void caseps(void);
+void casps1(int i);
+int findps(int i);
+void t_mchbits(void);
+void t_setps(void);
+Tchar t_setht(void);
+Tchar t_setslant(void);
+void caseft(void);
+void t_setfont(int a);
+void t_setwd(void);
+Tchar t_vmot(void);
+Tchar t_hmot(void);
+Tchar t_mot(void);
+Tchar t_sethl(int k);
+Tchar t_makem(int i);
+Tchar getlg(Tchar i);
+void caselg(void);
+void casefp(void);
+char *strdupl(const char *);
+int setfp(int pos, int f, char *truename, int print);
+void casecs(void);
+void casebd(void);
+void casevs(void);
+void casess(void);
+Tchar t_xlss(void);
+Uchar* unpair(int i);
+void outascii(Tchar i);
+
+/*
+ * c7.c
+ */
+void tbreak(void);
+void donum(void);
+void text(void);
+void nofill(void);
+void callsp(void);
+void ckul(void);
+void storeline(Tchar c, int w);
+void newline(int a);
+int findn1(int a);
+void chkpn(void);
+int findt(int a);
+int findt1(void);
+void eject(Stack *a);
+int movword(void);
+void horiz(int i);
+void setnel(void);
+int getword(int x);
+void storeword(Tchar c, int w);
+Tchar gettch(void);
+
+/*
+ * c8.c
+ */
+void hyphen(Tchar *wp);
+int punct(Tchar i);
+int alph(int i);
+void caseha(void);
+void caseht(void);
+void casehw(void);
+int exword(void);
+int suffix(void);
+int maplow(int i);
+int vowel(int i);
+Tchar* chkvow(Tchar *w);
+void digram(void);
+int dilook(int a, int b, char t[26][13]);
+
+/*
+ * c9.c
+ */
+Tchar setz(void);
+void setline(void);
+int eat(int c);
+void setov(void);
+void setbra(void);
+void setvline(void);
+void setdraw(void);
+void casefc(void);
+Tchar setfield(int x);
+
+/*
+ * t10.c
+ */
+void t_ptinit(void);
+void t_specnames(void);
+void t_ptout(Tchar i);
+int ptout0(Tchar *pi);
+void ptchname(int);
+void ptflush(void);
+void ptps(void);
+void ptfont(void);
+void ptfpcmd(int f, char *s, char *fn);
+void t_ptlead(void);
+void ptesc(void);
+void ptpage(int n);
+void pttrailer(void);
+void ptstop(void);
+void t_ptpause(void);
+
+/*
+ * t11.c
+ */
+int getdesc(char *name);
+int getfont(char *name, int pos);
+int chadd(char *s, int, int);
+char* chname(int n);
+int getlig(FILE *fin);
+
+/*
+ * n6.c
+ */
+int n_width(Tchar j);
+Tchar n_setch(int c);
+Tchar n_setabs(void);
+int n_findft(int i);
+void n_mchbits(void);
+void n_setps(void);
+Tchar n_setht(void);
+Tchar n_setslant(void);
+void n_caseft(void);
+void n_setfont(int a);
+void n_setwd(void);
+Tchar n_vmot(void);
+Tchar n_hmot(void);
+Tchar n_mot(void);
+Tchar n_sethl(int k);
+Tchar n_makem(int i);
+void n_casefp(void);
+void n_casebd(void);
+void n_casevs(void);
+Tchar n_xlss(void);
+
+/*
+ * n10.c
+ */
+void n_ptinit(void);
+char* skipstr(char *s);
+char* getstr(char *s, char *t);
+char* getint(char *s, int *pn);
+void twdone(void);
+void n_specnames(void);
+int findch(char *s);
+void n_ptout(Tchar i);
+void ptout1(void);
+char* plot(char *x);
+void move(void);
+void n_ptlead(void);
+void n_ptpause(void);
+
+/*
+ * indirect calls on TROFF/!TROFF. these are variables!
+ */
+extern Tchar (*hmot)(void);
+extern Tchar (*makem)(int i);
+extern Tchar (*setabs)(void);
+extern Tchar (*setch)(int c);
+extern Tchar (*sethl)(int k);
+extern Tchar (*setht)(void);
+extern Tchar (*setslant)(void);
+extern Tchar (*vmot)(void);
+extern Tchar (*xlss)(void);
+extern int (*findft)(int i);
+extern int (*width)(Tchar j);
+extern void (*mchbits)(void);
+extern void (*ptlead)(void);
+extern void (*ptout)(Tchar i);
+extern void (*ptpause)(void);
+extern void (*setfont)(int a);
+extern void (*setps)(void);
+extern void (*setwd)(void);
diff --git a/src/cmd/troff/hytab.c b/src/cmd/troff/hytab.c
new file mode 100644
index 00000000..623637fc
--- /dev/null
+++ b/src/cmd/troff/hytab.c
@@ -0,0 +1,126 @@
+/*
+ * Hyphenation digram tables
+ */
+
+typedef unsigned char Uchar;
+
+
+Uchar bxh[26][13] = {
+ 0060,0000,0040,0000,0040,0000,0000,0040,0000,0000,0040,0000,0040
+};
+
+Uchar hxx[26][13] = {
+ 0006,0042,0041,0123,0021,0024,0063,0042,0002,0043,0021,0001,0022,
+ 0140,0000,0200,0003,0260,0006,0000,0160,0007,0000,0140,0000,0320,
+ 0220,0000,0160,0005,0240,0010,0000,0100,0006,0000,0200,0000,0320,
+ 0240,0000,0120,0003,0140,0000,0000,0240,0010,0000,0220,0000,0160,
+ 0042,0023,0041,0040,0040,0022,0043,0041,0030,0064,0021,0000,0041,
+ 0100,0000,0140,0000,0220,0006,0000,0140,0003,0000,0200,0000,0000,
+ 0200,0000,0120,0002,0220,0010,0000,0160,0006,0000,0140,0000,0320,
+ 0020,0000,0020,0000,0020,0000,0000,0020,0000,0000,0020,0000,0000,
+ 0043,0163,0065,0044,0022,0043,0104,0042,0061,0146,0061,0000,0007,
+ 0100,0000,0140,0000,0040,0000,0000,0100,0000,0000,0120,0000,0000,
+ 0140,0000,0040,0011,0060,0004,0001,0120,0003,0000,0140,0000,0040,
+ 0200,0000,0100,0000,0140,0000,0000,0140,0000,0000,0140,0000,0240,
+ 0200,0000,0140,0000,0160,0000,0000,0220,0000,0000,0140,0000,0240,
+ 0200,0000,0140,0000,0160,0000,0000,0220,0000,0000,0060,0000,0240,
+ 0021,0043,0041,0121,0040,0023,0042,0003,0142,0042,0061,0001,0022,
+ 0120,0000,0140,0010,0140,0010,0000,0140,0002,0000,0120,0000,0120,
+ 0000,0000,0000,0000,0360,0000,0000,0000,0000,0000,0160,0000,0000,
+ 0100,0000,0040,0005,0120,0000,0000,0100,0000,0000,0060,0000,0140,
+ 0140,0040,0100,0001,0240,0041,0000,0242,0000,0002,0140,0000,0100,
+ 0240,0000,0120,0002,0200,0000,0000,0320,0007,0000,0240,0000,0340,
+ 0101,0021,0041,0020,0040,0005,0042,0121,0002,0021,0201,0000,0020,
+ 0160,0000,0100,0000,0140,0000,0000,0160,0006,0000,0220,0000,0140,
+ 0140,0000,0020,0001,0020,0000,0000,0100,0001,0000,0300,0000,0000,
+ 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,
+ 0106,0041,0040,0147,0040,0000,0063,0041,0001,0102,0160,0002,0002,
+ 0300,0000,0040,0017,0140,0017,0000,0240,0000,0000,0140,0000,0120,
+};
+
+Uchar bxxh[26][13] = {
+ 0005,0150,0153,0062,0062,0246,0152,0127,0146,0203,0310,0017,0206,
+ 0100,0000,0120,0000,0140,0000,0000,0100,0000,0000,0120,0000,0060,
+ 0100,0000,0040,0000,0060,0000,0000,0060,0000,0000,0220,0000,0040,
+ 0100,0000,0120,0000,0200,0000,0000,0100,0000,0000,0140,0000,0060,
+ 0043,0142,0046,0140,0062,0147,0210,0131,0046,0106,0246,0017,0111,
+ 0060,0000,0020,0000,0060,0000,0000,0040,0000,0000,0100,0000,0000,
+ 0060,0000,0040,0000,0040,0000,0000,0040,0000,0000,0100,0000,0040,
+ 0100,0000,0100,0000,0100,0000,0000,0040,0000,0000,0100,0000,0140,
+ 0066,0045,0145,0140,0000,0070,0377,0030,0130,0103,0003,0017,0006,
+ 0040,0000,0040,0000,0020,0000,0000,0040,0000,0000,0100,0000,0000,
+ 0200,0000,0020,0000,0140,0000,0000,0120,0000,0000,0120,0000,0040,
+ 0120,0000,0040,0000,0060,0000,0000,0060,0000,0000,0160,0000,0040,
+ 0120,0000,0040,0000,0120,0000,0000,0040,0000,0000,0160,0000,0040,
+ 0120,0000,0020,0000,0140,0000,0000,0120,0000,0000,0140,0000,0040,
+ 0051,0126,0150,0140,0060,0210,0146,0006,0006,0165,0003,0017,0244,
+ 0120,0000,0040,0000,0160,0000,0000,0140,0000,0000,0060,0000,0140,
+ 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,
+ 0140,0000,0140,0000,0060,0000,0000,0100,0000,0000,0140,0000,0020,
+ 0120,0000,0020,0000,0060,0000,0000,0060,0000,0000,0060,0000,0040,
+ 0140,0000,0020,0000,0100,0000,0000,0140,0000,0000,0140,0000,0020,
+ 0070,0125,0051,0162,0120,0105,0126,0104,0006,0044,0000,0017,0052,
+ 0140,0000,0020,0000,0140,0000,0000,0060,0000,0000,0060,0000,0040,
+ 0020,0000,0000,0000,0020,0000,0000,0000,0000,0000,0000,0000,0060,
+ 0140,0000,0160,0000,0200,0000,0000,0140,0000,0000,0000,0000,0240,
+ 0065,0042,0060,0200,0000,0210,0222,0146,0006,0204,0220,0012,0003,
+ 0240,0000,0020,0000,0120,0000,0000,0200,0000,0000,0200,0000,0240,
+};
+
+Uchar xhx[26][13] = {
+ 0032,0146,0042,0107,0076,0102,0042,0146,0202,0050,0006,0000,0051,
+ 0036,0377,0057,0013,0057,0366,0377,0057,0001,0377,0057,0000,0040,
+ 0037,0377,0020,0000,0100,0022,0377,0057,0362,0116,0100,0000,0017,
+ 0057,0377,0057,0031,0137,0363,0377,0037,0362,0270,0077,0000,0117,
+ 0074,0142,0012,0236,0076,0125,0063,0165,0341,0046,0047,0000,0024,
+ 0020,0017,0075,0377,0040,0001,0377,0017,0001,0204,0020,0000,0040,
+ 0057,0017,0057,0340,0140,0362,0314,0117,0003,0302,0100,0000,0057,
+ 0057,0357,0077,0017,0100,0366,0314,0057,0342,0346,0037,0000,0060,
+ 0252,0145,0072,0157,0377,0165,0063,0066,0164,0050,0363,0000,0362,
+ 0000,0000,0020,0000,0020,0000,0000,0017,0000,0000,0020,0000,0000,
+ 0117,0017,0237,0377,0200,0354,0125,0110,0004,0257,0000,0000,0300,
+ 0057,0367,0054,0357,0157,0216,0314,0114,0217,0353,0053,0000,0057,
+ 0077,0213,0077,0077,0177,0317,0377,0114,0377,0352,0077,0000,0076,
+ 0077,0213,0077,0077,0157,0177,0377,0054,0377,0352,0117,0000,0075,
+ 0125,0230,0065,0216,0057,0066,0063,0047,0345,0126,0011,0000,0033,
+ 0057,0377,0051,0360,0120,0361,0273,0056,0001,0256,0057,0000,0060,
+ 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,
+ 0076,0310,0056,0310,0137,0174,0273,0055,0335,0266,0033,0000,0155,
+ 0077,0157,0057,0360,0057,0063,0042,0024,0077,0206,0020,0000,0040,
+ 0057,0037,0077,0360,0100,0365,0377,0037,0362,0176,0050,0000,0026,
+ 0167,0146,0042,0112,0077,0110,0062,0254,0366,0052,0377,0000,0163,
+ 0060,0000,0040,0000,0120,0000,0377,0060,0012,0000,0037,0000,0257,
+ 0037,0232,0157,0361,0040,0003,0125,0010,0001,0256,0000,0000,0340,
+ 0377,0377,0377,0377,0377,0377,0377,0377,0377,0377,0377,0017,0277,
+ 0253,0315,0257,0216,0377,0206,0146,0306,0371,0126,0232,0000,0004,
+ 0057,0012,0100,0360,0160,0360,0000,0040,0000,0017,0157,0000,0176,
+};
+
+Uchar xxh[26][13] = {
+ 0045,0150,0154,0162,0042,0246,0210,0147,0152,0103,0230,0017,0206,
+ 0100,0000,0040,0000,0140,0000,0000,0100,0000,0021,0120,0017,0060,
+ 0100,0000,0040,0002,0140,0320,0000,0060,0000,0001,0220,0017,0040,
+ 0100,0001,0120,0001,0241,0000,0000,0100,0000,0020,0140,0017,0060,
+ 0023,0162,0046,0142,0022,0207,0210,0131,0052,0106,0250,0017,0110,
+ 0060,0000,0042,0000,0160,0000,0000,0040,0000,0212,0100,0017,0000,
+ 0140,0000,0040,0002,0140,0000,0000,0120,0000,0040,0120,0017,0040,
+ 0100,0000,0100,0000,0140,0001,0021,0140,0000,0046,0100,0017,0140,
+ 0066,0045,0025,0201,0020,0130,0146,0030,0130,0103,0025,0017,0006,
+ 0100,0000,0040,0000,0020,0000,0000,0040,0000,0000,0200,0017,0000,
+ 0200,0000,0020,0001,0140,0000,0000,0140,0000,0000,0120,0017,0040,
+ 0120,0026,0042,0020,0140,0161,0042,0143,0000,0022,0162,0017,0040,
+ 0121,0042,0060,0020,0140,0200,0000,0123,0000,0021,0220,0017,0041,
+ 0121,0042,0060,0120,0140,0200,0000,0123,0000,0021,0160,0017,0041,
+ 0051,0126,0150,0141,0060,0210,0146,0066,0026,0165,0026,0017,0247,
+ 0120,0000,0040,0003,0160,0000,0000,0140,0000,0021,0100,0017,0140,
+ 0000,0000,0000,0000,0200,0000,0000,0000,0000,0000,0000,0017,0000,
+ 0141,0023,0122,0040,0160,0143,0042,0142,0000,0047,0143,0017,0020,
+ 0120,0000,0040,0006,0140,0060,0000,0141,0000,0026,0100,0017,0040,
+ 0140,0000,0020,0007,0100,0000,0000,0140,0000,0001,0140,0017,0020,
+ 0110,0125,0051,0162,0120,0125,0127,0104,0006,0104,0000,0017,0052,
+ 0140,0000,0040,0000,0160,0000,0000,0140,0000,0000,0060,0017,0000,
+ 0040,0005,0020,0000,0040,0313,0231,0030,0000,0140,0000,0017,0056,
+ 0140,0000,0160,0000,0200,0000,0000,0140,0000,0000,0000,0017,0240,
+ 0065,0042,0060,0040,0000,0206,0231,0146,0006,0224,0220,0017,0004,
+ 0240,0000,0020,0000,0140,0000,0000,0220,0000,0000,0200,0017,0141,
+};
diff --git a/src/cmd/troff/mbwc.c b/src/cmd/troff/mbwc.c
new file mode 100644
index 00000000..66a98219
--- /dev/null
+++ b/src/cmd/troff/mbwc.c
@@ -0,0 +1,165 @@
+#include <stdlib.h>
+
+/*
+ * Use the FSS-UTF transformation proposed by posix.
+ * We define 7 byte types:
+ * T0 0xxxxxxx 7 free bits
+ * Tx 10xxxxxx 6 free bits
+ * T1 110xxxxx 5 free bits
+ * T2 1110xxxx 4 free bits
+ *
+ * Encoding is as follows.
+ * From hex Thru hex Sequence Bits
+ * 00000000 0000007F T0 7
+ * 00000080 000007FF T1 Tx 11
+ * 00000800 0000FFFF T2 Tx Tx 16
+ */
+
+int
+mblen(const char *s, size_t n)
+{
+
+ return mbtowc(0, s, n);
+}
+
+int
+mbtowc(wchar_t *pwc, const char *s, size_t n)
+{
+ int c, c1, c2;
+ long l;
+
+ if(!s)
+ return 0;
+
+ if(n < 1)
+ goto bad;
+ c = s[0] & 0xff;
+ if((c & 0x80) == 0x00) {
+ if(pwc)
+ *pwc = c;
+ if(c == 0)
+ return 0;
+ return 1;
+ }
+
+ if(n < 2)
+ goto bad;
+ c1 = (s[1] ^ 0x80) & 0xff;
+ if((c1 & 0xC0) != 0x00)
+ goto bad;
+ if((c & 0xE0) == 0xC0) {
+ l = ((c << 6) | c1) & 0x7FF;
+ if(l < 0x080)
+ goto bad;
+ if(pwc)
+ *pwc = l;
+ return 2;
+ }
+
+ if(n < 3)
+ goto bad;
+ c2 = (s[2] ^ 0x80) & 0xff;
+ if((c2 & 0xC0) != 0x00)
+ goto bad;
+ if((c & 0xF0) == 0xE0) {
+ l = ((((c << 6) | c1) << 6) | c2) & 0xFFFF;
+ if(l < 0x0800)
+ goto bad;
+ if(pwc)
+ *pwc = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ return -1;
+
+}
+
+int
+wctomb(char *s, wchar_t wchar)
+{
+ long c;
+
+ if(!s)
+ return 0;
+
+ c = wchar & 0xFFFF;
+ if(c < 0x80) {
+ s[0] = c;
+ return 1;
+ }
+
+ if(c < 0x800) {
+ s[0] = 0xC0 | (c >> 6);
+ s[1] = 0x80 | (c & 0x3F);
+ return 2;
+ }
+
+ s[0] = 0xE0 | (c >> 12);
+ s[1] = 0x80 | ((c >> 6) & 0x3F);
+ s[2] = 0x80 | (c & 0x3F);
+ return 3;
+}
+
+size_t
+mbstowcs(wchar_t *pwcs, const char *s, size_t n)
+{
+ int i, d, c;
+
+ for(i=0; i < n; i++) {
+ c = *s & 0xff;
+ if(c < 0x80) {
+ *pwcs = c;
+ if(c == 0)
+ break;
+ s++;
+ } else {
+ d = mbtowc(pwcs, s, 3);
+ if(d <= 0)
+ return (size_t)((d<0) ? -1 : i);
+ s += d;
+ }
+ pwcs++;
+ }
+ return i;
+}
+
+size_t
+wcstombs(char *s, const wchar_t *pwcs, size_t n)
+{
+ int i, d;
+ long c;
+ char *p, *pe;
+ char buf[3];
+
+ p = s;
+ pe = p+n-3;
+ while(p < pe) {
+ c = *pwcs++;
+ if(c < 0x80)
+ *p++ = c;
+ else
+ p += wctomb(p, c);
+ if(c == 0)
+ return p-s;
+ }
+ while(p < pe+3) {
+ c = *pwcs++;
+ d = wctomb(buf, c);
+ if(p+d <= pe+3) {
+ *p++ = buf[0];
+ if(d > 1) {
+ *p++ = buf[2];
+ if(d > 2)
+ *p++ = buf[3];
+ }
+ }
+ if(c == 0)
+ break;
+ }
+ return p-s;
+}
+
diff --git a/src/cmd/troff/mk.log b/src/cmd/troff/mk.log
new file mode 100644
index 00000000..26b610d6
--- /dev/null
+++ b/src/cmd/troff/mk.log
@@ -0,0 +1,136 @@
+9c -c -DUNICODE -DFONTDIR="sys/lib/troff/font" -DNTERMDIR="sys/lib/troff/term/tab." -DTEXHYPHENS="#9/sys/lib/texmf/tex/generic/hyphen/hyphen.tex" -DALTHYPHENS="sys/lib/texmf/tex/generic/hyphen/hyphen.tex" -DDWBHOME="#9/" n1.c
+n1.c:51: warning: return type defaults to `int'
+n1.c: In function `getch0':
+n1.c:676: warning: unused variable `j'
+n1.c:719: warning: label `g2' defined but not used
+n1.c: In function `get1ch':
+n1.c:745: warning: `n' might be used uninitialized in this function
+n1.c:745: warning: `c' might be used uninitialized in this function
+n1.c: In function `nextfile':
+n1.c:830: warning: implicit declaration of function `unsharp'
+n1.c:830: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+n1.c: At top level:
+n1.c:842: warning: return type defaults to `int'
+n1.c:875: warning: return type defaults to `int'
+n1.c:915: warning: return type defaults to `int'
+n1.c: In function `getname':
+n1.c:917: warning: unused variable `i'
+n1.c: In function `caseso':
+n1.c:939: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+n1.c:935: warning: unused variable `p'
+n1.c:935: warning: unused variable `q'
+n1.c:934: warning: `fp' might be used uninitialized in this function
+n1.c: In function `casecf':
+n1.c:1008: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+9c -c -DUNICODE n2.c
+n2.c: In function `outascii':
+n2.c:140: warning: unused variable `p'
+9c -c -DUNICODE n3.c
+n3.c: In function `grow':
+n3.c:67: warning: unused variable `new'
+n3.c: In function `finds':
+n3.c:310: warning: unused variable `j'
+n3.c: In function `copyb':
+n3.c:372: warning: `savoff' might be used uninitialized in this function
+9c -c -DUNICODE n4.c
+n4.c: In function `setn':
+n4.c:144: warning: int format, long unsigned int arg (arg 3)
+9c -c -DUNICODE n5.c
+n5.c:83: warning: return type defaults to `int'
+n5.c: In function `chget':
+n5.c:84: warning: `i' might be used uninitialized in this function
+n5.c: At top level:
+n5.c:147: warning: return type defaults to `int'
+n5.c:338: warning: return type defaults to `int'
+n5.c: In function `casefm':
+n5.c:411: warning: implicit declaration of function `unsharp'
+n5.c:411: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+n5.c: At top level:
+n5.c:747: warning: return type defaults to `int'
+n5.c:835: warning: return type defaults to `int'
+9c -c -DUNICODE t6.c
+t6.c:18: warning: return type defaults to `int'
+t6.c:79: warning: return type defaults to `int'
+t6.c:112: warning: return type defaults to `int'
+t6.c: In function `t_setch':
+t6.c:217: warning: unused variable `j'
+t6.c: At top level:
+t6.c:288: warning: return type defaults to `int'
+t6.c:367: warning: return type defaults to `int'
+t6.c: In function `t_setps':
+t6.c:397: warning: `j' might be used uninitialized in this function
+t6.c: At top level:
+t6.c:707: warning: return type defaults to `int'
+t6.c: In function `setfp':
+t6.c:708: warning: unused variable `sl'
+t6.c: In function `casebd':
+t6.c:781: warning: `j' might be used uninitialized in this function
+9c -c -DUNICODE n6.c
+n6.c:11: warning: return type defaults to `int'
+n6.c: In function `n_casebd':
+n6.c:295: warning: `j' might be used uninitialized in this function
+9c -c -DUNICODE n7.c
+n7.c: In function `newline':
+n7.c:354: warning: `nlss' might be used uninitialized in this function
+n7.c: At top level:
+n7.c:450: warning: return type defaults to `int'
+n7.c:482: warning: return type defaults to `int'
+n7.c:509: warning: return type defaults to `int'
+n7.c:544: warning: return type defaults to `int'
+n7.c:653: warning: return type defaults to `int'
+n7.c: In function `getword':
+n7.c:654: warning: `j' might be used uninitialized in this function
+9c -c -DUNICODE -DTEXHYPHENS="#9/sys/lib/texmf/tex/generic/hyphen/hyphen.tex" n8.c
+n8.c:76: warning: return type defaults to `int'
+n8.c:87: warning: return type defaults to `int'
+n8.c:222: warning: return type defaults to `int'
+n8.c:274: warning: return type defaults to `int'
+n8.c:282: warning: return type defaults to `int'
+n8.c: In function `digram':
+n8.c:310: warning: `maxw' might be used uninitialized in this function
+n8.c: At top level:
+n8.c:346: warning: return type defaults to `int'
+n8.c: In function `readpats':
+n8.c:469: warning: implicit declaration of function `unsharp'
+n8.c:469: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+n8.c:470: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+9c -c -DUNICODE n9.c
+n9.c:80: warning: return type defaults to `int'
+n9.c: In function `setfield':
+n9.c:340: warning: `rchar' might be used uninitialized in this function
+9c -c -DUNICODE -DTDEVNAME="utf" t10.c
+t10.c: In function `ptout0':
+t10.c:179: warning: int format, long int arg (arg 3)
+t10.c:183: warning: int format, long unsigned int arg (arg 3)
+t10.c:303: warning: int format, long int arg (arg 3)
+t10.c:157: warning: `w' might be used uninitialized in this function
+9c -c -DUNICODE -DTDEVNAME="utf" n10.c
+n10.c: In function `getnrfont':
+n10.c:77: warning: unused variable `fin'
+n10.c:81: warning: unused variable `cmd'
+n10.c:80: warning: `code' might be used uninitialized in this function
+n10.c: In function `n_ptinit':
+n10.c:189: warning: implicit declaration of function `unsharp'
+n10.c:189: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+n10.c:142: warning: unused variable `cp'
+9c -c -DUNICODE t11.c
+t11.c:21: warning: return type defaults to `int'
+t11.c: In function `getdesc':
+t11.c:26: warning: implicit declaration of function `unsharp'
+t11.c:26: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+t11.c: In function `checkfont':
+t11.c:67: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+t11.c: At top level:
+t11.c:89: warning: return type defaults to `int'
+t11.c: In function `getfont':
+t11.c:100: warning: passing arg 1 of `fopen' makes pointer from integer without a cast
+t11.c:94: warning: `nw' might be used uninitialized in this function
+t11.c:94: warning: `code' might be used uninitialized in this function
+t11.c: At top level:
+t11.c:193: warning: return type defaults to `int'
+t11.c:235: warning: return type defaults to `int'
+9c -c -DUNICODE -DTMACDIR="sys/lib/tmac/tmac." ni.c
+9c -c -DUNICODE hytab.c
+9c -c -DUNICODE suftab.c
+9c -c -DUNICODE -DDWBHOME="#9/" dwbinit.c
+9l -o o.troff n1.o n2.o n3.o n4.o n5.o t6.o n6.o n7.o n8.o n9.o t10.o n10.o t11.o ni.o hytab.o suftab.o dwbinit.o /usr/local/plan9/lib/libbio.a /usr/local/plan9/lib/lib9.a
diff --git a/src/cmd/troff/mkfile b/src/cmd/troff/mkfile
new file mode 100644
index 00000000..c02084e7
--- /dev/null
+++ b/src/cmd/troff/mkfile
@@ -0,0 +1,58 @@
+<$PLAN9/src/mkhdr
+
+TARG=troff
+OFILES=n1.$O\
+ n2.$O\
+ n3.$O\
+ n4.$O\
+ n5.$O\
+ t6.$O\
+ n6.$O\
+ n7.$O\
+ n8.$O\
+ n9.$O\
+ t10.$O\
+ n10.$O\
+ t11.$O\
+ ni.$O\
+ hytab.$O\
+ suftab.$O\
+ dwbinit.$O\
+ mbwc.$O
+
+HFILES=tdef.h\
+ fns.h\
+ ext.h\
+ dwbinit.h\
+
+
+SHORTLIB=bio 9
+<$PLAN9/src/mkone
+CFLAGS=-c -DUNICODE
+
+TMACDIR='"tmac/tmac."'
+FONTDIR='"troff/font"'
+NTERMDIR='"troff/term/tab."'
+ALTHYPHENS='"lib/hyphen.tex"'
+TEXHYPHENS='"#9/lib/hyphen.tex"'
+DWBHOME='"#9/"'
+TDEVNAME='"utf"'
+NDEVNAME='"utf"'
+
+ni.$O: ni.c $HFILES
+ $CC $CFLAGS -DTMACDIR=$TMACDIR ni.c
+
+t10.$O: t10.c $HFILES
+ $CC $CFLAGS -DTDEVNAME=$TDEVNAME t10.c
+
+n1.$O: n1.c $HFILES
+ $CC $CFLAGS -DFONTDIR=$FONTDIR -DNTERMDIR=$NTERMDIR -DTEXHYPHENS=$TEXHYPHENS -DALTHYPHENS=$ALTHYPHENS -DDWBHOME=$DWBHOME n1.c
+
+n10.$O: n10.c $HFILES
+ $CC $CFLAGS -DTDEVNAME=$NDEVNAME n10.c
+
+n8.$O: n8.c $HFILES
+ $CC $CFLAGS -DTEXHYPHENS=$TEXHYPHENS n8.c
+
+dwbinit.$O: dwbinit.c
+ $CC $CFLAGS -DDWBHOME=$DWBHOME dwbinit.c
diff --git a/src/cmd/troff/n1.c b/src/cmd/troff/n1.c
new file mode 100644
index 00000000..d0949fe2
--- /dev/null
+++ b/src/cmd/troff/n1.c
@@ -0,0 +1,1136 @@
+/*
+ * n1.c
+ *
+ * consume options, initialization, main loop,
+ * input routines, escape function calling
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+#include "dwbinit.h"
+
+#undef MB_CUR_MAX
+#define MB_CUR_MAX 3
+
+#include <setjmp.h>
+#include <time.h>
+
+char *Version = "March 11, 1994";
+
+#ifndef DWBVERSION
+#define DWBVERSION "???"
+#endif
+
+char *DWBfontdir = FONTDIR;
+char *DWBntermdir = NTERMDIR;
+char *DWBalthyphens = ALTHYPHENS;
+char *DWBhomedir = "";
+
+dwbinit dwbpaths[] = {
+ &DWBfontdir, NULL, 0,
+ &DWBntermdir, NULL, 0,
+ &DWBalthyphens, NULL, 0,
+ &DWBhomedir, NULL, 0,
+ NULL, nextf, NS,
+ NULL, NULL, 0
+};
+
+int TROFF = 1; /* assume we started in troff... */
+
+jmp_buf sjbuf;
+Offset ipl[NSO];
+
+static FILE *ifile;
+static FILE *ifl[NSO]; /* open input file pointers */
+char cfname[NSO+1][NS] = { "stdin" }; /* file name stack */
+int cfline[NSO]; /* input line count stack */
+char *progname; /* program name (troff or nroff) */
+
+int trace = 0; /* tracing mode: default off */
+int trace1 = 0;
+
+main(int argc, char *argv[])
+{
+ char *p;
+ int j;
+ Tchar i;
+ char buf[100];
+
+ ifile = stdin;
+ ptid = stdout;
+
+ buf[0] = '\0'; /* make sure it's empty (silly 3b2) */
+ progname = argv[0];
+ if ((p = strrchr(progname, '/')) == NULL)
+ p = progname;
+ else
+ p++;
+ DWBinit(progname, dwbpaths);
+ if (strcmp(p, "nroff") == 0)
+ TROFF = 0;
+#ifdef UNICODE
+ alphabet = 128; /* unicode for plan 9 */
+#endif /*UNICODE*/
+ mnspace();
+ nnspace();
+ mrehash();
+ nrehash();
+ numtabp[NL].val = -1;
+
+ while (--argc > 0 && (++argv)[0][0] == '-')
+ switch (argv[0][1]) {
+
+ case 'N': /* ought to be used first... */
+ TROFF = 0;
+ break;
+ case 'd':
+ fprintf(stderr, "troff/nroff version %s\n", Version);
+ break;
+ case 'F': /* switch font tables from default */
+ if (argv[0][2] != '\0') {
+ strcpy(termtab, &argv[0][2]);
+ strcpy(fontdir, &argv[0][2]);
+ } else {
+ argv++; argc--;
+ strcpy(termtab, argv[0]);
+ strcpy(fontdir, argv[0]);
+ }
+ break;
+ case 0:
+ goto start;
+ case 'i':
+ stdi++;
+ break;
+ case 'n':
+ npn = atoi(&argv[0][2]);
+ break;
+ case 'u': /* set emboldening amount */
+ bdtab[3] = atoi(&argv[0][2]);
+ if (bdtab[3] < 0 || bdtab[3] > 50)
+ bdtab[3] = 0;
+ break;
+ case 's':
+ if (!(stop = atoi(&argv[0][2])))
+ stop++;
+ break;
+ case 'r':
+ sprintf(buf + strlen(buf), ".nr %c %s\n",
+ argv[0][2], &argv[0][3]);
+ /* not yet cpushback(buf);*/
+ /* dotnr(&argv[0][2], &argv[0][3]); */
+ break;
+ case 'm':
+ if (mflg++ >= NMF) {
+ ERROR "Too many macro packages: %s", argv[0] WARN;
+ break;
+ }
+ strcpy(mfiles[nmfi], nextf);
+ strcat(mfiles[nmfi++], &argv[0][2]);
+ break;
+ case 'o':
+ getpn(&argv[0][2]);
+ break;
+ case 'T':
+ strcpy(devname, &argv[0][2]);
+ dotT++;
+ break;
+ case 'a':
+ ascii = 1;
+ break;
+ case 'h':
+ hflg++;
+ break;
+ case 'e':
+ eqflg++;
+ break;
+ case 'q':
+ quiet++;
+ save_tty();
+ break;
+ case 'V':
+ fprintf(stdout, "%croff: DWB %s\n",
+ TROFF ? 't' : 'n', DWBVERSION);
+ exit(0);
+ case 't':
+ if (argv[0][2] != '\0')
+ trace = trace1 = argv[0][2];
+ break; /* for the sake of compatibility */
+ default:
+ ERROR "unknown option %s", argv[0] WARN;
+ done(02);
+ }
+
+start:
+ /*
+ * cpushback maintains a LIFO, so push pack the -r arguments
+ * in reverse order to maintain a FIFO in case someone did -rC1 -rC3
+ */
+ if (buf[0]) {
+ char *p = buf;
+ while(*p++)
+ ;
+ while(p > buf) {
+ while(strncmp(p, ".nr", 3) != 0)
+ p--;
+ cpushback(p);
+ *p-- = '\0';
+ }
+ }
+ argp = argv;
+ rargc = argc;
+ nmfi = 0;
+ init2();
+ setjmp(sjbuf);
+loop:
+ copyf = lgf = nb = nflush = nlflg = 0;
+ if (ip && rbf0(ip) == 0 && ejf && frame->pframe <= ejl && dip == d) {
+ nflush++;
+ trap = 0;
+ eject((Stack *)0);
+ goto loop;
+ }
+ i = getch();
+ if (pendt)
+ goto Lt;
+ if ((j = cbits(i)) == XPAR) {
+ copyf++;
+ tflg++;
+ while (cbits(i) != '\n')
+ pchar(i = getch());
+ tflg = 0;
+ copyf--;
+ goto loop;
+ }
+ if (j == cc || j == c2) {
+ if (j == c2)
+ nb++;
+ copyf++;
+ while ((j = cbits(i = getch())) == ' ' || j == '\t')
+ ;
+ ch = i;
+ copyf--;
+ control(getrq(), 1);
+ flushi();
+ goto loop;
+ }
+Lt:
+ ch = i;
+ text();
+ if (nlflg)
+ numtabp[HP].val = 0;
+ goto loop;
+}
+
+
+
+void init2(void)
+{
+ int i;
+ char buf[100];
+
+ for (i = NTRTAB; --i; )
+ trtab[i] = i;
+ trtab[UNPAD] = ' ';
+ iflg = 0;
+ obufp = obuf;
+ if (TROFF)
+ t_ptinit();
+ else
+ n_ptinit();
+ mchbits();
+ cvtime();
+ numtabp[PID].val = getpid();
+ numtabp[HP].val = init = 0;
+ numtabp[NL].val = -1;
+ nfo = 0;
+ copyf = raw = 0;
+ sprintf(buf, ".ds .T %s\n", devname);
+ cpushback(buf);
+ sprintf(buf, ".ds .P %s\n", DWBhomedir);
+ cpushback(buf);
+ numtabp[CD].val = -1; /* compensation */
+ nx = mflg;
+ frame = stk = (Stack *)setbrk(STACKSIZE);
+ dip = &d[0];
+ nxf = frame + 1;
+ for (i = 1; i < NEV; i++) /* propagate the environment */
+ envcopy(&env[i], &env[0]);
+ for (i = 0; i < NEV; i++) {
+ if ((env[i]._word._bufp = (Tchar *)calloc(WDSIZE, sizeof(Tchar))) == NULL) {
+ ERROR "not enough room for word buffers" WARN;
+ done2(1);
+ }
+ env[i]._word._size = WDSIZE;
+ if ((env[i]._line._bufp = (Tchar *)calloc(LNSIZE, sizeof(Tchar))) == NULL) {
+ ERROR "not enough room for line buffers" WARN;
+ done2(1);
+ }
+ env[i]._line._size = LNSIZE;
+ }
+ if ((oline = (Tchar *)calloc(OLNSIZE, sizeof(Tchar))) == NULL) {
+ ERROR "not enough room for line buffers" WARN;
+ done2(1);
+ }
+ olinep = oline;
+ olnsize = OLNSIZE;
+ blockinit();
+}
+
+void cvtime(void)
+{
+ long tt;
+ struct tm *ltime;
+
+ time(&tt);
+ ltime = localtime(&tt);
+ numtabp[YR].val = ltime->tm_year % 100;
+ numtabp[YR].fmt = 2;
+ numtabp[MO].val = ltime->tm_mon + 1; /* troff uses 1..12 */
+ numtabp[DY].val = ltime->tm_mday;
+ numtabp[DW].val = ltime->tm_wday + 1; /* troff uses 1..7 */
+}
+
+
+
+char errbuf[200];
+
+void errprint(void) /* error message printer */
+{
+ int savecd = numtabp[CD].val;
+
+ if (!nlflg)
+ numtabp[CD].val++;
+
+ fprintf(stderr, "%s: ", progname);
+ fputs(errbuf, stderr);
+ if (cfname[ifi][0])
+ fprintf(stderr, "; %s:%d", cfname[ifi], numtabp[CD].val);
+ fputs("\n", stderr);
+ if (cfname[ifi][0])
+ stackdump();
+ numtabp[CD].val = savecd;
+}
+
+
+int control(int a, int b)
+{
+ int j, k;
+ extern Contab *contabp;
+
+ numerr.type = RQERR;
+ numerr.req = a;
+ if (a == 0 || (j = findmn(a)) == -1)
+ return(0);
+ if (contabp[j].f == 0) {
+ if (trace & TRMAC)
+ fprintf(stderr, "invoke macro %s\n", unpair(a));
+ if (dip != d)
+ for (k = dilev; k; k--)
+ if (d[k].curd == a) {
+ ERROR "diversion %s invokes itself during diversion",
+ unpair(a) WARN;
+ edone(0100);
+ }
+ nxf->nargs = 0;
+ if (b)
+ collect();
+ flushi();
+ return pushi(contabp[j].mx, a); /* BUG??? all that matters is 0/!0 */
+ }
+ if (b) {
+ if (trace & TRREQ)
+ fprintf(stderr, "invoke request %s\n", unpair(a));
+ (*contabp[j].f)();
+ }
+ return(0);
+}
+
+void casept(void)
+{
+ int i;
+
+ noscale++;
+ if (skip())
+ i = trace1;
+ else {
+ i = max(inumb(&trace), 0);
+ if (nonumb)
+ i = trace1;
+ }
+ trace1 = trace;
+ trace = i;
+ noscale = 0;
+}
+
+
+int getrq(void)
+{
+ int i, j;
+
+ if ((i = getach()) == 0 || (j = getach()) == 0)
+ goto rtn;
+ i = PAIR(i, j);
+rtn:
+ return(i);
+}
+
+/*
+ * table encodes some special characters, to speed up tests
+ * in getch, viz FLSS, RPT, f, \b, \n, fc, tabch, ldrch
+ */
+
+char gchtab[NCHARS] = {
+ 000,004,000,000,010,000,000,000, /* fc, ldr */
+ 001,002,001,000,001,000,000,000, /* \b, tab, nl, RPT */
+ 000,000,000,000,000,000,000,000,
+ 000,001,000,001,000,000,000,000, /* FLSS, ESC */
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,001,000, /* f */
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,000,000,
+ 000,000,000,000,000,000,000,000,
+};
+
+int realcbits(Tchar c) /* return character bits, or MOTCH if motion */
+{
+ if (ismot(c))
+ return MOTCH;
+ else
+ return c & 0xFFFF;
+}
+
+Tchar getch(void)
+{
+ int k;
+ Tchar i, j;
+
+g0:
+ if (ch) {
+ i = ch;
+ if (cbits(i) == '\n')
+ nlflg++;
+ ch = 0;
+ return(i);
+ }
+
+ if (nlflg)
+ return('\n');
+ i = getch0();
+ if (ismot(i))
+ return(i);
+ k = cbits(i);
+ if (k >= sizeof(gchtab)/sizeof(gchtab[0]) || gchtab[k] == 0) /* nothing special */
+ return(i);
+ if (k != ESC) {
+ if (k == '\n') {
+ nlflg++;
+ if (ip == 0)
+ numtabp[CD].val++; /* line number */
+ return(k);
+ }
+ if (k == FLSS) {
+ copyf++;
+ raw++;
+ i = getch0();
+ if (!fi)
+ flss = i;
+ copyf--;
+ raw--;
+ goto g0;
+ }
+ if (k == RPT) {
+ setrpt();
+ goto g0;
+ }
+ if (!copyf) {
+ if (k == 'f' && lg && !lgf) {
+ i = getlg(i);
+ return(i);
+ }
+ if (k == fc || k == tabch || k == ldrch) {
+ if ((i = setfield(k)) == 0)
+ goto g0;
+ else
+ return(i);
+ }
+ if (k == '\b') {
+ i = makem(-width(' ' | chbits));
+ return(i);
+ }
+ }
+ return(i);
+ }
+
+ k = cbits(j = getch0());
+ if (ismot(j))
+ return(j);
+
+ switch (k) {
+ case 'n': /* number register */
+ setn();
+ goto g0;
+ case '$': /* argument indicator */
+ seta();
+ goto g0;
+ case '*': /* string indicator */
+ setstr();
+ goto g0;
+ case '{': /* LEFT */
+ i = LEFT;
+ goto gx;
+ case '}': /* RIGHT */
+ i = RIGHT;
+ goto gx;
+ case '"': /* comment */
+ while (cbits(i = getch0()) != '\n')
+ ;
+ if (ip == 0)
+ numtabp[CD].val++; /* line number */
+ nlflg++;
+ return(i);
+
+/* experiment: put it here instead of copy mode */
+ case '(': /* special char name \(xx */
+ case 'C': /* \C'...' */
+ if ((i = setch(k)) == 0)
+ goto g0;
+ goto gx;
+
+ case ESC: /* double backslash */
+ i = eschar;
+ goto gx;
+ case 'e': /* printable version of current eschar */
+ i = PRESC;
+ goto gx;
+ case '\n': /* concealed newline */
+ numtabp[CD].val++;
+ goto g0;
+ case ' ': /* unpaddable space */
+ i = UNPAD;
+ goto gx;
+ case '\'': /* \(aa */
+ i = ACUTE;
+ goto gx;
+ case '`': /* \(ga */
+ i = GRAVE;
+ goto gx;
+ case '_': /* \(ul */
+ i = UNDERLINE;
+ goto gx;
+ case '-': /* current font minus */
+ i = MINUS;
+ goto gx;
+ case '&': /* filler */
+ i = FILLER;
+ goto gx;
+ case 'c': /* to be continued */
+ i = CONT;
+ goto gx;
+ case '!': /* transparent indicator */
+ i = XPAR;
+ goto gx;
+ case 't': /* tab */
+ i = '\t';
+ return(i);
+ case 'a': /* leader (SOH) */
+/* old: *pbp++ = LEADER; goto g0; */
+ i = LEADER;
+ return i;
+ case '%': /* ohc */
+ i = OHC;
+ return(i);
+ case 'g': /* return format of a number register */
+ setaf(); /* should this really be in copy mode??? */
+ goto g0;
+ case '.': /* . */
+ i = '.';
+gx:
+ setsfbits(i, sfbits(j));
+ return(i);
+ }
+ if (copyf) {
+ *pbp++ = j;
+ return(eschar);
+ }
+ switch (k) {
+
+ case 'f': /* font indicator */
+ setfont(0);
+ goto g0;
+ case 's': /* size indicator */
+ setps();
+ goto g0;
+ case 'v': /* vert mot */
+ numerr.type = numerr.escarg = 0; numerr.esc = k;
+ if (i = vmot()) {
+ return(i);
+ }
+ goto g0;
+ case 'h': /* horiz mot */
+ numerr.type = numerr.escarg = 0; numerr.esc = k;
+ if (i = hmot())
+ return(i);
+ goto g0;
+ case '|': /* narrow space */
+ if (NROFF)
+ goto g0;
+ return(makem((int)(EM)/6));
+ case '^': /* half narrow space */
+ if (NROFF)
+ goto g0;
+ return(makem((int)(EM)/12));
+ case 'w': /* width function */
+ setwd();
+ goto g0;
+ case 'p': /* spread */
+ spread++;
+ goto g0;
+ case 'N': /* absolute character number */
+ numerr.type = numerr.escarg = 0; numerr.esc = k;
+ if ((i = setabs()) == 0)
+ goto g0;
+ return i;
+ case 'H': /* character height */
+ numerr.type = numerr.escarg = 0; numerr.esc = k;
+ return(setht());
+ case 'S': /* slant */
+ numerr.type = numerr.escarg = 0; numerr.esc = k;
+ return(setslant());
+ case 'z': /* zero with char */
+ return(setz());
+ case 'l': /* hor line */
+ numerr.type = numerr.escarg = 0; numerr.esc = k;
+ setline();
+ goto g0;
+ case 'L': /* vert line */
+ numerr.type = numerr.escarg = 0; numerr.esc = k;
+ setvline();
+ goto g0;
+ case 'D': /* drawing function */
+ numerr.type = numerr.escarg = 0; numerr.esc = k;
+ setdraw();
+ goto g0;
+ case 'X': /* \X'...' for copy through */
+ setxon();
+ goto g0;
+ case 'b': /* bracket */
+ setbra();
+ goto g0;
+ case 'o': /* overstrike */
+ setov();
+ goto g0;
+ case 'k': /* mark hor place */
+ if ((k = findr(getsn())) != -1) {
+ numtabp[k].val = numtabp[HP].val;
+ }
+ goto g0;
+ case '0': /* number space */
+ return(makem(width('0' | chbits)));
+ case 'x': /* extra line space */
+ numerr.type = numerr.escarg = 0; numerr.esc = k;
+ if (i = xlss())
+ return(i);
+ goto g0;
+ case 'u': /* half em up */
+ case 'r': /* full em up */
+ case 'd': /* half em down */
+ return(sethl(k));
+ default:
+ return(j);
+ }
+ /* NOTREACHED */
+}
+
+void setxon(void) /* \X'...' for copy through */
+{
+ Tchar xbuf[NC];
+ Tchar *i;
+ Tchar c;
+ int delim, k;
+
+ if (ismot(c = getch()))
+ return;
+ delim = cbits(c);
+ i = xbuf;
+ *i++ = XON | chbits;
+ while ((k = cbits(c = getch())) != delim && k != '\n' && i < xbuf+NC-1) {
+ if (k == ' ')
+ setcbits(c, WORDSP);
+ *i++ = c | ZBIT;
+ }
+ *i++ = XOFF | chbits;
+ *i = 0;
+ pushback(xbuf);
+}
+
+
+char ifilt[32] = { 0, 001, 002, 003, 0, 005, 006, 007, 010, 011, 012 };
+
+Tchar getch0(void)
+{
+ int j;
+ Tchar i;
+
+again:
+ if (pbp > lastpbp)
+ i = *--pbp;
+ else if (ip) {
+ /* i = rbf(); */
+ i = rbf0(ip);
+ if (i == 0)
+ i = rbf();
+ else {
+ ++ip;
+ if (pastend(ip)) {
+ --ip;
+ rbf();
+ }
+ }
+ } else {
+ if (donef || ndone)
+ done(0);
+ if (nx || 1) { /* BUG: was ibufp >= eibuf, so EOF test is wrong */
+ if (nfo < 0)
+ ERROR "in getch0, nfo = %d", nfo WARN;
+ if (nfo == 0) {
+g0:
+ if (nextfile()) {
+ if (ip)
+ goto again;
+ }
+ }
+ nx = 0;
+#ifdef UNICODE
+ if (MB_CUR_MAX > 1)
+ i = get1ch(ifile);
+ else
+#endif /*UNICODE*/
+ i = getc(ifile);
+ if (i == EOF)
+ goto g0;
+ if (ip)
+ goto again;
+ }
+g2:
+ if (i >= 040) /* zapped: && i < 0177 */
+ goto g4;
+ i = ifilt[i];
+ }
+ if (cbits(i) == IMP && !raw)
+ goto again;
+ if (i == 0 && !init && !raw) { /* zapped: || i == 0177 */
+ goto again;
+ }
+g4:
+ if (ismot(i))
+ return i;
+ if (copyf == 0 && sfbits(i) == 0)
+ i |= chbits;
+ if (cbits(i) == eschar && !raw)
+ setcbits(i, ESC);
+ return(i);
+}
+
+
+#ifdef UNICODE
+Tchar get1ch(FILE *fp) /* get one "character" from input, figure out what alphabet */
+{
+ wchar_t wc;
+ char buf[100], *p;
+ int i, n, c;
+
+ for (i = 0, p = buf; i < MB_CUR_MAX; i++) {
+ if ((c = getc(fp)) == EOF)
+ return c;
+ *p++ = c;
+ if ((n = mbtowc(&wc, buf, p-buf)) >= 0)
+ break;
+ }
+ if (n == 1) /* real ascii, presumably */
+ return wc;
+ if (n == 0)
+ return p[-1]; /* illegal, but what else to do? */
+ if (c == EOF)
+ return EOF;
+ *p = 0;
+ return chadd(buf, MBchar, Install); /* add name even if haven't seen it */
+}
+#endif /*UNICODE*/
+
+void pushback(Tchar *b)
+{
+ Tchar *ob = b;
+
+ while (*b++)
+ ;
+ b--;
+ while (b > ob && pbp < &pbbuf[NC-3])
+ *pbp++ = *--b;
+ if (pbp >= &pbbuf[NC-3]) {
+ ERROR "pushback overflow" WARN;
+ done(2);
+ }
+}
+
+void cpushback(char *b)
+{
+ char *ob = b;
+
+ while (*b++)
+ ;
+ b--;
+ while (b > ob && pbp < &pbbuf[NC-3])
+ *pbp++ = *--b;
+ if (pbp >= &pbbuf[NC-3]) {
+ ERROR "cpushback overflow" WARN;
+ done(2);
+ }
+}
+
+int nextfile(void)
+{
+ char *p;
+
+n0:
+ if (ifile != stdin)
+ fclose(ifile);
+ if (ifi > 0 && !nx) {
+ if (popf())
+ goto n0; /* popf error */
+ return(1); /* popf ok */
+ }
+ if (nx || nmfi < mflg) {
+ p = mfiles[nmfi++];
+ if (*p != 0)
+ goto n1;
+ }
+ if (rargc-- <= 0) {
+ if ((nfo -= mflg) && !stdi) {
+ done(0);
+}
+ nfo++;
+ numtabp[CD].val = stdi = mflg = 0;
+ ifile = stdin;
+ strcpy(cfname[ifi], "stdin");
+ return(0);
+ }
+ p = (argp++)[0];
+ if (rargc >= 0)
+ cfname[ifi][0] = 0;
+n1:
+ numtabp[CD].val = 0;
+ if (p[0] == '-' && p[1] == 0) {
+ ifile = stdin;
+ strcpy(cfname[ifi], "stdin");
+ } else if ((ifile = fopen(unsharp(p), "r")) == NULL) {
+ ERROR "cannot open file %s", p WARN;
+ nfo -= mflg;
+ done(02);
+ } else
+ strcpy(cfname[ifi],p);
+ nfo++;
+ return(0);
+}
+
+
+popf(void)
+{
+ --ifi;
+ if (ifi < 0) {
+ ERROR "popf went negative" WARN;
+ return 1;
+ }
+ numtabp[CD].val = cfline[ifi]; /* restore line counter */
+ ip = ipl[ifi]; /* input pointer */
+ ifile = ifl[ifi]; /* input FILE * */
+ return(0);
+}
+
+
+void flushi(void)
+{
+ if (nflush)
+ return;
+ ch = 0;
+ copyf++;
+ while (!nlflg) {
+ if (donef && frame == stk)
+ break;
+ getch();
+ }
+ copyf--;
+}
+
+/*
+ * return 16-bit, ascii/alphabetic character, ignore chars with more bits,
+ * (internal names), spaces and special cookies (below 040).
+ * Leave STX ETX ENQ ACK and BELL in to maintain compatibility with v7 troff.
+ */
+getach(void)
+{
+ Tchar i;
+ int j;
+
+ lgf++;
+ j = cbits(i = getch());
+ if (ismot(i)
+ || j > SHORTMASK
+ || (j <= 040 && j != 002 /*STX*/
+ && j != 003 /*ETX*/
+ && j != 005 /*ENQ*/
+ && j != 006 /*ACK*/
+ && j != 007)) { /*BELL*/
+ ch = i;
+ j = 0;
+ }
+ lgf--;
+ return j;
+}
+
+
+void casenx(void)
+{
+ lgf++;
+ skip();
+ getname();
+ nx++;
+ if (nmfi > 0)
+ nmfi--;
+ strcpy(mfiles[nmfi], nextf);
+ nextfile();
+ nlflg++;
+ ip = 0;
+ pendt = 0;
+ frame = stk;
+ nxf = frame + 1;
+}
+
+
+getname(void)
+{
+ int j, k;
+ Tchar i;
+
+ lgf++;
+ for (k = 0; k < NS - 1; k++) {
+ j = getach();
+ if (!j)
+ break;
+ nextf[k] = j;
+ }
+ nextf[k] = 0;
+ lgf--;
+ return(nextf[0]);
+}
+
+
+void caseso(void)
+{
+ FILE *fp;
+ char *p, *q;
+
+ lgf++;
+ nextf[0] = 0;
+ if (skip() || !getname() || (fp = fopen(unsharp(nextf), "r")) == NULL || ifi >= NSO) {
+ ERROR "can't open file %s", nextf WARN;
+ done(02);
+ }
+ strcpy(cfname[ifi+1], nextf);
+ cfline[ifi] = numtabp[CD].val; /*hold line counter*/
+ numtabp[CD].val = 0;
+ flushi();
+ ifl[ifi] = ifile;
+ ifile = fp;
+ ipl[ifi] = ip;
+ ip = 0;
+ nx++;
+ nflush++;
+ ifi++;
+}
+
+void caself(void) /* set line number and file */
+{
+ int n;
+
+ if (skip())
+ return;
+ n = atoi0();
+ if (!nonumb)
+ cfline[ifi] = numtabp[CD].val = n - 1;
+ if (!skip())
+ if (getname()) { /* eats '\n' ? */
+ strcpy(cfname[ifi], nextf);
+ if (!nonumb)
+ numtabp[CD].val--;
+ }
+}
+
+void cpout(FILE *fin, char *token)
+{
+ int n;
+ char buf[1024];
+
+ if (token) { /* BUG: There should be no NULL bytes in input */
+ char *newl = buf;
+ while ((fgets(buf, sizeof buf, fin)) != NULL) {
+ if (newl) {
+ numtabp[CD].val++; /* line number */
+ if (strcmp(token, buf) == 0)
+ return;
+ }
+ newl = strchr(buf, '\n');
+ fputs(buf, ptid);
+ }
+ } else {
+ while ((n = fread(buf, sizeof *buf, sizeof buf, fin)) > 0)
+ fwrite(buf, n, 1, ptid);
+ fclose(fin);
+ }
+}
+
+void casecf(void)
+{ /* copy file without change */
+ FILE *fd;
+ char *eof, *p;
+ extern int hpos, esc, po;
+
+ /* this may not make much sense in nroff... */
+
+ lgf++;
+ nextf[0] = 0;
+ if (!skip() && getname()) {
+ if (strncmp("<<", nextf, 2) != 0) {
+ if ((fd = fopen(unsharp(nextf), "r")) == NULL) {
+ ERROR "can't open file %s", nextf WARN;
+ done(02);
+ }
+ eof = (char *) NULL;
+ } else { /* current file */
+ if (pbp > lastpbp || ip) {
+ ERROR "casecf: not reading from file" WARN;
+ done(02);
+ }
+ eof = &nextf[2];
+ if (!*eof) {
+ ERROR "casecf: missing end of input token" WARN;
+ done(02);
+ }
+ p = eof;
+ while(*++p)
+ ;
+ *p++ = '\n';
+ *p = 0;
+ fd = ifile;
+ }
+ } else {
+ ERROR "casecf: no argument" WARN;
+ lgf--;
+ return;
+ }
+ lgf--;
+
+ /* make it into a clean state, be sure that everything is out */
+ tbreak();
+ hpos = po;
+ esc = 0;
+ ptesc(); /* to left margin */
+ esc = un;
+ ptesc();
+ ptlead();
+ ptps();
+ ptfont();
+ flusho();
+ cpout(fd, eof);
+ ptps();
+ ptfont();
+}
+
+void getline(char *s, int n) /* get rest of input line into s */
+{
+ int i;
+
+ lgf++;
+ copyf++;
+ skip();
+ for (i = 0; i < n-1; i++)
+ if ((s[i] = cbits(getch())) == '\n' || s[i] == RIGHT)
+ break;
+ s[i] = 0;
+ copyf--;
+ lgf--;
+}
+
+void casesy(void) /* call system */
+{
+ char sybuf[NTM];
+
+ getline(sybuf, NTM);
+ system(sybuf);
+}
+
+
+void getpn(char *a)
+{
+ int n, neg;
+
+ if (*a == 0)
+ return;
+ neg = 0;
+ for ( ; *a; a++)
+ switch (*a) {
+ case '+':
+ case ',':
+ continue;
+ case '-':
+ neg = 1;
+ continue;
+ default:
+ n = 0;
+ if (isdigit(*a)) {
+ do
+ n = 10 * n + *a++ - '0';
+ while (isdigit(*a));
+ a--;
+ } else
+ n = 9999;
+ *pnp++ = neg ? -n : n;
+ neg = 0;
+ if (pnp >= &pnlist[NPN-2]) {
+ ERROR "too many page numbers" WARN;
+ done3(-3);
+ }
+ }
+ if (neg)
+ *pnp++ = -9999;
+ *pnp = -INT_MAX;
+ print = 0;
+ pnp = pnlist;
+ if (*pnp != -INT_MAX)
+ chkpn();
+}
+
+
+void setrpt(void)
+{
+ Tchar i, j;
+
+ copyf++;
+ raw++;
+ i = getch0();
+ copyf--;
+ raw--;
+ if ((long) i < 0 || cbits(j = getch0()) == RPT)
+ return;
+ while (i > 0 && pbp < &pbbuf[NC-3]) {
+ i--;
+ *pbp++ = j;
+ }
+}
diff --git a/src/cmd/troff/n10.c b/src/cmd/troff/n10.c
new file mode 100644
index 00000000..0183cadc
--- /dev/null
+++ b/src/cmd/troff/n10.c
@@ -0,0 +1,549 @@
+/*
+n10.c
+
+Device interfaces
+*/
+
+#include "tdef.h"
+#include "ext.h"
+#include "fns.h"
+#include <ctype.h>
+
+Term t; /* terminal characteristics */
+
+int dtab;
+int plotmode;
+int esct;
+
+enum { Notype = 0, Type = 1 };
+
+static char *parse(char *s, int typeit) /* convert \0, etc to nroff driving table format */
+{ /* typeit => add a type id to the front for later use */
+ static char buf[100], *t, *obuf;
+ int quote = 0;
+ wchar_t wc;
+
+ obuf = typeit == Type ? buf : buf+1;
+#ifdef UNICODE
+ if (mbtowc(&wc, s, strlen(s)) > 1) { /* it's multibyte, */
+ buf[0] = MBchar;
+ strcpy(buf+1, s);
+ return obuf;
+ } /* so just hand it back */
+#endif /*UNICODE*/
+ buf[0] = Troffchar;
+ t = buf + 1;
+ if (*s == '"') {
+ s++;
+ quote = 1;
+ }
+ for (;;) {
+ if (quote && *s == '"') {
+ s++;
+ break;
+ }
+ if (!quote && (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\0'))
+ break;
+ if (*s != '\\')
+ *t++ = *s++;
+ else {
+ s++; /* skip \\ */
+ if (isdigit(s[0]) && isdigit(s[1]) && isdigit(s[2])) {
+ *t++ = (s[0]-'0')<<6 | (s[1]-'0')<<3 | s[2]-'0';
+ s += 2;
+ } else if (isdigit(s[0])) {
+ *t++ = *s - '0';
+ } else if (*s == 'b') {
+ *t++ = '\b';
+ } else if (*s == 'n') {
+ *t++ = '\n';
+ } else if (*s == 'r') {
+ *t++ = '\r';
+ } else if (*s == 't') {
+ *t++ = '\t';
+ } else {
+ *t++ = *s;
+ }
+ s++;
+ }
+ }
+ *t = '\0';
+ return obuf;
+}
+
+
+static int getnrfont(FILE *fp) /* read the nroff description file */
+{
+ FILE *fin;
+ Chwid chtemp[NCHARS];
+ static Chwid chinit;
+ int i, nw, n, wid, code, type;
+ char buf[100], ch[100], s1[100], s2[100], cmd[300];
+ wchar_t wc;
+
+
+ chinit.wid = 1;
+ chinit.str = "";
+ for (i = 0; i < ALPHABET; i++) {
+ chtemp[i] = chinit; /* zero out to begin with */
+ chtemp[i].num = chtemp[i].code = i; /* every alphabetic character is itself */
+ chtemp[i].wid = 1; /* default ascii widths */
+ }
+ skipline(fp);
+ nw = ALPHABET;
+ while (fgets(buf, sizeof buf, fp) != NULL) {
+ sscanf(buf, "%s %s %[^\n]", ch, s1, s2);
+ if (!eq(s1, "\"")) { /* genuine new character */
+ sscanf(s1, "%d", &wid);
+ } /* else it's a synonym for prev character, */
+ /* so leave previous values intact */
+
+ /* decide what kind of alphabet it might come from */
+
+ if (strlen(ch) == 1) { /* it's ascii */
+ n = ch[0]; /* origin includes non-graphics */
+ chtemp[n].num = ch[0];
+ } else if (ch[0] == '\\' && ch[1] == '0') {
+ n = strtol(ch+1, 0, 0); /* \0octal or \0xhex */
+ chtemp[n].num = n;
+#ifdef UNICODE
+ } else if (mbtowc(&wc, ch, strlen(ch)) > 1) {
+ chtemp[nw].num = chadd(ch, MBchar, Install);
+ n = nw;
+ nw++;
+#endif /*UNICODE*/
+ } else {
+ if (strcmp(ch, "---") == 0) { /* no name */
+ sprintf(ch, "%d", code);
+ type = Number;
+ } else
+ type = Troffchar;
+/* BUG in here somewhere when same character occurs twice in table */
+ chtemp[nw].num = chadd(ch, type, Install);
+ n = nw;
+ nw++;
+ }
+ chtemp[n].wid = wid;
+ chtemp[n].str = strdupl(parse(s2, Type));
+ }
+ t.tfont.nchars = nw;
+ t.tfont.wp = (Chwid *) malloc(nw * sizeof(Chwid));
+ if (t.tfont.wp == NULL)
+ return -1;
+ for (i = 0; i < nw; i++)
+ t.tfont.wp[i] = chtemp[i];
+ return 1;
+}
+
+
+void n_ptinit(void)
+{
+ int i;
+ char *p, *cp;
+ char opt[50], cmd[100];
+ FILE *fp;
+
+ hmot = n_hmot;
+ makem = n_makem;
+ setabs = n_setabs;
+ setch = n_setch;
+ sethl = n_sethl;
+ setht = n_setht;
+ setslant = n_setslant;
+ vmot = n_vmot;
+ xlss = n_xlss;
+ findft = n_findft;
+ width = n_width;
+ mchbits = n_mchbits;
+ ptlead = n_ptlead;
+ ptout = n_ptout;
+ ptpause = n_ptpause;
+ setfont = n_setfont;
+ setps = n_setps;
+ setwd = n_setwd;
+
+ if ((p = getenv("NROFFTERM")) != 0)
+ strcpy(devname, p);
+ if (termtab[0] == 0)
+ strcpy(termtab,DWBntermdir);
+ if (fontdir[0] == 0)
+ strcpy(fontdir, "");
+ if (devname[0] == 0)
+ strcpy(devname, NDEVNAME);
+ pl = 11*INCH;
+ po = PO;
+ hyf = 0;
+ ascii = 1;
+ lg = 0;
+ fontlab[1] = 'R';
+ fontlab[2] = 'I';
+ fontlab[3] = 'B';
+ fontlab[4] = PAIR('B','I');
+ fontlab[5] = 'D';
+ bdtab[3] = 3;
+ bdtab[4] = 3;
+
+ /* hyphalg = 0; /* for testing */
+
+ strcat(termtab, devname);
+ if ((fp = fopen(unsharp(termtab), "r")) == NULL) {
+ ERROR "cannot open %s", termtab WARN;
+ exit(-1);
+ }
+
+
+/* this loop isn't robust about input format errors. */
+/* it assumes name, name-value pairs..., charset */
+/* god help us if we get out of sync. */
+
+ fscanf(fp, "%s", cmd); /* should be device name... */
+ if (!is(devname) && trace)
+ ERROR "wrong terminal name: saw %s, wanted %s", cmd, devname WARN;
+ for (;;) {
+ fscanf(fp, "%s", cmd);
+ if (is("charset"))
+ break;
+ fscanf(fp, " %[^\n]", opt);
+ if (is("bset")) t.bset = atoi(opt);
+ else if (is("breset")) t.breset = atoi(opt);
+ else if (is("Hor")) t.Hor = atoi(opt);
+ else if (is("Vert")) t.Vert = atoi(opt);
+ else if (is("Newline")) t.Newline = atoi(opt);
+ else if (is("Char")) t.Char = atoi(opt);
+ else if (is("Em")) t.Em = atoi(opt);
+ else if (is("Halfline")) t.Halfline = atoi(opt);
+ else if (is("Adj")) t.Adj = atoi(opt);
+ else if (is("twinit")) t.twinit = strdupl(parse(opt, Notype));
+ else if (is("twrest")) t.twrest = strdupl(parse(opt, Notype));
+ else if (is("twnl")) t.twnl = strdupl(parse(opt, Notype));
+ else if (is("hlr")) t.hlr = strdupl(parse(opt, Notype));
+ else if (is("hlf")) t.hlf = strdupl(parse(opt, Notype));
+ else if (is("flr")) t.flr = strdupl(parse(opt, Notype));
+ else if (is("bdon")) t.bdon = strdupl(parse(opt, Notype));
+ else if (is("bdoff")) t.bdoff = strdupl(parse(opt, Notype));
+ else if (is("iton")) t.iton = strdupl(parse(opt, Notype));
+ else if (is("itoff")) t.itoff = strdupl(parse(opt, Notype));
+ else if (is("ploton")) t.ploton = strdupl(parse(opt, Notype));
+ else if (is("plotoff")) t.plotoff = strdupl(parse(opt, Notype));
+ else if (is("up")) t.up = strdupl(parse(opt, Notype));
+ else if (is("down")) t.down = strdupl(parse(opt, Notype));
+ else if (is("right")) t.right = strdupl(parse(opt, Notype));
+ else if (is("left")) t.left = strdupl(parse(opt, Notype));
+ else
+ ERROR "bad tab.%s file, %s %s", devname, cmd, opt WARN;
+ }
+
+ getnrfont(fp);
+ fclose(fp);
+
+ sps = EM;
+ ics = EM * 2;
+ dtab = 8 * t.Em;
+ for (i = 0; i < 16; i++)
+ tabtab[i] = dtab * (i + 1);
+ pl = 11 * INCH;
+ po = PO;
+ spacesz = SS;
+ lss = lss1 = VS;
+ ll = ll1 = lt = lt1 = LL;
+ smnt = nfonts = 5; /* R I B BI S */
+ n_specnames(); /* install names like "hyphen", etc. */
+ if (eqflg)
+ t.Adj = t.Hor;
+}
+
+
+void n_specnames(void)
+{
+
+ int i;
+
+ for (i = 0; spnames[i].n; i++)
+ *spnames[i].n = chadd(spnames[i].v, Troffchar, Install);
+ if (c_isalnum == 0)
+ c_isalnum = NROFFCHARS;
+}
+
+void twdone(void)
+{
+ if (!TROFF && t.twrest) {
+ obufp = obuf;
+ oputs(t.twrest);
+ flusho();
+ if (pipeflg) {
+ pclose(ptid);
+ }
+ restore_tty();
+ }
+}
+
+
+void n_ptout(Tchar i)
+{
+ *olinep++ = i;
+ if (olinep >= &oline[LNSIZE])
+ olinep--;
+ if (cbits(i) != '\n')
+ return;
+ olinep--;
+ lead += dip->blss + lss - t.Newline;
+ dip->blss = 0;
+ esct = esc = 0;
+ if (olinep > oline) {
+ move();
+ ptout1();
+ oputs(t.twnl);
+ } else {
+ lead += t.Newline;
+ move();
+ }
+ lead += dip->alss;
+ dip->alss = 0;
+ olinep = oline;
+}
+
+
+void ptout1(void)
+{
+ int k;
+ char *codep;
+ int w, j, phyw;
+ Tchar *q, i;
+ static int oxfont = FT; /* start off in roman */
+
+ for (q = oline; q < olinep; q++) {
+ i = *q;
+ if (ismot(i)) {
+ j = absmot(i);
+ if (isnmot(i))
+ j = -j;
+ if (isvmot(i))
+ lead += j;
+ else
+ esc += j;
+ continue;
+ }
+ if ((k = cbits(i)) <= ' ') {
+ switch (k) {
+ case ' ': /*space*/
+ esc += t.Char;
+ break;
+ case '\033':
+ case '\007':
+ case '\016':
+ case '\017':
+ oput(k);
+ break;
+ }
+ continue;
+ }
+ phyw = w = t.Char * t.tfont.wp[k].wid;
+ if (iszbit(i))
+ w = 0;
+ if (esc || lead)
+ move();
+ esct += w;
+ xfont = fbits(i);
+ if (xfont != oxfont) {
+ switch (oxfont) {
+ case ULFONT: oputs(t.itoff); break;
+ case BDFONT: oputs(t.bdoff); break;
+ case BIFONT: oputs(t.itoff); oputs(t.bdoff); break;
+ }
+ switch (xfont) {
+ case ULFONT:
+ if (*t.iton & 0377) oputs(t.iton); break;
+ case BDFONT:
+ if (*t.bdon & 0377) oputs(t.bdon); break;
+ case BIFONT:
+ if (*t.bdon & 0377) oputs(t.bdon);
+ if (*t.iton & 0377) oputs(t.iton);
+ break;
+ }
+ oxfont = xfont;
+ }
+ if ((xfont == ulfont || xfont == BIFONT) && !(*t.iton & 0377)) {
+ for (j = w / t.Char; j > 0; j--)
+ oput('_');
+ for (j = w / t.Char; j > 0; j--)
+ oput('\b');
+ }
+ if (!(*t.bdon & 0377) && ((j = bdtab[xfont]) || xfont == BDFONT || xfont == BIFONT))
+ j++;
+ else
+ j = 1; /* number of overstrikes for bold */
+ if (k < ALPHABET) { /* ordinary ascii */
+ oput(k);
+ while (--j > 0) {
+ oput('\b');
+ oput(k);
+ }
+ } else if (k >= t.tfont.nchars) { /* BUG -- not really understood */
+/* fprintf(stderr, "big char %d, name %s\n", k, chname(k)); /* */
+ oputs(chname(k)+1); /* BUG: should separate Troffchar and MBchar... */
+ } else if (t.tfont.wp[k].str == 0) {
+/* fprintf(stderr, "nostr char %d, name %s\n", k, chname(k)); /* */
+ oputs(chname(k)+1); /* BUG: should separate Troffchar and MBchar... */
+ } else if (t.tfont.wp[k].str[0] == MBchar) { /* parse() puts this on */
+/* fprintf(stderr, "MBstr char %d, name %s\n", k, chname(k)); /* */
+ oputs(t.tfont.wp[k].str+1);
+ } else {
+ int oj = j;
+/* fprintf(stderr, "str char %d, name %s\n", k, chname(k)); /* */
+ codep = t.tfont.wp[k].str+1; /* Troffchar by default */
+ while (*codep != 0) {
+ if (*codep & 0200) {
+ codep = plot(codep);
+ oput(' ');
+ } else {
+ if (*codep == '%') /* escape */
+ codep++;
+ oput(*codep);
+ if (*codep == '\033')
+ oput(*++codep);
+ else if (*codep != '\b')
+ for (j = oj; --j > 0; ) {
+ oput('\b');
+ oput(*codep);
+ }
+ codep++;
+ }
+ }
+ }
+ if (!w)
+ for (j = phyw / t.Char; j > 0; j--)
+ oput('\b');
+ }
+}
+
+
+char *plot(char *x)
+{
+ int i;
+ char *j, *k;
+
+ oputs(t.ploton);
+ k = x;
+ if ((*k & 0377) == 0200)
+ k++;
+ for (; *k; k++) {
+ if (*k == '%') { /* quote char within plot mode */
+ oput(*++k);
+ } else if (*k & 0200) {
+ if (*k & 0100) {
+ if (*k & 040)
+ j = t.up;
+ else
+ j = t.down;
+ } else {
+ if (*k & 040)
+ j = t.left;
+ else
+ j = t.right;
+ }
+ if ((i = *k & 037) == 0) { /* 2nd 0200 turns it off */
+ ++k;
+ break;
+ }
+ while (i--)
+ oputs(j);
+ } else
+ oput(*k);
+ }
+ oputs(t.plotoff);
+ return(k);
+}
+
+
+void move(void)
+{
+ int k;
+ char *i, *j;
+ char *p, *q;
+ int iesct, dt;
+
+ iesct = esct;
+ if (esct += esc)
+ i = "\0";
+ else
+ i = "\n\0";
+ j = t.hlf;
+ p = t.right;
+ q = t.down;
+ if (lead) {
+ if (lead < 0) {
+ lead = -lead;
+ i = t.flr;
+ /* if(!esct)i = t.flr; else i = "\0";*/
+ j = t.hlr;
+ q = t.up;
+ }
+ if (*i & 0377) {
+ k = lead / t.Newline;
+ lead = lead % t.Newline;
+ while (k--)
+ oputs(i);
+ }
+ if (*j & 0377) {
+ k = lead / t.Halfline;
+ lead = lead % t.Halfline;
+ while (k--)
+ oputs(j);
+ } else { /* no half-line forward, not at line begining */
+ k = lead / t.Newline;
+ lead = lead % t.Newline;
+ if (k > 0)
+ esc = esct;
+ i = "\n";
+ while (k--)
+ oputs(i);
+ }
+ }
+ if (esc) {
+ if (esc < 0) {
+ esc = -esc;
+ j = "\b";
+ p = t.left;
+ } else {
+ j = " ";
+ if (hflg)
+ while ((dt = dtab - (iesct % dtab)) <= esc) {
+ if (dt % t.Em)
+ break;
+ oput(TAB);
+ esc -= dt;
+ iesct += dt;
+ }
+ }
+ k = esc / t.Em;
+ esc = esc % t.Em;
+ while (k--)
+ oputs(j);
+ }
+ if ((*t.ploton & 0377) && (esc || lead)) {
+ oputs(t.ploton);
+ esc /= t.Hor;
+ lead /= t.Vert;
+ while (esc--)
+ oputs(p);
+ while (lead--)
+ oputs(q);
+ oputs(t.plotoff);
+ }
+ esc = lead = 0;
+}
+
+
+void n_ptlead(void)
+{
+ move();
+}
+
+
+void n_ptpause(void )
+{
+ char junk;
+
+ flusho();
+ read(2, &junk, 1);
+}
diff --git a/src/cmd/troff/n2.c b/src/cmd/troff/n2.c
new file mode 100644
index 00000000..8164c038
--- /dev/null
+++ b/src/cmd/troff/n2.c
@@ -0,0 +1,325 @@
+/*
+ * n2.c
+ *
+ * output, cleanup
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+#include <setjmp.h>
+
+#ifdef STRICT
+ /* not in ANSI or POSIX */
+FILE* popen(char*, char*);
+#endif
+
+
+extern jmp_buf sjbuf;
+int toolate;
+int error;
+
+char obuf[2*BUFSIZ];
+char *obufp = obuf;
+
+ /* pipe command structure; allows redicously long commends for .pi */
+struct Pipe {
+ char *buf;
+ int tick;
+ int cnt;
+} Pipe;
+
+
+int xon = 0; /* records if in middle of \X */
+
+int pchar(Tchar i)
+{
+ int j;
+ static int hx = 0; /* records if have seen HX */
+
+ if (hx) {
+ hx = 0;
+ j = absmot(i);
+ if (isnmot(i)) {
+ if (j > dip->blss)
+ dip->blss = j;
+ } else {
+ if (j > dip->alss)
+ dip->alss = j;
+ ralss = dip->alss;
+ }
+ return 0;
+ }
+ if (ismot(i)) {
+ pchar1(i);
+ return 0;
+ }
+ switch (j = cbits(i)) {
+ case 0:
+ case IMP:
+ case RIGHT:
+ case LEFT:
+ return 0;
+ case HX:
+ hx = 1;
+ return 0;
+ case XON:
+ xon++;
+ break;
+ case XOFF:
+ xon--;
+ break;
+ case PRESC:
+ if (!xon && !tflg && dip == &d[0])
+ j = eschar; /* fall through */
+ default:
+ setcbits(i, trtab[j]);
+ }
+ if (NROFF & xon) /* rob fix for man2html */
+ return 0;
+ pchar1(i);
+ return 0;
+}
+
+
+void pchar1(Tchar i)
+{
+ int j;
+
+ j = cbits(i);
+ if (dip != &d[0]) {
+ wbf(i);
+ dip->op = offset;
+ return;
+ }
+ if (!tflg && !print) {
+ if (j == '\n')
+ dip->alss = dip->blss = 0;
+ return;
+ }
+ if (j == FILLER && !xon)
+ return;
+ if (tflg) { /* transparent mode, undiverted */
+ if (print) /* assumes that it's ok to print */
+ /* OUT "%c", j PUT; /* i.e., is ascii */
+ outascii(i);
+ return;
+ }
+ if (TROFF && ascii)
+ outascii(i);
+ else
+ ptout(i);
+}
+
+
+void outweird(int k) /* like ptchname() but ascii */
+{
+ char *chn = chname(k);
+
+ switch (chn[0]) {
+ case MBchar:
+ OUT "%s", chn+1 PUT; /* \n not needed? */
+ break;
+ case Number:
+ OUT "\\N'%s'", chn+1 PUT;
+ break;
+ case Troffchar:
+ if (strlen(chn+1) == 2)
+ OUT "\\(%s", chn+1 PUT;
+ else
+ OUT "\\C'%s'", chn+1 PUT;
+ break;
+ default:
+ OUT " %s? ", chn PUT;
+ break;
+ }
+}
+
+void outascii(Tchar i) /* print i in best-guess ascii */
+{
+ char *p;
+ int j = cbits(i);
+
+/* is this ever called with NROFF set? probably doesn't work at all. */
+
+ if (ismot(i))
+ oput(' ');
+ else if (j < ALPHABET && j >= ' ' || j == '\n' || j == '\t')
+ oput(j);
+ else if (j == DRAWFCN)
+ oputs("\\D");
+ else if (j == HYPHEN)
+ oput('-');
+ else if (j == MINUS) /* special pleading for strange encodings */
+ oputs("\\-");
+ else if (j == PRESC)
+ oputs("\\e");
+ else if (j == FILLER)
+ oputs("\\&");
+ else if (j == UNPAD)
+ oputs("\\ ");
+ else if (j == OHC) /* this will never occur; stripped out earlier */
+ oputs("\\%");
+ else if (j == XON)
+ oputs("\\X");
+ else if (j == XOFF)
+ oputs(" ");
+ else if (j == LIG_FI)
+ oputs("fi");
+ else if (j == LIG_FL)
+ oputs("fl");
+ else if (j == LIG_FF)
+ oputs("ff");
+ else if (j == LIG_FFI)
+ oputs("ffi");
+ else if (j == LIG_FFL)
+ oputs("ffl");
+ else if (j == WORDSP) { /* nothing at all */
+ if (xon) /* except in \X */
+ oput(' ');
+
+ } else
+ outweird(j);
+}
+
+int flusho(void)
+{
+ if (NROFF && !toolate && t.twinit)
+ fwrite(t.twinit, strlen(t.twinit), 1, ptid);
+
+ if (obufp > obuf) {
+ if (pipeflg && !toolate) {
+ /* fprintf(stderr, "Pipe to <%s>\n", Pipe.buf); */
+ if (!Pipe.buf[0] || (ptid = popen(Pipe.buf, "w")) == NULL)
+ ERROR "pipe %s not created.", Pipe.buf WARN;
+ if (Pipe.buf)
+ free(Pipe.buf);
+ }
+ if (!toolate)
+ toolate++;
+ *obufp = 0;
+ fputs(obuf, ptid);
+ fflush(ptid);
+ obufp = obuf;
+ }
+ return 1;
+}
+
+
+void caseex(void)
+{
+ done(0);
+}
+
+
+void done(int x)
+{
+ int i;
+
+ error |= x;
+ app = ds = lgf = 0;
+ if (i = em) {
+ donef = -1;
+ eschar = '\\';
+ em = 0;
+ if (control(i, 0))
+ longjmp(sjbuf, 1);
+ }
+ if (!nfo)
+ done3(0);
+ mflg = 0;
+ dip = &d[0];
+ if (woff) /* BUG!!! This isn't set anywhere */
+ wbf((Tchar)0);
+ if (pendw)
+ getword(1);
+ pendnf = 0;
+ if (donef == 1)
+ done1(0);
+ donef = 1;
+ ip = 0;
+ frame = stk;
+ nxf = frame + 1;
+ if (!ejf)
+ tbreak();
+ nflush++;
+ eject((Stack *)0);
+ longjmp(sjbuf, 1);
+}
+
+
+void done1(int x)
+{
+ error |= x;
+ if (numtabp[NL].val) {
+ trap = 0;
+ eject((Stack *)0);
+ longjmp(sjbuf, 1);
+ }
+ if (!ascii)
+ pttrailer();
+ done2(0);
+}
+
+
+void done2(int x)
+{
+ ptlead();
+ if (TROFF && !ascii)
+ ptstop();
+ flusho();
+ done3(x);
+}
+
+void done3(int x)
+{
+ error |= x;
+ flusho();
+ if (NROFF)
+ twdone();
+ if (pipeflg)
+ pclose(ptid);
+ exit(error);
+}
+
+
+void edone(int x)
+{
+ frame = stk;
+ nxf = frame + 1;
+ ip = 0;
+ done(x);
+}
+
+
+void casepi(void)
+{
+ int j;
+ char buf[NTM];
+
+ if (Pipe.buf == NULL) {
+ if ((Pipe.buf = (char *)calloc(NTM, sizeof(char))) == NULL) {
+ ERROR "No buf space for pipe cmd" WARN;
+ return;
+ }
+ Pipe.tick = 1;
+ } else
+ Pipe.buf[Pipe.cnt++] = '|';
+
+ getline(buf, NTM);
+ j = strlen(buf);
+ if (toolate) {
+ ERROR "Cannot create pipe to %s", buf WARN;
+ return;
+ }
+ Pipe.cnt += j;
+ if (j >= NTM +1) {
+ Pipe.tick++;
+ if ((Pipe.buf = (char *)realloc(Pipe.buf, Pipe.tick * NTM * sizeof(char))) == NULL) {
+ ERROR "No more buf space for pipe cmd" WARN;
+ return;
+ }
+ }
+ strcat(Pipe.buf, buf);
+ pipeflg++;
+}
diff --git a/src/cmd/troff/n3.c b/src/cmd/troff/n3.c
new file mode 100644
index 00000000..6918d06d
--- /dev/null
+++ b/src/cmd/troff/n3.c
@@ -0,0 +1,954 @@
+/*
+ * troff3.c
+ *
+ * macro and string routines, storage allocation
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+Tchar *argtop;
+int pagech = '%';
+int strflg;
+
+#define MHASHSIZE 128 /* must be 2**n */
+#define MHASH(x) ((x>>6)^x) & (MHASHSIZE-1)
+Contab *mhash[MHASHSIZE];
+
+
+Blockp *blist; /* allocated blocks for macros and strings */
+int nblist; /* how many there are */
+int bfree = -1; /* first (possible) free block in the list */
+
+Contab *contabp = NULL;
+#define MDELTA 500
+int nm = 0;
+
+int savname; /* name of macro/string being defined */
+int savslot; /* place in Contab of savname */
+int freeslot = -1; /* first (possible) free slot in contab */
+
+void prcontab(Contab *p)
+{
+ int i;
+ for (i = 0; i < nm; i++)
+ if (p)
+ if (p[i].rq != 0)
+ fprintf(stderr, "slot %d, %-2.2s\n", i, unpair(p[i].rq));
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+}
+
+
+void blockinit(void)
+{
+ blist = (Blockp *) calloc(NBLIST, sizeof(Blockp));
+ if (blist == NULL) {
+ ERROR "not enough room for %d blocks", NBLIST WARN;
+ done2(1);
+ }
+ nblist = NBLIST;
+ blist[0].nextoff = blist[1].nextoff = -1;
+ blist[0].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
+ blist[1].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
+ /* -1 prevents blist[0] from being used; temporary fix */
+ /* for a design botch: offset==0 is overloaded. */
+ /* blist[1] reserved for .rd indicator -- also unused. */
+ /* but someone unwittingly looks at these, so allocate something */
+ bfree = 2;
+}
+
+
+char *grow(char *ptr, int num, int size) /* make array bigger */
+{
+ char *p, new;
+
+ if (ptr == NULL)
+ p = (char *) calloc(num, size);
+ else
+ p = (char *) realloc(ptr, num * size);
+ return p;
+}
+
+void mnspace(void)
+{
+ nm = sizeof(contab)/sizeof(Contab) + MDELTA;
+ freeslot = sizeof(contab)/sizeof(Contab) + 1;
+ contabp = (Contab *) grow((char *) contabp, nm, sizeof(Contab));
+ if (contabp == NULL) {
+ ERROR "not enough memory for namespace of %d marcos", nm WARN;
+ exit(1);
+ }
+ contabp = (Contab *) memcpy((char *) contabp, (char *)contab,
+ sizeof(contab));
+ if (contabp == NULL) {
+ ERROR "Cannot reinitialize macro/request name list" WARN;
+ exit(1);
+ }
+
+}
+
+void caseig(void)
+{
+ int i;
+ Offset oldoff = offset;
+
+ offset = 0;
+ i = copyb();
+ offset = oldoff;
+ if (i != '.')
+ control(i, 1);
+}
+
+
+void casern(void)
+{
+ int i, j, k;
+
+ lgf++;
+ skip();
+ if ((i = getrq()) == 0 || (oldmn = findmn(i)) < 0)
+ return;
+ skip();
+ clrmn(findmn(j = getrq()));
+ if (j) {
+ munhash(&contabp[oldmn]);
+ contabp[oldmn].rq = j;
+ maddhash(&contabp[oldmn]);
+ if (dip != d )
+ for (k = dilev; k; k--)
+ if (d[k].curd == i)
+ d[k].curd = j;
+ }
+}
+
+void maddhash(Contab *rp)
+{
+ Contab **hp;
+
+ if (rp->rq == 0)
+ return;
+ hp = &mhash[MHASH(rp->rq)];
+ rp->link = *hp;
+ *hp = rp;
+}
+
+void munhash(Contab *mp)
+{
+ Contab *p;
+ Contab **lp;
+
+ if (mp->rq == 0)
+ return;
+ lp = &mhash[MHASH(mp->rq)];
+ p = *lp;
+ while (p) {
+ if (p == mp) {
+ *lp = p->link;
+ p->link = 0;
+ return;
+ }
+ lp = &p->link;
+ p = p->link;
+ }
+}
+
+void mrehash(void)
+{
+ Contab *p;
+ int i;
+
+ for (i=0; i < MHASHSIZE; i++)
+ mhash[i] = 0;
+ for (p=contabp; p < &contabp[nm]; p++)
+ p->link = 0;
+ for (p=contabp; p < &contabp[nm]; p++) {
+ if (p->rq == 0)
+ continue;
+ i = MHASH(p->rq);
+ p->link = mhash[i];
+ mhash[i] = p;
+ }
+}
+
+void caserm(void)
+{
+ int j;
+ int k = 0;
+
+ lgf++;
+g0:
+ while (!skip() && (j = getrq()) != 0) {
+ if (dip != d)
+ for (k = dilev; k; k--)
+ if (d[k].curd == j) {
+ ERROR "cannot remove diversion %s during definition",
+ unpair(j) WARN;
+ goto g0;
+ }
+ clrmn(findmn(j));
+ }
+ lgf--;
+}
+
+
+void caseas(void)
+{
+ app++;
+ caseds();
+}
+
+
+void caseds(void)
+{
+ ds++;
+ casede();
+}
+
+
+void caseam(void)
+{
+ app++;
+ casede();
+}
+
+
+void casede(void)
+{
+ int i, req;
+ Offset savoff;
+
+ req = '.';
+ lgf++;
+ skip();
+ if ((i = getrq()) == 0)
+ goto de1;
+ if ((offset = finds(i)) == 0)
+ goto de1;
+ if (newmn)
+ savslot = newmn;
+ else
+ savslot = findmn(i);
+ savname = i;
+ if (ds)
+ copys();
+ else
+ req = copyb();
+ clrmn(oldmn);
+ if (newmn) {
+ if (contabp[newmn].rq)
+ munhash(&contabp[newmn]);
+ contabp[newmn].rq = i;
+ maddhash(&contabp[newmn]);
+
+ }
+ if (apptr) {
+ savoff = offset;
+ offset = apptr;
+ wbf((Tchar) IMP);
+ offset = savoff;
+ }
+ offset = dip->op;
+ if (req != '.')
+ control(req, 1);
+de1:
+ ds = app = 0;
+}
+
+
+int findmn(int i)
+{
+ Contab *p;
+
+ for (p = mhash[MHASH(i)]; p; p = p->link)
+ if (i == p->rq)
+ return(p - contabp);
+ return(-1);
+}
+
+
+void clrmn(int i)
+{
+ if (i >= 0) {
+ if (contabp[i].mx)
+ ffree(contabp[i].mx);
+ munhash(&contabp[i]);
+ contabp[i].rq = 0;
+ contabp[i].mx = 0;
+ contabp[i].emx = 0;
+ contabp[i].f = 0;
+ if (contabp[i].divsiz != NULL) {
+ free(contabp[i].divsiz);
+ contabp[i].divsiz = NULL;
+ }
+ if (freeslot > i)
+ freeslot = i;
+ }
+}
+
+void growcontab(void)
+{
+ nm += MDELTA;
+ contabp = (Contab *) grow((char *) contabp , nm, sizeof(Contab));
+ if (contabp == NULL) {
+ ERROR "Too many (%d) string/macro names", nm WARN;
+ done2(02);
+ } else {
+ memset((char *)(contabp) + (nm - MDELTA) * sizeof(Contab),
+ 0, MDELTA * sizeof(Contab));
+ mrehash();
+ }
+}
+
+
+Offset finds(int mn)
+{
+ int i;
+ Tchar j = IMP;
+ Offset savip;
+
+ oldmn = findmn(mn);
+ newmn = 0;
+ apptr = 0;
+ if (app && oldmn >= 0 && contabp[oldmn].mx) {
+ savip = ip;
+ ip = contabp[oldmn].emx;
+ oldmn = -1;
+ apptr = ip;
+ if (!diflg)
+ ip = incoff(ip);
+ nextb = ip;
+ ip = savip;
+ } else {
+ for (i = freeslot; i < nm; i++) {
+ if (contabp[i].rq == 0)
+ break;
+ }
+ if (i == nm)
+ growcontab();
+ freeslot = i + 1;
+ if ((nextb = alloc()) == -1) {
+ app = 0;
+ if (macerr++ > 1)
+ done2(02);
+ if (nextb == 0)
+ ERROR "Not enough space for string/macro names" WARN;
+ edone(04);
+ return(offset = 0);
+ }
+ contabp[i].mx = nextb;
+ if (!diflg) {
+ newmn = i;
+ if (oldmn == -1)
+ contabp[i].rq = -1;
+ } else {
+ contabp[i].rq = mn;
+ maddhash(&contabp[i]);
+ }
+ }
+ app = 0;
+ return(offset = nextb);
+}
+
+int skip(void)
+{
+ Tchar i;
+
+ while (cbits(i = getch()) == ' ' || ismot(i))
+ ;
+ ch = i;
+ return(nlflg);
+}
+
+
+int copyb(void)
+{
+ int i, j, state;
+ Tchar ii;
+ int req, k;
+ Offset savoff;
+ Uchar *p;
+
+ if (skip() || !(j = getrq()))
+ j = '.';
+ req = j;
+ p = unpair(j);
+ /* was: k = j >> BYTE; j &= BYTEMASK; */
+ j = p[0];
+ k = p[1];
+ copyf++;
+ flushi();
+ nlflg = 0;
+ state = 1;
+
+/* state 0 eat up
+ * state 1 look for .
+ * state 2 look for first char of end macro
+ * state 3 look for second char of end macro
+ */
+
+ while (1) {
+ i = cbits(ii = getch());
+ if (state == 3) {
+ if (i == k)
+ break;
+ if (!k) {
+ ch = ii;
+ i = getach();
+ ch = ii;
+ if (!i)
+ break;
+ }
+ state = 0;
+ goto c0;
+ }
+ if (i == '\n') {
+ state = 1;
+ nlflg = 0;
+ goto c0;
+ }
+ if (state == 1 && i == '.') {
+ state++;
+ savoff = offset;
+ goto c0;
+ }
+ if (state == 2 && i == j) {
+ state++;
+ goto c0;
+ }
+ state = 0;
+c0:
+ if (offset)
+ wbf(ii);
+ }
+ if (offset) {
+ offset = savoff;
+ wbf((Tchar)0);
+ }
+ copyf--;
+ return(req);
+}
+
+
+void copys(void)
+{
+ Tchar i;
+
+ copyf++;
+ if (skip())
+ goto c0;
+ if (cbits(i = getch()) != '"')
+ wbf(i);
+ while (cbits(i = getch()) != '\n')
+ wbf(i);
+c0:
+ wbf((Tchar)0);
+ copyf--;
+}
+
+
+Offset alloc(void) /* return free Offset in nextb */
+{
+ int i, j;
+
+ for (i = bfree; i < nblist; i++)
+ if (blist[i].nextoff == 0)
+ break;
+ if (i == nblist) {
+ blist = (Blockp *) realloc((char *) blist, 2 * nblist * sizeof(Blockp));
+ if (blist == NULL) {
+ ERROR "can't grow blist for string/macro defns" WARN;
+ done2(2);
+ }
+ nblist *= 2;
+ for (j = i; j < nblist; j++) {
+ blist[j].nextoff = 0;
+ blist[j].bp = 0;
+ }
+ }
+ blist[i].nextoff = -1; /* this block is the end */
+ bfree = i + 1;
+ if (blist[i].bp == 0)
+ blist[i].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
+ if (blist[i].bp == NULL) {
+ ERROR "can't allocate memory for string/macro definitions" WARN;
+ done2(2);
+ }
+ nextb = (Offset) i * BLK;
+ return nextb;
+}
+
+
+void ffree(Offset i) /* free list of blocks starting at blist(o) */
+{ /* (doesn't actually free the blocks, just the pointers) */
+ int j;
+
+ for ( ; blist[j = bindex(i)].nextoff != -1; ) {
+ if (bfree > j)
+ bfree = j;
+ i = blist[j].nextoff;
+ blist[j].nextoff = 0;
+ }
+ blist[j].nextoff = 0;
+}
+
+
+void wbf(Tchar i) /* store i into offset, get ready for next one */
+{
+ int j, off;
+
+ if (!offset)
+ return;
+ j = bindex(offset);
+ if (i == 0)
+ contabp[savslot].emx = offset;
+ off = boffset(offset);
+ blist[j].bp[off++] = i;
+ offset++;
+ if (pastend(offset)) { /* off the end of this block */
+ if (blist[j].nextoff == -1) {
+ if ((nextb = alloc()) == -1) {
+ ERROR "Out of temp file space" WARN;
+ done2(01);
+ }
+ blist[j].nextoff = nextb;
+ }
+ offset = blist[j].nextoff;
+ }
+}
+
+
+Tchar rbf(void) /* return next char from blist[] block */
+{
+ Tchar i, j;
+
+ if (ip == RD_OFFSET) { /* for rdtty */
+ if (j = rdtty())
+ return(j);
+ else
+ return(popi());
+ }
+
+ i = rbf0(ip);
+ if (i == 0) {
+ if (!app)
+ i = popi();
+ return(i);
+ }
+ ip = incoff(ip);
+ return(i);
+}
+
+
+Offset xxxincoff(Offset p) /* get next blist[] block */
+{
+ p++;
+ if (pastend(p)) { /* off the end of this block */
+ if ((p = blist[bindex(p-1)].nextoff) == -1) { /* and nothing was allocated after it */
+ ERROR "Bad storage allocation" WARN;
+ done2(-5);
+ }
+ }
+ return(p);
+}
+
+
+Tchar popi(void)
+{
+ Stack *p;
+
+ if (frame == stk)
+ return(0);
+ if (strflg)
+ strflg--;
+ p = nxf = frame;
+ p->nargs = 0;
+ frame = p->pframe;
+ ip = p->pip;
+ pendt = p->ppendt;
+ lastpbp = p->lastpbp;
+ return(p->pch);
+}
+
+/*
+ * test that the end of the allocation is above a certain location
+ * in memory
+ */
+#define SPACETEST(base, size) \
+ if ((char*)base + size >= (char*)stk+STACKSIZE) \
+ ERROR "Stacksize overflow in n3" WARN
+
+Offset pushi(Offset newip, int mname)
+{
+ Stack *p;
+
+ SPACETEST(nxf, sizeof(Stack));
+ p = nxf;
+ p->pframe = frame;
+ p->pip = ip;
+ p->ppendt = pendt;
+ p->pch = ch;
+ p->lastpbp = lastpbp;
+ p->mname = mname;
+ lastpbp = pbp;
+ pendt = ch = 0;
+ frame = nxf;
+ if (nxf->nargs == 0)
+ nxf += 1;
+ else
+ nxf = (Stack *)argtop;
+ return(ip = newip);
+}
+
+
+void *setbrk(int x)
+{
+ char *i;
+
+ if ((i = (char *) calloc(x, 1)) == 0) {
+ ERROR "Core limit reached" WARN;
+ edone(0100);
+ }
+ return(i);
+}
+
+
+int getsn(void)
+{
+ int i;
+
+ if ((i = getach()) == 0)
+ return(0);
+ if (i == '(')
+ return(getrq());
+ else
+ return(i);
+}
+
+
+Offset setstr(void)
+{
+ int i, j;
+
+ lgf++;
+ if ((i = getsn()) == 0 || (j = findmn(i)) == -1 || !contabp[j].mx) {
+ lgf--;
+ return(0);
+ } else {
+ SPACETEST(nxf, sizeof(Stack));
+ nxf->nargs = 0;
+ strflg++;
+ lgf--;
+ return pushi(contabp[j].mx, i);
+ }
+}
+
+
+
+void collect(void)
+{
+ int j;
+ Tchar i, *strp, *lim, **argpp, **argppend;
+ int quote;
+ Stack *savnxf;
+
+ copyf++;
+ nxf->nargs = 0;
+ savnxf = nxf;
+ if (skip())
+ goto rtn;
+
+ {
+ char *memp;
+ memp = (char *)savnxf;
+ /*
+ * 1 s structure for the macro descriptor
+ * APERMAC Tchar *'s for pointers into the strings
+ * space for the Tchar's themselves
+ */
+ memp += sizeof(Stack);
+ /*
+ * CPERMAC = the total # of characters for ALL arguments
+ */
+#define CPERMAC 200
+#define APERMAC 9
+ memp += APERMAC * sizeof(Tchar *);
+ memp += CPERMAC * sizeof(Tchar);
+ nxf = (Stack *)memp;
+ }
+ lim = (Tchar *)nxf;
+ argpp = (Tchar **)(savnxf + 1);
+ argppend = &argpp[APERMAC];
+ SPACETEST(argppend, sizeof(Tchar *));
+ strp = (Tchar *)argppend;
+ /*
+ * Zero out all the string pointers before filling them in.
+ */
+ for (j = 0; j < APERMAC; j++)
+ argpp[j] = 0;
+ /* ERROR "savnxf=0x%x,nxf=0x%x,argpp=0x%x,strp=argppend=0x%x, lim=0x%x",
+ * savnxf, nxf, argpp, strp, lim WARN;
+ */
+ strflg = 0;
+ while (argpp != argppend && !skip()) {
+ *argpp++ = strp;
+ quote = 0;
+ if (cbits(i = getch()) == '"')
+ quote++;
+ else
+ ch = i;
+ while (1) {
+ i = getch();
+/* fprintf(stderr, "collect %c %d\n", cbits(i), cbits(i)); */
+ if (nlflg || (!quote && argpp != argppend && cbits(i) == ' '))
+ break; /* collects rest into $9 */
+ if ( quote
+ && cbits(i) == '"'
+ && cbits(i = getch()) != '"') {
+ ch = i;
+ break;
+ }
+ *strp++ = i;
+ if (strflg && strp >= lim) {
+ /* ERROR "strp=0x%x, lim = 0x%x", strp, lim WARN; */
+ ERROR "Macro argument too long" WARN;
+ copyf--;
+ edone(004);
+ }
+ SPACETEST(strp, 3 * sizeof(Tchar));
+ }
+ *strp++ = 0;
+ }
+ nxf = savnxf;
+ nxf->nargs = argpp - (Tchar **)(savnxf + 1);
+ argtop = strp;
+rtn:
+ copyf--;
+}
+
+
+void seta(void)
+{
+ int i;
+
+ i = cbits(getch()) - '0';
+ if (i > 0 && i <= APERMAC && i <= frame->nargs)
+ pushback(*(((Tchar **)(frame + 1)) + i - 1));
+}
+
+
+void caseda(void)
+{
+ app++;
+ casedi();
+}
+
+void casegd(void)
+{
+ int i, j;
+
+ skip();
+ if ((i = getrq()) == 0)
+ return;
+ if ((j = findmn(i)) >= 0) {
+ if (contabp[j].divsiz != NULL) {
+ numtabp[DN].val = contabp[j].divsiz->dix;
+ numtabp[DL].val = contabp[j].divsiz->diy;
+ }
+ }
+}
+
+#define FINDDIV(o) if ((o = findmn(dip->curd)) < 0) \
+ ERROR "lost diversion %s", unpair(dip->curd) WARN
+
+void casedi(void)
+{
+ int i, j, *k;
+
+ lgf++;
+ if (skip() || (i = getrq()) == 0) {
+ if (dip != d) {
+ FINDDIV(savslot);
+ wbf((Tchar)0);
+ }
+ if (dilev > 0) {
+ numtabp[DN].val = dip->dnl;
+ numtabp[DL].val = dip->maxl;
+ FINDDIV(j);
+ if ((contabp[j].divsiz = (Divsiz *) malloc(sizeof(Divsiz))) == NULL) {
+ ERROR "Cannot alloc diversion size" WARN;
+ done2(1);
+ } else {
+ contabp[j].divsiz->dix = numtabp[DN].val;
+ contabp[j].divsiz->diy = numtabp[DL].val;
+ }
+ dip = &d[--dilev];
+ offset = dip->op;
+ }
+ goto rtn;
+ }
+ if (++dilev == NDI) {
+ --dilev;
+ ERROR "Diversions nested too deep" WARN;
+ edone(02);
+ }
+ if (dip != d) {
+ FINDDIV(j);
+ savslot = j;
+ wbf((Tchar)0);
+ }
+ diflg++;
+ dip = &d[dilev];
+ dip->op = finds(i);
+ dip->curd = i;
+ clrmn(oldmn);
+ k = (int *) & dip->dnl;
+ for (j = 0; j < 10; j++)
+ k[j] = 0; /*not op and curd*/
+rtn:
+ app = 0;
+ diflg = 0;
+}
+
+
+void casedt(void)
+{
+ lgf++;
+ dip->dimac = dip->ditrap = dip->ditf = 0;
+ skip();
+ dip->ditrap = vnumb((int *)0);
+ if (nonumb)
+ return;
+ skip();
+ dip->dimac = getrq();
+}
+
+#define LNSIZE 4000
+void casetl(void)
+{
+ int j;
+ int w[3];
+ Tchar buf[LNSIZE];
+ Tchar *tp;
+ Tchar i, delim;
+
+ /*
+ * bug fix
+ *
+ * if .tl is the first thing in the file, the p1
+ * doesn't come out, also the pagenumber will be 0
+ *
+ * tends too confuse the device filter (and the user as well)
+ */
+ if (dip == d && numtabp[NL].val == -1)
+ newline(1);
+ dip->nls = 0;
+ skip();
+ if (ismot(delim = getch())) {
+ ch = delim;
+ delim = '\'';
+ } else
+ delim = cbits(delim);
+ tp = buf;
+ numtabp[HP].val = 0;
+ w[0] = w[1] = w[2] = 0;
+ j = 0;
+ while (cbits(i = getch()) != '\n') {
+ if (cbits(i) == cbits(delim)) {
+ if (j < 3)
+ w[j] = numtabp[HP].val;
+ numtabp[HP].val = 0;
+ if (w[j] != 0)
+ *tp++ = WORDSP;
+ j++;
+ *tp++ = 0;
+ } else {
+ if (cbits(i) == pagech) {
+ setn1(numtabp[PN].val, numtabp[findr('%')].fmt,
+ i&SFMASK);
+ continue;
+ }
+ numtabp[HP].val += width(i);
+ if (tp < &buf[LNSIZE-10]) {
+ if (cbits(i) == ' ' && *tp != WORDSP)
+ *tp++ = WORDSP;
+ *tp++ = i;
+ } else {
+ ERROR "Overflow in casetl" WARN;
+ }
+ }
+ }
+ if (j<3)
+ w[j] = numtabp[HP].val;
+ *tp++ = 0;
+ *tp++ = 0;
+ *tp = 0;
+ tp = buf;
+ if (NROFF)
+ horiz(po);
+ while (i = *tp++)
+ pchar(i);
+ if (w[1] || w[2])
+ horiz(j = quant((lt - w[1]) / 2 - w[0], HOR));
+ while (i = *tp++)
+ pchar(i);
+ if (w[2]) {
+ horiz(lt - w[0] - w[1] - w[2] - j);
+ while (i = *tp++)
+ pchar(i);
+ }
+ newline(0);
+ if (dip != d) {
+ if (dip->dnl > dip->hnl)
+ dip->hnl = dip->dnl;
+ } else {
+ if (numtabp[NL].val > dip->hnl)
+ dip->hnl = numtabp[NL].val;
+ }
+}
+
+
+void casepc(void)
+{
+ pagech = chget(IMP);
+}
+
+
+void casepm(void)
+{
+ int i, k;
+ int xx, cnt, tcnt, kk, tot;
+ Offset j;
+
+ kk = cnt = tcnt = 0;
+ tot = !skip();
+ stackdump();
+ for (i = 0; i < nm; i++) {
+ if ((xx = contabp[i].rq) == 0 || contabp[i].mx == 0)
+ continue;
+ tcnt++;
+ j = contabp[i].mx;
+ for (k = 1; (j = blist[bindex(j)].nextoff) != -1; )
+ k++;
+ cnt++;
+ kk += k;
+ if (!tot)
+ fprintf(stderr, "%-2.2s %d\n", unpair(xx), k);
+ }
+ fprintf(stderr, "pm: total %d, macros %d, space %d\n", tcnt, cnt, kk);
+}
+
+void stackdump(void) /* dumps stack of macros in process */
+{
+ Stack *p;
+
+ if (frame != stk) {
+ fprintf(stderr, "stack: ");
+ for (p = frame; p != stk; p = p->pframe)
+ fprintf(stderr, "%s ", unpair(p->mname));
+ fprintf(stderr, "\n");
+ }
+}
diff --git a/src/cmd/troff/n4.c b/src/cmd/troff/n4.c
new file mode 100644
index 00000000..3b3698e4
--- /dev/null
+++ b/src/cmd/troff/n4.c
@@ -0,0 +1,828 @@
+/*
+ * troff4.c
+ *
+ * number registers, conversion, arithmetic
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+
+int regcnt = NNAMES;
+int falsef = 0; /* on if inside false branch of if */
+
+#define NHASHSIZE 128 /* must be 2**n */
+#define NHASH(i) ((i>>6)^i) & (NHASHSIZE-1)
+Numtab *nhash[NHASHSIZE];
+
+Numtab *numtabp = NULL;
+#define NDELTA 400
+int ncnt = 0;
+
+void setn(void)
+{
+ int i, j, f;
+ Tchar ii;
+ Uchar *p;
+ char buf[NTM]; /* for \n(.S */
+
+ f = nform = 0;
+ if ((i = cbits(ii = getach())) == '+')
+ f = 1;
+ else if (i == '-')
+ f = -1;
+ else if (ii) /* don't put it back if it's already back (thanks to jaap) */
+ ch = ii;
+ if (falsef)
+ f = 0;
+ if ((i = getsn()) == 0)
+ return;
+ p = unpair(i);
+ if (p[0] == '.')
+ switch (p[1]) {
+ case 's':
+ i = pts;
+ break;
+ case 'v':
+ i = lss;
+ break;
+ case 'f':
+ i = font;
+ break;
+ case 'p':
+ i = pl;
+ break;
+ case 't':
+ i = findt1();
+ break;
+ case 'o':
+ i = po;
+ break;
+ case 'l':
+ i = ll;
+ break;
+ case 'i':
+ i = in;
+ break;
+ case '$':
+ i = frame->nargs;
+ break;
+ case 'A':
+ i = ascii;
+ break;
+ case 'c':
+ i = numtabp[CD].val;
+ break;
+ case 'n':
+ i = lastl;
+ break;
+ case 'a':
+ i = ralss;
+ break;
+ case 'h':
+ i = dip->hnl;
+ break;
+ case 'd':
+ if (dip != d)
+ i = dip->dnl;
+ else
+ i = numtabp[NL].val;
+ break;
+ case 'u':
+ i = fi;
+ break;
+ case 'j':
+ i = ad + 2 * admod;
+ break;
+ case 'w':
+ i = widthp;
+ break;
+ case 'x':
+ i = nel;
+ break;
+ case 'y':
+ i = un;
+ break;
+ case 'T':
+ i = dotT;
+ break; /* -Tterm used in nroff */
+ case 'V':
+ i = VERT;
+ break;
+ case 'H':
+ i = HOR;
+ break;
+ case 'k':
+ i = ne;
+ break;
+ case 'P':
+ i = print;
+ break;
+ case 'L':
+ i = ls;
+ break;
+ case 'R': /* maximal # of regs that can be addressed */
+ i = 255*256 - regcnt;
+ break;
+ case 'z':
+ p = unpair(dip->curd);
+ *pbp++ = p[1]; /* watch order */
+ *pbp++ = p[0];
+ return;
+ case 'b':
+ i = bdtab[font];
+ break;
+ case 'F':
+ cpushback(cfname[ifi]);
+ return;
+ case 'S':
+ buf[0] = j = 0;
+ for( i = 0; tabtab[i] != 0 && i < NTAB; i++) {
+ if (i > 0)
+ buf[j++] = ' ';
+ sprintf(&buf[j], "%d", tabtab[i] & TABMASK);
+ j = strlen(buf);
+ if ( tabtab[i] & RTAB)
+ sprintf(&buf[j], "uR");
+ else if (tabtab[i] & CTAB)
+ sprintf(&buf[j], "uC");
+ else
+ sprintf(&buf[j], "uL");
+ j += 2;
+ }
+ cpushback(buf);
+ return;
+ default:
+ goto s0;
+ }
+ else {
+s0:
+ if ((j = findr(i)) == -1)
+ i = 0;
+ else {
+ i = numtabp[j].val = numtabp[j].val + numtabp[j].inc * f;
+ nform = numtabp[j].fmt;
+ }
+ }
+ setn1(i, nform, (Tchar) 0);
+}
+
+Tchar numbuf[25];
+Tchar *numbufp;
+
+int wrc(Tchar i)
+{
+ if (numbufp >= &numbuf[24])
+ return(0);
+ *numbufp++ = i;
+ return(1);
+}
+
+
+
+/* insert into input number i, in format form, with size-font bits bits */
+void setn1(int i, int form, Tchar bits)
+{
+ numbufp = numbuf;
+ nrbits = bits;
+ nform = form;
+ fnumb(i, wrc);
+ *numbufp = 0;
+ pushback(numbuf);
+}
+
+void prnumtab(Numtab *p)
+{
+ int i;
+ for (i = 0; i < ncnt; i++)
+ if (p)
+ if (p[i].r != 0)
+ fprintf(stderr, "slot %d, %s, val %d\n", i, unpair(p[i].r), p[i].val);
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+}
+
+void nnspace(void)
+{
+ ncnt = sizeof(numtab)/sizeof(Numtab) + NDELTA;
+ numtabp = (Numtab *) grow((char *)numtabp, ncnt, sizeof(Numtab));
+ if (numtabp == NULL) {
+ ERROR "not enough memory for registers (%d)", ncnt WARN;
+ exit(1);
+ }
+ numtabp = (Numtab *) memcpy((char *)numtabp, (char *)numtab,
+ sizeof(numtab));
+ if (numtabp == NULL) {
+ ERROR "Cannot initialize registers" WARN;
+ exit(1);
+ }
+}
+
+void grownumtab(void)
+{
+ ncnt += NDELTA;
+ numtabp = (Numtab *) grow((char *) numtabp, ncnt, sizeof(Numtab));
+ if (numtabp == NULL) {
+ ERROR "Too many number registers (%d)", ncnt WARN;
+ done2(04);
+ } else {
+ memset((char *)(numtabp) + (ncnt - NDELTA) * sizeof(Numtab),
+ 0, NDELTA * sizeof(Numtab));
+ nrehash();
+ }
+}
+
+void nrehash(void)
+{
+ Numtab *p;
+ int i;
+
+ for (i=0; i<NHASHSIZE; i++)
+ nhash[i] = 0;
+ for (p=numtabp; p < &numtabp[ncnt]; p++)
+ p->link = 0;
+ for (p=numtabp; p < &numtabp[ncnt]; p++) {
+ if (p->r == 0)
+ continue;
+ i = NHASH(p->r);
+ p->link = nhash[i];
+ nhash[i] = p;
+ }
+}
+
+void nunhash(Numtab *rp)
+{
+ Numtab *p;
+ Numtab **lp;
+
+ if (rp->r == 0)
+ return;
+ lp = &nhash[NHASH(rp->r)];
+ p = *lp;
+ while (p) {
+ if (p == rp) {
+ *lp = p->link;
+ p->link = 0;
+ return;
+ }
+ lp = &p->link;
+ p = p->link;
+ }
+}
+
+int findr(int i)
+{
+ Numtab *p;
+ int h = NHASH(i);
+
+ if (i == 0)
+ return(-1);
+a0:
+ for (p = nhash[h]; p; p = p->link)
+ if (i == p->r)
+ return(p - numtabp);
+ for (p = numtabp; p < &numtabp[ncnt]; p++) {
+ if (p->r == 0) {
+ p->r = i;
+ p->link = nhash[h];
+ nhash[h] = p;
+ regcnt++;
+ return(p - numtabp);
+ }
+ }
+ grownumtab();
+ goto a0;
+}
+
+int usedr(int i) /* returns -1 if nr i has never been used */
+{
+ Numtab *p;
+
+ if (i == 0)
+ return(-1);
+ for (p = nhash[NHASH(i)]; p; p = p->link)
+ if (i == p->r)
+ return(p - numtabp);
+ return -1;
+}
+
+
+int fnumb(int i, int (*f)(Tchar))
+{
+ int j;
+
+ j = 0;
+ if (i < 0) {
+ j = (*f)('-' | nrbits);
+ i = -i;
+ }
+ switch (nform) {
+ default:
+ case '1':
+ case 0:
+ return decml(i, f) + j;
+ case 'i':
+ case 'I':
+ return roman(i, f) + j;
+ case 'a':
+ case 'A':
+ return abc(i, f) + j;
+ }
+}
+
+
+int decml(int i, int (*f)(Tchar))
+{
+ int j, k;
+
+ k = 0;
+ nform--;
+ if ((j = i / 10) || (nform > 0))
+ k = decml(j, f);
+ return(k + (*f)((i % 10 + '0') | nrbits));
+}
+
+
+int roman(int i, int (*f)(Tchar))
+{
+
+ if (!i)
+ return((*f)('0' | nrbits));
+ if (nform == 'i')
+ return(roman0(i, f, "ixcmz", "vldw"));
+ else
+ return(roman0(i, f, "IXCMZ", "VLDW"));
+}
+
+
+int roman0(int i, int (*f)(Tchar), char *onesp, char *fivesp)
+{
+ int q, rem, k;
+
+ if (!i)
+ return(0);
+ k = roman0(i / 10, f, onesp + 1, fivesp + 1);
+ q = (i = i % 10) / 5;
+ rem = i % 5;
+ if (rem == 4) {
+ k += (*f)(*onesp | nrbits);
+ if (q)
+ i = *(onesp + 1);
+ else
+ i = *fivesp;
+ return(k += (*f)(i | nrbits));
+ }
+ if (q)
+ k += (*f)(*fivesp | nrbits);
+ while (--rem >= 0)
+ k += (*f)(*onesp | nrbits);
+ return(k);
+}
+
+
+int abc(int i, int (*f)(Tchar))
+{
+ if (!i)
+ return((*f)('0' | nrbits));
+ else
+ return(abc0(i - 1, f));
+}
+
+
+int abc0(int i, int (*f)(Tchar))
+{
+ int j, k;
+
+ k = 0;
+ if (j = i / 26)
+ k = abc0(j - 1, f);
+ return(k + (*f)((i % 26 + nform) | nrbits));
+}
+
+long atoi0(void)
+{
+ int c, k, cnt;
+ Tchar ii;
+ long i, acc;
+
+ acc = 0;
+ nonumb = 0;
+ cnt = -1;
+a0:
+ cnt++;
+ ii = getch();
+ c = cbits(ii);
+ switch (c) {
+ default:
+ ch = ii;
+ if (cnt)
+ break;
+ case '+':
+ i = ckph();
+ if (nonumb)
+ break;
+ acc += i;
+ goto a0;
+ case '-':
+ i = ckph();
+ if (nonumb)
+ break;
+ acc -= i;
+ goto a0;
+ case '*':
+ i = ckph();
+ if (nonumb)
+ break;
+ acc *= i;
+ goto a0;
+ case '/':
+ i = ckph();
+ if (nonumb)
+ break;
+ if (i == 0) {
+ flusho();
+ ERROR "divide by zero." WARN;
+ acc = 0;
+ } else
+ acc /= i;
+ goto a0;
+ case '%':
+ i = ckph();
+ if (nonumb)
+ break;
+ acc %= i;
+ goto a0;
+ case '&': /*and*/
+ i = ckph();
+ if (nonumb)
+ break;
+ if ((acc > 0) && (i > 0))
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case ':': /*or*/
+ i = ckph();
+ if (nonumb)
+ break;
+ if ((acc > 0) || (i > 0))
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case '=':
+ if (cbits(ii = getch()) != '=')
+ ch = ii;
+ i = ckph();
+ if (nonumb) {
+ acc = 0;
+ break;
+ }
+ if (i == acc)
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case '>':
+ k = 0;
+ if (cbits(ii = getch()) == '=')
+ k++;
+ else
+ ch = ii;
+ i = ckph();
+ if (nonumb) {
+ acc = 0;
+ break;
+ }
+ if (acc > (i - k))
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case '<':
+ k = 0;
+ if (cbits(ii = getch()) == '=')
+ k++;
+ else
+ ch = ii;
+ i = ckph();
+ if (nonumb) {
+ acc = 0;
+ break;
+ }
+ if (acc < (i + k))
+ acc = 1;
+ else
+ acc = 0;
+ goto a0;
+ case ')':
+ break;
+ case '(':
+ acc = atoi0();
+ goto a0;
+ }
+ return(acc);
+}
+
+
+long ckph(void)
+{
+ Tchar i;
+ long j;
+
+ if (cbits(i = getch()) == '(')
+ j = atoi0();
+ else {
+ j = atoi1(i);
+ }
+ return(j);
+}
+
+
+/*
+ * print error about illegal numeric argument;
+ */
+void prnumerr(void)
+{
+ char err_buf[40];
+ static char warn[] = "Numeric argument expected";
+ int savcd = numtabp[CD].val;
+
+ if (numerr.type == RQERR)
+ sprintf(err_buf, "%c%s: %s", nb ? cbits(c2) : cbits(cc),
+ unpair(numerr.req), warn);
+ else
+ sprintf(err_buf, "\\%c'%s': %s", numerr.esc, &numerr.escarg,
+ warn);
+ if (frame != stk) /* uncertainty correction */
+ numtabp[CD].val--;
+ ERROR err_buf WARN;
+ numtabp[CD].val = savcd;
+}
+
+
+long atoi1(Tchar ii)
+{
+ int i, j, digits;
+ double acc; /* this is the only double in troff! */
+ int neg, abs, field, decpnt;
+ extern int ifnum;
+
+
+ neg = abs = field = decpnt = digits = 0;
+ acc = 0;
+ for (;;) {
+ i = cbits(ii);
+ switch (i) {
+ default:
+ break;
+ case '+':
+ ii = getch();
+ continue;
+ case '-':
+ neg = 1;
+ ii = getch();
+ continue;
+ case '|':
+ abs = 1 + neg;
+ neg = 0;
+ ii = getch();
+ continue;
+ }
+ break;
+ }
+a1:
+ while (i >= '0' && i <= '9') {
+ field++;
+ digits++;
+ acc = 10 * acc + i - '0';
+ ii = getch();
+ i = cbits(ii);
+ }
+ if (i == '.' && !decpnt++) {
+ field++;
+ digits = 0;
+ ii = getch();
+ i = cbits(ii);
+ goto a1;
+ }
+ if (!field) {
+ ch = ii;
+ goto a2;
+ }
+ switch (i) {
+ case 'u':
+ i = j = 1; /* should this be related to HOR?? */
+ break;
+ case 'v': /*VSs - vert spacing*/
+ j = lss;
+ i = 1;
+ break;
+ case 'm': /*Ems*/
+ j = EM;
+ i = 1;
+ break;
+ case 'n': /*Ens*/
+ j = EM;
+ if (TROFF)
+ i = 2;
+ else
+ i = 1; /*Same as Ems in NROFF*/
+ break;
+ case 'p': /*Points*/
+ j = INCH;
+ i = 72;
+ break;
+ case 'i': /*Inches*/
+ j = INCH;
+ i = 1;
+ break;
+ case 'c': /*Centimeters*/
+ /* if INCH is too big, this will overflow */
+ j = INCH * 50;
+ i = 127;
+ break;
+ case 'P': /*Picas*/
+ j = INCH;
+ i = 6;
+ break;
+ default:
+ j = dfact;
+ ch = ii;
+ i = dfactd;
+ }
+ if (neg)
+ acc = -acc;
+ if (!noscale) {
+ acc = (acc * j) / i;
+ }
+ if (field != digits && digits > 0)
+ while (digits--)
+ acc /= 10;
+ if (abs) {
+ if (dip != d)
+ j = dip->dnl;
+ else
+ j = numtabp[NL].val;
+ if (!vflag) {
+ j = numtabp[HP].val;
+ }
+ if (abs == 2)
+ j = -j;
+ acc -= j;
+ }
+a2:
+ nonumb = (!field || field == decpnt);
+ if (nonumb && (trace & TRNARGS) && !ismot(ii) && !nlflg && !ifnum) {
+ if (cbits(ii) != RIGHT ) /* Too painful to do right */
+ prnumerr();
+ }
+ return(acc);
+}
+
+
+void caserr(void)
+{
+ int i, j;
+ Numtab *p;
+
+ lgf++;
+ while (!skip() && (i = getrq()) ) {
+ j = usedr(i);
+ if (j < 0)
+ continue;
+ p = &numtabp[j];
+ nunhash(p);
+ p->r = p->val = p->inc = p->fmt = 0;
+ regcnt--;
+ }
+}
+
+/*
+ * .nr request; if tracing, don't check optional
+ * 2nd argument because tbl generates .in 1.5n
+ */
+void casenr(void)
+{
+ int i, j;
+ int savtr = trace;
+
+ lgf++;
+ skip();
+ if ((i = findr(getrq())) == -1)
+ goto rtn;
+ skip();
+ j = inumb(&numtabp[i].val);
+ if (nonumb)
+ goto rtn;
+ numtabp[i].val = j;
+ skip();
+ trace = 0;
+ j = atoi0(); /* BUG??? */
+ trace = savtr;
+ if (nonumb)
+ goto rtn;
+ numtabp[i].inc = j;
+rtn:
+ return;
+}
+
+void caseaf(void)
+{
+ int i, k;
+ Tchar j;
+
+ lgf++;
+ if (skip() || !(i = getrq()) || skip())
+ return;
+ k = 0;
+ j = getch();
+ if (!isalpha(cbits(j))) {
+ ch = j;
+ while ((j = cbits(getch())) >= '0' && j <= '9')
+ k++;
+ }
+ if (!k)
+ k = j;
+ numtabp[findr(i)].fmt = k; /* was k & BYTEMASK */
+}
+
+void setaf(void) /* return format of number register */
+{
+ int i, j;
+
+ i = usedr(getsn());
+ if (i == -1)
+ return;
+ if (numtabp[i].fmt > 20) /* it was probably a, A, i or I */
+ *pbp++ = numtabp[i].fmt;
+ else
+ for (j = (numtabp[i].fmt ? numtabp[i].fmt : 1); j; j--)
+ *pbp++ = '0';
+}
+
+
+int vnumb(int *i)
+{
+ vflag++;
+ dfact = lss;
+ res = VERT;
+ return(inumb(i));
+}
+
+
+int hnumb(int *i)
+{
+ dfact = EM;
+ res = HOR;
+ return(inumb(i));
+}
+
+
+int inumb(int *n)
+{
+ int i, j, f;
+ Tchar ii;
+
+ f = 0;
+ if (n) {
+ if ((j = cbits(ii = getch())) == '+')
+ f = 1;
+ else if (j == '-')
+ f = -1;
+ else
+ ch = ii;
+ }
+ i = atoi0();
+ if (n && f)
+ i = *n + f * i;
+ i = quant(i, res);
+ vflag = 0;
+ res = dfactd = dfact = 1;
+ if (nonumb)
+ i = 0;
+ return(i);
+}
+
+
+int quant(int n, int m)
+{
+ int i, neg;
+
+ neg = 0;
+ if (n < 0) {
+ neg++;
+ n = -n;
+ }
+ /* better as i = ((n + m/2)/m)*m */
+ i = n / m;
+ if (n - m * i > m / 2)
+ i += 1;
+ i *= m;
+ if (neg)
+ i = -i;
+ return(i);
+}
diff --git a/src/cmd/troff/n5.c b/src/cmd/troff/n5.c
new file mode 100644
index 00000000..c2801e47
--- /dev/null
+++ b/src/cmd/troff/n5.c
@@ -0,0 +1,1149 @@
+/*
+ * troff5.c
+ *
+ * misc processing requests
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+int iflist[NIF];
+int ifx;
+int ifnum = 0; /* trying numeric expression for .if or .ie condition */
+
+void casead(void)
+{
+ int i;
+
+ ad = 1;
+ /* leave admod alone */
+ if (skip())
+ return;
+ switch (i = cbits(getch())) {
+ case 'r': /* right adj, left ragged */
+ admod = 2;
+ break;
+ case 'l': /* left adj, right ragged */
+ admod = ad = 0; /* same as casena */
+ break;
+ case 'c': /*centered adj*/
+ admod = 1;
+ break;
+ case 'b':
+ case 'n':
+ admod = 0;
+ break;
+ case '0':
+ case '2':
+ case '4':
+ ad = 0;
+ case '1':
+ case '3':
+ case '5':
+ admod = (i - '0') / 2;
+ }
+}
+
+
+void casena(void)
+{
+ ad = 0;
+}
+
+
+void casefi(void)
+{
+ tbreak();
+ fi = 1;
+ pendnf = 0;
+}
+
+
+void casenf(void)
+{
+ tbreak();
+ fi = 0;
+}
+
+
+void casers(void)
+{
+ dip->nls = 0;
+}
+
+
+void casens(void)
+{
+ dip->nls++;
+}
+
+
+chget(int c)
+{
+ Tchar i;
+
+ if (skip() || ismot(i = getch()) || cbits(i) == ' ' || cbits(i) == '\n') {
+ ch = i;
+ return(c);
+ } else
+ return cbits(i); /* was (i & BYTEMASK) */
+}
+
+
+void casecc(void)
+{
+ cc = chget('.');
+}
+
+
+void casec2(void)
+{
+ c2 = chget('\'');
+}
+
+
+void casehc(void)
+{
+ ohc = chget(OHC);
+}
+
+
+void casetc(void)
+{
+ tabc = chget(0);
+}
+
+
+void caselc(void)
+{
+ dotc = chget(0);
+}
+
+
+void casehy(void)
+{
+ int i;
+
+ hyf = 1;
+ if (skip())
+ return;
+ noscale++;
+ i = atoi0();
+ noscale = 0;
+ if (nonumb)
+ return;
+ hyf = max(i, 0);
+}
+
+
+void casenh(void)
+{
+ hyf = 0;
+}
+
+
+max(int aa, int bb)
+{
+ if (aa > bb)
+ return(aa);
+ else
+ return(bb);
+}
+
+
+void casece(void)
+{
+ int i;
+
+ noscale++;
+ skip();
+ i = max(atoi0(), 0);
+ if (nonumb)
+ i = 1;
+ tbreak();
+ ce = i;
+ noscale = 0;
+}
+
+
+void casein(void)
+{
+ int i;
+
+ if (skip())
+ i = in1;
+ else {
+ i = max(hnumb(&in), 0);
+ if (nonumb)
+ i = in1;
+ }
+ tbreak();
+ in1 = in;
+ in = i;
+ if (!nc) {
+ un = in;
+ setnel();
+ }
+}
+
+
+void casell(void)
+{
+ int i;
+
+ if (skip())
+ i = ll1;
+ else {
+ i = max(hnumb(&ll), INCH / 10);
+ if (nonumb)
+ i = ll1;
+ }
+ ll1 = ll;
+ ll = i;
+ setnel();
+}
+
+
+void caselt(void)
+{
+ int i;
+
+ if (skip())
+ i = lt1;
+ else {
+ i = max(hnumb(&lt), 0);
+ if (nonumb)
+ i = lt1;
+ }
+ lt1 = lt;
+ lt = i;
+}
+
+
+void caseti(void)
+{
+ int i;
+
+ if (skip())
+ return;
+ i = max(hnumb(&in), 0);
+ tbreak();
+ un1 = i;
+ setnel();
+}
+
+
+void casels(void)
+{
+ int i;
+
+ noscale++;
+ if (skip())
+ i = ls1;
+ else {
+ i = max(inumb(&ls), 1);
+ if (nonumb)
+ i = ls1;
+ }
+ ls1 = ls;
+ ls = i;
+ noscale = 0;
+}
+
+
+void casepo(void)
+{
+ int i;
+
+ if (skip())
+ i = po1;
+ else {
+ i = max(hnumb(&po), 0);
+ if (nonumb)
+ i = po1;
+ }
+ po1 = po;
+ po = i;
+ if (TROFF & !ascii)
+ esc += po - po1;
+}
+
+
+void casepl(void)
+{
+ int i;
+
+ skip();
+ if ((i = vnumb(&pl)) == 0)
+ pl = 11 * INCH; /*11in*/
+ else
+ pl = i;
+ if (numtabp[NL].val > pl)
+ numtabp[NL].val = pl;
+}
+
+
+void casewh(void)
+{
+ int i, j, k;
+
+ lgf++;
+ skip();
+ i = vnumb((int *)0);
+ if (nonumb)
+ return;
+ skip();
+ j = getrq();
+ if ((k = findn(i)) != NTRAP) {
+ mlist[k] = j;
+ return;
+ }
+ for (k = 0; k < NTRAP; k++)
+ if (mlist[k] == 0)
+ break;
+ if (k == NTRAP) {
+ flusho();
+ ERROR "cannot plant trap." WARN;
+ return;
+ }
+ mlist[k] = j;
+ nlist[k] = i;
+}
+
+
+void casech(void)
+{
+ int i, j, k;
+
+ lgf++;
+ skip();
+ if (!(j = getrq()))
+ return;
+ else
+ for (k = 0; k < NTRAP; k++)
+ if (mlist[k] == j)
+ break;
+ if (k == NTRAP)
+ return;
+ skip();
+ i = vnumb((int *)0);
+ if (nonumb)
+ mlist[k] = 0;
+ nlist[k] = i;
+}
+
+
+findn(int i)
+{
+ int k;
+
+ for (k = 0; k < NTRAP; k++)
+ if ((nlist[k] == i) && (mlist[k] != 0))
+ break;
+ return(k);
+}
+
+
+void casepn(void)
+{
+ int i;
+
+ skip();
+ noscale++;
+ i = max(inumb(&numtabp[PN].val), 0);
+ noscale = 0;
+ if (!nonumb) {
+ npn = i;
+ npnflg++;
+ }
+}
+
+
+void casebp(void)
+{
+ int i;
+ Stack *savframe;
+
+ if (dip != d)
+ return;
+ savframe = frame;
+ skip();
+ if ((i = inumb(&numtabp[PN].val)) < 0)
+ i = 0;
+ tbreak();
+ if (!nonumb) {
+ npn = i;
+ npnflg++;
+ } else if (dip->nls)
+ return;
+ eject(savframe);
+}
+
+void casetm(void)
+{
+ casetm1(0, stderr);
+}
+
+
+void casefm(void)
+{
+ static struct fcache {
+ char *name;
+ FILE *fp;
+ } fcache[15];
+ int i;
+
+ if ( skip() || !getname()) {
+ ERROR "fm: missing filename" WARN;
+ return;
+ }
+
+ for (i = 0; i < 15 && fcache[i].fp != NULL; i++) {
+ if (strcmp(nextf, fcache[i].name) == 0)
+ break;
+ }
+ if (i >= 15) {
+ ERROR "fm: too many streams" WARN;
+ return;
+ }
+ if (fcache[i].fp == NULL) {
+ if( (fcache[i].fp = fopen(unsharp(nextf), "w")) == NULL) {
+ ERROR "fm: cannot open %s", nextf WARN;
+ return;
+ }
+ fcache[i].name = strdupl(nextf);
+ }
+ casetm1(0, fcache[i].fp);
+}
+
+void casetm1(int ab, FILE *out)
+{
+ int i, j, c;
+ char *p;
+ char tmbuf[NTM];
+
+ lgf++;
+ copyf++;
+ if (ab) {
+ if (skip())
+ ERROR "User Abort" WARN;
+ else {
+ extern int error;
+ int savtrac = trace;
+ i = trace = 0;
+ noscale++;
+ i = inumb(&trace);
+ noscale--;
+ if (i) {
+ error = i;
+ if (nlflg || skip())
+ ERROR "User Abort, exit code %d", i WARN;
+ }
+ trace = savtrac;
+ }
+ } else
+ skip();
+ for (i = 0; i < NTM - 2; ) {
+ if ((c = cbits(getch())) == '\n' || c == RIGHT)
+ break;
+ else if (c == MINUS) { /* special pleading for strange encodings */
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = '-';
+ } else if (c == PRESC) {
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = 'e';
+ } else if (c == FILLER) {
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = '&';
+ } else if (c == UNPAD) {
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = ' ';
+ } else if (c == OHC) {
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = '%';
+ } else if (c >= ALPHABET) {
+ p = chname(c);
+ switch (*p) {
+ case MBchar:
+ sprintf(&tmbuf[i], p+1);
+ break;
+ case Number:
+ sprintf(&tmbuf[i], "\\N'%s'", p+1);
+ break;
+ case Troffchar:
+ if ((j = strlen(p+1)) == 2)
+ sprintf(&tmbuf[i], "\\(%s", p+1);
+ else
+ sprintf(&tmbuf[i], "\\C'%s'", p+1);
+ break;
+ default:
+ sprintf(&tmbuf[i]," %s? ", p);
+ break;
+ }
+ j = strlen(&tmbuf[i]);
+ i += j;
+ } else
+ tmbuf[i++] = c;
+ }
+ tmbuf[i] = 0;
+ if (ab) /* truncate output */
+ obufp = obuf; /* should be a function in n2.c */
+ flusho();
+ if (i)
+ fprintf(out, "%s\n", tmbuf);
+ fflush(out);
+ copyf--;
+ lgf--;
+}
+
+
+void casesp(void)
+{
+ casesp1(0);
+}
+
+void casesp1(int a)
+{
+ int i, j, savlss;
+
+ tbreak();
+ if (dip->nls || trap)
+ return;
+ i = findt1();
+ if (!a) {
+ skip();
+ j = vnumb((int *)0);
+ if (nonumb)
+ j = lss;
+ } else
+ j = a;
+ if (j == 0)
+ return;
+ if (i < j)
+ j = i;
+ savlss = lss;
+ if (dip != d)
+ i = dip->dnl;
+ else
+ i = numtabp[NL].val;
+ if ((i + j) < 0)
+ j = -i;
+ lss = j;
+ newline(0);
+ lss = savlss;
+}
+
+
+void casert(void)
+{
+ int a, *p;
+
+ skip();
+ if (dip != d)
+ p = &dip->dnl;
+ else
+ p = &numtabp[NL].val;
+ a = vnumb(p);
+ if (nonumb)
+ a = dip->mkline;
+ if ((a < 0) || (a >= *p))
+ return;
+ nb++;
+ casesp1(a - *p);
+}
+
+
+void caseem(void)
+{
+ lgf++;
+ skip();
+ em = getrq();
+}
+
+
+void casefl(void)
+{
+ tbreak();
+ if (!ascii)
+ ptflush();
+ flusho();
+}
+
+
+void caseev(void)
+{
+ int nxev;
+
+ if (skip()) {
+e0:
+ if (evi == 0)
+ return;
+ nxev = evlist[--evi];
+ goto e1;
+ }
+ noscale++;
+ nxev = atoi0();
+ noscale = 0;
+ if (nonumb)
+ goto e0;
+ flushi();
+ if (nxev >= NEV || nxev < 0 || evi >= EVLSZ) {
+ flusho();
+ ERROR "cannot do .ev %d", nxev WARN;
+ if (error)
+ done2(040);
+ else
+ edone(040);
+ return;
+ }
+ evlist[evi++] = ev;
+e1:
+ if (ev == nxev)
+ return;
+ ev = nxev;
+ envp = &env[ev];
+}
+
+void envcopy(Env *e1, Env *e2) /* copy env e2 to e1 */
+{
+ *e1 = *e2; /* rumor hath that this fails on some machines */
+}
+
+
+void caseel(void)
+{
+ if (--ifx < 0) {
+ ifx = 0;
+ iflist[0] = 0;
+ }
+ caseif1(2);
+}
+
+
+void caseie(void)
+{
+ if (ifx >= NIF) {
+ ERROR "if-else overflow." WARN;
+ ifx = 0;
+ edone(040);
+ }
+ caseif1(1);
+ ifx++;
+}
+
+
+void caseif(void)
+{
+ caseif1(0);
+}
+
+void caseif1(int x)
+{
+ extern int falsef;
+ int notflag, true;
+ Tchar i;
+
+ if (x == 2) {
+ notflag = 0;
+ true = iflist[ifx];
+ goto i1;
+ }
+ true = 0;
+ skip();
+ if ((cbits(i = getch())) == '!') {
+ notflag = 1;
+ } else {
+ notflag = 0;
+ ch = i;
+ }
+ ifnum++;
+ i = atoi0();
+ ifnum = 0;
+ if (!nonumb) {
+ if (i > 0)
+ true++;
+ goto i1;
+ }
+ i = getch();
+ switch (cbits(i)) {
+ case 'e':
+ if (!(numtabp[PN].val & 01))
+ true++;
+ break;
+ case 'o':
+ if (numtabp[PN].val & 01)
+ true++;
+ break;
+ case 'n':
+ if (NROFF)
+ true++;
+ break;
+ case 't':
+ if (TROFF)
+ true++;
+ break;
+ case ' ':
+ break;
+ default:
+ true = cmpstr(i);
+ }
+i1:
+ true ^= notflag;
+ if (x == 1)
+ iflist[ifx] = !true;
+ if (true) {
+i2:
+ while ((cbits(i = getch())) == ' ')
+ ;
+ if (cbits(i) == LEFT)
+ goto i2;
+ ch = i;
+ nflush++;
+ } else {
+ if (!nlflg) {
+ copyf++;
+ falsef++;
+ eatblk(0);
+ copyf--;
+ falsef--;
+ }
+ }
+}
+
+void eatblk(int inblk)
+{
+ int cnt, i;
+
+ cnt = 0;
+ do {
+ if (ch) {
+ i = cbits(ch);
+ ch = 0;
+ } else
+ i = cbits(getch0());
+ if (i == ESC)
+ cnt++;
+ else {
+ if (cnt == 1)
+ switch (i) {
+ case '{': i = LEFT; break;
+ case '}': i = RIGHT; break;
+ case '\n': i = 'x'; break;
+ }
+ cnt = 0;
+ }
+ if (i == LEFT) eatblk(1);
+ } while ((!inblk && (i != '\n')) || (inblk && (i != RIGHT)));
+ if (i == '\n') {
+ nlflg++;
+ if (ip == 0)
+ numtabp[CD].val++;
+ }
+}
+
+
+cmpstr(Tchar c)
+{
+ int j, delim;
+ Tchar i;
+ int val;
+ int savapts, savapts1, savfont, savfont1, savpts, savpts1;
+ Tchar string[1280];
+ Tchar *sp;
+
+ if (ismot(c))
+ return(0);
+ delim = cbits(c);
+ savapts = apts;
+ savapts1 = apts1;
+ savfont = font;
+ savfont1 = font1;
+ savpts = pts;
+ savpts1 = pts1;
+ sp = string;
+ while ((j = cbits(i = getch()))!=delim && j!='\n' && sp<&string[1280-1])
+ *sp++ = i;
+ if (sp >= string + 1280) {
+ ERROR "too-long string compare." WARN;
+ edone(0100);
+ }
+ if (nlflg) {
+ val = sp==string;
+ goto rtn;
+ }
+ *sp = 0;
+ apts = savapts;
+ apts1 = savapts1;
+ font = savfont;
+ font1 = savfont1;
+ pts = savpts;
+ pts1 = savpts1;
+ mchbits();
+ val = 1;
+ sp = string;
+ while ((j = cbits(i = getch())) != delim && j != '\n') {
+ if (*sp != i) {
+ eat(delim);
+ val = 0;
+ goto rtn;
+ }
+ sp++;
+ }
+ if (*sp)
+ val = 0;
+rtn:
+ apts = savapts;
+ apts1 = savapts1;
+ font = savfont;
+ font1 = savfont1;
+ pts = savpts;
+ pts1 = savpts1;
+ mchbits();
+ return(val);
+}
+
+
+void caserd(void)
+{
+
+ lgf++;
+ skip();
+ getname();
+ if (!iflg) {
+ if (quiet) {
+ if (NROFF) {
+ echo_off();
+ flusho();
+ }
+ fprintf(stderr, "\007"); /*bell*/
+ } else {
+ if (nextf[0]) {
+ fprintf(stderr, "%s:", nextf);
+ } else {
+ fprintf(stderr, "\007"); /*bell*/
+ }
+ }
+ }
+ collect();
+ tty++;
+ pushi(RD_OFFSET, PAIR('r','d'));
+}
+
+
+rdtty(void)
+{
+ char onechar;
+
+ onechar = 0;
+ if (read(0, &onechar, 1) == 1) {
+ if (onechar == '\n')
+ tty++;
+ else
+ tty = 1;
+ if (tty != 3)
+ return(onechar);
+ }
+ tty = 0;
+ if (NROFF && quiet)
+ echo_on();
+ return(0);
+}
+
+
+void caseec(void)
+{
+ eschar = chget('\\');
+}
+
+
+void caseeo(void)
+{
+ eschar = 0;
+}
+
+
+void caseta(void)
+{
+ int i, j, k;
+
+ tabtab[0] = nonumb = 0;
+ for (i = 0; ((i < (NTAB - 1)) && !nonumb); i++) {
+ if (skip())
+ break;
+ k = tabtab[max(i-1, 0)] & TABMASK;
+ if ((j = max(hnumb(&k), 0)) > TABMASK) {
+ ERROR "Tab too far away" WARN;
+ j = TABMASK;
+ }
+ tabtab[i] = j & TABMASK;
+ if (!nonumb)
+ switch (cbits(ch)) {
+ case 'C':
+ tabtab[i] |= CTAB;
+ break;
+ case 'R':
+ tabtab[i] |= RTAB;
+ break;
+ default: /*includes L*/
+ break;
+ }
+ nonumb = ch = 0;
+ }
+ if (!skip())
+ ERROR "Too many tab stops" WARN;
+ tabtab[i] = 0;
+}
+
+
+void casene(void)
+{
+ int i, j;
+
+ skip();
+ i = vnumb((int *)0);
+ if (nonumb)
+ i = lss;
+ if (dip == d && numtabp[NL].val == -1) {
+ newline(1);
+ return;
+ }
+ if (i > (j = findt1())) {
+ i = lss;
+ lss = j;
+ dip->nls = 0;
+ newline(0);
+ lss = i;
+ }
+}
+
+
+void casetr(void)
+{
+ int i, j;
+ Tchar k;
+
+ lgf++;
+ skip();
+ while ((i = cbits(k=getch())) != '\n') {
+ if (ismot(k))
+ return;
+ if (ismot(k = getch()))
+ return;
+ if ((j = cbits(k)) == '\n')
+ j = ' ';
+ trtab[i] = j;
+ }
+}
+
+
+void casecu(void)
+{
+ cu++;
+ caseul();
+}
+
+
+void caseul(void)
+{
+ int i;
+
+ noscale++;
+ skip();
+ i = max(atoi0(), 0);
+ if (nonumb)
+ i = 1;
+ if (ul && (i == 0)) {
+ font = sfont;
+ ul = cu = 0;
+ }
+ if (i) {
+ if (!ul) {
+ sfont = font;
+ font = ulfont;
+ }
+ ul = i;
+ }
+ noscale = 0;
+ mchbits();
+}
+
+
+void caseuf(void)
+{
+ int i, j;
+
+ if (skip() || !(i = getrq()) || i == 'S' || (j = findft(i)) == -1)
+ ulfont = ULFONT; /*default underline position*/
+ else
+ ulfont = j;
+ if (NROFF && ulfont == FT)
+ ulfont = ULFONT;
+}
+
+
+void caseit(void)
+{
+ int i;
+
+ lgf++;
+ it = itmac = 0;
+ noscale++;
+ skip();
+ i = atoi0();
+ skip();
+ if (!nonumb && (itmac = getrq()))
+ it = i;
+ noscale = 0;
+}
+
+
+void casemc(void)
+{
+ int i;
+
+ if (icf > 1)
+ ic = 0;
+ icf = 0;
+ if (skip())
+ return;
+ ic = getch();
+ icf = 1;
+ skip();
+ i = max(hnumb((int *)0), 0);
+ if (!nonumb)
+ ics = i;
+}
+
+
+void casemk(void)
+{
+ int i, j;
+
+ if (dip != d)
+ j = dip->dnl;
+ else
+ j = numtabp[NL].val;
+ if (skip()) {
+ dip->mkline = j;
+ return;
+ }
+ if ((i = getrq()) == 0)
+ return;
+ numtabp[findr(i)].val = j;
+}
+
+
+void casesv(void)
+{
+ int i;
+
+ skip();
+ if ((i = vnumb((int *)0)) < 0)
+ return;
+ if (nonumb)
+ i = 1;
+ sv += i;
+ caseos();
+}
+
+
+void caseos(void)
+{
+ int savlss;
+
+ if (sv <= findt1()) {
+ savlss = lss;
+ lss = sv;
+ newline(0);
+ lss = savlss;
+ sv = 0;
+ }
+}
+
+
+void casenm(void)
+{
+ int i;
+
+ lnmod = nn = 0;
+ if (skip())
+ return;
+ lnmod++;
+ noscale++;
+ i = inumb(&numtabp[LN].val);
+ if (!nonumb)
+ numtabp[LN].val = max(i, 0);
+ getnm(&ndf, 1);
+ getnm(&nms, 0);
+ getnm(&ni, 0);
+ getnm(&nmwid, 3); /* really kludgy! */
+ noscale = 0;
+ nmbits = chbits;
+}
+
+/*
+ * .nm relies on the fact that illegal args are skipped; don't warn
+ * for illegality of these
+ */
+void getnm(int *p, int min)
+{
+ int i;
+ int savtr = trace;
+
+ eat(' ');
+ if (skip())
+ return;
+ trace = 0;
+ i = atoi0();
+ if (nonumb)
+ return;
+ *p = max(i, min);
+ trace = savtr;
+}
+
+
+void casenn(void)
+{
+ noscale++;
+ skip();
+ nn = max(atoi0(), 1);
+ noscale = 0;
+}
+
+
+void caseab(void)
+{
+ casetm1(1, stderr);
+ done3(0);
+}
+
+
+/* nroff terminal handling has been pretty well excised */
+/* as part of the merge with troff. these are ghostly remnants, */
+/* called, but doing nothing. restore them at your peril. */
+
+
+void save_tty(void) /*save any tty settings that may be changed*/
+{
+}
+
+
+void restore_tty(void) /*restore tty settings from beginning*/
+{
+}
+
+
+void set_tty(void)
+{
+}
+
+
+void echo_off(void) /*turn off ECHO for .rd in "-q" mode*/
+{
+}
+
+
+void echo_on(void) /*restore ECHO after .rd in "-q" mode*/
+{
+}
diff --git a/src/cmd/troff/n6.c b/src/cmd/troff/n6.c
new file mode 100644
index 00000000..e4affa17
--- /dev/null
+++ b/src/cmd/troff/n6.c
@@ -0,0 +1,362 @@
+#include "tdef.h"
+#include "ext.h"
+#include "fns.h"
+#include <ctype.h>
+
+/*
+ * n6.c -- width functions, sizes and fonts
+*/
+
+n_width(Tchar j)
+{
+ int i, k;
+
+ if (iszbit(j))
+ return 0;
+ if (ismot(j)) {
+ if (isvmot(j))
+ return(0);
+ k = absmot(j);
+ if (isnmot(j))
+ k = -k;
+ return(k);
+ }
+ i = cbits(j);
+ if (i < ' ') {
+ if (i == '\b')
+ return(-widthp);
+ if (i == PRESC)
+ i = eschar;
+ else if (i == HX)
+ return(0);
+ }
+ if (i == ohc)
+ return(0);
+ i = trtab[i];
+ if (i < ' ')
+ return(0);
+ if (i >= t.tfont.nchars) /* not on the font */
+ k = t.Char; /* really ought to check properly */
+ else
+ k = t.tfont.wp[i].wid * t.Char;
+ widthp = k;
+ return(k);
+}
+
+
+Tchar n_setch(int c)
+{
+ return t_setch(c);
+}
+
+Tchar n_setabs(void) /* set absolute char from \N'...' */
+{ /* for now, a no-op */
+ return t_setabs();
+}
+
+int n_findft(int i)
+{
+ int k;
+
+ if ((k = i - '0') >= 0 && k <= nfonts && k < smnt)
+ return(k);
+ for (k = 0; fontlab[k] != i; k++)
+ if (k > nfonts)
+ return(-1);
+ return(k);
+}
+
+
+
+void n_mchbits(void)
+{
+ chbits = 0;
+ setfbits(chbits, font);
+ sps = width(' ' | chbits);
+}
+
+
+void n_setps(void )
+{
+ int i, j;
+
+ i = cbits(getch());
+ if (isdigit(i)) { /* \sd or \sdd */
+ i -= '0';
+ if (i == 0) /* \s0 */
+ ;
+ else if (i <= 3 && (ch=getch()) && isdigit(cbits(ch))) { /* \sdd */
+ ch = 0;
+ }
+ } else if (i == '(') { /* \s(dd */
+ getch();
+ getch();
+ } else if (i == '+' || i == '-') { /* \s+, \s- */
+ j = cbits(getch());
+ if (isdigit(j)) { /* \s+d, \s-d */
+ ;
+ } else if (j == '(') { /* \s+(dd, \s-(dd */
+ getch();
+ getch();
+ }
+ }
+}
+
+
+Tchar n_setht(void) /* set character height from \H'...' */
+{
+
+ getch();
+ inumb(&apts);
+ getch();
+ return(0);
+}
+
+
+Tchar n_setslant(void) /* set slant from \S'...' */
+{
+ int n;
+
+ getch();
+ n = 0;
+ n = inumb(&n);
+ getch();
+ return(0);
+}
+
+
+void n_caseft(void)
+{
+ skip();
+ setfont(1);
+}
+
+
+void n_setfont(int a)
+{
+ int i, j;
+
+ if (a)
+ i = getrq();
+ else
+ i = getsn();
+ if (!i || i == 'P') {
+ j = font1;
+ goto s0;
+ }
+ if (i == 'S' || i == '0')
+ return;
+ if ((j = findft(i)) == -1)
+ return;
+s0:
+ font1 = font;
+ font = j;
+ mchbits();
+}
+
+
+void n_setwd(void)
+{
+ int base, wid;
+ Tchar i;
+ int delim, emsz, k;
+ int savhp, savapts, savapts1, savfont, savfont1, savpts, savpts1;
+
+ base = numtabp[ST].val = numtabp[ST].val = wid = numtabp[CT].val = 0;
+ if (ismot(i = getch()))
+ return;
+ delim = cbits(i);
+ savhp = numtabp[HP].val;
+ numtabp[HP].val = 0;
+ savapts = apts;
+ savapts1 = apts1;
+ savfont = font;
+ savfont1 = font1;
+ savpts = pts;
+ savpts1 = pts1;
+ setwdf++;
+ while (cbits(i = getch()) != delim && !nlflg) {
+ k = width(i);
+ wid += k;
+ numtabp[HP].val += k;
+ if (!ismot(i)) {
+ emsz = (INCH * pts + 36) / 72;
+ } else if (isvmot(i)) {
+ k = absmot(i);
+ if (isnmot(i))
+ k = -k;
+ base -= k;
+ emsz = 0;
+ } else
+ continue;
+ if (base < numtabp[SB].val)
+ numtabp[SB].val = base;
+ if ((k = base + emsz) > numtabp[ST].val)
+ numtabp[ST].val = k;
+ }
+ setn1(wid, 0, (Tchar) 0);
+ numtabp[HP].val = savhp;
+ apts = savapts;
+ apts1 = savapts1;
+ font = savfont;
+ font1 = savfont1;
+ pts = savpts;
+ pts1 = savpts1;
+ mchbits();
+ setwdf = 0;
+}
+
+
+Tchar n_vmot(void)
+{
+ dfact = lss;
+ vflag++;
+ return n_mot();
+}
+
+
+Tchar n_hmot(void)
+{
+ dfact = EM;
+ return n_mot();
+}
+
+
+Tchar n_mot(void)
+{
+ int j, n;
+ Tchar i;
+
+ j = HOR;
+ getch(); /*eat delim*/
+ if (n = atoi0()) {
+ if (vflag)
+ j = VERT;
+ i = makem(quant(n, j));
+ } else
+ i = 0;
+ getch();
+ vflag = 0;
+ dfact = 1;
+ return(i);
+}
+
+
+Tchar n_sethl(int k)
+{
+ int j;
+ Tchar i;
+
+ j = t.Halfline;
+ if (k == 'u')
+ j = -j;
+ else if (k == 'r')
+ j = -2 * j;
+ vflag++;
+ i = makem(j);
+ vflag = 0;
+ return(i);
+}
+
+
+Tchar n_makem(int i)
+{
+ Tchar j;
+
+ if (i >= 0)
+ j = i;
+ else
+ j = -i;
+ j |= MOT;
+ if (i < 0)
+ j |= NMOT;
+ if (vflag)
+ j |= VMOT;
+ return(j);
+}
+
+
+void n_casefp(void)
+{
+ int i, j;
+
+ skip();
+ if ((i = cbits(getch()) - '0') < 0 || i > nfonts)
+ return;
+ if (skip() || !(j = getrq()))
+ return;
+ fontlab[i] = j;
+}
+
+
+
+void n_casebd(void)
+{
+ int i, j, k;
+
+ k = 0;
+bd0:
+ if (skip() || !(i = getrq()) || (j = findft(i)) == -1) {
+ if (k)
+ goto bd1;
+ else
+ return;
+ }
+ if (j == smnt) {
+ k = smnt;
+ goto bd0;
+ }
+ if (k) {
+ sbold = j;
+ j = k;
+ }
+bd1:
+ skip();
+ noscale++;
+ bdtab[j] = atoi0();
+ noscale = 0;
+}
+
+
+void n_casevs(void)
+{
+ int i;
+
+ skip();
+ vflag++;
+ dfact = INCH; /*default scaling is points!*/
+ dfactd = 72;
+ res = VERT;
+ i = inumb(&lss);
+ if (nonumb)
+ i = lss1;
+ if (i < VERT)
+ i = VERT; /* was VERT */
+ lss1 = lss;
+ lss = i;
+}
+
+
+
+
+Tchar n_xlss(void)
+{
+ /* stores \x'...' into
+ /* two successive Tchars.
+ /* the first contains HX, the second the value,
+ /* encoded as a vertical motion.
+ /* decoding is done in n2.c by pchar().
+ */
+ int i;
+
+ getch();
+ dfact = lss;
+ i = quant(atoi0(), VERT);
+ dfact = 1;
+ getch();
+ if (i >= 0)
+ *pbp++ = MOT | VMOT | i;
+ else
+ *pbp++ = MOT | VMOT | NMOT | -i;
+ return(HX);
+}
diff --git a/src/cmd/troff/n7.c b/src/cmd/troff/n7.c
new file mode 100644
index 00000000..c22a485c
--- /dev/null
+++ b/src/cmd/troff/n7.c
@@ -0,0 +1,834 @@
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+#ifdef STRICT
+ /* not in ANSI or POSIX */
+#define isascii(a) ((a) >= 0 && (a) <= 127)
+#endif
+
+#define GETCH gettch
+Tchar gettch(void);
+
+
+/*
+ * troff7.c
+ *
+ * text
+ */
+
+int brflg;
+
+void tbreak(void)
+{
+ int pad, k;
+ Tchar *i, j;
+ int resol;
+ int un0 = un;
+
+ trap = 0;
+ if (nb)
+ return;
+ if (dip == d && numtabp[NL].val == -1) {
+ newline(1);
+ return;
+ }
+ if (!nc) {
+ setnel();
+ if (!wch)
+ return;
+ if (pendw)
+ getword(1);
+ movword();
+ } else if (pendw && !brflg) {
+ getword(1);
+ movword();
+ }
+ *linep = dip->nls = 0;
+ if (NROFF && dip == d)
+ horiz(po);
+ if (lnmod)
+ donum();
+ lastl = ne;
+ if (brflg != 1) {
+ totout = 0;
+ } else if (ad) {
+ if ((lastl = ll - un) < ne)
+ lastl = ne;
+ }
+ if (admod && ad && (brflg != 2)) {
+ lastl = ne;
+ adsp = adrem = 0;
+ if (admod == 1)
+ un += quant(nel / 2, HOR);
+ else if (admod == 2)
+ un += nel;
+ }
+ totout++;
+ brflg = 0;
+ if (lastl + un > dip->maxl)
+ dip->maxl = lastl + un;
+ horiz(un);
+ if (NROFF) {
+ if (adrem % t.Adj)
+ resol = t.Hor;
+ else
+ resol = t.Adj;
+ } else
+ resol = HOR;
+
+ lastl = ne + (nwd-1) * adsp + adrem;
+ for (i = line; nc > 0; ) {
+ if ((cbits(j = *i++)) == ' ') {
+ pad = 0;
+ do {
+ pad += width(j);
+ nc--;
+ } while ((cbits(j = *i++)) == ' ');
+ i--;
+ pad += adsp;
+ --nwd;
+ if (adrem) {
+ if (adrem < 0) {
+ pad -= resol;
+ adrem += resol;
+ } else if ((totout & 01) || adrem / resol >= nwd) {
+ pad += resol;
+ adrem -= resol;
+ }
+ }
+ pchar((Tchar) WORDSP);
+ horiz(pad);
+ } else {
+ pchar(j);
+ nc--;
+ }
+ }
+ if (ic) {
+ if ((k = ll - un0 - lastl + ics) > 0)
+ horiz(k);
+ pchar(ic);
+ }
+ if (icf)
+ icf++;
+ else
+ ic = 0;
+ ne = nwd = 0;
+ un = in;
+ setnel();
+ newline(0);
+ if (dip != d) {
+ if (dip->dnl > dip->hnl)
+ dip->hnl = dip->dnl;
+ } else {
+ if (numtabp[NL].val > dip->hnl)
+ dip->hnl = numtabp[NL].val;
+ }
+ for (k = ls - 1; k > 0 && !trap; k--)
+ newline(0);
+ spread = 0;
+}
+
+void donum(void)
+{
+ int i, nw;
+ int lnv = numtabp[LN].val;
+
+ nrbits = nmbits;
+ nw = width('1' | nrbits);
+ if (nn) {
+ nn--;
+ goto d1;
+ }
+ if (lnv % ndf) {
+ numtabp[LN].val++;
+d1:
+ un += nw * (nmwid + nms + ni);
+ return;
+ }
+ i = 0;
+ do { /* count digits in numtabp[LN].val */
+ i++;
+ } while ((lnv /= 10) > 0);
+ horiz(nw * (ni + max(nmwid-i, 0)));
+ nform = 0;
+ fnumb(numtabp[LN].val, pchar);
+ un += nw * nms;
+ numtabp[LN].val++;
+}
+
+
+void text(void)
+{
+ Tchar i;
+ static int spcnt;
+
+ nflush++;
+ numtabp[HP].val = 0;
+ if ((dip == d) && (numtabp[NL].val == -1)) {
+ newline(1);
+ return;
+ }
+ setnel();
+ if (ce || !fi) {
+ nofill();
+ return;
+ }
+ if (pendw)
+ goto t4;
+ if (pendt)
+ if (spcnt)
+ goto t2;
+ else
+ goto t3;
+ pendt++;
+ if (spcnt)
+ goto t2;
+ while ((cbits(i = GETCH())) == ' ') {
+ spcnt++;
+ numtabp[HP].val += sps;
+ widthp = sps;
+ }
+ if (nlflg) {
+t1:
+ nflush = pendt = ch = spcnt = 0;
+ callsp();
+ return;
+ }
+ ch = i;
+ if (spcnt) {
+t2:
+ tbreak();
+ if (nc || wch)
+ goto rtn;
+ un += spcnt * sps;
+ spcnt = 0;
+ setnel();
+ if (trap)
+ goto rtn;
+ if (nlflg)
+ goto t1;
+ }
+t3:
+ if (spread)
+ goto t5;
+ if (pendw || !wch)
+t4:
+ if (getword(0))
+ goto t6;
+ if (!movword())
+ goto t3;
+t5:
+ if (nlflg)
+ pendt = 0;
+ adsp = adrem = 0;
+ if (ad) {
+ if (nwd == 1)
+ adsp = nel;
+ else
+ adsp = nel / (nwd - 1);
+ adsp = (adsp / HOR) * HOR;
+ adrem = nel - adsp*(nwd-1);
+ }
+ brflg = 1;
+ tbreak();
+ spread = 0;
+ if (!trap)
+ goto t3;
+ if (!nlflg)
+ goto rtn;
+t6:
+ pendt = 0;
+ ckul();
+rtn:
+ nflush = 0;
+}
+
+
+void nofill(void)
+{
+ int j;
+ Tchar i;
+
+ if (!pendnf) {
+ over = 0;
+ tbreak();
+ if (trap)
+ goto rtn;
+ if (nlflg) {
+ ch = nflush = 0;
+ callsp();
+ return;
+ }
+ adsp = adrem = 0;
+ nwd = 10000;
+ }
+ while ((j = (cbits(i = GETCH()))) != '\n') {
+ if (j == ohc)
+ continue;
+ if (j == CONT) {
+ pendnf++;
+ nflush = 0;
+ flushi();
+ ckul();
+ return;
+ }
+ j = width(i);
+ widthp = j;
+ numtabp[HP].val += j;
+ storeline(i, j);
+ }
+ if (ce) {
+ ce--;
+ if ((i = quant(nel / 2, HOR)) > 0)
+ un += i;
+ }
+ if (!nc)
+ storeline((Tchar)FILLER, 0);
+ brflg = 2;
+ tbreak();
+ ckul();
+rtn:
+ pendnf = nflush = 0;
+}
+
+
+void callsp(void)
+{
+ int i;
+
+ if (flss)
+ i = flss;
+ else
+ i = lss;
+ flss = 0;
+ casesp1(i);
+}
+
+
+void ckul(void)
+{
+ if (ul && (--ul == 0)) {
+ cu = 0;
+ font = sfont;
+ mchbits();
+ }
+ if (it && --it == 0 && itmac)
+ control(itmac, 0);
+}
+
+
+void storeline(Tchar c, int w)
+{
+ int diff;
+
+ if (linep >= line + lnsize - 2) {
+ lnsize += LNSIZE;
+ diff = linep - line;
+ if (( line = (Tchar *)realloc((char *)line, lnsize * sizeof(Tchar))) != NULL) {
+ if (linep && diff)
+ linep = line + diff;
+ } else {
+ if (over) {
+ return;
+ } else {
+ flusho();
+ ERROR "Line overflow." WARN;
+ over++;
+ *linep++ = LEFTHAND;
+ w = width(LEFTHAND);
+ nc++;
+ c = '\n';
+ }
+ }
+ }
+ *linep++ = c;
+ ne += w;
+ nel -= w;
+ nc++;
+}
+
+
+void newline(int a)
+{
+ int i, j, nlss;
+ int opn;
+
+ if (a)
+ goto nl1;
+ if (dip != d) {
+ j = lss;
+ pchar1((Tchar)FLSS);
+ if (flss)
+ lss = flss;
+ i = lss + dip->blss;
+ dip->dnl += i;
+ pchar1((Tchar)i);
+ pchar1((Tchar)'\n');
+ lss = j;
+ dip->blss = flss = 0;
+ if (dip->alss) {
+ pchar1((Tchar)FLSS);
+ pchar1((Tchar)dip->alss);
+ pchar1((Tchar)'\n');
+ dip->dnl += dip->alss;
+ dip->alss = 0;
+ }
+ if (dip->ditrap && !dip->ditf && dip->dnl >= dip->ditrap && dip->dimac)
+ if (control(dip->dimac, 0)) {
+ trap++;
+ dip->ditf++;
+ }
+ return;
+ }
+ j = lss;
+ if (flss)
+ lss = flss;
+ nlss = dip->alss + dip->blss + lss;
+ numtabp[NL].val += nlss;
+ if (TROFF && ascii) {
+ dip->alss = dip->blss = 0;
+ }
+ pchar1((Tchar)'\n');
+ flss = 0;
+ lss = j;
+ if (numtabp[NL].val < pl)
+ goto nl2;
+nl1:
+ ejf = dip->hnl = numtabp[NL].val = 0;
+ ejl = frame;
+ if (donef) {
+ if ((!nc && !wch) || ndone)
+ done1(0);
+ ndone++;
+ donef = 0;
+ if (frame == stk)
+ nflush++;
+ }
+ opn = numtabp[PN].val;
+ numtabp[PN].val++;
+ if (npnflg) {
+ numtabp[PN].val = npn;
+ npn = npnflg = 0;
+ }
+nlpn:
+ if (numtabp[PN].val == pfrom) {
+ print++;
+ pfrom = -1;
+ } else if (opn == pto) {
+ print = 0;
+ opn = -1;
+ chkpn();
+ goto nlpn;
+ }
+ if (print)
+ ptpage(numtabp[PN].val); /* supposedly in a clean state so can pause */
+ if (stop && print) {
+ dpn++;
+ if (dpn >= stop) {
+ dpn = 0;
+ ptpause();
+ }
+ }
+nl2:
+ trap = 0;
+ if (numtabp[NL].val == 0) {
+ if ((j = findn(0)) != NTRAP)
+ trap = control(mlist[j], 0);
+ } else if ((i = findt(numtabp[NL].val - nlss)) <= nlss) {
+ if ((j = findn1(numtabp[NL].val - nlss + i)) == NTRAP) {
+ flusho();
+ ERROR "Trap botch." WARN;
+ done2(-5);
+ }
+ trap = control(mlist[j], 0);
+ }
+}
+
+
+findn1(int a)
+{
+ int i, j;
+
+ for (i = 0; i < NTRAP; i++) {
+ if (mlist[i]) {
+ if ((j = nlist[i]) < 0)
+ j += pl;
+ if (j == a)
+ break;
+ }
+ }
+ return(i);
+}
+
+
+void chkpn(void)
+{
+ pto = *(pnp++);
+ pfrom = pto>=0 ? pto : -pto;
+ if (pto == -INT_MAX) {
+ flusho();
+ done1(0);
+ }
+ if (pto < 0) {
+ pto = -pto;
+ print++;
+ pfrom = 0;
+ }
+}
+
+
+findt(int a)
+{
+ int i, j, k;
+
+ k = INT_MAX;
+ if (dip != d) {
+ if (dip->dimac && (i = dip->ditrap - a) > 0)
+ k = i;
+ return(k);
+ }
+ for (i = 0; i < NTRAP; i++) {
+ if (mlist[i]) {
+ if ((j = nlist[i]) < 0)
+ j += pl;
+ if ((j -= a) <= 0)
+ continue;
+ if (j < k)
+ k = j;
+ }
+ }
+ i = pl - a;
+ if (k > i)
+ k = i;
+ return(k);
+}
+
+
+findt1(void)
+{
+ int i;
+
+ if (dip != d)
+ i = dip->dnl;
+ else
+ i = numtabp[NL].val;
+ return(findt(i));
+}
+
+
+void eject(Stack *a)
+{
+ int savlss;
+
+ if (dip != d)
+ return;
+ ejf++;
+ if (a)
+ ejl = a;
+ else
+ ejl = frame;
+ if (trap)
+ return;
+e1:
+ savlss = lss;
+ lss = findt(numtabp[NL].val);
+ newline(0);
+ lss = savlss;
+ if (numtabp[NL].val && !trap)
+ goto e1;
+}
+
+
+movword(void)
+{
+ int w;
+ Tchar i, *wp;
+ int savwch, hys;
+
+ over = 0;
+ wp = wordp;
+ if (!nwd) {
+ while (cbits(*wp++) == ' ') {
+ wch--;
+ wne -= sps;
+ }
+ wp--;
+ }
+ if (wne > nel && !hyoff && hyf && (!nwd || nel > 3 * sps) &&
+ (!(hyf & 02) || (findt1() > lss)))
+ hyphen(wp);
+ savwch = wch;
+ hyp = hyptr;
+ nhyp = 0;
+ while (*hyp && *hyp <= wp)
+ hyp++;
+ while (wch) {
+ if (hyoff != 1 && *hyp == wp) {
+ hyp++;
+ if (!wdstart || (wp > wdstart + 1 && wp < wdend &&
+ (!(hyf & 04) || wp < wdend - 1) && /* 04 => last 2 */
+ (!(hyf & 010) || wp > wdstart + 2))) { /* 010 => 1st 2 */
+ nhyp++;
+ storeline((Tchar)IMP, 0);
+ }
+ }
+ i = *wp++;
+ w = width(i);
+ wne -= w;
+ wch--;
+ storeline(i, w);
+ }
+ if (nel >= 0) {
+ nwd++;
+ return(0); /* line didn't fill up */
+ }
+ if (TROFF)
+ xbits((Tchar)HYPHEN, 1);
+ hys = width((Tchar)HYPHEN);
+m1:
+ if (!nhyp) {
+ if (!nwd)
+ goto m3;
+ if (wch == savwch)
+ goto m4;
+ }
+ if (*--linep != IMP)
+ goto m5;
+ if (!(--nhyp))
+ if (!nwd)
+ goto m2;
+ if (nel < hys) {
+ nc--;
+ goto m1;
+ }
+m2:
+ if ((i = cbits(*(linep - 1))) != '-' && i != EMDASH) {
+ *linep = (*(linep - 1) & SFMASK) | HYPHEN;
+ w = width(*linep);
+ nel -= w;
+ ne += w;
+ linep++;
+ }
+m3:
+ nwd++;
+m4:
+ wordp = wp;
+ return(1); /* line filled up */
+m5:
+ nc--;
+ w = width(*linep);
+ ne -= w;
+ nel += w;
+ wne += w;
+ wch++;
+ wp--;
+ goto m1;
+}
+
+
+void horiz(int i)
+{
+ vflag = 0;
+ if (i)
+ pchar(makem(i));
+}
+
+
+void setnel(void)
+{
+ if (!nc) {
+ linep = line;
+ if (un1 >= 0) {
+ un = un1;
+ un1 = -1;
+ }
+ nel = ll - un;
+ ne = adsp = adrem = 0;
+ }
+}
+
+
+getword(int x)
+{
+ int j, k;
+ Tchar i, *wp;
+ int noword;
+ int obits;
+
+ noword = 0;
+ if (x)
+ if (pendw) {
+ *pendw = 0;
+ goto rtn;
+ }
+ if (wordp = pendw)
+ goto g1;
+ hyp = hyptr;
+ wordp = word;
+ over = wne = wch = 0;
+ hyoff = 0;
+ obits = chbits;
+ while (1) { /* picks up 1st char of word */
+ j = cbits(i = GETCH());
+ if (j == '\n') {
+ wne = wch = 0;
+ noword = 1;
+ goto rtn;
+ }
+ if (j == ohc) {
+ hyoff = 1; /* 1 => don't hyphenate */
+ continue;
+ }
+ if (j == ' ') {
+ numtabp[HP].val += sps;
+ widthp = sps;
+ storeword(i, sps);
+ continue;
+ }
+ break;
+ }
+ storeword(' ' | obits, sps);
+ if (spflg) {
+ storeword(' ' | obits, sps);
+ spflg = 0;
+ }
+g0:
+ if (j == CONT) {
+ pendw = wordp;
+ nflush = 0;
+ flushi();
+ return(1);
+ }
+ if (hyoff != 1) {
+ if (j == ohc) {
+ hyoff = 2;
+ *hyp++ = wordp;
+ if (hyp > hyptr + NHYP - 1)
+ hyp = hyptr + NHYP - 1;
+ goto g1;
+ }
+ if (((j == '-' || j == EMDASH)) && !(i & ZBIT)) /* zbit avoids \X */
+ if (wordp > word + 1) {
+ hyoff = 2;
+ *hyp++ = wordp + 1;
+ if (hyp > hyptr + NHYP - 1)
+ hyp = hyptr + NHYP - 1;
+ }
+ }
+ j = width(i);
+ numtabp[HP].val += j;
+ storeword(i, j);
+g1:
+ j = cbits(i = GETCH());
+ if (j != ' ') {
+ static char *sentchar = ".?!"; /* sentence terminators */
+ if (j != '\n')
+ goto g0;
+ wp = wordp-1; /* handle extra space at end of sentence */
+ while (wp >= word) {
+ j = cbits(*wp--);
+ if (j=='"' || j=='\'' || j==')' || j==']' || j=='*' || j==DAGGER)
+ continue;
+ for (k = 0; sentchar[k]; k++)
+ if (j == sentchar[k]) {
+ spflg++;
+ break;
+ }
+ break;
+ }
+ }
+ *wordp = 0;
+ numtabp[HP].val += sps;
+rtn:
+ for (wp = word; *wp; wp++) {
+ if (ismot(j))
+ break; /* drechsler */
+ j = cbits(*wp);
+ if (j == ' ')
+ continue;
+ if (!(isascii(j) && isdigit(j)) && j != '-')
+ break;
+ }
+ if (*wp == 0) /* all numbers, so don't hyphenate */
+ hyoff = 1;
+ wdstart = 0;
+ wordp = word;
+ pendw = 0;
+ *hyp++ = 0;
+ setnel();
+ return(noword);
+}
+
+
+void storeword(Tchar c, int w)
+{
+ Tchar *savp;
+ int i;
+
+ if (wordp >= word + wdsize - 2) {
+ wdsize += WDSIZE;
+ savp = word;
+ if (( word = (Tchar *)realloc((char *)word, wdsize * sizeof(Tchar))) != NULL) {
+ if (wordp)
+ wordp = word + (wordp - savp);
+ if (pendw)
+ pendw = word + (pendw - savp);
+ if (wdstart)
+ wdstart = word + (wdstart - savp);
+ if (wdend)
+ wdend = word + (wdend - savp);
+ for (i = 0; i < NHYP; i++)
+ if (hyptr[i])
+ hyptr[i] = word + (hyptr[i] - savp);
+ } else {
+ if (over) {
+ return;
+ } else {
+ flusho();
+ ERROR "Word overflow." WARN;
+ over++;
+ c = LEFTHAND;
+ w = width(LEFTHAND);
+ }
+ }
+ }
+ widthp = w;
+ wne += w;
+ *wordp++ = c;
+ wch++;
+}
+
+
+Tchar gettch(void)
+{
+ extern int c_isalnum;
+ Tchar i;
+ int j;
+
+ if (TROFF)
+ return getch();
+
+ i = getch();
+ j = cbits(i);
+ if (ismot(i) || fbits(i) != ulfont)
+ return(i);
+ if (cu) {
+ if (trtab[j] == ' ') {
+ setcbits(i, '_');
+ setfbits(i, FT); /* default */
+ }
+ return(i);
+ }
+ /* should test here for characters that ought to be underlined */
+ /* in the old nroff, that was the 200 bit on the width! */
+ /* for now, just do letters, digits and certain special chars */
+ if (j <= 127) {
+ if (!isalnum(j))
+ setfbits(i, FT);
+ } else {
+ if (j < c_isalnum)
+ setfbits(i, FT);
+ }
+ return(i);
+}
diff --git a/src/cmd/troff/n8.c b/src/cmd/troff/n8.c
new file mode 100644
index 00000000..d1be5080
--- /dev/null
+++ b/src/cmd/troff/n8.c
@@ -0,0 +1,540 @@
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+#define HY_BIT 0200 /* stuff in here only works for 7-bit ascii */
+ /* this value is used (as a literal) in suftab.c */
+ /* to encode possible hyphenation points in suffixes. */
+ /* it could be changed, by widening the tables */
+ /* to be shorts instead of chars. */
+
+/*
+ * troff8.c
+ *
+ * hyphenation
+ */
+
+int hexsize = 0; /* hyphenation exception list size */
+char *hbufp = NULL; /* base of list */
+char *nexth = NULL; /* first free slot in list */
+Tchar *hyend;
+
+#define THRESH 160 /* digram goodness threshold */
+int thresh = THRESH;
+
+int texhyphen(void);
+static int alpha(Tchar);
+
+void hyphen(Tchar *wp)
+{
+ int j;
+ Tchar *i;
+
+ i = wp;
+ while (punct((*i++)))
+ ;
+ if (!alpha(*--i))
+ return;
+ wdstart = i++;
+ while (alpha(*i++))
+ ;
+ hyend = wdend = --i - 1;
+ while (punct((*i++)))
+ ;
+ if (*--i)
+ return;
+ if (wdend - wdstart < 4) /* 4 chars is too short to hyphenate */
+ return;
+ hyp = hyptr;
+ *hyp = 0;
+ hyoff = 2;
+
+ /* for now, try exceptions first, then tex (if hyphalg is non-zero),
+ then suffix and digram if tex didn't hyphenate it at all.
+ */
+
+ if (!exword() && !texhyphen() && !suffix())
+ digram();
+
+ /* this appears to sort hyphenation points into increasing order */
+ *hyp++ = 0;
+ if (*hyptr)
+ for (j = 1; j; ) {
+ j = 0;
+ for (hyp = hyptr + 1; *hyp != 0; hyp++) {
+ if (*(hyp - 1) > *hyp) {
+ j++;
+ i = *hyp;
+ *hyp = *(hyp - 1);
+ *(hyp - 1) = i;
+ }
+ }
+ }
+}
+
+static alpha(Tchar i) /* non-zero if really alphabetic */
+{
+ if (ismot(i))
+ return 0;
+ else if (cbits(i) >= ALPHABET) /* this isn't very elegant, but there's */
+ return 0; /* no good way to make sure i is in range for */
+ else /* the call of isalpha */
+ return isalpha(cbits(i));
+}
+
+
+punct(Tchar i)
+{
+ if (!i || alpha(i))
+ return(0);
+ else
+ return(1);
+}
+
+
+void caseha(void) /* set hyphenation algorithm */
+{
+ hyphalg = HYPHALG;
+ if (skip())
+ return;
+ noscale++;
+ hyphalg = atoi0();
+ noscale = 0;
+}
+
+
+void caseht(void) /* set hyphenation threshold; not in manual! */
+{
+ thresh = THRESH;
+ if (skip())
+ return;
+ noscale++;
+ thresh = atoi0();
+ noscale = 0;
+}
+
+
+char *growh(char *where)
+{
+ char *new;
+
+ hexsize += NHEX;
+ if ((new = grow(hbufp, hexsize, sizeof(char))) == NULL)
+ return NULL;
+ if (new == hbufp) {
+ return where;
+ } else {
+ int diff;
+ diff = where - hbufp;
+ hbufp = new;
+ return new + diff;
+ }
+}
+
+
+void casehw(void)
+{
+ int i, k;
+ char *j;
+ Tchar t;
+
+ if (nexth == NULL) {
+ if ((nexth = hbufp = grow(hbufp, NHEX, sizeof(char))) == NULL) {
+ ERROR "No space for exception word list." WARN;
+ return;
+ }
+ hexsize = NHEX;
+ }
+ k = 0;
+ while (!skip()) {
+ if ((j = nexth) >= hbufp + hexsize - 2)
+ if ((j = nexth = growh(j)) == NULL)
+ goto full;
+ for (;;) {
+ if (ismot(t = getch()))
+ continue;
+ i = cbits(t);
+ if (i == ' ' || i == '\n') {
+ *j++ = 0;
+ nexth = j;
+ *j = 0;
+ if (i == ' ')
+ break;
+ else
+ return;
+ }
+ if (i == '-') {
+ k = HY_BIT;
+ continue;
+ }
+ *j++ = maplow(i) | k;
+ k = 0;
+ if (j >= hbufp + hexsize - 2)
+ if ((j = growh(j)) == NULL)
+ goto full;
+ }
+ }
+ return;
+full:
+ ERROR "Cannot grow exception word list." WARN;
+ *nexth = 0;
+}
+
+
+int exword(void)
+{
+ Tchar *w;
+ char *e, *save;
+
+ e = hbufp;
+ while (1) {
+ save = e;
+ if (e == NULL || *e == 0)
+ return(0);
+ w = wdstart;
+ while (*e && w <= hyend && (*e & 0177) == maplow(cbits(*w))) {
+ e++;
+ w++;
+ }
+ if (!*e) {
+ if (w-1 == hyend || (w == wdend && maplow(cbits(*w)) == 's')) {
+ w = wdstart;
+ for (e = save; *e; e++) {
+ if (*e & HY_BIT)
+ *hyp++ = w;
+ if (hyp > hyptr + NHYP - 1)
+ hyp = hyptr + NHYP - 1;
+ w++;
+ }
+ return(1);
+ } else {
+ e++;
+ continue;
+ }
+ } else
+ while (*e++)
+ ;
+ }
+}
+
+
+suffix(void)
+{
+ Tchar *w;
+ char *s, *s0;
+ Tchar i;
+ extern char *suftab[];
+
+again:
+ i = cbits(*hyend);
+ if (!alpha(i))
+ return(0);
+ if (i < 'a')
+ i -= 'A' - 'a';
+ if ((s0 = suftab[i-'a']) == 0)
+ return(0);
+ for (;;) {
+ if ((i = *s0 & 017) == 0)
+ return(0);
+ s = s0 + i - 1;
+ w = hyend - 1;
+ while (s > s0 && w >= wdstart && (*s & 0177) == maplow(cbits(*w))) {
+ s--;
+ w--;
+ }
+ if (s == s0)
+ break;
+ s0 += i;
+ }
+ s = s0 + i - 1;
+ w = hyend;
+ if (*s0 & HY_BIT)
+ goto mark;
+ while (s > s0) {
+ w--;
+ if (*s-- & HY_BIT) {
+mark:
+ hyend = w - 1;
+ if (*s0 & 0100) /* 0100 used in suftab to encode something too */
+ continue;
+ if (!chkvow(w))
+ return(0);
+ *hyp++ = w;
+ }
+ }
+ if (*s0 & 040)
+ return(0);
+ if (exword())
+ return(1);
+ goto again;
+}
+
+
+maplow(int i)
+{
+ if (isupper(i))
+ i = tolower(i);
+ return(i);
+}
+
+
+vowel(int i)
+{
+ switch (i) {
+ case 'a': case 'A':
+ case 'e': case 'E':
+ case 'i': case 'I':
+ case 'o': case 'O':
+ case 'u': case 'U':
+ case 'y': case 'Y':
+ return(1);
+ default:
+ return(0);
+ }
+}
+
+
+Tchar *chkvow(Tchar *w)
+{
+ while (--w >= wdstart)
+ if (vowel(cbits(*w)))
+ return(w);
+ return(0);
+}
+
+
+void digram(void)
+{
+ Tchar *w;
+ int val;
+ Tchar *nhyend, *maxw;
+ int maxval;
+ extern char bxh[26][13], bxxh[26][13], xxh[26][13], xhx[26][13], hxx[26][13];
+
+again:
+ if (!(w = chkvow(hyend + 1)))
+ return;
+ hyend = w;
+ if (!(w = chkvow(hyend)))
+ return;
+ nhyend = w;
+ maxval = 0;
+ w--;
+ while (++w < hyend && w < wdend - 1) {
+ val = 1;
+ if (w == wdstart)
+ val *= dilook('a', cbits(*w), bxh);
+ else if (w == wdstart + 1)
+ val *= dilook(cbits(*(w-1)), cbits(*w), bxxh);
+ else
+ val *= dilook(cbits(*(w-1)), cbits(*w), xxh);
+ val *= dilook(cbits(*w), cbits(*(w+1)), xhx);
+ val *= dilook(cbits(*(w+1)), cbits(*(w+2)), hxx);
+ if (val > maxval) {
+ maxval = val;
+ maxw = w + 1;
+ }
+ }
+ hyend = nhyend;
+ if (maxval > thresh)
+ *hyp++ = maxw;
+ goto again;
+}
+
+
+dilook(int a, int b, char t[26][13])
+{
+ int i, j;
+
+ i = t[maplow(a)-'a'][(j = maplow(b)-'a')/2];
+ if (!(j & 01))
+ i >>= 4;
+ return(i & 017);
+}
+
+
+/* here beginneth the tex hyphenation code, as interpreted freely */
+/* the main difference is that there is no attempt to squeeze space */
+/* as tightly at tex does. */
+
+static int texit(Tchar *, Tchar *);
+static int readpats(void);
+static void install(char *);
+static void fixup(void);
+static int trieindex(int, int);
+
+static char pats[50000]; /* size ought to be computed dynamically */
+static char *nextpat = pats;
+static char *trie[27*27]; /* english-specific sizes */
+
+int texhyphen(void)
+{
+ static int loaded = 0; /* -1: couldn't find tex file */
+
+ if (hyphalg == 0 || loaded == -1) /* non-zero => tex for now */
+ return 0;
+ if (loaded == 0) {
+ if (readpats())
+ loaded = 1;
+ else
+ loaded = -1;
+ }
+ return texit(wdstart, wdend);
+}
+
+static int texit(Tchar *start, Tchar *end) /* hyphenate as in tex, return # found */
+{
+ int nw, i, k, equal, cnt[500];
+ char w[500+1], *np, *pp, *wp, *xpp, *xwp;
+
+ w[0] = '.';
+ for (nw = 1; start <= end && nw < 500-1; nw++, start++)
+ w[nw] = maplow(tolower(cbits(*start)));
+ start -= (nw - 1);
+ w[nw++] = '.';
+ w[nw] = 0;
+/*
+ * printf("try %s\n", w);
+*/
+ for (i = 0; i <= nw; i++)
+ cnt[i] = '0';
+
+ for (wp = w; wp < w + nw; wp++) {
+ for (pp = trie[trieindex(*wp, *(wp+1))]; pp < nextpat; ) {
+ if (pp == 0 /* no trie entry */
+ || *pp != *wp /* no match on 1st letter */
+ || *(pp+1) != *(wp+1)) /* no match on 2nd letter */
+ break; /* so move to next letter of word */
+ equal = 1;
+ for (xpp = pp+2, xwp = wp+2; *xpp; )
+ if (*xpp++ != *xwp++) {
+ equal = 0;
+ break;
+ }
+ if (equal) {
+ np = xpp+1; /* numpat */
+ for (k = wp-w; *np; k++, np++)
+ if (*np > cnt[k])
+ cnt[k] = *np;
+/*
+ * printf("match: %s %s\n", pp, xpp+1);
+*/
+ }
+ pp += *(pp-1); /* skip over pattern and numbers to next */
+ }
+ }
+/*
+ * for (i = 0; i < nw; i++) printf("%c", w[i]);
+ * printf(" ");
+ * for (i = 0; i <= nw; i++) printf("%c", cnt[i]);
+ * printf("\n");
+*/
+/*
+ * for (i = 1; i < nw - 1; i++) {
+ * if (i > 2 && i < nw - 3 && cnt[i] % 2)
+ * printf("-");
+ * if (cbits(start[i-1]) != '.')
+ * printf("%c", cbits(start[i-1]));
+ * }
+ * printf("\n");
+*/
+ for (i = 1; i < nw -1; i++)
+ if (i > 2 && i < nw - 3 && cnt[i] % 2)
+ *hyp++ = start + i - 1;
+ return hyp - hyptr; /* non-zero if a hyphen was found */
+}
+
+/*
+ This code assumes that hyphen.tex looks like
+ % some comments
+ \patterns{ % more comments
+ pat5ter4ns, 1 per line, SORTED, nothing else
+ }
+ more goo
+ \hyphenation{ % more comments
+ ex-cep-tions, one per line; i ignore this part for now
+ }
+
+ this code is NOT robust against variations. unfortunately,
+ it looks like every local language version of this file has
+ a different format. i have also made no provision for weird
+ characters. sigh.
+*/
+
+static int readpats(void)
+{
+ FILE *fp;
+ char buf[200], buf1[200];
+
+ if ((fp = fopen(unsharp(TEXHYPHENS), "r")) == NULL
+ && (fp = fopen(unsharp(DWBalthyphens), "r")) == NULL) {
+ ERROR "warning: can't find hyphen.tex" WARN;
+ return 0;
+ }
+
+ while (fgets(buf, sizeof buf, fp) != NULL) {
+ sscanf(buf, "%s", buf1);
+ if (strcmp(buf1, "\\patterns{") == 0)
+ break;
+ }
+ while (fgets(buf, sizeof buf, fp) != NULL) {
+ if (buf[0] == '}')
+ break;
+ install(buf);
+ }
+ fclose(fp);
+ fixup();
+ return 1;
+}
+
+static void install(char *s) /* map ab4c5de to: 12 abcde \0 00405 \0 */
+{
+ int npat, lastpat;
+ char num[500], *onextpat = nextpat;
+
+ num[0] = '0';
+ *nextpat++ = ' '; /* fill in with count later */
+ for (npat = lastpat = 0; *s != '\n' && *s != '\0'; s++) {
+ if (isdigit(*s)) {
+ num[npat] = *s;
+ lastpat = npat;
+ } else {
+ *nextpat++ = *s;
+ npat++;
+ num[npat] = '0';
+ }
+ }
+ *nextpat++ = 0;
+ if (nextpat > pats + sizeof(pats)-20) {
+ ERROR "tex hyphenation table overflow, tail end ignored" WARN;
+ nextpat = onextpat;
+ }
+ num[lastpat+1] = 0;
+ strcat(nextpat, num);
+ nextpat += strlen(nextpat) + 1;
+}
+
+static void fixup(void) /* build indexes of where . a b c ... start */
+{
+ char *p, *lastc;
+ int n;
+
+ for (lastc = pats, p = pats+1; p < nextpat; p++)
+ if (*p == ' ') {
+ *lastc = p - lastc;
+ lastc = p;
+ }
+ *lastc = p - lastc;
+ for (p = pats+1; p < nextpat; ) {
+ n = trieindex(p[0], p[1]);
+ if (trie[n] == 0)
+ trie[n] = p;
+ p += p[-1];
+ }
+ /* printf("pats = %d\n", nextpat - pats); */
+}
+
+static int trieindex(int d1, int d2)
+{
+ return 27 * (d1 == '.' ? 0 : d1 - 'a' + 1) + (d2 == '.' ? 0 : d2 - 'a' + 1);
+}
diff --git a/src/cmd/troff/n9.c b/src/cmd/troff/n9.c
new file mode 100644
index 00000000..5cd70648
--- /dev/null
+++ b/src/cmd/troff/n9.c
@@ -0,0 +1,488 @@
+#include "tdef.h"
+#include "ext.h"
+#include "fns.h"
+
+/*
+ * troff9.c
+ *
+ * misc functions
+ */
+
+Tchar setz(void)
+{
+ Tchar i;
+
+ if (!ismot(i = getch()))
+ i |= ZBIT;
+ return(i);
+}
+
+void setline(void)
+{
+ Tchar *i;
+ Tchar c;
+ int length;
+ int j, w, cnt, delim, rem, temp;
+ Tchar linebuf[NC];
+
+ if (ismot(c = getch()))
+ return;
+ delim = cbits(c);
+ vflag = 0;
+ dfact = EM;
+ length = quant(atoi0(), HOR);
+ dfact = 1;
+ if (!length) {
+ eat(delim);
+ return;
+ }
+s0:
+ if ((j = cbits(c = getch())) == delim || j == '\n') {
+ ch = c;
+ c = RULE | chbits;
+ } else if (cbits(c) == FILLER)
+ goto s0;
+ w = width(c);
+ if (w <= 0) {
+ ERROR "zero-width underline character ignored" WARN;
+ c = RULE | chbits;
+ w = width(c);
+ }
+ i = linebuf;
+ if (length < 0) {
+ *i++ = makem(length);
+ length = -length;
+ }
+ if (!(cnt = length / w)) {
+ *i++ = makem(-(temp = ((w - length) / 2)));
+ *i++ = c;
+ *i++ = makem(-(w - length - temp));
+ goto s1;
+ }
+ if (rem = length % w) {
+ if (cbits(c) == RULE || cbits(c) == UNDERLINE || cbits(c) == ROOTEN)
+ *i++ = c | ZBIT;
+ *i++ = makem(rem);
+ }
+ if (cnt) {
+ *i++ = RPT;
+ *i++ = cnt;
+ *i++ = c;
+ }
+s1:
+ *i = 0;
+ eat(delim);
+ pushback(linebuf);
+}
+
+
+eat(int c)
+{
+ int i;
+
+ while ((i = cbits(getch())) != c && i != '\n')
+ ;
+ return(i);
+}
+
+
+void setov(void)
+{
+ int j, k;
+ Tchar i, o[NOV+1];
+ int delim, w[NOV+1];
+
+ if (ismot(i = getch()))
+ return;
+ delim = cbits(i);
+ for (k = 0; k < NOV && (j = cbits(i = getch())) != delim && j != '\n'; k++) {
+ o[k] = i;
+ w[k] = width(i);
+ }
+ o[k] = w[k] = 0;
+ if (o[0])
+ for (j = 1; j; ) {
+ j = 0;
+ for (k = 1; o[k] ; k++) {
+ if (w[k-1] < w[k]) {
+ j++;
+ i = w[k];
+ w[k] = w[k-1];
+ w[k-1] = i;
+ i = o[k];
+ o[k] = o[k-1];
+ o[k-1] = i;
+ }
+ }
+ }
+ else
+ return;
+ *pbp++ = makem(w[0] / 2);
+ for (k = 0; o[k]; k++)
+ ;
+ while (k>0) {
+ k--;
+ *pbp++ = makem(-((w[k] + w[k+1]) / 2));
+ *pbp++ = o[k];
+ }
+}
+
+
+void setbra(void)
+{
+ int k;
+ Tchar i, *j, dwn;
+ int cnt, delim;
+ Tchar brabuf[NC];
+
+ if (ismot(i = getch()))
+ return;
+ delim = cbits(i);
+ j = brabuf + 1;
+ cnt = 0;
+ if (NROFF)
+ dwn = (2 * t.Halfline) | MOT | VMOT;
+ else
+ dwn = EM | MOT | VMOT;
+ while ((k = cbits(i = getch())) != delim && k != '\n' && j <= brabuf + NC - 4) {
+ *j++ = i | ZBIT;
+ *j++ = dwn;
+ cnt++;
+ }
+ if (--cnt < 0)
+ return;
+ else if (!cnt) {
+ ch = *(j - 2);
+ return;
+ }
+ *j = 0;
+ if (NROFF)
+ *--j = *brabuf = (cnt * t.Halfline) | MOT | NMOT | VMOT;
+ else
+ *--j = *brabuf = (cnt * EM) / 2 | MOT | NMOT | VMOT;
+ *--j &= ~ZBIT;
+ pushback(brabuf);
+}
+
+
+void setvline(void)
+{
+ int i;
+ Tchar c, rem, ver, neg;
+ int cnt, delim, v;
+ Tchar vlbuf[NC];
+ Tchar *vlp;
+
+ if (ismot(c = getch()))
+ return;
+ delim = cbits(c);
+ dfact = lss;
+ vflag++;
+ i = quant(atoi0(), VERT);
+ dfact = 1;
+ if (!i) {
+ eat(delim);
+ vflag = 0;
+ return;
+ }
+ if ((cbits(c = getch())) == delim) {
+ c = BOXRULE | chbits; /*default box rule*/
+ } else
+ getch();
+ c |= ZBIT;
+ neg = 0;
+ if (i < 0) {
+ i = -i;
+ neg = NMOT;
+ }
+ if (NROFF)
+ v = 2 * t.Halfline;
+ else {
+ v = EM;
+ if (v < VERT) /* ATT EVK hack: Erik van Konijnenburg, */
+ v = VERT; /* hvlpb!evkonij, ATT NSI Hilversum, Holland */
+ }
+
+ cnt = i / v;
+ rem = makem(i % v) | neg;
+ ver = makem(v) | neg;
+ vlp = vlbuf;
+ if (!neg)
+ *vlp++ = ver;
+ if (absmot(rem) != 0) {
+ *vlp++ = c;
+ *vlp++ = rem;
+ }
+ while (vlp < vlbuf + NC - 3 && cnt--) {
+ *vlp++ = c;
+ *vlp++ = ver;
+ }
+ *(vlp - 2) &= ~ZBIT;
+ if (!neg)
+ vlp--;
+ *vlp = 0;
+ pushback(vlbuf);
+ vflag = 0;
+}
+
+#define NPAIR (NC/2-6) /* max pairs in spline, etc. */
+
+void setdraw(void) /* generate internal cookies for a drawing function */
+{
+ int i, j, k, dx[NPAIR], dy[NPAIR], delim, type;
+ Tchar c, drawbuf[NC];
+ int drawch = '.'; /* character to draw with */
+
+ /* input is \D'f dx dy dx dy ... c' (or at least it had better be) */
+ /* this does drawing function f with character c and the */
+ /* specified dx,dy pairs interpreted as appropriate */
+ /* pairs are deltas from last point, except for radii */
+
+ /* l dx dy: line from here by dx,dy */
+ /* c x: circle of diameter x, left side here */
+ /* e x y: ellipse of diameters x,y, left side here */
+ /* a dx1 dy1 dx2 dy2:
+ ccw arc: ctr at dx1,dy1, then end at dx2,dy2 from there */
+ /* ~ dx1 dy1 dx2 dy2...:
+ spline to dx1,dy1 to dx2,dy2 ... */
+ /* b x c:
+ built-up character of type c, ht x */
+ /* f dx dy ...: f is any other char: like spline */
+
+ if (ismot(c = getch()))
+ return;
+ delim = cbits(c);
+ numerr.escarg = type = cbits(getch());
+ if (type == '~') /* head off the .tr ~ problem */
+ type = 's';
+ for (i = 0; i < NPAIR ; i++) {
+ skip();
+ vflag = 0;
+ dfact = EM;
+ dx[i] = quant(atoi0(), HOR);
+ if (dx[i] > MAXMOT)
+ dx[i] = MAXMOT;
+ else if (dx[i] < -MAXMOT)
+ dx[i] = -MAXMOT;
+ skip();
+ if (type == 'c') {
+ dy[i] = 0;
+ goto eat;
+ }
+ vflag = 1;
+ dfact = lss;
+ dy[i] = quant(atoi0(), VERT);
+ if (dy[i] > MAXMOT)
+ dy[i] = MAXMOT;
+ else if (dy[i] < -MAXMOT)
+ dy[i] = -MAXMOT;
+eat:
+ if (cbits(c = getch()) != ' ') { /* must be the end */
+ if (cbits(c) != delim) {
+ drawch = cbits(c);
+ getch();
+ }
+ i++;
+ break;
+ }
+ }
+ dfact = 1;
+ vflag = 0;
+ if (TROFF) {
+ drawbuf[0] = DRAWFCN | chbits | ZBIT;
+ drawbuf[1] = type | chbits | ZBIT;
+ drawbuf[2] = drawch | chbits | ZBIT;
+ for (k = 0, j = 3; k < i; k++) {
+ drawbuf[j++] = MOT | ((dx[k] >= 0) ? dx[k] : (NMOT | -dx[k]));
+ drawbuf[j++] = MOT | VMOT | ((dy[k] >= 0) ? dy[k] : (NMOT | -dy[k]));
+ }
+ if (type == DRAWELLIPSE) {
+ drawbuf[5] = drawbuf[4] | NMOT; /* so the net vertical is zero */
+ j = 6;
+ } else if (type == DRAWBUILD) {
+ drawbuf[4] = drawbuf[3] | NMOT; /* net horizontal motion is zero */
+ drawbuf[2] &= ~ZBIT; /* width taken from drawing char */
+ j = 5;
+ }
+ drawbuf[j++] = DRAWFCN | chbits | ZBIT; /* marks end for ptout */
+ drawbuf[j] = 0;
+ pushback(drawbuf);
+ }
+}
+
+
+void casefc(void)
+{
+ int i;
+ Tchar j;
+
+ gchtab[fc] &= ~FCBIT;
+ fc = IMP;
+ padc = ' ';
+ if (skip() || ismot(j = getch()) || (i = cbits(j)) == '\n')
+ return;
+ fc = i;
+ gchtab[fc] |= FCBIT;
+ if (skip() || ismot(ch) || (ch = cbits(ch)) == fc)
+ return;
+ padc = ch;
+}
+
+
+Tchar setfield(int x)
+{
+ Tchar ii, jj, *fp;
+ int i, j;
+ int length, ws, npad, temp, type;
+ Tchar **pp, *padptr[NPP];
+ Tchar fbuf[FBUFSZ];
+ int savfc, savtc, savlc;
+ Tchar rchar;
+ int savepos;
+ static Tchar wbuf[] = { WORDSP, 0};
+
+ if (x == tabch)
+ rchar = tabc | chbits;
+ else if (x == ldrch)
+ rchar = dotc | chbits;
+ temp = npad = ws = 0;
+ savfc = fc;
+ savtc = tabch;
+ savlc = ldrch;
+ tabch = ldrch = fc = IMP;
+ savepos = numtabp[HP].val;
+ gchtab[tabch] &= ~TABBIT;
+ gchtab[ldrch] &= ~LDRBIT;
+ gchtab[fc] &= ~FCBIT;
+ gchtab[IMP] |= TABBIT|LDRBIT|FCBIT;
+ for (j = 0; ; j++) {
+ if ((tabtab[j] & TABMASK) == 0) {
+ if (x == savfc)
+ ERROR "zero field width." WARN;
+ jj = 0;
+ goto rtn;
+ }
+ if ((length = ((tabtab[j] & TABMASK) - numtabp[HP].val)) > 0 )
+ break;
+ }
+ type = tabtab[j] & ~TABMASK;
+ fp = fbuf;
+ pp = padptr;
+ if (x == savfc) {
+ while (1) {
+ j = cbits(ii = getch());
+ jj = width(ii);
+ widthp = jj;
+ numtabp[HP].val += jj;
+ if (j == padc) {
+ npad++;
+ *pp++ = fp;
+ if (pp > padptr + NPP - 1)
+ break;
+ goto s1;
+ } else if (j == savfc)
+ break;
+ else if (j == '\n') {
+ temp = j;
+ if (nlflg && ip == 0) {
+ numtabp[CD].val--;
+ nlflg = 0;
+ }
+ break;
+ }
+ ws += jj;
+s1:
+ *fp++ = ii;
+ if (fp > fbuf + FBUFSZ - 3)
+ break;
+ }
+ if (ws)
+ *fp++ = WORDSP;
+ if (!npad) {
+ npad++;
+ *pp++ = fp;
+ *fp++ = 0;
+ }
+ *fp++ = temp;
+ *fp = 0;
+ temp = i = (j = length - ws) / npad;
+ i = (i / HOR) * HOR;
+ if ((j -= i * npad) < 0)
+ j = -j;
+ ii = makem(i);
+ if (temp < 0)
+ ii |= NMOT;
+ for (; npad > 0; npad--) {
+ *(*--pp) = ii;
+ if (j) {
+ j -= HOR;
+ (*(*pp)) += HOR;
+ }
+ }
+ pushback(fbuf);
+ jj = 0;
+ } else if (type == 0) {
+ /*plain tab or leader*/
+ if ((j = width(rchar)) > 0) {
+ int nchar = length / j;
+ while (nchar-->0 && pbp < &pbbuf[NC-3]) {
+ numtabp[HP].val += j;
+ widthp = j;
+ *pbp++ = rchar;
+ }
+ length %= j;
+ }
+ if (length)
+ jj = length | MOT;
+ else
+ jj = getch0();
+ if (savepos > 0)
+ pushback(wbuf);
+ } else {
+ /*center tab*/
+ /*right tab*/
+ while ((j = cbits(ii = getch())) != savtc && j != '\n' && j != savlc) {
+ jj = width(ii);
+ ws += jj;
+ numtabp[HP].val += jj;
+ widthp = jj;
+ *fp++ = ii;
+ if (fp > fbuf + FBUFSZ - 3)
+ break;
+ }
+ *fp++ = ii;
+ *fp = 0;
+ if (type == RTAB)
+ length -= ws;
+ else
+ length -= ws / 2; /*CTAB*/
+ pushback(fbuf);
+ if ((j = width(rchar)) != 0 && length > 0) {
+ int nchar = length / j;
+ while (nchar-- > 0 && pbp < &pbbuf[NC-3])
+ *pbp++ = rchar;
+ length %= j;
+ }
+ if (savepos > 0)
+ pushback(wbuf);
+ length = (length / HOR) * HOR;
+ jj = makem(length);
+ if (nlflg) {
+ if (ip == 0)
+ numtabp[CD].val--;
+ nlflg = 0;
+ }
+ }
+rtn:
+ gchtab[fc] &= ~FCBIT;
+ gchtab[tabch] &= ~TABBIT;
+ gchtab[ldrch] &= ~LDRBIT;
+ fc = savfc;
+ tabch = savtc;
+ ldrch = savlc;
+ gchtab[fc] |= FCBIT;
+ gchtab[tabch] = TABBIT;
+ gchtab[ldrch] |= LDRBIT;
+ numtabp[HP].val = savepos;
+ return(jj);
+}
diff --git a/src/cmd/troff/ni.c b/src/cmd/troff/ni.c
new file mode 100644
index 00000000..a80cec64
--- /dev/null
+++ b/src/cmd/troff/ni.c
@@ -0,0 +1,390 @@
+#include <stdio.h>
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+char termtab[NS]; /* term type added in ptinit() */
+char fontdir[NS]; /* added in casefp; not used by nroff */
+char devname[20]; /* default output device */
+
+Numtab numtab[NN] = {
+ { PAIR('%', 0) },
+ { PAIR('n', 'l') },
+ { PAIR('y', 'r') },
+ { PAIR('h', 'p') },
+ { PAIR('c', 't') },
+ { PAIR('d', 'n') },
+ { PAIR('m', 'o') },
+ { PAIR('d', 'y') },
+ { PAIR('d', 'w') },
+ { PAIR('l', 'n') },
+ { PAIR('d', 'l') },
+ { PAIR('s', 't') },
+ { PAIR('s', 'b') },
+ { PAIR('c', '.') },
+ { PAIR('$', '$') },
+};
+
+
+int alphabet = 256; /* latin-1 */
+int pto = 10000;
+int pfrom = 1;
+int print = 1;
+char nextf[NS] = TMACDIR;
+char mfiles[NMF][NS];
+int nmfi = 0;
+int oldbits = -1;
+int init = 1;
+int fc = IMP; /* field character */
+int eschar = '\\';
+int pl;
+int po;
+FILE *ptid;
+
+int dfact = 1;
+int dfactd = 1;
+int res = 1;
+int smnt = 0; /* beginning of special fonts */
+int ascii = 0; /* ascii normally off for troff, on for nroff; -a turns on */
+int lg;
+int pnlist[NPN] = { -1 };
+
+
+int *pnp = pnlist;
+int npn = 1;
+int npnflg = 1;
+int dpn = -1;
+int totout = 1;
+int ulfont = ULFONT;
+int tabch = TAB;
+int ldrch = LEADER;
+
+
+Contab contab[NM] = {
+ C(PAIR('d', 's'), caseds),
+ C(PAIR('a', 's'), caseas),
+ C(PAIR('s', 'p'), casesp),
+ C(PAIR('f', 't'), caseft),
+ C(PAIR('p', 's'), caseps),
+ C(PAIR('v', 's'), casevs),
+ C(PAIR('n', 'r'), casenr),
+ C(PAIR('i', 'f'), caseif),
+ C(PAIR('i', 'e'), caseie),
+ C(PAIR('e', 'l'), caseel),
+ C(PAIR('p', 'o'), casepo),
+ C(PAIR('t', 'l'), casetl),
+ C(PAIR('t', 'm'), casetm),
+ C(PAIR('f', 'm'), casefm),
+ C(PAIR('b', 'p'), casebp),
+ C(PAIR('c', 'h'), casech),
+ C(PAIR('p', 'n'), casepn),
+ C(PAIR('b', 'r'), tbreak),
+ C(PAIR('t', 'i'), caseti),
+ C(PAIR('n', 'e'), casene),
+ C(PAIR('n', 'f'), casenf),
+ C(PAIR('c', 'e'), casece),
+ C(PAIR('f', 'i'), casefi),
+ C(PAIR('i', 'n'), casein),
+ C(PAIR('l', 'l'), casell),
+ C(PAIR('n', 's'), casens),
+ C(PAIR('m', 'k'), casemk),
+ C(PAIR('r', 't'), casert),
+ C(PAIR('a', 'm'), caseam),
+ C(PAIR('d', 'e'), casede),
+ C(PAIR('d', 'i'), casedi),
+ C(PAIR('d', 'a'), caseda),
+ C(PAIR('w', 'h'), casewh),
+ C(PAIR('d', 't'), casedt),
+ C(PAIR('i', 't'), caseit),
+ C(PAIR('r', 'm'), caserm),
+ C(PAIR('r', 'r'), caserr),
+ C(PAIR('r', 'n'), casern),
+ C(PAIR('a', 'd'), casead),
+ C(PAIR('r', 's'), casers),
+ C(PAIR('n', 'a'), casena),
+ C(PAIR('p', 'l'), casepl),
+ C(PAIR('t', 'a'), caseta),
+ C(PAIR('t', 'r'), casetr),
+ C(PAIR('u', 'l'), caseul),
+ C(PAIR('c', 'u'), casecu),
+ C(PAIR('l', 't'), caselt),
+ C(PAIR('n', 'x'), casenx),
+ C(PAIR('s', 'o'), caseso),
+ C(PAIR('i', 'g'), caseig),
+ C(PAIR('t', 'c'), casetc),
+ C(PAIR('f', 'c'), casefc),
+ C(PAIR('e', 'c'), caseec),
+ C(PAIR('e', 'o'), caseeo),
+ C(PAIR('l', 'c'), caselc),
+ C(PAIR('e', 'v'), caseev),
+ C(PAIR('r', 'd'), caserd),
+ C(PAIR('a', 'b'), caseab),
+ C(PAIR('f', 'l'), casefl),
+ C(PAIR('e', 'x'), caseex),
+ C(PAIR('s', 's'), casess),
+ C(PAIR('f', 'p'), casefp),
+ C(PAIR('c', 's'), casecs),
+ C(PAIR('b', 'd'), casebd),
+ C(PAIR('l', 'g'), caselg),
+ C(PAIR('h', 'c'), casehc),
+ C(PAIR('h', 'y'), casehy),
+ C(PAIR('n', 'h'), casenh),
+ C(PAIR('n', 'm'), casenm),
+ C(PAIR('n', 'n'), casenn),
+ C(PAIR('s', 'v'), casesv),
+ C(PAIR('o', 's'), caseos),
+ C(PAIR('l', 's'), casels),
+ C(PAIR('c', 'c'), casecc),
+ C(PAIR('c', '2'), casec2),
+ C(PAIR('e', 'm'), caseem),
+ C(PAIR('a', 'f'), caseaf),
+ C(PAIR('h', 'a'), caseha),
+ C(PAIR('h', 'w'), casehw),
+ C(PAIR('m', 'c'), casemc),
+ C(PAIR('p', 'm'), casepm),
+ C(PAIR('p', 'i'), casepi),
+ C(PAIR('u', 'f'), caseuf),
+ C(PAIR('p', 'c'), casepc),
+ C(PAIR('h', 't'), caseht),
+ C(PAIR('c', 'f'), casecf),
+ C(PAIR('s', 'y'), casesy),
+ C(PAIR('l', 'f'), caself),
+ C(PAIR('p', 't'), casept),
+ C(PAIR('g', 'd'), casegd),
+};
+
+
+Tbuf _oline;
+
+/*
+ * troff environment block
+ */
+
+Env env[NEV] = { { /* this sets up env[0] */
+/* int ics */ 0, /* insertion character space, set by .mc */
+/* int sps */ 0,
+/* int spacesz */ 0,
+/* int lss */ 0,
+/* int lss1 */ 0,
+/* int ll */ 0,
+/* int ll1 */ 0,
+/* int lt */ 0,
+/* int lt1 */ 0,
+/* Tchar ic */ 0, /* insertion character (= margin character) */
+/* int icf */ 0, /* insertion character flag */
+/* Tchar chbits */ 0, /* size+font bits for current character */
+/* Tchar spbits */ 0,
+/* Tchar nmbits */ 0, /* size+font bits for number from .nm */
+/* int apts */ PS, /* actual point size -- as requested by user */
+/* int apts1 */ PS, /* need not match an existent size */
+/* int pts */ PS, /* hence, this is the size that really exists */
+/* int pts1 */ PS,
+/* int font */ FT,
+/* int font1 */ FT,
+/* int ls */ 1,
+/* int ls1 */ 1,
+/* int ad */ 1,
+/* int nms */ 1, /* .nm multiplier */
+/* int ndf */ 1, /* .nm separator */
+/* int nmwid */ 3, /* max width of .nm numbers */
+/* int fi */ 1,
+/* int cc */ '.',
+/* int c2 */ '\'',
+/* int ohc */ OHC,
+/* int tdelim */ IMP,
+/* int hyf */ 1,
+/* int hyoff */ 0,
+/* int hyphalg */ HYPHALG,
+/* int un1 */ -1,
+/* int tabc */ 0,
+/* int dotc */ '.',
+/* int adsp */ 0, /* add this much space to each padding point */
+/* int adrem */ 0, /* excess space to add until it runs out */
+/* int lastl */ 0, /* last text on current output line */
+/* int nel */ 0, /* how much space left on current output line */
+/* int admod */ 0, /* adjust mode */
+/* Tchar *wordp */ 0,
+/* int spflg */ 0, /* probably to indicate space after punctuation needed */
+/* Tchar *linep */ 0,
+/* Tchar *wdend */ 0,
+/* Tchar *wdstart */ 0,
+/* int wne */ 0,
+/* int ne */ 0, /* how much space taken on current output line */
+/* int nc */ 0, /* #characters (incl blank) on output line */
+/* int nb */ 0,
+/* int lnmod */ 0, /* line number mode, set by .nm */
+/* int nwd */ 0, /* number of words on current output line */
+/* int nn */ 0, /* from .nn command */
+/* int ni */ 0, /* indent of .nm numbers, probably */
+/* int ul */ 0,
+/* int cu */ 0,
+/* int ce */ 0,
+/* int in */ 0, /* indent and previous value */
+/* int in1 */ 0,
+/* int un */ 0, /* unindent of left margin in some way */
+/* int wch */ 0,
+/* int pendt */ 0,
+/* Tchar *pendw */ (Tchar *)0,
+/* int pendnf */ 0,
+/* int spread */ 0,
+/* int it */ 0, /* input trap count */
+/* int itmac */ 0,
+} };
+
+Env *envp = env; /* start off in env 0 */
+
+Numerr numerr;
+
+Stack *frame, *stk, *ejl;
+Stack *nxf;
+
+int pipeflg;
+int hflg; /* used in nroff only */
+int eqflg; /* used in nroff only */
+
+int xpts;
+int ppts;
+int pfont;
+int mpts;
+int mfont;
+int cs;
+int ccs;
+int bd;
+
+int stdi;
+int quiet;
+int stop;
+char ibuf[IBUFSZ];
+char xbuf[IBUFSZ];
+char *ibufp;
+char *xbufp;
+char *eibuf;
+char *xeibuf;
+Tchar pbbuf[NC]; /* pushback buffer for arguments, \n, etc. */
+Tchar *pbp = pbbuf; /* next free slot in pbbuf */
+Tchar *lastpbp = pbbuf; /* pbp in previous stack frame */
+int nx;
+int mflg;
+Tchar ch = 0;
+int ibf;
+int ifi;
+int iflg;
+int rargc;
+char **argp;
+Ushort trtab[NTRTAB];
+int lgf;
+int copyf;
+Offset ip;
+int nlflg;
+int donef;
+int nflush;
+int nfo;
+int padc;
+int raw;
+int flss;
+int nonumb;
+int trap;
+int tflg;
+int ejf;
+int dilev;
+Offset offset;
+int em;
+int ds;
+Offset woff;
+int app;
+int ndone;
+int lead;
+int ralss;
+Offset nextb;
+Tchar nrbits;
+int nform;
+int oldmn;
+int newmn;
+int macerr;
+Offset apptr;
+int diflg;
+int evi;
+int vflag;
+int noscale;
+int po1;
+int nlist[NTRAP];
+int mlist[NTRAP];
+int evlist[EVLSZ];
+int ev;
+int tty;
+int sfont = FT; /* appears to be "standard" font; used by .ul */
+int sv;
+int esc;
+int widthp;
+int xfont;
+int setwdf;
+int over;
+int nhyp;
+Tchar **hyp;
+Tchar *olinep;
+int dotT;
+char *unlkp;
+Wcache widcache[NWIDCACHE];
+Diver d[NDI];
+Diver *dip;
+
+int c_hyphen;
+int c_emdash;
+int c_rule;
+int c_minus;
+int c_fi;
+int c_fl;
+int c_ff;
+int c_ffi;
+int c_ffl;
+int c_acute;
+int c_grave;
+int c_under;
+int c_rooten;
+int c_boxrule;
+int c_lefthand;
+int c_dagger;
+int c_isalnum;
+
+Spnames spnames[] =
+{
+ &c_hyphen, "hy",
+ &c_emdash, "em",
+ &c_rule, "ru",
+ &c_minus, "\\-",
+ &c_fi, "fi",
+ &c_fl, "fl",
+ &c_ff, "ff",
+ &c_ffi, "Fi",
+ &c_ffl, "Fl",
+ &c_acute, "aa",
+ &c_grave, "ga",
+ &c_under, "ul",
+ &c_rooten, "rn",
+ &c_boxrule, "br",
+ &c_lefthand, "lh",
+ &c_dagger, "dg", /* not in nroff?? */
+ &c_isalnum, "__",
+ 0, 0
+};
+
+
+Tchar (*hmot)(void);
+Tchar (*makem)(int i);
+Tchar (*setabs)(void);
+Tchar (*setch)(int c);
+Tchar (*sethl)(int k);
+Tchar (*setht)(void);
+Tchar (*setslant)(void);
+Tchar (*vmot)(void);
+Tchar (*xlss)(void);
+int (*findft)(int i);
+int (*width)(Tchar j);
+void (*mchbits)(void);
+void (*ptlead)(void);
+void (*ptout)(Tchar i);
+void (*ptpause)(void);
+void (*setfont)(int a);
+void (*setps)(void);
+void (*setwd)(void);
+
diff --git a/src/cmd/troff/suftab.c b/src/cmd/troff/suftab.c
new file mode 100644
index 00000000..1aa8a009
--- /dev/null
+++ b/src/cmd/troff/suftab.c
@@ -0,0 +1,612 @@
+/*
+ * Suffix table
+ */
+
+typedef unsigned char Uchar;
+
+static Uchar sufa[] = {
+ 02,0200+'t', /* -TA */
+ 02,0200+'s', /* -SA */
+ 03,0200+'t','r', /* -TRA */
+ 03,0200+'d','r', /* -DRA */
+ 03,0200+'b','r', /* -BRA */
+ 02,0200+'p', /* -PA */
+ 02,0200+'n', /* -NA */
+ 02,0200+'m', /* -MA */
+ 03,0200+'p','l', /* -PLA */
+ 02,0200+'l', /* -LA */
+ 02,0200+'k', /* -KA */
+ 03,0200+'t','h', /* -THA */
+ 03,0200+'s','h', /* -SHA */
+ 02,0200+'g', /* -GA */
+ 02,0200+'d', /* -DA */
+ 02,0200+'c', /* -CA */
+ 02,0200+'b', /* -BA */
+ 00
+};
+
+static Uchar sufc[] = {
+ 04,'e','t',0200+'i', /* ET-IC */
+ 07,'a','l',0200+'i','s',0200+'t','i', /* AL-IS-TIC */
+ 04,'s',0200+'t','i', /* S-TIC */
+ 04,'p',0200+'t','i', /* P-TIC */
+ 05,0200+'l','y','t',0200+'i', /* -LYT-IC */
+ 04,'o','t',0200+'i', /* OT-IC */
+ 05,'a','n',0200+'t','i', /* AN-TIC */
+ 04,'n',0200+'t','i', /* N-TIC */
+ 04,'c',0200+'t','i', /* C-TIC */
+ 04,'a','t',0200+'i', /* AT-IC */
+ 04,'h',0200+'n','i', /* H-NIC */
+ 03,'n',0200+'i', /* N-IC */
+ 03,'m',0200+'i', /* M-IC */
+ 04,'l',0200+'l','i', /* L-LIC */
+ 04,'b',0200+'l','i', /* B-LIC */
+ 04,0200+'c','l','i', /* -CLIC */
+ 03,'l',0200+'i', /* L-IC */
+ 03,'h',0200+'i', /* H-IC */
+ 03,'f',0200+'i', /* F-IC */
+ 03,'d',0200+'i', /* D-IC */
+ 03,0200+'b','i', /* -BIC */
+ 03,'a',0200+'i', /* A-IC */
+ 03,0200+'m','a', /* -MAC */
+ 03,'i',0200+'a', /* I-AC */
+ 00
+};
+
+static Uchar sufd[] = {
+ 04,0200+'w','o','r', /* -WORD */
+ 04,0200+'l','o','r', /* -LORD */
+ 04,0200+'f','o','r', /* -FORD */
+ 04,0200+'y','a','r', /* -YARD */
+ 04,0200+'w','a','r', /* -WARD */
+ 05,0200+'g','u','a','r', /* -GUARD */
+ 04,0200+'t','a','r', /* -TARD */
+ 05,0200+'b','o','a','r', /* -BOARD */
+ 04,0200+'n','a','r', /* -NARD */
+ 05,0200+'l','i','a','r', /* -LIARD */
+ 04,0200+'i','a','r', /* -IARD */
+ 04,0200+'g','a','r', /* -GARD */
+ 04,0200+'b','a','r', /* -BARD */
+ 03,0200+'r','o', /* -ROD */
+ 04,0200+'w','o','o', /* -WOOD */
+ 04,0200+'h','o','o', /* -HOOD */
+ 04,0200+'m','o','n', /* -MOND */
+ 04,0200+'t','e','n', /* -TEND */
+ 05,0200+'s','t','a','n', /* -STAND */
+ 04,0200+'l','a','n', /* -LAND */
+ 04,0200+'h','a','n', /* -HAND */
+ 04,0200+'h','o','l', /* -HOLD */
+ 04,0200+'f','o','l', /* -FOLD */
+ 05,0200+'f','i','e','l', /* -FIELD */
+ 03,0200+'v','i', /* -VID */
+ 03,0200+'c','i', /* -CID */
+ 04,0200+'s','a','i', /* -SAID */
+ 04,0200+'m','a','i', /* -MAID */
+ 04,'t',0200+'t','e', /* T-TED */
+ 03,'t',0200+'e', /* T-ED */
+ 04,0200+'d','r','e', /* -DRED */
+ 04,0200+'c','r','e', /* -CRED */
+ 04,0200+'b','r','e', /* -BRED */
+ 05,'v',0200+'e','l','e', /* V-ELED */
+ 0100+04,'a','l',0200+'e', /* AL/ED */
+ 0140+03,0200+'e','e', /* /EED */
+ 040+05,'e','d',0200+'d','e', /* ED-DED */
+ 04,'d',0200+'d','e', /* D-DED */
+ 040+04,'e','d',0200+'e', /* ED-ED */
+ 03,'d',0200+'e', /* D-ED */
+ 05,0200+'d','u','c','e', /* -DUCED */
+ 0300+02,'e', /* E/D */
+ 05,0200+'s','t','e','a', /* -STEAD */
+ 05,0200+'a','h','e','a', /* -AHEAD */
+ 04,0200+'h','e','a', /* -HEAD */
+ 00
+};
+
+static Uchar sufe[] = {
+ 05,'a','r',0200+'i','z', /* AR-IZE */
+ 05,'a','n',0200+'i','z', /* AN-IZE */
+ 05,'a','l',0200+'i','z', /* AL-IZE */
+ 06,0200+'a','r','d',0200+'i','z', /* -ARD-IZE */
+ 05,0200+'s','e','l','v', /* -SELVE */
+ 05,0200+'k','n','i','v', /* -KNIVE */
+ 05,0200+'l','i','e','v', /* -LIEVE */
+ 0100+03,0200+'q','u', /* /QUE */
+ 07,'o','n',0200+'t','i','n',0200+'u', /* ON-TIN-UE */
+ 03,0200+'n','u', /* -NUE */
+ 03,0200+'d','u', /* -DUE */
+ 0300+02,'u', /* U/E */
+ 0300+05,'q','u','a','t', /* QUAT/E */
+ 04,'u',0200+'a','t', /* U-ATE */
+ 05,0200+'s','t','a','t', /* -STATE */
+ 04,0200+'t','a','t', /* -TATE */
+ 06,0200+'t','o','r',0200+'a','t', /* -TOR-ATE */
+ 05,'e','n',0200+'a','t', /* EN-ATE */
+ 04,0200+'m','a','t', /* -MATE */
+ 05,0200+'h','o','u','s', /* -HOUSE */
+ 05,0200+'c','l','o','s', /* -CLOSE */
+ 04,'i',0200+'o','s', /* I-OSE */
+ 04,0200+'w','i','s', /* -WISE */
+ 05,'a','s',0200+'u','r', /* AS-URE */
+ 040+04,0200+'s','u','r', /* -SURE */
+ 06,0200+'f','i','g',0200+'u','r', /* -FIG-URE */
+ 040+03,0200+'t','r', /* -TRE */
+ 05,0200+'s','t','o','r', /* -STORE */
+ 04,0200+'f','o','r', /* -FORE */
+ 05,0200+'w','h','e','r', /* -WHERE */
+ 06,0200+'s','p','h','e','r', /* -SPHERE */
+ 03,0200+'d','r', /* -DRE */
+ 03,0200+'c','r', /* -CRE */
+ 03,0200+'b','r', /* -BRE */
+ 05,0200+'s','c','o','p', /* -SCOPE */
+ 04,'y',0200+'o','n', /* Y-ONE */
+ 05,0200+'s','t','o','n', /* -STONE */
+ 05,0200+'p','h','o','n', /* -PHONE */
+ 04,0200+'g','o','n', /* -GONE */
+ 04,'e',0200+'o','n', /* E-ONE */
+ 040+04,0200+'e','n','n', /* -ENNE */
+ 040+05,'a',0200+'r','i','n', /* A-RINE */
+ 05,0200+'c','l','i','n', /* -CLINE */
+ 04,0200+'l','i','n', /* -LINE */
+ 007,00200+'r','o','u',00200+'t','i','n', /*-ROU-TINE */
+ 04,0200+'s','o','m', /* -SOME */
+ 04,0200+'c','o','m', /* -COME */
+ 04,0200+'t','i','m', /* -TIME */
+ 03,0200+'z','l', /* -ZLE */
+ 03,0200+'t','l', /* -TLE */
+ 03,0200+'s','l', /* -SLE */
+ 03,0200+'p','l', /* -PLE */
+ 05,0200+'v','i','l','l', /* -VILLE */
+ 04,'c','k',0200+'l', /* CK-LE */
+ 03,0200+'k','l', /* -KLE */
+ 03,0200+'g','l', /* -GLE */
+ 03,0200+'f','l', /* -FLE */
+ 03,0200+'d','l', /* -DLE */
+ 03,0200+'c','l', /* -CLE */
+ 05,0200+'p','a',0200+'b','l', /* -PA-BLE */
+ 05,'f','a',0200+'b','l', /* FA-BLE */
+ 05,0200+'c','a',0200+'b','l', /* -CA-BLE */
+ 06,0200+'s','t','a','b','l', /* -STABLE */
+ 04,0200+'a','b','l', /* -ABLE */
+ 03,0200+'b','l', /* -BLE */
+ 04,0200+'d','a','l', /* -DALE */
+ 04,0200+'m','a','l', /* -MALE */
+ 04,0200+'s','a','l', /* -SALE */
+ 04,0200+'l','i','k', /* -LIKE */
+ 0340+05,'g',0200+'u','a','g', /* -G/UAGE */
+ 05,0200+'r','i','a','g', /* -RIAGE */
+ 05,'e','r',0200+'a','g', /* ER-AGE */
+ 04,'m',0200+'a','g', /* M-AGE */
+ 04,'k',0200+'a','g', /* K-AGE */
+ 04,'d',0200+'a','g', /* D-AGE */
+ 04,0200+'w','i','f', /* -WIFE */
+ 05,0200+'k','n','i','f', /* -KNIFE */
+ 03,0200+'s','e', /* -SEE */
+ 04,0200+'f','r','e', /* -FREE */
+ 0340+02,'e', /* EE */
+ 04,0200+'w','i','d', /* -WIDE */
+ 04,0200+'t','i','d', /* -TIDE */
+ 04,0200+'s','i','d', /* -SIDE */
+ 06,0200+'q','u','e','n','c', /* -QUENCE */
+ 07,0200+'f','l','u',0200+'e','n','c', /* -FLU-ENCE */
+ 040+06,'e','s',0200+'e','n','c', /* ES-ENCE */
+ 06,'e','r',0200+'e','n','c', /* ER-ENCE */
+ 05,'i',0200+'e','n','c', /* I-ENCE */
+ 040+05,0200+'s','a','n','c', /* -SANCE */
+ 06,'e','r',0200+'a','n','c', /* ER-ANCE */
+ 06,'a','r',0200+'a','n','c', /* AR-ANCE */
+ 05,0200+'n','a','n','c', /* -NANCE */
+ 07,0200+'b','a','l',0200+'a','n','c', /* -BAL-ANCE */
+ 05,'i',0200+'a','n','c', /* I-ANCE */
+ 07,0200+'j','u','s',0200+'t','i','c', /* -JUS-TICE */
+ 05,0200+'s','t','i','c', /* -STICE */
+ 06,0200+'n','o','v',0200+'i','c', /* NOV-ICE */
+ 04,0200+'v','i','c', /* -VICE */
+ 05,0200+'p','i','e','c', /* -PIECE */
+ 05,0200+'p','l','a','c', /* -PLACE */
+ 0340+01, /* /E */
+ 00
+};
+
+static Uchar suff[] = {
+ 03,0200+'o','f', /* -OFF */
+ 05,0200+'p','r','o','o', /* -PROOF */
+ 04,0200+'s','e','l', /* -SELF */
+ 03,0200+'r','i', /* -RIF */
+ 040+04,0200+'l','i','e', /* -LIEF */
+ 00
+};
+
+static Uchar sufg[] = {
+ 03,0200+'l','o', /* -LOG */
+ 04,0200+'l','o','n', /* -LONG */
+ 05,'t',0200+'t','i','n', /* T-TING */
+ 06,0200+'s','t','r','i','n', /* -STRING */
+ 05,'r',0200+'r','i','n', /* R-RING */
+ 05,'p',0200+'p','i','n', /* P-PING */
+ 05,'n',0200+'n','i','n', /* N-NING */
+ 05,'m',0200+'m','i','n', /* M-MING */
+ 05,'l',0200+'l','i','n', /* L-LING */
+ 05,0200+'z','l','i','n', /* -ZLING */
+ 05,0200+'t','l','i','n', /* -TLING */
+ 040+05,'s',0200+'l','i','n', /* S-LING */
+ 05,'r',0200+'l','i','n', /* R-LING */
+ 05,0200+'p','l','i','n', /* -PLING */
+ 06,'n',0200+'k','l','i','n', /* N-KLING */
+ 05,'k',0200+'l','i','n', /* K-LING */
+ 05,0200+'g','l','i','n', /* -GLING */
+ 05,0200+'f','l','i','n', /* -FLING */
+ 05,0200+'d','l','i','n', /* -DLING */
+ 05,0200+'c','l','i','n', /* -CLING */
+ 05,0200+'b','l','i','n', /* -BLING */
+ 06,'y',0200+'t','h','i','n', /* Y-THING */
+ 07,'e','e','t','h',0200+'i','n', /* EETH-ING */
+ 06,'e',0200+'t','h','i','n', /* E-THING */
+ 05,'g',0200+'g','i','n', /* G-GING */
+ 05,'d',0200+'d','i','n', /* D-DING */
+ 05,'b',0200+'b','i','n', /* B-BING */
+ 03,0200+'i','n', /* -ING */
+ 00
+};
+
+static Uchar sufh[] = {
+ 05,0200+'m','o','u','t', /* -MOUTH */
+ 05,0200+'w','o','r','t', /* -WORTH */
+ 04,0200+'w','i','t', /* -WITH */
+ 05,'t',0200+'t','i','s', /* T-TISH */
+ 05,'e',0200+'t','i','s', /* E-TISH */
+ 05,'p',0200+'p','i','s', /* P-PISH */
+ 05,'r',0200+'n','i','s', /* R-NISH */
+ 05,'n',0200+'n','i','s', /* N-NISH */
+ 05,0200+'p','l','i','s', /* -PLISH */
+ 05,0200+'g','u','i','s', /* -GUISH */
+ 05,0200+'g','l','i','s', /* -GLISH */
+ 05,'b',0200+'l','i','s', /* B-LISH */
+ 05,'g',0200+'g','i','s', /* G-GISH */
+ 05,'d',0200+'d','i','s', /* D-DISH */
+ 03,0200+'i','s', /* -ISH */
+ 05,0200+'g','r','a','p', /* -GRAPH */
+ 07,0200+'b','o','r',0200+'o','u','g', /* -BOR-OUGH */
+ 05,0200+'b','u','r','g', /* -BURGH */
+ 04,0200+'v','i','c', /* -VICH */
+ 03,0200+'n','a', /* -NAH */
+ 03,0200+'l','a', /* -LAH */
+ 04,0200+'m','i',0200+'a', /* -MI-AH */
+ 00
+};
+
+static Uchar sufi[] = {
+ 03,0200+'t','r', /* -TRI */
+ 03,0200+'c','h', /* -CHI */
+ 0200+03,'i','f', /* IF-I */
+ 0200+03,'e','d', /* ED-I */
+ 05,0200+'a','s','c','i', /* -ASCII */
+ 04,0200+'s','e','m', /* -SEMI */
+ 00
+};
+
+static Uchar sufk[] = {
+ 04,0200+'w','o','r', /* -WORK */
+ 04,0200+'m','a','r', /* -MARK */
+ 04,0200+'b','o','o', /* -BOOK */
+ 04,0200+'w','a','l', /* -WALK */
+ 05,0200+'c','r','a','c', /* -CRACK */
+ 04,0200+'b','a','c', /* -BACK */
+ 00
+};
+
+static Uchar sufl[] = {
+ 03,0200+'f','u', /* -FUL */
+ 05,'s',0200+'w','e','l', /* S-WELL */
+ 04,0200+'t','e','l', /* -TELL */
+ 05,0200+'s','h','e','l', /* -SHELL */
+ 05,0200+'s','t','a','l', /* -STALL */
+ 04,'s',0200+'t','a', /* S-TAL */
+ 04,0200+'b','a','l', /* -BALL */
+ 04,0200+'c','a','l', /* -CALL */
+ 03,'v',0200+'e', /* V-EL */
+ 03,'u',0200+'e', /* U-EL */
+ 03,'k',0200+'e', /* K-EL */
+ 04,'t','h',0200+'e', /* TH-EL */
+ 05,'t','c','h',0200+'e', /* TCH-EL */
+ 03,'a',0200+'e', /* A-EL */
+ 0140+04,0200+'q','u','a', /* /QUAL */
+ 040+03,'u',0200+'a', /* U-AL */
+ 03,0200+'t','a', /* -TAL */
+ 04,'u','r',0200+'a', /* UR-AL */
+ 040+05,'g',0200+'o',0200+'n','a', /* G-O-NAL */
+ 04,'o','n',0200+'a', /* ON-AL */
+ 03,0200+'n','a', /* -NAL */
+ 04,0200+'t','i','a', /* -TIAL */
+ 04,0200+'s','i','a', /* -SIAL */
+ 040+05,0200+'t','r','i',0200+'a', /* -TRI-AL */
+ 04,'r','i',0200+'a', /* RI-AL */
+ 04,0200+'n','i',0200+'a', /* -NI-AL */
+ 04,0200+'d','i',0200+'a', /* -DI-AL */
+ 04,0200+'c','i','a', /* -CIAL */
+ 03,0200+'g','a', /* -GAL */
+ 04,0200+'m','e','a', /* -MEAL */
+/* 040+04,0200+'r','e',0200+'a', /* -RE-AL */
+ 040+04,0200+'r','e','a', /* -REAL */
+ 06,'c',0200+'t','i',0200+'c','a', /* C-TI-CAL */
+ 05,0200+'s','i',0200+'c','a', /* -SI-CAL */
+ 04,0200+'i',0200+'c','a', /* -I-CAL */
+ 03,0200+'c','a', /* -CAL */
+ 03,0200+'b','a', /* -BAL */
+ 06,0200+'n','o',0200+'m','i',0200+'a', /* -NO-MI-AL */
+ 00
+};
+
+static Uchar sufm[] = {
+ 03,0200+'n','u', /* -NUM */
+ 05,'o',0200+'r','i',0200+'u', /* O-RI-UM */
+ 040+03,'i',0200+'u', /* I-UM */
+ 040+03,'e',0200+'u', /* E-UM */
+ 05,'i','v',0200+'i','s', /* IV-ISM */
+ 04,0200+'t','i','s', /* -TISM */
+ 05,'i',0200+'m','i','s', /* I-MISM */
+ 05,'a','l',0200+'i','s', /* AL-ISM */
+ 040+04,'e',0200+'i','s', /* E-ISM */
+ 040+04,'a',0200+'i','s', /* A-ISM */
+ 04,0200+'r','o','o', /* -ROOM */
+ 03,0200+'d','o', /* -DOM */
+ 03,0200+'h','a', /* -HAM */
+ 06,0200+'a',0200+'r','i','t','h', /* -A-RITHM */
+ 05,0200+'r','i','t','h', /* -RITHM */
+ 00
+};
+
+static Uchar sufn[] = {
+ 05,0200+'k','n','o','w', /* -KNOWN */
+ 04,0200+'t','o','w', /* -TOWN */
+ 04,0200+'d','o','w', /* -DOWN */
+ 04,0200+'t','u','r', /* -TURN */
+ 05,0200+'s','p','o','o', /* -SPOON */
+ 04,0200+'n','o','o', /* -NOON */
+ 04,0200+'m','o','o', /* -MOON */
+ 011,'a','l',0200+'i',0200+'z','a',0200+'t','i','o', /* AL-I-ZA-TION */
+ 07,0200+'i',0200+'z','a',0200+'t','i','o', /* -I-ZA-TION */
+ 07,'l',0200+'i',0200+'a',0200+'t','i','o', /* L-I-A-TION */
+ 04,0200+'t','i','o', /* -TION */
+ 040+05,'s',0200+'s','i','o', /* S-SION */
+ 04,0200+'s','i','o', /* -SION */
+ 04,'n',0200+'i','o', /* N-ION */
+ 04,0200+'g','i','o', /* -GION */
+ 04,0200+'c','i','o', /* -CION */
+ 03,0200+'c','o', /* -CON */
+ 05,0200+'c','o','l','o', /* -COLON */
+ 03,0200+'t','o', /* -TON */
+ 04,'i','s',0200+'o', /* IS-ON */
+ 03,0200+'s','o', /* -SON */
+ 03,0200+'r','i', /* -RIN */
+ 03,0200+'p','i', /* -PIN */
+ 03,0200+'n','i', /* -NIN */
+ 03,0200+'m','i', /* -MIN */
+ 03,0200+'l','i', /* -LIN */
+ 03,0200+'k','i', /* -KIN */
+ 05,0200+'s','t','e','i', /* -STEIN */
+ 04,0200+'t','a','i', /* -TAIN */
+ 05,'g','h','t',0200+'e', /* GHT-EN */
+ 05,0200+'w','o','m',0200+'e', /* -WOM-EN */
+ 03,0200+'m','e', /* -MEN */
+ 04,'o',0200+'k','e', /* O-KEN */
+ 03,'k',0200+'e', /* K-EN */
+ 04,0200+'t','e','e', /* -TEEN */
+ 04,0200+'s','e','e', /* -SEEN */
+ 040+03,0200+'s','a', /* -SAN */
+ 05,0200+'w','o','m',0200+'a', /* -WOM-AN */
+ 03,0200+'m','a', /* -MAN */
+ 04,0200+'t','i','a', /* -TIAN */
+ 04,0200+'s','i','a', /* -SIAN */
+ 040+04,'e',0200+'i','a', /* E-IAN */
+ 04,0200+'c','i','a', /* -CIAN */
+ 0300+03,'i','a', /* IA/N */
+ 05,0200+'c','l','e','a', /* -CLEAN */
+ 04,0200+'m','e','a', /* -MEAN */
+ 040+03,'e',0200+'a', /* E-AN */
+ 00
+};
+
+static Uchar sufo[] = {
+ 05,0200+'m','a','c',0200+'r', /* -MAC-RO */
+ 00
+};
+
+static Uchar sufp[] = {
+ 05,0200+'g','r','o','u', /* -GROUP */
+ 02,0200+'u', /* -UP */
+ 04,0200+'s','h','i', /* -SHIP */
+ 04,0200+'k','e','e', /* -KEEP */
+ 00
+};
+
+static Uchar sufr[] = {
+ 04,0200+'z','a','r', /* -ZARR */
+ 0300+02,'r', /* R/R */
+ 03,0200+'t','o', /* -TOR */
+ 040+03,0200+'s','o', /* -SOR */
+ 040+04,0200+'r','i',0200+'o', /* -RI-OR */
+ 04,'i','z',0200+'e', /* IZ-ER */
+ 05,0200+'c','o','v',0200+'e', /* -COV-ER */
+ 04,0200+'o','v','e', /* -OVER */
+ 04,0200+'e','v',0200+'e', /* -EV-ER */
+ 8,0200+'c','o','m',0200+'p','u','t',0200+'e', /* -COM-PUT-ER */
+ 040+05,'u','s',0200+'t','e', /* US-TER */
+ 05,'o','s','t',0200+'e', /* OST-ER */
+ 040+05,0200+'a','c',0200+'t','e', /* -AC-TER */
+ 06,0200+'w','r','i','t',0200+'e', /* -WRIT-ER */
+ 040+05,'i','s',0200+'t','e', /* IS-TER */
+ 040+05,'e','s',0200+'t','e', /* ES-TER */
+ 040+05,'a','s',0200+'t','e', /* AS-TER */
+ 04,0200+'s','t','e', /* -STER */
+ 05,'a','r',0200+'t','e', /* AR-TER */
+ 04,'r','t',0200+'e', /* RT-ER */
+ 040+05,'m',0200+'e',0200+'t','e', /* M-E-TER */
+ 05,0200+'w','a',0200+'t','e', /* -WA-TER */
+ 03,'r',0200+'e', /* R-ER */
+ 04,'o','p',0200+'e', /* OP-ER */
+ 05,0200+'p','a',0200+'p','e', /* -PA-PER */
+ 04,'w','n',0200+'e', /* WN-ER */
+ 040+04,'s',0200+'n','e', /* S-NER */
+ 04,'o','n',0200+'e', /* ON-ER */
+ 04,'r','m',0200+'e', /* RM-ER */
+ 03,0200+'m','e', /* -MER */
+ 04,'l','l',0200+'e', /* LL-ER */
+ 05,'d',0200+'d','l','e', /* D-DLER */
+ 04,0200+'b','l','e', /* -BLER */
+ 03,'k',0200+'e', /* K-ER */
+ 05,'n',0200+'t','h','e', /* N-THER */
+ 06,0200+'f','a',0200+'t','h','e', /* -FA-THER */
+ 06,'e','i',0200+'t','h','e', /* EI-THER */
+ 04,'t','h',0200+'e', /* TH-ER */
+ 04,'s','h',0200+'e', /* SH-ER */
+ 04,0200+'p','h','e', /* -PHER */
+ 04,'c','h',0200+'e', /* CH-ER */
+ 04,'d','g',0200+'e', /* DG-ER */
+ 04,'r','d',0200+'e', /* RD-ER */
+ 06,'o','u','n','d',0200+'e', /* OUND-ER */
+ 04,'l','d',0200+'e', /* LD-ER */
+ 04,'i','d',0200+'e', /* ID-ER */
+ 05,0200+'d','u','c',0200+'e', /* -DUC-ER */
+ 04,'n','c',0200+'e', /* NC-ER */
+ 0100+02, 0200+'e', /* /ER */
+ 03,0200+'s','a', /* -SAR */
+ 040+06,'a','c',0200+'u',0200+'l','a', /* AC-U-LAR */
+ 040+06,'e','c',0200+'u',0200+'l','a', /* EC-U-LAR */
+ 040+06,'i','c',0200+'u',0200+'l','a', /* IC-U-LAR */
+ 040+06,'e','g',0200+'u',0200+'l','a', /* EG-U-LAR */
+ 00
+};
+
+static Uchar sufs[] = {
+ 040+04,'u',0200+'o','u', /* U-OUS */
+ 05,0200+'t','i','o','u', /* -TIOUS */
+ 05,0200+'g','i','o','u', /* -GIOUS */
+ 05,0200+'c','i','o','u', /* -CIOUS */
+ 040+04,'i',0200+'o','u', /* I-OUS */
+ 05,0200+'g','e','o','u', /* -GEOUS */
+ 05,0200+'c','e','o','u', /* -CEOUS */
+ 04,'e',0200+'o','u', /* E-OUS */
+ 0140+02,0200+'u', /* /US */
+ 04,0200+'n','e','s', /* -NESS */
+ 04,0200+'l','e','s', /* -LESS */
+ 0140+02,0200+'s', /* /SS */
+ 040+05,'p',0200+'o',0200+'l','i', /* P-O-LIS */
+ 0140+02,0200+'i', /* /IS */
+ 0100+03,0200+'x','e', /* X/ES */
+ 0100+03,0200+'s','e', /* S/ES */
+ 0100+04,'s','h',0200+'e', /* SH/ES */
+ 0100+04,'c','h',0200+'e', /* CH/ES */
+ 0300+01, /* /S */
+ 00
+};
+
+static Uchar suft[] = {
+ 05,0200+'l','i','m',0200+'i', /* -LIM-IT */
+ 06,'i','o','n',0200+'i','s', /* ION-IST */
+ 05,'i','n',0200+'i','s', /* IN-IST */
+ 05,'a','l',0200+'i','s', /* AL-IST */
+ 06,'l',0200+'o',0200+'g','i','s', /* L-O-GIST */
+ 05,'h','t',0200+'e','s', /* HT-EST */
+ 04,'i',0200+'e','s', /* I-EST */
+ 05,'g',0200+'g','e','s', /* G-GEST */
+ 04,'g',0200+'e','s', /* G-EST */
+ 05,'d',0200+'d','e','s', /* D-DEST */
+ 04,'d',0200+'e','s', /* D-EST */
+ 04,0200+'c','a','s', /* -CAST */
+ 05,0200+'h','e','a','r', /* -HEART */
+ 04,0200+'f','o','o', /* -FOOT */
+ 03,'i',0200+'o', /* I-OT */
+ 05,0200+'f','r','o','n', /* -FRONT */
+ 05,0200+'p','r','i','n', /* -PRINT */
+ 04,0200+'m','e','n', /* -MENT */
+ 05,0200+'c','i','e','n', /* -CIENT */
+ 04,'i',0200+'a','n', /* I-ANT */
+ 06,0200+'w','r','i','g','h', /* -WRIGHT */
+ 06,0200+'b','r','i','g','h', /* -BRIGHT */
+ 06,0200+'f','l','i','g','h', /* -FLIGHT */
+ 06,0200+'w','e','i','g','h', /* -WEIGHT */
+ 05,0200+'s','h','i','f', /* -SHIFT */
+ 05,0200+'c','r','a','f', /* -CRAFT */
+ 040+04,'d','g',0200+'e', /* DG-ET */
+ 04,0200+'g','o','a', /* -GOAT */
+ 04,0200+'c','o','a', /* -COAT */
+ 04,0200+'b','o','a', /* -BOAT */
+ 04,0200+'w','h','a', /* -WHAT */
+ 04,0200+'c','u','i', /* -CUIT */
+ 00
+};
+
+static Uchar sufy[] = {
+ 040+04,'e','s',0200+'t', /* ES-TY */
+ 040+05,'q','u','i',0200+'t', /* QUI-TY */
+ 04,0200+'t','i',0200+'t', /* -TI-TY */
+ 040+05,'o','s',0200+'i',0200+'t', /* OS-I-TY */
+ 04,0200+'s','i',0200+'t', /* -SI-TY */
+ 05,'i','n',0200+'i',0200+'t', /* IN-I-TY */
+ 04,'n','i',0200+'t', /* NI-TY */
+ 040+010,'f','a',0200+'b','i','l',0200+'i',0200+'t', /* FA-BIL-I-TY */
+ 010,0200+'c','a',0200+'b','i','l',0200+'i',0200+'t', /* -CA-BIL-I-TY */
+ 010,0200+'p','a',0200+'b','i','l',0200+'i',0200+'t', /* -PA-BIL-I-TY */
+ 06,0200+'b','i','l',0200+'i',0200+'t', /* -BIL-I-TY */
+ 03,'i',0200+'t', /* I-TY */
+ 04,0200+'b','u','r', /* -BUR-Y */
+ 04,0200+'t','o',0200+'r', /* -TO-RY */
+ 05,0200+'q','u','a','r', /* -QUAR-Y */
+ 040+04,'u',0200+'a','r', /* U-ARY */
+ 07,0200+'m','e','n',0200+'t','a',0200+'r', /* -MEN-TA-RY */
+ 06,'i','o','n',0200+'a','r', /* ION-ARY */
+ 04,'i',0200+'a','r', /* I-ARY */
+ 04,'n',0200+'o',0200+'m', /* N-O-MY */
+ 03,0200+'p','l', /* -PLY */
+ 04,'g',0200+'g','l', /* G-GLY */
+ 05,0200+'p','a',0200+'b','l', /* -PA-BLY */
+ 05,'f','a',0200+'b','l', /* FA-BLY */
+ 05,0200+'c','a',0200+'b','l', /* -CA-BLY */
+ 04,0200+'a','b','l', /* -ABLY */
+ 03,0200+'b','l', /* -BLY */
+ 02,0200+'l', /* -LY */
+ 03,0200+'s','k', /* -SKY */
+ 040+06,'g',0200+'r','a',0200+'p','h', /* G-RA-PHY */
+ 04,'l',0200+'o',0200+'g', /* L-O-GY */
+ 02,0200+'f', /* -FY */
+ 03,0200+'n','e', /* -NEY */
+ 03,0200+'l','e', /* -LEY */
+ 04,'c','k',0200+'e', /* CK-EY */
+ 03,0200+'k','e', /* -KEY */
+ 04,0200+'b','o','d', /* -BODY */
+ 05,0200+'s','t','u','d', /* -STUDY */
+ 0340+04,'e','e','d', /* EEDY */
+ 02,0200+'b', /* -BY */
+ 03,0200+'w','a', /* -WAY */
+ 03,0200+'d','a', /* -DAY */
+ 00
+};
+
+Uchar *suftab[] = {
+ sufa,
+ 0,
+ sufc,
+ sufd,
+ sufe,
+ suff,
+ sufg,
+ sufh,
+ sufi,
+ 0,
+ sufk,
+ sufl,
+ sufm,
+ sufn,
+ sufo,
+ sufp,
+ 0,
+ sufr,
+ sufs,
+ suft,
+ 0,
+ 0,
+ 0,
+ 0,
+ sufy,
+ 0,
+};
diff --git a/src/cmd/troff/t10.c b/src/cmd/troff/t10.c
new file mode 100644
index 00000000..3e8026d1
--- /dev/null
+++ b/src/cmd/troff/t10.c
@@ -0,0 +1,512 @@
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+/*
+ * troff10.c
+ *
+ * typesetter interface
+ */
+
+int vpos = 0; /* absolute vertical position on page */
+int hpos = 0; /* ditto horizontal */
+
+extern Font fonts[MAXFONTS+1];
+
+int Inch;
+int Hor;
+int Vert;
+int Unitwidth;
+int nfonts;
+
+
+
+void t_ptinit(void)
+{
+ int i;
+ char buf[100], *p;
+
+ hmot = t_hmot;
+ makem = t_makem;
+ setabs = t_setabs;
+ setch = t_setch;
+ sethl = t_sethl;
+ setht = t_setht;
+ setslant = t_setslant;
+ vmot = t_vmot;
+ xlss = t_xlss;
+ findft = t_findft;
+ width = t_width;
+ mchbits = t_mchbits;
+ ptlead = t_ptlead;
+ ptout = t_ptout;
+ ptpause = t_ptpause;
+ setfont = t_setfont;
+ setps = t_setps;
+ setwd = t_setwd;
+
+ /* open table for device, */
+ /* read in resolution, size info, font info, etc., set params */
+ if ((p = getenv("TYPESETTER")) != 0)
+ strcpy(devname, p);
+ if (termtab[0] == 0)
+ strcpy(termtab, DWBfontdir);
+ if (fontdir[0] == 0)
+ strcpy(fontdir, DWBfontdir);
+ if (devname[0] == 0)
+ strcpy(devname, TDEVNAME);
+ hyf = 1;
+ lg = 1;
+
+ sprintf(buf, "/dev%s/DESC", devname);
+ strcat(termtab, buf);
+ if (getdesc(termtab) < 0) {
+ ERROR "can't open DESC file %s", termtab WARN;
+ done3(1);
+ }
+ if (!ascii) {
+ OUT "x T %s\n", devname PUT;
+ OUT "x res %d %d %d\n", Inch, Hor, Vert PUT;
+ OUT "x init\n" PUT;
+ }
+ for (i = 1; i <= nfonts; i++)
+ setfp(i, fontlab[i], (char *) 0, 0);
+ sps = EM/3; /* space size */
+ ics = EM; /* insertion character space */
+ for (i = 0; i < (NTAB - 1) && DTAB * (i + 1) < TABMASK; i++)
+ tabtab[i] = DTAB * (i + 1);
+ tabtab[NTAB] = 0;
+ pl = 11 * INCH; /* paper length */
+ po = PO; /* page offset */
+ spacesz = SS;
+ lss = lss1 = VS;
+ ll = ll1 = lt = lt1 = LL;
+ t_specnames(); /* install names like "hyphen", etc. */
+}
+
+void t_specnames(void)
+{
+ int i;
+
+ for (i = 0; spnames[i].n; i++)
+ *spnames[i].n = chadd(spnames[i].v, Troffchar, Install);
+}
+
+void t_ptout(Tchar i)
+{
+ int dv;
+ Tchar *k;
+ int temp, a, b;
+ int diff;
+
+ if (cbits(i) != '\n') {
+ if (olinep >= oline + olnsize) {
+ diff = olinep - oline;
+ olnsize += OLNSIZE;
+ if ((oline = (Tchar *)realloc((char *)oline, olnsize * sizeof(Tchar))) != NULL) {
+ if (diff && olinep)
+ olinep = oline + diff;
+ } else {
+ ERROR "Output line overflow." WARN;
+ done(2);
+ }
+ }
+ *olinep++ = i;
+ return;
+ }
+ if (olinep == oline) {
+ lead += lss;
+ return;
+ }
+
+ hpos = po; /* ??? */
+ esc = 0; /* ??? */
+ ptesc(); /* the problem is to get back to the left end of the line */
+ dv = 0;
+ for (k = oline; k < olinep; k++) {
+ if (ismot(*k) && isvmot(*k)) {
+ temp = absmot(*k);
+ if (isnmot(*k))
+ temp = -temp;
+ dv += temp;
+ }
+ }
+ if (dv) {
+ vflag++;
+ *olinep++ = makem(-dv);
+ vflag = 0;
+ }
+
+ b = dip->blss + lss;
+ lead += dip->blss + lss;
+ dip->blss = 0;
+ for (k = oline; k < olinep; )
+ k += ptout0(k); /* now passing a pointer! */
+ olinep = oline;
+ lead += dip->alss;
+ a = dip->alss;
+ dip->alss = 0;
+ /*
+ OUT "x xxx end of line: hpos=%d, vpos=%d\n", hpos, vpos PUT;
+*/
+ OUT "n%d %d\n", b, a PUT; /* be nice to chuck */
+}
+
+int ptout0(Tchar *pi)
+{
+ int j, k, w;
+ int z, dx, dy, dx2, dy2, n;
+ Tchar i;
+ int outsize; /* size of object being printed */
+
+ outsize = 1; /* default */
+ i = *pi;
+ k = cbits(i);
+ if (ismot(i)) {
+ j = absmot(i);
+ if (isnmot(i))
+ j = -j;
+ if (isvmot(i))
+ lead += j;
+ else
+ esc += j;
+ return(outsize);
+ }
+ if (k == CHARHT) {
+ xpts = fbits(i); /* sneaky, font bits as size bits */
+ if (xpts != mpts)
+ ptps();
+ OUT "x H %d\n", sbits(i) PUT;
+ return(outsize);
+ }
+ if (k == SLANT) {
+ OUT "x S %d\n", sfbits(i)-180 PUT;
+ return(outsize);
+ }
+ if (k == WORDSP) {
+ oput('w');
+ return(outsize);
+ }
+ if (sfbits(i) == oldbits) {
+ xfont = pfont;
+ xpts = ppts;
+ } else
+ xbits(i, 2);
+ if (k == XON) {
+ extern int xon;
+ ptflush(); /* guarantee that everything is out */
+ if (esc)
+ ptesc();
+ if (xfont != mfont)
+ ptfont();
+ if (xpts != mpts)
+ ptps();
+ if (lead)
+ ptlead();
+ OUT "x X " PUT;
+ xon++;
+ for (j = 1; cbits(pi[j]) != XOFF; j++)
+ outascii(pi[j]);
+ oput('\n');
+ xon--;
+ return j+1;
+ }
+ if (k < 040 && k != DRAWFCN)
+ return(outsize);
+ j = z = 0;
+ if (k != DRAWFCN) {
+ if (widcache[k].fontpts == (xfont<<8) + xpts && !setwdf) {
+ w = widcache[k].width;
+ bd = 0;
+ cs = 0;
+ } else
+ w = getcw(k);
+ if (cs) {
+ if (bd)
+ w += (bd - 1) * HOR;
+ j = (cs - w) / 2;
+ w = cs - j;
+ if (bd)
+ w -= (bd - 1) * HOR;
+ }
+ if (iszbit(i)) {
+ if (cs)
+ w = -j;
+ else
+ w = 0;
+ z = 1;
+ }
+ }
+ esc += j;
+ if (xfont != mfont)
+ ptfont();
+ if (xpts != mpts)
+ ptps();
+ if (lead)
+ ptlead();
+ /* put out the real character here */
+ if (k == DRAWFCN) {
+ if (esc)
+ ptesc();
+ w = 0;
+ dx = absmot(pi[3]);
+ if (isnmot(pi[3]))
+ dx = -dx;
+ dy = absmot(pi[4]);
+ if (isnmot(pi[4]))
+ dy = -dy;
+ switch (cbits(pi[1])) {
+ case DRAWCIRCLE: /* circle */
+ OUT "D%c %d\n", DRAWCIRCLE, dx PUT; /* dx is diameter */
+ hpos += dx;
+ break;
+ case DRAWELLIPSE:
+ OUT "D%c %d %d\n", DRAWELLIPSE, dx, dy PUT;
+ hpos += dx;
+ break;
+ case DRAWBUILD:
+ k = cbits(pi[2]);
+ OUT "D%c %d ", DRAWBUILD, dx PUT;
+ if (k < ALPHABET)
+ OUT "%c\n", k PUT;
+ else
+ ptchname(k);
+ hpos += dx;
+ break;
+ case DRAWLINE: /* line */
+ k = cbits(pi[2]);
+ OUT "D%c %d %d ", DRAWLINE, dx, dy PUT;
+ if (k < ALPHABET)
+ OUT "%c\n", k PUT;
+ else
+ ptchname(k);
+ hpos += dx;
+ vpos += dy;
+ break;
+ case DRAWARC: /* arc */
+ dx2 = absmot(pi[5]);
+ if (isnmot(pi[5]))
+ dx2 = -dx2;
+ dy2 = absmot(pi[6]);
+ if (isnmot(pi[6]))
+ dy2 = -dy2;
+ OUT "D%c %d %d %d %d\n", DRAWARC,
+ dx, dy, dx2, dy2 PUT;
+ hpos += dx + dx2;
+ vpos += dy + dy2;
+ break;
+
+ case 's': /* using 's' internally to avoid .tr ~ */
+ pi[1] = '~';
+ case DRAWSPLINE: /* spline */
+ default: /* something else; copy it like spline */
+ OUT "D%c %d %d", cbits(pi[1]), dx, dy PUT;
+ hpos += dx;
+ vpos += dy;
+ if (cbits(pi[3]) == DRAWFCN || cbits(pi[4]) == DRAWFCN) {
+ /* it was somehow defective */
+ OUT "\n" PUT;
+ break;
+ }
+ for (n = 5; cbits(pi[n]) != DRAWFCN; n += 2) {
+ dx = absmot(pi[n]);
+ if (isnmot(pi[n]))
+ dx = -dx;
+ dy = absmot(pi[n+1]);
+ if (isnmot(pi[n+1]))
+ dy = -dy;
+ OUT " %d %d", dx, dy PUT;
+ hpos += dx;
+ vpos += dy;
+ }
+ OUT "\n" PUT;
+ break;
+ }
+ for (n = 3; cbits(pi[n]) != DRAWFCN; n++)
+ ;
+ outsize = n + 1;
+ } else if (k < ALPHABET) {
+ /* try to go faster and compress output */
+ /* by printing nnc for small positive motion followed by c */
+ /* kludgery; have to make sure set all the vars too */
+ if (esc > 0 && esc < 100) {
+ oput(esc / 10 + '0');
+ oput(esc % 10 + '0');
+ oput(k);
+ hpos += esc;
+ esc = 0;
+ } else {
+ if (esc)
+ ptesc();
+ oput('c');
+ oput(k);
+ oput('\n');
+ }
+ } else {
+ if (esc)
+ ptesc();
+ ptchname(k);
+ }
+ if (bd) {
+ bd -= HOR;
+ if (esc += bd)
+ ptesc();
+ if (k < ALPHABET)
+ OUT "c%c\n", k PUT;
+ else
+ ptchname(k);
+ if (z)
+ esc -= bd;
+ }
+ esc += w;
+ return(outsize);
+}
+
+void ptchname(int k)
+{
+ char *chn = chname(k);
+
+ switch (chn[0]) {
+ case MBchar:
+ OUT "c%s\n", chn+1 PUT; /* \n not needed? */
+ break;
+ case Number:
+ OUT "N%s\n", chn+1 PUT;
+ break;
+ case Troffchar:
+ OUT "C%s\n", chn+1 PUT;
+ break;
+ default:
+ ERROR "illegal char type %s", chn WARN;
+ break;
+ }
+}
+
+void ptflush(void) /* get us to a clean output state */
+{
+ if (TROFF) {
+ /* ptesc(); but always H, no h */
+ hpos += esc;
+ OUT "\nH%d\n", hpos PUT;
+ esc = 0;
+ ptps();
+ ptfont();
+ ptlead();
+ }
+}
+
+void ptps(void)
+{
+ int i, j, k;
+
+ i = xpts;
+ for (j = 0; i > (k = pstab[j]); j++)
+ if (!k) {
+ k = pstab[--j];
+ break;
+ }
+ if (!ascii)
+ OUT "s%d\n", k PUT; /* really should put out string rep of size */
+ mpts = i;
+}
+
+void ptfont(void)
+{
+ mfont = xfont;
+ if (ascii)
+ return;
+ if (xfont > nfonts) {
+ ptfpcmd(0, fonts[xfont].longname, 0); /* Put the desired font in the
+ * fontcache of the filter */
+ OUT "f0\n" PUT; /* make sure that it gets noticed */
+ } else
+ OUT "f%d\n", xfont PUT;
+}
+
+void ptfpcmd(int f, char *s, char *longname)
+{
+ if (f > nfonts) /* a bit risky? */
+ f = 0;
+ if (longname) {
+ OUT "x font %d %s %s\n", f, s, longname PUT;
+ } else {
+ OUT "x font %d %s\n", f, s PUT;
+ }
+/* OUT "f%d\n", xfont PUT; /* need this for buggy version of adobe transcript */
+ /* which apparently believes that x font means */
+ /* to set the font, not just the position. */
+}
+
+void t_ptlead(void)
+{
+ vpos += lead;
+ if (!ascii)
+ OUT "V%d\n", vpos PUT;
+ lead = 0;
+}
+
+void ptesc(void)
+{
+ hpos += esc;
+ if (!ascii)
+ if (esc > 0) {
+ oput('h');
+ if (esc>=10 && esc<100) {
+ oput(esc/10 + '0');
+ oput(esc%10 + '0');
+ } else
+ OUT "%d", esc PUT;
+ } else
+ OUT "H%d\n", hpos PUT;
+ esc = 0;
+}
+
+void ptpage(int n) /* called at end of each output page, we hope */
+{
+ int i;
+
+ if (NROFF)
+ return;
+ ptlead();
+ vpos = 0;
+ if (ascii)
+ return;
+ OUT "p%d\n", n PUT; /* new page */
+ for (i = 0; i <= nfonts; i++)
+ if (fontlab[i]) {
+ if (fonts[i].truename)
+ OUT "x font %d %s %s\n", i, fonts[i].longname, fonts[i].truename PUT;
+ else
+ OUT "x font %d %s\n", i, fonts[i].longname PUT;
+ }
+ ptps();
+ ptfont();
+}
+
+void pttrailer(void)
+{
+ if (TROFF)
+ OUT "x trailer\n" PUT;
+}
+
+void ptstop(void)
+{
+ if (TROFF)
+ OUT "x stop\n" PUT;
+}
+
+void t_ptpause(void)
+{
+ if (ascii)
+ return;
+ ptlead();
+ vpos = 0;
+ pttrailer();
+ ptlead();
+ OUT "x pause\n" PUT;
+ flusho();
+ mpts = mfont = 0;
+ ptesc();
+ esc = po;
+ hpos = vpos = 0; /* probably in wrong place */
+}
diff --git a/src/cmd/troff/t11.c b/src/cmd/troff/t11.c
new file mode 100644
index 00000000..5511748c
--- /dev/null
+++ b/src/cmd/troff/t11.c
@@ -0,0 +1,255 @@
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+#define MAXCH NCHARS /* maximum number of global char names */
+char *chnames[MAXCH]; /* chnames[n-ALPHABET] -> name of char n */
+int nchnames; /* number of Cxy names currently seen */
+
+#define MAXPS 100 /* max number of point sizes */
+int pstab[MAXPS]; /* point sizes */
+int nsizes; /* number in DESC */
+
+Font fonts[MAXFONTS+1]; /* font info + ptr to width info */
+
+
+#define skipline(f) while (getc(f) != '\n')
+
+#define eq(s1, s2) (strcmp(s1, s2) == 0)
+
+getdesc(char *name)
+{
+ FILE *fin;
+ char cmd[100], s[100];
+ int i, v;
+
+ if ((fin = fopen(unsharp(name), "r")) == NULL)
+ return -1;
+ while (fscanf(fin, "%s", cmd) != EOF) {
+ if (strcmp(cmd, "res") == 0) {
+ fscanf(fin, "%d", &Inch);
+ } else if (strcmp(cmd, "hor") == 0) {
+ fscanf(fin, "%d", &Hor);
+ } else if (strcmp(cmd, "vert") == 0) {
+ fscanf(fin, "%d", &Vert);
+ } else if (strcmp(cmd, "unitwidth") == 0) {
+ fscanf(fin, "%d", &Unitwidth);
+ } else if (strcmp(cmd, "sizes") == 0) {
+ nsizes = 0;
+ while (fscanf(fin, "%d", &v) != EOF && v != 0 && nsizes < MAXPS)
+ pstab[nsizes++] = v;
+ } else if (strcmp(cmd, "fonts") == 0) {
+ fscanf(fin, "%d", &nfonts);
+ for (i = 1; i <= nfonts; i++) {
+ fscanf(fin, "%s", s);
+ fontlab[i] = PAIR(s[0], s[1]);
+ }
+ } else if (strcmp(cmd, "charset") == 0) { /* add any names */
+ while (fscanf(fin, "%s", s) != EOF)
+ chadd(s, Troffchar, Install);
+ break;
+ }
+ /* else
+ just skip anything else */
+ skipline(fin);
+ }
+ fclose(fin);
+ return 1;
+}
+
+static int checkfont(char *name)
+{ /* in case it's not really a font description file */
+ /* really paranoid, but consider \f. */
+ FILE *fp;
+ char buf[300], buf2[300];
+ int i, status = -1;
+
+ if ((fp = fopen(unsharp(name), "r")) == NULL)
+ return -1;
+ for (i = 1; i <= 10; i++) {
+ if (fgets(buf, sizeof buf, fp) == NULL)
+ break;
+ sscanf(buf, "%s", buf2);
+ if (buf2[0] == '#') {
+ i--;
+ continue;
+ }
+ if (eq(buf2, "name") || eq(buf2, "fontname") ||
+ eq(buf2, "special") || eq(buf2, "charset")) {
+ status = 1;
+ break;
+ }
+ }
+ fclose(fp);
+ return status;
+
+}
+
+getfont(char *name, int pos) /* create width tab for font */
+{
+ FILE *fin;
+ Font *ftemp = &fonts[pos];
+ Chwid chtemp[MAXCH];
+ static Chwid chinit;
+ int i, nw, n, wid, kern, code, type;
+ char buf[100], ch[100], s1[100], s2[100], s3[100], cmd[300];
+
+ /* fprintf(stderr, "read font %s onto %d\n", name, pos); */
+ if (checkfont(name) == -1)
+ return -1;
+ if ((fin = fopen(unsharp(name), "r")) == NULL)
+ return -1;
+ for (i = 0; i < ALPHABET; i++)
+ chtemp[i] = chinit; /* zero out to begin with */
+ ftemp->specfont = ftemp->ligfont = 0;
+ ftemp->defaultwidth = ftemp->spacewidth = Inch * Unitwidth / 72 / 3; /* should be rounded */
+ while (fscanf(fin, "%s", cmd) != EOF) {
+ if (strcmp(cmd, "name") == 0)
+ fscanf(fin, "%s", ftemp->longname);
+ else if (strcmp(cmd, "special") == 0)
+ ftemp->specfont = 1;
+ else if (strcmp(cmd, "ligatures") == 0) {
+ ftemp->ligfont = getlig(fin);
+ } else if (strcmp(cmd, "spacewidth") == 0) {
+ fscanf(fin, "%d", &ftemp->spacewidth);
+ } else if (strcmp(cmd, "defaultwidth") == 0) {
+ fscanf(fin, "%d", &ftemp->defaultwidth);
+ } else if (strcmp(cmd, "charset") == 0) {
+ wchar_t wc;
+ skipline(fin);
+ nw = ALPHABET;
+ while (fgets(buf, sizeof buf, fin) != NULL) {
+ sscanf(buf, "%s %s %s %s", ch, s1, s2, s3);
+ if (s1[0] != '"') { /* genuine new character */
+ sscanf(s1, "%d", &wid);
+ sscanf(s2, "%d", &kern);
+ code = strtol(s3, 0, 0); /* dec/oct/hex */
+ }
+ /* otherwise it's a synonym for prev character, */
+ /* so leave previous values intact */
+
+
+ /* decide what kind of alphabet it might come from here */
+
+
+ if (strlen(ch) == 1) { /* it's ascii */
+ n = ch[0]; /* origin includes non-graphics */
+ chtemp[n].num = ch[0];
+ } else if (ch[0] == '\\' && ch[1] == '0') {
+ n = strtol(ch+1, 0, 0); /* \0octal or \0xhex */
+ chtemp[n].num = n;
+#ifdef UNICODE
+ } else if (mbtowc(&wc, ch, strlen(ch)) > 1) {
+ chtemp[nw].num = chadd(ch, MBchar, Install);
+ n = nw;
+ nw++;
+#endif /*UNICODE*/
+ } else {
+ if (strcmp(ch, "---") == 0) { /* no name */
+ sprintf(ch, "%d", code);
+ type = Number;
+ } else
+ type = Troffchar;
+ chtemp[nw].num = chadd(ch, type, Install);
+ n = nw;
+ nw++;
+ }
+ chtemp[n].wid = wid;
+ chtemp[n].kern = kern;
+ chtemp[n].code = code;
+ /*fprintf(stderr, "font %2.2s char %4.4s num %3d wid %2d code %3d\n",
+ ftemp->longname, ch, n, wid, code);
+ */
+ }
+ break;
+ }
+ skipline(fin);
+ }
+ fclose(fin);
+ chtemp[' '].wid = ftemp->spacewidth; /* width of space on this font */
+ ftemp->nchars = nw;
+ if (ftemp->wp)
+ free(ftemp->wp); /* god help us if this wasn't allocated */
+ ftemp->wp = (Chwid *) malloc(nw * sizeof(Chwid));
+ if (ftemp->wp == NULL)
+ return -1;
+ for (i = 0; i < nw; i++)
+ ftemp->wp[i] = chtemp[i];
+/*
+ * printf("%d chars: ", nw);
+ * for (i = 0; i < nw; i++)
+ * if (ftemp->wp[i].num > 0 && ftemp->wp[i].num < ALPHABET) {
+ * printf("%c %d ", ftemp->wp[i].num, ftemp->wp[i].wid);
+ * else if (i >= ALPHABET)
+ * printf("%d (%s) %d ", ftemp->wp[i].num,
+ * chnames[ftemp->wp[i].num-ALPHABET], ftemp->wp[i].wid);
+ * }
+ * printf("\n");
+ */
+ return 1;
+}
+
+chadd(char *s, int type, int install) /* add s to global character name table; */
+{ /* or just look it up */
+
+ /* a temporary kludge: store the "type" as the first character */
+ /* of the string, so we can remember from whence it came */
+
+ char *p;
+ int i;
+
+/* fprintf(stderr, "into chadd %s %c %c\n", s, type, install); /* */
+ for (i = 0; i < nchnames; i++)
+ if (type == chnames[i][0] && eq(s, chnames[i]+1)) /* +1 since type at front */
+ break;
+/* fprintf(stderr, "i %d, nchnames %d\n", i, nchnames); /* */
+ if (i < nchnames) /* found same type and bytes at position i */
+ return ALPHABET + i;
+ else if (install == Lookup) /* not found, and we were just looking */
+ return -1;
+
+ chnames[nchnames] = p = (char *) malloc(strlen(s)+1+1); /* type + \0 */
+ if (p == NULL) {
+ ERROR "out of space adding character %s", s WARN;
+ return LEFTHAND;
+ }
+ if (nchnames >= NCHARS - ALPHABET) {
+ ERROR "out of table space adding character %s", s WARN;
+ return LEFTHAND;
+ }
+ strcpy(chnames[nchnames]+1, s);
+ chnames[nchnames][0] = type;
+/* fprintf(stderr, "installed %c%s at %d\n", type, s, nchnames); /* */
+ return nchnames++ + ALPHABET;
+}
+
+char *chname(int n) /* return string for char with index n */
+{ /* includes type char at front, to be peeled off elsewhere */
+ if (n >= ALPHABET && n < nchnames + ALPHABET)
+ return chnames[n-ALPHABET];
+ else
+ return "";
+}
+
+getlig(FILE *fin) /* pick up ligature list */
+{
+ int lig;
+ char temp[200];
+
+ lig = 0;
+ while (fscanf(fin, "%s", temp) != EOF && strcmp(temp, "0") != 0) {
+ if (strcmp(temp, "fi") == 0)
+ lig |= LFI;
+ else if (strcmp(temp, "fl") == 0)
+ lig |= LFL;
+ else if (strcmp(temp, "ff") == 0)
+ lig |= LFF;
+ else if (strcmp(temp, "ffi") == 0)
+ lig |= LFFI;
+ else if (strcmp(temp, "ffl") == 0)
+ lig |= LFFL;
+ else
+ fprintf(stderr, "illegal ligature %s ignored\n", temp);
+ }
+ return lig;
+}
diff --git a/src/cmd/troff/t6.c b/src/cmd/troff/t6.c
new file mode 100644
index 00000000..b778916c
--- /dev/null
+++ b/src/cmd/troff/t6.c
@@ -0,0 +1,881 @@
+/*
+ * t6.c
+ *
+ * width functions, sizes and fonts
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+int fontlab[MAXFONTS+1];
+int cstab[MAXFONTS+1];
+int ccstab[MAXFONTS+1];
+int bdtab[MAXFONTS+1];
+int sbold = 0;
+
+t_width(Tchar j)
+{
+ int i, k;
+
+ if (iszbit(j))
+ return 0;
+ if (ismot(j)) {
+ if (isvmot(j))
+ return(0);
+ k = absmot(j);
+ if (isnmot(j))
+ k = -k;
+ return(k);
+ }
+ i = cbits(j);
+ if (i < ' ') {
+ if (i == '\b')
+ return(-widthp);
+ if (i == PRESC)
+ i = eschar;
+ else if (i == HX)
+ return(0);
+ }
+ if (i == ohc)
+ return(0);
+ i = trtab[i];
+ if (i < ' ')
+ return(0);
+ if (sfbits(j) == oldbits) {
+ xfont = pfont;
+ xpts = ppts;
+ } else
+ xbits(j, 0);
+ if (i < nchnames + ALPHABET && widcache[i].fontpts == (xfont<<8) + xpts && !setwdf)
+ k = widcache[i].width;
+ else {
+ k = getcw(i);
+ if (bd)
+ k += (bd - 1) * HOR;
+ if (cs)
+ k = cs;
+ }
+ widthp = k;
+ return(k);
+}
+
+/*
+ * clear width cache-- s means just space
+ */
+void zapwcache(int s)
+{
+ int i;
+
+ if (s) {
+ widcache[' '].fontpts = 0;
+ return;
+ }
+ for (i=0; i<NWIDCACHE; i++)
+ widcache[i].fontpts = 0;
+}
+
+onfont(int n, int f) /* is char n on font f? */
+{
+ int i;
+ Font *fp = &fonts[f];
+ Chwid *cp, *ep;
+ char *np;
+
+ if (n < ALPHABET) {
+ if (fp->wp[n].num == n) /* ascii at front */
+ return n;
+ else
+ return -1;
+ }
+ cp = &fp->wp[ALPHABET];
+ ep = &fp->wp[fp->nchars];
+ for ( ; cp < ep; cp++) /* search others */
+ if (cp->num == n)
+ return cp - &fp->wp[0];
+ /* maybe it was a \N... */
+ np = chname(n);
+ if (*np == Number) {
+ i = atoi(np+1); /* sscanf(np+1, "%d", &i); */
+ cp = &fp->wp[0];
+ ep = &fp->wp[fp->nchars];
+ for ( ; cp < ep; cp++) { /* search others */
+ if (cp->code == i)
+ return cp - &fp->wp[0];
+ }
+ return -2; /* a \N that doesn't have an entry */
+ }
+ return -1; /* vanilla not found */
+}
+
+getcw(int i)
+{
+ int k, n, x;
+ Font *fp;
+ int nocache = 0;
+ if (i < ' ')
+ return 0;
+ bd = 0;
+ fp = &fonts[xfont];
+ if (i == ' ') { /* a blank */
+ k = (fp->spacewidth * spacesz + 6) / 12;
+ /* this nonsense because .ss cmd uses 1/36 em as its units */
+ /* and default is 12 */
+ } else if ((n = onfont(i, xfont)) >= 0) { /* on this font at n */
+ k = fp->wp[n].wid;
+ if (setwdf)
+ numtabp[CT].val |= fp->wp[n].kern;
+ } else if (n == -2) { /* \N with default width */
+
+ k = fp->defaultwidth;
+ } else { /* not on current font */
+ nocache = 1;
+ k = fp->defaultwidth; /* default-size space */
+ if (smnt) {
+ int ii, jj;
+ for (ii=smnt, jj=0; jj < nfonts; jj++, ii=ii % nfonts + 1) {
+ if ((n = onfont(i, ii)) >= 0) {
+ k = fonts[ii].wp[n].wid;
+ if (xfont == sbold)
+ bd = bdtab[ii];
+ if (setwdf)
+ numtabp[CT].val |= fonts[ii].wp[n].kern;
+ break;
+ }
+ }
+ }
+ }
+ if (!bd)
+ bd = bdtab[xfont];
+ if (cs = cstab[xfont]) {
+ nocache = 1;
+ if (ccs = ccstab[xfont])
+ x = ccs;
+ else
+ x = xpts;
+ cs = (cs * EMPTS(x)) / 36;
+ }
+ /* was (k & BYTEMASK); since .wid is unsigned, should never happen */
+ if (k < 0)
+ ERROR "can't happen: negative width %d in getcw %d\n", k, i WARN;
+ k = (k * xpts + (Unitwidth / 2)) / Unitwidth;
+ if (nocache|bd)
+ widcache[i].fontpts = 0;
+ else {
+ widcache[i].fontpts = (xfont<<8) + xpts;
+ widcache[i].width = k;
+ }
+ return(k);
+ /* Unitwidth is Units/Point, where
+ /* Units is the fundamental digitization
+ /* of the character set widths, and
+ /* Point is the number of goobies in a point
+ /* e.g., for cat, Units=36, Point=6, so Unitwidth=36/6=6
+ /* In effect, it's the size at which the widths
+ /* translate directly into units.
+ */
+}
+
+void xbits(Tchar i, int bitf)
+{
+ int k;
+
+ if(TROFF) {
+ xfont = fbits(i);
+ k = sbits(i);
+ if(k) {
+ xpts = pstab[k-1];
+ oldbits = sfbits(i);
+ pfont = xfont;
+ ppts = xpts;
+ return;
+ }
+ switch(bitf) {
+ case 0:
+ xfont = font;
+ xpts = pts;
+ break;
+ case 1:
+ xfont = pfont;
+ xpts = ppts;
+ break;
+ case 2:
+ xfont = mfont;
+ xpts = mpts;
+ }
+ }
+}
+
+
+/* these next two functions ought to be the same in troff and nroff, */
+/* but the data structures they search are different. */
+/* silly historical problem. */
+
+
+Tchar t_setch(int c)
+{
+ int j;
+ char temp[50];
+ char *s;
+
+ s = temp;
+ if (c == '(') { /* \(xx */
+ if ((*s++ = getach()) == 0 || (*s++ = getach()) == 0)
+ return(0);
+ } else { /* \C'...' */
+ c = getach();
+ while ((*s = getach()) != c && *s != 0 && s < temp + sizeof(temp) - 1)
+ s++;
+ }
+ *s = '\0';
+#ifdef UNICODE
+ return chadd(temp, Troffchar, Install) | chbits; /* add name even if haven't seen it */
+#else
+ if (NROFF) {
+ j = chadd(temp, Troffchar, Lookup);
+ if ( j == -1)
+ return 0;
+ else
+ return j | chbits;
+ } else
+ return chadd(temp, Troffchar, Install) | chbits; /* add name even if haven't seen it */
+
+#endif /*UNICODE*/
+}
+
+Tchar t_setabs(void) /* set absolute char from \N'...' */
+{
+ int n;
+ char temp[10];
+
+ getch(); /* delim */
+ n = 0;
+ n = inumb(&n);
+ getch(); /* delim */
+ if (nonumb)
+ return 0;
+ sprintf(temp, "%d", n); /* convert into "#n" */
+ n = chadd(temp, Number, Install);
+ return n | chbits;
+}
+
+
+/*
+ * fontlab[] is a cache that contains font information
+ * for each font.
+ * fontlab[] contains the 1- or 2-character name of the
+ * font current associated with that font.
+ * fonts 1..nfonts correspond to the mounted fonts;
+ * the last of these are the special fonts.
+ * If we don't use the (named) font in one of the
+ * standard positions, we install the name in the next
+ * free slot of fontlab[] and font[].
+ * Whenever we need info about the font, we
+ * read in the data into the next free slot with getfont.
+ * The ptfont() (t10.c) routine will tell
+ * the device filter to put the font always at position
+ * zero if xfont > nfonts, so no need to change these filters.
+ * Yes, this is a bit kludgy.
+ *
+ * This gives the new specs of findft:
+ * find the font name i, where i also can be a number.
+ * Installs the font(name) i when not present
+ * returns -1 on error
+ */
+
+
+t_findft(int i)
+{
+ int k;
+ Uchar *p;
+
+ p = unpair(i);
+
+ if (isdigit(p[0])) { /* first look for numbers */
+ k = p[0] - '0';
+ if (p[1] > 0 && isdigit(p[1]))
+ k = 10 * k + p[1] - '0';
+ if (k > 0 && k <= nfonts && k < smnt)
+ return(k); /* mounted font: .ft 3 */
+ if (fontlab[k] && k <= MAXFONTS) { /* translate */
+ return(k); /*number to a name */
+ } else {
+ fprintf(stderr, "troff: no font at position %d\n", k);
+ return(-1); /* wild number */
+ }
+ }
+
+ /*
+ * Now we look for font names
+ */
+ for (k = 1; fontlab[k] != i; k++) {
+ if (k > MAXFONTS)
+ return(-1); /* running out of fontlab space */
+ if (fontlab[k] == 0) { /* passed all existing names */
+ if (setfp(k, i, (char *) 0, 1) == -1)
+ return(-1);
+ else {
+ fontlab[k] = i; /* install the name */
+ return(k);
+ }
+ }
+ }
+ return(k); /* was one of the existing names */
+}
+
+
+void caseps(void)
+{
+ int i;
+
+ if (TROFF) {
+ if(skip())
+ i = apts1;
+ else {
+ noscale++;
+ i = inumb(&apts); /* this is a disaster for fractional point sizes */
+ noscale = 0;
+ if(nonumb)
+ i = apts1;
+ }
+ casps1(i);
+ }
+}
+
+
+void casps1(int i)
+{
+
+/*
+ * in olden times, it used to ignore changes to 0 or negative.
+ * this is meant to allow the requested size to be anything,
+ * in particular so eqn can generate lots of \s-3's and still
+ * get back by matching \s+3's.
+
+ if (i <= 0)
+ return;
+*/
+ apts1 = apts;
+ apts = i;
+ pts1 = pts;
+ pts = findps(i);
+ mchbits();
+}
+
+
+findps(int i)
+{
+ int j, k;
+
+ for (j=k=0 ; pstab[j] != 0 ; j++)
+ if (abs(pstab[j]-i) < abs(pstab[k]-i))
+ k = j;
+
+ return(pstab[k]);
+}
+
+
+void t_mchbits(void)
+{
+ int i, j, k;
+
+ i = pts;
+ for (j = 0; i > (k = pstab[j]); j++)
+ if (!k) {
+ j--;
+ break;
+ }
+ chbits = 0;
+ setsbits(chbits, ++j);
+ setfbits(chbits, font);
+ sps = width(' ' | chbits);
+ zapwcache(1);
+}
+
+void t_setps(void)
+{
+ int i, j;
+
+ i = cbits(getch());
+ if (isdigit(i)) { /* \sd or \sdd */
+ i -= '0';
+ if (i == 0) /* \s0 */
+ j = apts1;
+ else if (i <= 3 && (ch=getch()) && isdigit(j = cbits(ch))) { /* \sdd */
+ j = 10 * i + j - '0';
+ ch = 0;
+ } else /* \sd */
+ j = i;
+ } else if (i == '(') { /* \s(dd */
+ j = cbits(getch()) - '0';
+ j = 10 * j + cbits(getch()) - '0';
+ if (j == 0) /* \s(00 */
+ j = apts1;
+ } else if (i == '+' || i == '-') { /* \s+, \s- */
+ j = cbits(getch());
+ if (isdigit(j)) { /* \s+d, \s-d */
+ j -= '0';
+ } else if (j == '(') { /* \s+(dd, \s-(dd */
+ j = cbits(getch()) - '0';
+ j = 10 * j + cbits(getch()) - '0';
+ }
+ if (i == '-')
+ j = -j;
+ j += apts;
+ }
+ casps1(j);
+}
+
+
+Tchar t_setht(void) /* set character height from \H'...' */
+{
+ int n;
+ Tchar c;
+
+ getch();
+ n = inumb(&apts);
+ getch();
+ if (n == 0 || nonumb)
+ n = apts; /* does this work? */
+ c = CHARHT;
+ c |= ZBIT;
+ setsbits(c, n);
+ setfbits(c, pts); /* sneaky, CHARHT font bits are size bits */
+ return(c);
+}
+
+Tchar t_setslant(void) /* set slant from \S'...' */
+{
+ int n;
+ Tchar c;
+
+ getch();
+ n = 0;
+ n = inumb(&n);
+ getch();
+ if (nonumb)
+ n = 0;
+ c = SLANT;
+ c |= ZBIT;
+ setsfbits(c, n+180);
+ return(c);
+}
+
+
+void caseft(void)
+{
+ if (!TROFF) {
+ n_caseft();
+ return;
+ }
+ skip();
+ setfont(1);
+}
+
+
+void t_setfont(int a)
+{
+ int i, j;
+
+ if (a)
+ i = getrq();
+ else
+ i = getsn();
+ if (!i || i == 'P') {
+ j = font1;
+ goto s0;
+ }
+ if (/* i == 'S' || */ i == '0') /* an experiment -- why can't we change to it? */
+ return;
+ if ((j = findft(i)) == -1)
+ if ((j = setfp(0, i, (char*) 0, 1)) == -1) /* try to put it in position 0 */
+ return;
+s0:
+ font1 = font;
+ font = j;
+ mchbits();
+}
+
+
+void t_setwd(void)
+{
+ int base, wid;
+ Tchar i;
+ int delim, emsz, k;
+ int savhp, savapts, savapts1, savfont, savfont1, savpts, savpts1;
+
+ base = numtabp[ST].val = numtabp[SB].val = wid = numtabp[CT].val = 0;
+ if (ismot(i = getch()))
+ return;
+ delim = cbits(i);
+ savhp = numtabp[HP].val;
+ numtabp[HP].val = 0;
+ savapts = apts;
+ savapts1 = apts1;
+ savfont = font;
+ savfont1 = font1;
+ savpts = pts;
+ savpts1 = pts1;
+ setwdf++;
+ while (cbits(i = getch()) != delim && !nlflg) {
+ k = width(i);
+ wid += k;
+ numtabp[HP].val += k;
+ if (!ismot(i)) {
+ emsz = (INCH/72) * xpts;
+ } else if (isvmot(i)) {
+ k = absmot(i);
+ if (isnmot(i))
+ k = -k;
+ base -= k;
+ emsz = 0;
+ } else
+ continue;
+ if (base < numtabp[SB].val)
+ numtabp[SB].val = base;
+ if ((k = base + emsz) > numtabp[ST].val)
+ numtabp[ST].val = k;
+ }
+ setn1(wid, 0, (Tchar) 0);
+ numtabp[HP].val = savhp;
+ apts = savapts;
+ apts1 = savapts1;
+ font = savfont;
+ font1 = savfont1;
+ pts = savpts;
+ pts1 = savpts1;
+ mchbits();
+ setwdf = 0;
+}
+
+
+Tchar t_vmot(void)
+{
+ dfact = lss;
+ vflag++;
+ return t_mot();
+}
+
+
+Tchar t_hmot(void)
+{
+ dfact = EM;
+ return t_mot();
+}
+
+
+Tchar t_mot(void)
+{
+ int j, n;
+ Tchar i;
+
+ j = HOR;
+ getch(); /*eat delim*/
+ if (n = atoi0()) {
+ if (vflag)
+ j = VERT;
+ i = makem(quant(n, j));
+ } else
+ i = 0;
+ getch();
+ vflag = 0;
+ dfact = 1;
+ return(i);
+}
+
+
+Tchar t_sethl(int k)
+{
+ int j;
+ Tchar i;
+
+ j = EM / 2;
+ if (k == 'u')
+ j = -j;
+ else if (k == 'r')
+ j = -2 * j;
+ vflag++;
+ i = makem(j);
+ vflag = 0;
+ return(i);
+}
+
+
+Tchar t_makem(int i)
+{
+ Tchar j;
+
+ if (i >= 0)
+ j = i;
+ else
+ j = -i;
+ if (Hor > 1 && !vflag)
+ j = (j + Hor/2)/Hor * Hor;
+ j |= MOT;
+ if (i < 0)
+ j |= NMOT;
+ if (vflag)
+ j |= VMOT;
+ return(j);
+}
+
+
+Tchar getlg(Tchar i)
+{
+ Tchar j, k;
+ int lf;
+
+ if (!TROFF)
+ return i;
+ if ((lf = fonts[fbits(i)].ligfont) == 0) /* font lacks ligatures */
+ return(i);
+ j = getch0();
+ if (cbits(j) == 'i' && (lf & LFI))
+ j = LIG_FI;
+ else if (cbits(j) == 'l' && (lf & LFL))
+ j = LIG_FL;
+ else if (cbits(j) == 'f' && (lf & LFF)) {
+ if ((lf & (LFFI|LFFL)) && lg != 2) {
+ k = getch0();
+ if (cbits(k)=='i' && (lf&LFFI))
+ j = LIG_FFI;
+ else if (cbits(k)=='l' && (lf&LFFL))
+ j = LIG_FFL;
+ else {
+ *pbp++ = k;
+ j = LIG_FF;
+ }
+ } else
+ j = LIG_FF;
+ } else {
+ *pbp++ = j;
+ j = i;
+ }
+ return(i & SFMASK | j);
+}
+
+
+void caselg(void)
+{
+
+ if(TROFF) {
+ skip();
+ lg = atoi0();
+ if (nonumb)
+ lg = 1;
+ }
+}
+
+void casefp(void)
+{
+ int i, j;
+
+ if (!TROFF) {
+ n_casefp();
+ return;
+ }
+ skip();
+ i = cbits(getch());
+ if (isdigit(i)) {
+ i -= '0';
+ j = cbits(getch());
+ if (isdigit(j))
+ i = 10 * i + j - '0';
+ }
+ if (i <= 0 || i > nfonts)
+ ERROR "fp: bad font position %d", i WARN;
+ else if (skip() || !(j = getrq()))
+ ERROR "fp: no font name" WARN;
+ else if (skip() || !getname())
+ setfp(i, j, (char*) 0, 1);
+ else /* 3rd argument = filename */
+ setfp(i, j, nextf, 1);
+}
+
+char *strdupl(const char *s) /* make a copy of s */
+{
+ char *t;
+
+ t = (char *) malloc(strlen(s) + 1);
+ if (t == NULL)
+ ERROR "out of space in strdupl(%s)", s FATAL;
+ strcpy(t, s);
+ return t;
+}
+
+setfp(int pos, int f, char *truename, int print) /* mount font f at position pos[0...nfonts] */
+{
+ char pathname[NS], shortname[NS], *sl;
+
+ zapwcache(0);
+ if (truename)
+ strcpy(shortname, truename);
+ else
+ strcpy(shortname, (char *) unpair(f));
+ if (truename && strrchr(truename, '/')) { /* .fp 1 R dir/file: use verbatim */
+ sprintf(pathname, "%s", truename);
+ if (fonts[pos].truename)
+ free(fonts[pos].truename);
+ fonts[pos].truename = strdupl(truename);
+ } else if (truename) { /* synonym: .fp 1 R Avant */
+ sprintf(pathname, "%s/dev%s/%s", fontdir, devname, truename);
+ truename = 0; /* so doesn't get repeated by ptfpcmd */
+ } else /* vanilla: .fp 5 XX */
+ sprintf(pathname, "%s/dev%s/%s", fontdir, devname, shortname);
+ if (truename == 0 && fonts[pos].truename != 0) {
+ free(fonts[pos].truename);
+ fonts[pos].truename = 0;
+ }
+ if (getfont(pathname, pos) < 0) {
+ ERROR "Can't open font file %s", pathname WARN;
+ return -1;
+ }
+ if (print && !ascii) {
+ ptfpcmd(pos, fonts[pos].longname, truename);
+ ptfont();
+ }
+ if (pos == smnt) {
+ smnt = 0;
+ sbold = 0;
+ }
+ fontlab[pos] = f;
+ if (smnt == 0 && fonts[pos].specfont)
+ smnt = pos;
+ bdtab[pos] = cstab[pos] = ccstab[pos] = 0;
+ return pos;
+}
+
+/*
+ * .cs request; don't check legality of optional arguments
+ */
+void casecs(void)
+{
+ int i, j;
+
+ if (TROFF) {
+ int savtr = trace;
+
+ trace = 0;
+ noscale++;
+ skip();
+ if (!(i = getrq()) || (i = findft(i)) < 0)
+ goto rtn;
+ skip();
+ cstab[i] = atoi0();
+ skip();
+ j = atoi0();
+ if(nonumb)
+ ccstab[i] = 0;
+ else
+ ccstab[i] = findps(j);
+ rtn:
+ zapwcache(0);
+ noscale = 0;
+ trace = savtr;
+ }
+}
+
+
+void casebd(void)
+{
+ int i, j, k;
+
+ if (!TROFF) {
+ n_casebd();
+ return;
+ }
+ zapwcache(0);
+ k = 0;
+bd0:
+ if (skip() || !(i = getrq()) || (j = findft(i)) == -1) {
+ if (k)
+ goto bd1;
+ else
+ return;
+ }
+ if (j == smnt) {
+ k = smnt;
+ goto bd0;
+ }
+ if (k) {
+ sbold = j;
+ j = k;
+ }
+bd1:
+ skip();
+ noscale++;
+ bdtab[j] = atoi0();
+ noscale = 0;
+}
+
+
+void casevs(void)
+{
+ int i;
+
+ if (!TROFF) {
+ n_casevs();
+ return;
+ }
+ skip();
+ vflag++;
+ dfact = INCH; /* default scaling is points! */
+ dfactd = 72;
+ res = VERT;
+ i = inumb(&lss);
+ if (nonumb)
+ i = lss1;
+ if (i < VERT)
+ i = VERT;
+ lss1 = lss;
+ lss = i;
+}
+
+
+void casess(void)
+{
+ int i;
+
+ if(TROFF) {
+ noscale++;
+ skip();
+ if(i = atoi0()) {
+ spacesz = i & 0177;
+ zapwcache(0);
+ sps = width(' ' | chbits);
+ }
+ noscale = 0;
+ }
+}
+
+
+Tchar t_xlss(void)
+{
+ /* stores \x'...' into two successive Tchars.
+ /* the first contains HX, the second the value,
+ /* encoded as a vertical motion.
+ /* decoding is done in n2.c by pchar().
+ */
+ int i;
+
+ getch();
+ dfact = lss;
+ i = quant(atoi0(), VERT);
+ dfact = 1;
+ getch();
+ if (i >= 0)
+ *pbp++ = MOT | VMOT | i;
+ else
+ *pbp++ = MOT | VMOT | NMOT | -i;
+ return(HX);
+}
+
+Uchar *unpair(int i)
+{
+ static Uchar name[3];
+
+ name[0] = i & SHORTMASK;
+ name[1] = (i >> SHORT) & SHORTMASK;
+ name[2] = 0;
+ return name;
+}
diff --git a/src/cmd/troff/tdef.h b/src/cmd/troff/tdef.h
new file mode 100644
index 00000000..e9e1f65c
--- /dev/null
+++ b/src/cmd/troff/tdef.h
@@ -0,0 +1,670 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <string.h>
+
+#define NROFF (!TROFF)
+
+
+/* Site dependent definitions */
+
+#ifndef TMACDIR
+#define TMACDIR "lib/tmac/tmac."
+#endif
+#ifndef FONTDIR
+#define FONTDIR "lib/font"
+#endif
+#ifndef NTERMDIR
+#define NTERMDIR "lib/term/tab."
+#endif
+#ifndef TDEVNAME
+#define TDEVNAME "post"
+#endif
+#ifndef NDEVNAME
+#define NDEVNAME "37"
+#endif
+#ifndef TEXHYPHENS
+#define TEXHYPHENS "/usr/lib/tex/macros/hyphen.tex"
+#endif
+#ifndef ALTHYPHENS
+#define ALTHYPHENS "lib/tmac/hyphen.tex" /* another place to look */
+#endif
+
+typedef unsigned char Uchar;
+typedef unsigned short Ushort;
+
+typedef /*unsigned*/ long Tchar;
+
+typedef struct Blockp Blockp;
+typedef struct Diver Diver;
+typedef struct Stack Stack;
+typedef struct Divsiz Divsiz;
+typedef struct Contab Contab;
+typedef struct Numtab Numtab;
+typedef struct Numerr Numerr;
+typedef struct Env Env;
+typedef struct Term Term;
+typedef struct Chwid Chwid;
+typedef struct Font Font;
+typedef struct Spnames Spnames;
+typedef struct Wcache Wcache;
+typedef struct Tbuf Tbuf;
+
+/* this simulates printf into a buffer that gets flushed sporadically */
+/* the BSD goo is because SunOS sprintf doesn't return anything useful */
+
+#ifdef BSD4_2
+#define OUT (obufp += strlen(sprintf(obufp,
+#define PUT ))) > obuf+BUFSIZ ? flusho() : 1
+#else
+#define OUT (obufp += sprintf(obufp,
+#define PUT )) > obuf+BUFSIZ ? flusho() : 1
+#endif
+
+#define oputs(a) OUT "%s", a PUT
+#define oput(c) ( *obufp++ = (c), obufp > obuf+BUFSIZ ? flusho() : 1 )
+
+extern char errbuf[];
+#define ERROR sprintf(errbuf,
+#define WARN ), errprint()
+#define FATAL ), errprint(), exit(1)
+
+/* starting values for typesetting parameters: */
+
+#define PS 10 /* default point size */
+#define FT 1 /* default font position */
+#define ULFONT 2 /* default underline font */
+#define BDFONT 3 /* default emboldening font */
+#define BIFONT 4 /* default bold italic font */
+#define LL (unsigned) 65*INCH/10 /* line length; 39picas=6.5in */
+#define VS ((12*INCH)/72) /* initial vert space */
+
+
+#define EMPTS(pts) (((long)Inch*(pts) + 36) / 72)
+#define EM (TROFF? EMPTS(pts): t.Em)
+#define INCH (TROFF? Inch: 240)
+#define HOR (TROFF? Hor: t.Adj)
+#define VERT (TROFF? Vert: t.Vert)
+#define PO (TROFF? Inch: 0)
+#define SPS (TROFF? EMPTS(pts)/3: INCH/10)
+#define SS (TROFF? 12: INCH/10)
+#define ICS (TROFF? EMPTS(pts): 2*INCH/10)
+#define DTAB (TROFF? (INCH/2): 0)
+
+/* These "characters" are used to encode various internal functions
+/* Some make use of the fact that most ascii characters between
+/* 0 and 040 don't have any graphic or other function.
+/* The few that do have a purpose (e.g., \n, \b, \t, ...
+/* are avoided by the ad hoc choices here.
+/* See ifilt[] in n1.c for others -- 1, 2, 3, 5, 6, 7, 010, 011, 012
+*/
+
+#define LEADER 001
+#define IMP 004 /* impossible char; glues things together */
+#define TAB 011
+#define RPT 014 /* next character is to be repeated many times */
+#define CHARHT 015 /* size field sets character height */
+#define SLANT 016 /* size field sets amount of slant */
+#define DRAWFCN 017 /* next several chars describe arb drawing fcns */
+# define DRAWLINE 'l' /* line: 'l' dx dy char */
+# define DRAWCIRCLE 'c' /* circle: 'c' r */
+# define DRAWELLIPSE 'e' /* ellipse: 'e' rx ry */
+# define DRAWARC 'a' /* arc: 'a' dx dy dx dy */
+# define DRAWSPLINE '~' /* quadratic B spline: '~' dx dy dx dy ... */
+ /* other splines go thru too */
+/* NOTE: the use of ~ is a botch since it's often used in .tr commands */
+/* better to use a letter like s, but change it in the postprocessors too */
+/* for now, this is taken care of in n9.c and t10.c */
+# define DRAWBUILD 'b' /* built-up character (e.g., { */
+
+#define LEFT 020 /* \{ */
+#define RIGHT 021 /* \} */
+#define FILLER 022 /* \& and similar purposes */
+#define XON 023 /* \X'...' starts here */
+#define OHC 024 /* optional hyphenation character \% */
+#define CONT 025 /* \c character */
+#define PRESC 026 /* printable escape */
+#define UNPAD 027 /* unpaddable blank */
+#define XPAR 030 /* transparent mode indicator */
+#define FLSS 031 /* next Tchar contains vertical space */
+ /* used when recalling diverted text */
+#define WORDSP 032 /* paddable word space */
+#define ESC 033 /* current escape character */
+#define XOFF 034 /* \X'...' ends here */
+ /* matches XON, but they will probably never nest */
+ /* so could drop this when another control is needed */
+#define HX 035 /* next character is value of \x'...' */
+#define MOTCH 036 /* this "character" is really motion; used by cbits() */
+
+#define HYPHEN c_hyphen
+#define EMDASH c_emdash /* \(em */
+#define RULE c_rule /* \(ru */
+#define MINUS c_minus /* minus sign on current font */
+#define LIG_FI c_fi /* \(ff */
+#define LIG_FL c_fl /* \(fl */
+#define LIG_FF c_ff /* \(ff */
+#define LIG_FFI c_ffi /* \(Fi */
+#define LIG_FFL c_ffl /* \(Fl */
+#define ACUTE c_acute /* acute accent \(aa */
+#define GRAVE c_grave /* grave accent \(ga */
+#define UNDERLINE c_under /* \(ul */
+#define ROOTEN c_rooten /* root en \(rn */
+#define BOXRULE c_boxrule /* box rule \(br */
+#define LEFTHAND c_lefthand /* left hand for word overflow */
+#define DAGGER c_dagger /* dagger for end of sentence/footnote */
+
+#define HYPHALG 1 /* hyphenation algorithm: 0=>good old troff, 1=>tex */
+
+
+/* array sizes, and similar limits: */
+
+#define MAXFONTS 99 /* Maximum number of fonts in fontab */
+#define NM 90 /* requests + macros */
+#define NN NNAMES /* number registers */
+#define NNAMES 15 /* predefined reg names */
+#define NIF 15 /* if-else nesting */
+#define NS 128 /* name buffer */
+#define NTM 1024 /* tm buffer */
+#define NEV 3 /* environments */
+#define EVLSZ 10 /* size of ev stack */
+
+#define STACKSIZE (6*1024) /* stack for macros and strings in progress */
+#define NHYP 10 /* max hyphens per word */
+#define NHEX 512 /* byte size of exception word list */
+#define NTAB 100 /* tab stops */
+#define NSO 5 /* "so" depth */
+#define NMF 5 /* number of -m flags */
+#define WDSIZE 500 /* word buffer click size */
+#define LNSIZE 4000 /* line buffer click size */
+#define OLNSIZE 5000 /* output line buffer click; bigger for 'w', etc. */
+#define NDI 5 /* number of diversions */
+
+#define ALPHABET alphabet /* number of characters in basic alphabet. */
+ /* 128 for parochial USA 7-bit ascii, */
+ /* 256 for "European" mode with e.g., Latin-1 */
+
+ /* NCHARS must be greater than
+ ALPHABET (ascii stuff) + total number of distinct char names
+ from all fonts that will be run in this job (including
+ unnamed ones and \N's)
+ */
+
+#define NCHARS (8*1024) /* maximum size of troff character set*/
+
+
+ /* However for nroff you want only :
+ 1. number of special codes in charset of DESC, which ends up being the
+ value of nchtab and which must be less than 512.
+ 2. ALPHABET, which apparently is the size of the portion of the tables reserved
+ for special control symbols
+ Apparently the max N of \N is irrelevant; */
+ /* to allow \N of up to 254 with up to 338 special characters
+ you need NCHARS of 338 + ALPHABET = 466 */
+
+#define NROFFCHARS 1024 /* maximum size of nroff character set */
+
+#define NTRTAB NCHARS /* number of items in trtab[] */
+#define NWIDCACHE NCHARS /* number of items in widcache[] */
+
+#define NTRAP 20 /* number of traps */
+#define NPN 20 /* numbers in "-o" */
+#define FBUFSZ 512 /* field buf size words */
+#define IBUFSZ 4096 /* bytes */
+#define NC 1024 /* cbuf size words */
+#define NOV 10 /* number of overstrike chars */
+#define NPP 10 /* pads per field */
+
+/*
+ Internal character representation:
+ Internally, every character is carried around as
+ a 32 bit cookie, called a "Tchar" (typedef long).
+ Bits are numbered 31..0 from left to right.
+ If bit 15 is 1, the character is motion, with
+ if bit 16 it's vertical motion
+ if bit 17 it's negative motion
+ If bit 15 is 0, the character is a real character.
+ if bit 31 zero motion
+ bits 30..24 size
+ bits 23..16 font
+*/
+
+/* in the following, "L" should really be a Tchar, but ... */
+/* numerology leaves room for 16 bit chars */
+
+#define MOT (01uL << 16) /* motion character indicator */
+#define VMOT (01uL << 30) /* vertical motion bit */
+#define NMOT (01uL << 29) /* negative motion indicator */
+/* #define MOTV (MOT|VMOT|NMOT) /* motion flags */
+/* #define MAXMOT (~MOTV) /* maximum motion permitted */
+#define MAXMOT 0xFFFF
+
+#define ismot(n) ((n) & MOT)
+#define isvmot(n) (((n) & (MOT|VMOT)) == (MOT|VMOT)) /* must have tested MOT previously */
+#define isnmot(n) (((n) & (MOT|NMOT)) == (MOT|NMOT)) /* ditto */
+#define absmot(n) ((n) & 0xFFFF)
+
+#define ZBIT (01uL << 31) /* zero width char */
+#define iszbit(n) ((n) & ZBIT)
+
+#define FSHIFT 17
+#define SSHIFT (FSHIFT+7)
+#define SMASK (0177uL << SSHIFT) /* 128 distinct sizes */
+#define FMASK (0177uL << FSHIFT) /* 128 distinct fonts */
+#define SFMASK (SMASK|FMASK) /* size and font in a Tchar */
+#define sbits(n) (((n) >> SSHIFT) & 0177)
+#define fbits(n) (((n) >> FSHIFT) & 0177)
+#define sfbits(n) (((n) & SFMASK) >> FSHIFT)
+#define cbits(n) ((n) & 0x1FFFF) /* isolate character bits, */
+ /* but don't include motions */
+extern int realcbits(Tchar);
+
+#define setsbits(n,s) n = (n & ~SMASK) | (Tchar)(s) << SSHIFT
+#define setfbits(n,f) n = (n & ~FMASK) | (Tchar)(f) << FSHIFT
+#define setsfbits(n,sf) n = (n & ~SFMASK) | (Tchar)(sf) << FSHIFT
+#define setcbits(n,c) n = (n & ~0xFFFFuL | (c)) /* set character bits */
+
+#define BYTEMASK 0377
+#define BYTE 8
+
+#define SHORTMASK 0XFFFF
+#define SHORT 16
+
+#define TABMASK ((unsigned) INT_MAX >> 1)
+#define RTAB ((TABMASK << 1) & ~TABMASK)
+#define CTAB (RTAB << 1)
+
+#define TABBIT 02 /* bits in gchtab */
+#define LDRBIT 04
+#define FCBIT 010
+
+#define PAIR(A,B) (A|(B<<SHORT))
+
+
+extern int Inch, Hor, Vert, Unitwidth;
+
+struct Spnames
+{
+ int *n;
+ char *v;
+};
+
+extern Spnames spnames[];
+
+/*
+ String and macro definitions are stored conceptually in a giant array
+ indexed by type Offset. In olden times, this array was real, and thus
+ both huge and limited in size, leading to the "Out of temp file space"
+ error. In this version, the array is represented by a list of blocks,
+ pointed to by blist[].bp. Each block is of size BLK Tchars, and BLK
+ MUST be a power of 2 for the macros below to work.
+
+ The blocks associated with a particular string or macro are chained
+ together in the array blist[]. Each blist[i].nextoff contains the
+ Offset associated with the next block in the giant array, or -1 if
+ this is the last block in the chain. If .nextoff is 0, the block is
+ free.
+
+ To find the right index in blist for an Offset, divide by BLK.
+*/
+
+#define NBLIST 2048 /* starting number of blocks in all definitions */
+
+#define BLK 128 /* number of Tchars in a block; must be 2^N with defns below */
+
+#define rbf0(o) (blist[bindex(o)].bp[boffset(o)])
+#define bindex(o) ((o) / BLK)
+#define boffset(o) ((o) & (BLK-1))
+#define pastend(o) (((o) & (BLK-1)) == 0)
+/* #define incoff(o) ( (++o & (BLK-1)) ? o : blist[bindex(o-1)].nextoff ) */
+#define incoff(o) ( (((o)+1) & (BLK-1)) ? o+1 : blist[bindex(o)].nextoff )
+
+#define skipline(f) while (getc(f) != '\n')
+#define is(s) (strcmp(cmd, s) == 0)
+#define eq(s1, s2) (strcmp(s1, s2) == 0)
+
+
+typedef unsigned long Offset; /* an offset in macro/string storage */
+
+struct Blockp { /* info about a block: */
+ Tchar *bp; /* the data */
+ Offset nextoff; /* offset of next block in a chain */
+};
+
+extern Blockp *blist;
+
+#define RD_OFFSET (1 * BLK) /* .rd command uses block 1 */
+
+struct Diver { /* diversion */
+ Offset op;
+ int dnl;
+ int dimac;
+ int ditrap;
+ int ditf;
+ int alss;
+ int blss;
+ int nls;
+ int mkline;
+ int maxl;
+ int hnl;
+ int curd;
+};
+
+struct Stack { /* stack frame */
+ int nargs;
+ Stack *pframe;
+ Offset pip;
+ int pnchar;
+ Tchar prchar;
+ int ppendt;
+ Tchar pch;
+ Tchar *lastpbp;
+ int mname;
+};
+
+extern Stack s;
+
+struct Divsiz {
+ int dix;
+ int diy;
+};
+
+struct Contab { /* command or macro */
+ unsigned int rq;
+ Contab *link;
+ void (*f)(void);
+ Offset mx;
+ Offset emx;
+ Divsiz *divsiz;
+};
+
+#define C(a,b) {a, 0, b, 0, 0} /* how to initialize a contab entry */
+
+extern Contab contab[NM];
+
+struct Numtab { /* number registers */
+ unsigned int r; /* name */
+ int val;
+ short fmt;
+ short inc;
+ Numtab *link;
+};
+
+extern Numtab numtab[NN];
+
+#define PN 0
+#define NL 1
+#define YR 2
+#define HP 3
+#define CT 4
+#define DN 5
+#define MO 6
+#define DY 7
+#define DW 8
+#define LN 9
+#define DL 10
+#define ST 11
+#define SB 12
+#define CD 13
+#define PID 14
+
+struct Wcache { /* width cache, indexed by character */
+ short fontpts;
+ short width;
+};
+
+struct Tbuf { /* growable Tchar buffer */
+ Tchar *_bufp;
+ unsigned int _size;
+};
+
+/* the infamous environment block */
+
+#define ics envp->_ics
+#define sps envp->_sps
+#define spacesz envp->_spacesz
+#define lss envp->_lss
+#define lss1 envp->_lss1
+#define ll envp->_ll
+#define ll1 envp->_ll1
+#define lt envp->_lt
+#define lt1 envp->_lt1
+#define ic envp->_ic
+#define icf envp->_icf
+#define chbits envp->_chbits
+#define spbits envp->_spbits
+#define nmbits envp->_nmbits
+#define apts envp->_apts
+#define apts1 envp->_apts1
+#define pts envp->_pts
+#define pts1 envp->_pts1
+#define font envp->_font
+#define font1 envp->_font1
+#define ls envp->_ls
+#define ls1 envp->_ls1
+#define ad envp->_ad
+#define nms envp->_nms
+#define ndf envp->_ndf
+#define nmwid envp->_nmwid
+#define fi envp->_fi
+#define cc envp->_cc
+#define c2 envp->_c2
+#define ohc envp->_ohc
+#define tdelim envp->_tdelim
+#define hyf envp->_hyf
+#define hyoff envp->_hyoff
+#define hyphalg envp->_hyphalg
+#define un1 envp->_un1
+#define tabc envp->_tabc
+#define dotc envp->_dotc
+#define adsp envp->_adsp
+#define adrem envp->_adrem
+#define lastl envp->_lastl
+#define nel envp->_nel
+#define admod envp->_admod
+#define wordp envp->_wordp
+#define spflg envp->_spflg
+#define linep envp->_linep
+#define wdend envp->_wdend
+#define wdstart envp->_wdstart
+#define wne envp->_wne
+#define ne envp->_ne
+#define nc envp->_nc
+#define nb envp->_nb
+#define lnmod envp->_lnmod
+#define nwd envp->_nwd
+#define nn envp->_nn
+#define ni envp->_ni
+#define ul envp->_ul
+#define cu envp->_cu
+#define ce envp->_ce
+#define in envp->_in
+#define in1 envp->_in1
+#define un envp->_un
+#define wch envp->_wch
+#define pendt envp->_pendt
+#define pendw envp->_pendw
+#define pendnf envp->_pendnf
+#define spread envp->_spread
+#define it envp->_it
+#define itmac envp->_itmac
+#define hyptr envp->_hyptr
+#define tabtab envp->_tabtab
+#define line envp->_line._bufp
+#define lnsize envp->_line._size
+#define word envp->_word._bufp
+#define wdsize envp->_word._size
+
+#define oline _oline._bufp
+#define olnsize _oline._size
+
+/*
+ * Note:
+ * If this structure changes in ni.c, you must change
+ * this as well, and vice versa.
+ */
+
+struct Env {
+ int _ics;
+ int _sps;
+ int _spacesz;
+ int _lss;
+ int _lss1;
+ int _ll;
+ int _ll1;
+ int _lt;
+ int _lt1;
+ Tchar _ic;
+ int _icf;
+ Tchar _chbits;
+ Tchar _spbits;
+ Tchar _nmbits;
+ int _apts;
+ int _apts1;
+ int _pts;
+ int _pts1;
+ int _font;
+ int _font1;
+ int _ls;
+ int _ls1;
+ int _ad;
+ int _nms;
+ int _ndf;
+ int _nmwid;
+ int _fi;
+ int _cc;
+ int _c2;
+ int _ohc;
+ int _tdelim;
+ int _hyf;
+ int _hyoff;
+ int _hyphalg;
+ int _un1;
+ int _tabc;
+ int _dotc;
+ int _adsp;
+ int _adrem;
+ int _lastl;
+ int _nel;
+ int _admod;
+ Tchar *_wordp;
+ int _spflg;
+ Tchar *_linep;
+ Tchar *_wdend;
+ Tchar *_wdstart;
+ int _wne;
+ int _ne;
+ int _nc;
+ int _nb;
+ int _lnmod;
+ int _nwd;
+ int _nn;
+ int _ni;
+ int _ul;
+ int _cu;
+ int _ce;
+ int _in;
+ int _in1;
+ int _un;
+ int _wch;
+ int _pendt;
+ Tchar *_pendw;
+ int _pendnf;
+ int _spread;
+ int _it;
+ int _itmac;
+ Tchar *_hyptr[NHYP];
+ long _tabtab[NTAB];
+ Tbuf _line;
+ Tbuf _word;
+};
+
+extern Env env[];
+extern Env *envp;
+
+enum { MBchar = 'U', Troffchar = 'C', Number = 'N', Install = 'i', Lookup = 'l' };
+ /* U => utf, for instance; C => \(xx, N => \N'...' */
+
+
+
+struct Chwid { /* data on one character */
+ Ushort num; /* character number:
+ 0 -> not on this font
+ >= ALPHABET -> its number among all Cxy's */
+ Ushort code; /* char code for actual device. used for \N */
+ char *str; /* code string for nroff */
+ Uchar wid; /* width */
+ Uchar kern; /* ascender/descender */
+};
+
+struct Font { /* characteristics of a font */
+ int name; /* int name, e.g., BI (2 chars) */
+ char longname[64]; /* long name of this font (e.g., "Bembo" */
+ char *truename; /* path name of table if not in standard place */
+ int nchars; /* number of width entries for this font */
+ char specfont; /* 1 == special font */
+ int spacewidth; /* width of space on this font */
+ int defaultwidth; /* default width of characters on this font */
+ Chwid *wp; /* widths, etc., of the real characters */
+ char ligfont; /* 1 == ligatures exist on this font */
+};
+
+/* ligatures, ORed into ligfont */
+
+#define LFF 01
+#define LFI 02
+#define LFL 04
+#define LFFI 010
+#define LFFL 020
+
+/* tracing modes */
+#define TRNARGS 01 /* trace legality of numeric arguments */
+#define TRREQ 02 /* trace requests */
+#define TRMAC 04 /* trace macros */
+#define RQERR 01 /* processing request/macro */
+
+/* typewriter driving table structure */
+
+
+extern Term t;
+struct Term {
+ int bset; /* these bits have to be on */
+ int breset; /* these bits have to be off */
+ int Hor; /* #units in minimum horiz motion */
+ int Vert; /* #units in minimum vert motion */
+ int Newline; /* #units in single line space */
+ int Char; /* #units in character width */
+ int Em; /* ditto */
+ int Halfline; /* half line units */
+ int Adj; /* minimum units for horizontal adjustment */
+ char *twinit; /* initialize terminal */
+ char *twrest; /* reinitialize terminal */
+ char *twnl; /* terminal sequence for newline */
+ char *hlr; /* half-line reverse */
+ char *hlf; /* half-line forward */
+ char *flr; /* full-line reverse */
+ char *bdon; /* turn bold mode on */
+ char *bdoff; /* turn bold mode off */
+ char *iton; /* turn italic mode on */
+ char *itoff; /* turn italic mode off */
+ char *ploton; /* turn plot mode on */
+ char *plotoff; /* turn plot mode off */
+ char *up; /* sequence to move up in plot mode */
+ char *down; /* ditto */
+ char *right; /* ditto */
+ char *left; /* ditto */
+
+ Font tfont; /* widths and other info, as in a troff font */
+};
+
+extern Term t;
+
+/*
+ * for error reporting; keep track of escapes/requests with numeric arguments
+ */
+struct Numerr {
+ char type; /* request or escape? */
+ char esc; /* was escape sequence named esc */
+ char escarg; /* argument of esc's like \D'l' */
+ unsigned int req; /* was request or macro named req */
+};
diff --git a/src/cmd/troff/unansi b/src/cmd/troff/unansi
new file mode 100644
index 00000000..67aed817
--- /dev/null
+++ b/src/cmd/troff/unansi
@@ -0,0 +1,49 @@
+# The awk program cvt will convert the relatively sterotyped ansi c
+# in this troff distribution into older-style c, by munging function
+# declarations.
+
+# You will also have to edit fns.h, by
+# sed 's/(.*)/()/g' fns.h >foo; mv foo fns.h
+# check this before doing the move!
+
+# you will also have to make some editing changes in
+# tdef.h in the Contab structure: s/(void)/()/
+# you may have to fix up some function declarations
+# in n4.c, the ones with (*f)(Tchar).
+
+# you will surely also have header files to deal with.
+
+# the most obvious cases are dealt with by the following
+# commands. make sure you do this stuff on a copy!
+
+# function prototypes in n8.c probably belong in fns.h. readpats(void) must
+# be readpats() before cvt runs.
+
+sed \
+ -e 's/(void)/()/' \
+ -e 's/(Tchar[^)]*);/();/' \
+ -e 's/(char[^)]*);/();/' \
+ -e 's/(int[^)]*);/();/' \
+n8.c >foo
+mv foo n8.c
+
+for i in *.c
+do
+ cvt $i >foo
+ mv foo $i
+done
+
+sed 's/(.*)/()/g' fns.h >foo
+mv foo fns.h
+
+sed -e 's/(void)/()/g' -e '/stdlib/d' tdef.h >foo
+mv foo tdef.h
+
+# Compliers may not approve of void *setbrk() in fns.h and n3.c.
+
+sed 's/^void\*[ ]setbrk/char* setbrk/' fns.h >foo
+mv foo fns.h
+
+sed 's/^void \*setbrk/char *setbrk/' n3.c >foo
+mv foo n3.c
+