Let's try this.  It's BUGGERED.
diff --git a/src/cmd/tbl/mkfile b/src/cmd/tbl/mkfile
new file mode 100644
index 0000000..cdaf4a7
--- /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 0000000..efcab37
--- /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 0000000..0c0895b
--- /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 0000000..1e6cbf1
--- /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 0000000..6d2d741
--- /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 0000000..a4dc9f9
--- /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 0000000..558d3ba
--- /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 0000000..8b7a65d
--- /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 0000000..a78368f
--- /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 0000000..2fa9de5
--- /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 0000000..92277e2
--- /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 0000000..bf1978a
--- /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 0000000..5cc5988
--- /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 0000000..635dbf8
--- /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 0000000..3df8d44
--- /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 0000000..3791c32
--- /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 0000000..6abb149
--- /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 0000000..ef995bb
--- /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 0000000..8fa4e49
--- /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 0000000..7d586e8
--- /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 0000000..43cc84e
--- /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 0000000..96270b7
--- /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 0000000..217869e
--- /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 0000000..19a4b03
--- /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);
+}
+
+