xref: /plan9-contrib/sys/src/cmd/html2ms.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier #include <u.h>
2*7dd7cddfSDavid du Colombier #include <libc.h>
3*7dd7cddfSDavid du Colombier #include <ctype.h>
4*7dd7cddfSDavid du Colombier #include <bio.h>
5*7dd7cddfSDavid du Colombier 
6*7dd7cddfSDavid du Colombier enum
7*7dd7cddfSDavid du Colombier {
8*7dd7cddfSDavid du Colombier 	SSIZE = 10,
9*7dd7cddfSDavid du Colombier 
10*7dd7cddfSDavid du Colombier 	/* list types */
11*7dd7cddfSDavid du Colombier 	Lordered = 0,
12*7dd7cddfSDavid du Colombier 	Lunordered,
13*7dd7cddfSDavid du Colombier 	Lmenu,
14*7dd7cddfSDavid du Colombier 	Ldir,
15*7dd7cddfSDavid du Colombier 
16*7dd7cddfSDavid du Colombier };
17*7dd7cddfSDavid du Colombier 
18*7dd7cddfSDavid du Colombier Biobuf in, out;
19*7dd7cddfSDavid du Colombier int lastc = '\n';
20*7dd7cddfSDavid du Colombier int inpre = 0;
21*7dd7cddfSDavid du Colombier 
22*7dd7cddfSDavid du Colombier /* stack for fonts */
23*7dd7cddfSDavid du Colombier char *fontstack[SSIZE];
24*7dd7cddfSDavid du Colombier char *font = "R";
25*7dd7cddfSDavid du Colombier int fsp;
26*7dd7cddfSDavid du Colombier 
27*7dd7cddfSDavid du Colombier /* stack for lists */
28*7dd7cddfSDavid du Colombier struct
29*7dd7cddfSDavid du Colombier {
30*7dd7cddfSDavid du Colombier 	int	type;
31*7dd7cddfSDavid du Colombier 	int	ord;
32*7dd7cddfSDavid du Colombier } liststack[SSIZE];
33*7dd7cddfSDavid du Colombier int lsp;
34*7dd7cddfSDavid du Colombier 
35*7dd7cddfSDavid du Colombier int quoting;
36*7dd7cddfSDavid du Colombier 
37*7dd7cddfSDavid du Colombier typedef struct Goobie Goobie;
38*7dd7cddfSDavid du Colombier struct Goobie
39*7dd7cddfSDavid du Colombier {
40*7dd7cddfSDavid du Colombier 	char *name;
41*7dd7cddfSDavid du Colombier 	void (*f)(Goobie*, char*);
42*7dd7cddfSDavid du Colombier 	void (*ef)(Goobie*, char*);
43*7dd7cddfSDavid du Colombier };
44*7dd7cddfSDavid du Colombier 
45*7dd7cddfSDavid du Colombier void	eatwhite(void);
46*7dd7cddfSDavid du Colombier void	escape(void);
47*7dd7cddfSDavid du Colombier 
48*7dd7cddfSDavid du Colombier typedef void Action(Goobie*, char*);
49*7dd7cddfSDavid du Colombier 
50*7dd7cddfSDavid du Colombier Action	g_ignore;
51*7dd7cddfSDavid du Colombier Action	g_unexpected;
52*7dd7cddfSDavid du Colombier Action	g_title;
53*7dd7cddfSDavid du Colombier Action	g_p;
54*7dd7cddfSDavid du Colombier Action	g_h;
55*7dd7cddfSDavid du Colombier Action	g_li;
56*7dd7cddfSDavid du Colombier Action	g_list, g_listend;
57*7dd7cddfSDavid du Colombier Action	g_pre;
58*7dd7cddfSDavid du Colombier Action	g_fpush, g_fpop;
59*7dd7cddfSDavid du Colombier Action	g_indent, g_exdent;
60*7dd7cddfSDavid du Colombier Action	g_dt;
61*7dd7cddfSDavid du Colombier Action	g_display;
62*7dd7cddfSDavid du Colombier Action	g_displayend;
63*7dd7cddfSDavid du Colombier Action	g_table, g_tableend, g_caption, g_captionend;
64*7dd7cddfSDavid du Colombier Action	g_br, g_hr;
65*7dd7cddfSDavid du Colombier 
66*7dd7cddfSDavid du Colombier Goobie gtab[] =
67*7dd7cddfSDavid du Colombier {
68*7dd7cddfSDavid du Colombier 	"!--",		g_ignore,	g_unexpected,
69*7dd7cddfSDavid du Colombier 	"!doctype",	g_ignore,	g_unexpected,
70*7dd7cddfSDavid du Colombier 	"a",		g_ignore,	g_ignore,
71*7dd7cddfSDavid du Colombier 	"address",	g_display,	g_displayend,
72*7dd7cddfSDavid du Colombier 	"b",		g_fpush,	g_fpop,
73*7dd7cddfSDavid du Colombier 	"base",		g_ignore,	g_unexpected,
74*7dd7cddfSDavid du Colombier 	"blink",	g_ignore,	g_ignore,
75*7dd7cddfSDavid du Colombier 	"blockquote",	g_ignore,	g_ignore,
76*7dd7cddfSDavid du Colombier 	"body",		g_ignore,	g_ignore,
77*7dd7cddfSDavid du Colombier 	"br",		g_br,		g_unexpected,
78*7dd7cddfSDavid du Colombier 	"caption",	g_caption,	g_captionend,
79*7dd7cddfSDavid du Colombier 	"center",	g_ignore,	g_ignore,
80*7dd7cddfSDavid du Colombier 	"cite",		g_ignore,	g_ignore,
81*7dd7cddfSDavid du Colombier 	"code",		g_ignore,	g_ignore,
82*7dd7cddfSDavid du Colombier 	"dd",		g_ignore,	g_unexpected,
83*7dd7cddfSDavid du Colombier 	"dfn",		g_ignore,	g_ignore,
84*7dd7cddfSDavid du Colombier 	"dir",		g_list,		g_listend,
85*7dd7cddfSDavid du Colombier 	"dl",		g_indent,	g_exdent,
86*7dd7cddfSDavid du Colombier 	"dt",		g_dt,		g_unexpected,
87*7dd7cddfSDavid du Colombier 	"em",		g_ignore,	g_ignore,
88*7dd7cddfSDavid du Colombier 	"font",		g_ignore,	g_ignore,
89*7dd7cddfSDavid du Colombier 	"form",		g_ignore,	g_ignore,
90*7dd7cddfSDavid du Colombier 	"h1",		g_h,		g_p,
91*7dd7cddfSDavid du Colombier 	"h2",		g_h,		g_p,
92*7dd7cddfSDavid du Colombier 	"h3",		g_h,		g_p,
93*7dd7cddfSDavid du Colombier 	"h4",		g_h,		g_p,
94*7dd7cddfSDavid du Colombier 	"h5",		g_h,		g_p,
95*7dd7cddfSDavid du Colombier 	"h6",		g_h,		g_p,
96*7dd7cddfSDavid du Colombier 	"head",		g_ignore,	g_ignore,
97*7dd7cddfSDavid du Colombier 	"hr",		g_hr,		g_unexpected,
98*7dd7cddfSDavid du Colombier 	"html",		g_ignore,	g_ignore,
99*7dd7cddfSDavid du Colombier 	"i",		g_fpush,	g_fpop,
100*7dd7cddfSDavid du Colombier 	"input",	g_ignore,	g_unexpected,
101*7dd7cddfSDavid du Colombier 	"img",		g_ignore,	g_unexpected,
102*7dd7cddfSDavid du Colombier 	"isindex",	g_ignore,	g_unexpected,
103*7dd7cddfSDavid du Colombier 	"kbd",		g_fpush,	g_fpop,
104*7dd7cddfSDavid du Colombier 	"key",		g_ignore,	g_ignore,
105*7dd7cddfSDavid du Colombier 	"li",		g_li,		g_unexpected,
106*7dd7cddfSDavid du Colombier 	"link",		g_ignore,	g_unexpected,
107*7dd7cddfSDavid du Colombier 	"listing",	g_ignore,	g_ignore,
108*7dd7cddfSDavid du Colombier 	"menu",		g_list,		g_listend,
109*7dd7cddfSDavid du Colombier 	"meta",		g_ignore,	g_unexpected,
110*7dd7cddfSDavid du Colombier 	"nextid",	g_ignore,	g_unexpected,
111*7dd7cddfSDavid du Colombier 	"ol",		g_list,		g_listend,
112*7dd7cddfSDavid du Colombier 	"option",	g_ignore,	g_unexpected,
113*7dd7cddfSDavid du Colombier 	"p",		g_p,		g_ignore,
114*7dd7cddfSDavid du Colombier 	"plaintext",	g_ignore,	g_unexpected,
115*7dd7cddfSDavid du Colombier 	"pre",		g_pre,		g_displayend,
116*7dd7cddfSDavid du Colombier 	"samp",		g_ignore,	g_ignore,
117*7dd7cddfSDavid du Colombier 	"select",	g_ignore,	g_ignore,
118*7dd7cddfSDavid du Colombier 	"strong",	g_ignore,	g_ignore,
119*7dd7cddfSDavid du Colombier 	"table",	g_table,	g_tableend,
120*7dd7cddfSDavid du Colombier 	"textarea",	g_ignore,	g_ignore,
121*7dd7cddfSDavid du Colombier 	"title",	g_title,	g_ignore,
122*7dd7cddfSDavid du Colombier 	"tt",		g_fpush,	g_fpop,
123*7dd7cddfSDavid du Colombier 	"u",		g_ignore,	g_ignore,
124*7dd7cddfSDavid du Colombier 	"ul",		g_list,		g_listend,
125*7dd7cddfSDavid du Colombier 	"var",		g_ignore,	g_ignore,
126*7dd7cddfSDavid du Colombier 	"xmp",		g_ignore,	g_ignore,
127*7dd7cddfSDavid du Colombier 	0,		0,	0,
128*7dd7cddfSDavid du Colombier };
129*7dd7cddfSDavid du Colombier 
130*7dd7cddfSDavid du Colombier typedef struct Entity Entity;
131*7dd7cddfSDavid du Colombier struct Entity
132*7dd7cddfSDavid du Colombier {
133*7dd7cddfSDavid du Colombier 	char *name;
134*7dd7cddfSDavid du Colombier 	Rune value;
135*7dd7cddfSDavid du Colombier };
136*7dd7cddfSDavid du Colombier 
137*7dd7cddfSDavid du Colombier Entity pl_entity[]=
138*7dd7cddfSDavid du Colombier {
139*7dd7cddfSDavid du Colombier "#SPACE", L' ', "#RS",   L'\n', "#RE",   L'\r', "quot",   L'"',
140*7dd7cddfSDavid du Colombier "AElig",  L'Æ', "Aacute", L'Á', "Acirc",  L'Â', "Agrave", L'À', "Aring",  L'Å',
141*7dd7cddfSDavid du Colombier "Atilde", L'Ã', "Auml",   L'Ä', "Ccedil", L'Ç', "ETH",    L'Ð', "Eacute", L'É',
142*7dd7cddfSDavid du Colombier "Ecirc",  L'Ê', "Egrave", L'È', "Euml",   L'Ë', "Iacute", L'Í', "Icirc",  L'Î',
143*7dd7cddfSDavid du Colombier "Igrave", L'Ì', "Iuml",   L'Ï', "Ntilde", L'Ñ', "Oacute", L'Ó', "Ocirc",  L'Ô',
144*7dd7cddfSDavid du Colombier "Ograve", L'Ò', "Oslash", L'Ø', "Otilde", L'Õ', "Ouml",   L'Ö', "THORN",  L'Þ',
145*7dd7cddfSDavid du Colombier "Uacute", L'Ú', "Ucirc",  L'Û', "Ugrave", L'Ù', "Uuml",   L'Ü', "Yacute", L'Ý',
146*7dd7cddfSDavid du Colombier "aacute", L'á', "acirc",  L'â', "aelig",  L'æ', "agrave", L'à', "amp",    L'&',
147*7dd7cddfSDavid du Colombier "aring",  L'å', "atilde", L'ã', "auml",   L'ä', "ccedil", L'ç', "eacute", L'é',
148*7dd7cddfSDavid du Colombier "ecirc",  L'ê', "egrave", L'è', "eth",    L'ð', "euml",   L'ë', "gt",     L'>',
149*7dd7cddfSDavid du Colombier "iacute", L'í', "icirc",  L'î', "igrave", L'ì', "iuml",   L'ï', "lt",     L'<',
150*7dd7cddfSDavid du Colombier "ntilde", L'ñ', "oacute", L'ó', "ocirc",  L'ô', "ograve", L'ò', "oslash", L'ø',
151*7dd7cddfSDavid du Colombier "otilde", L'õ', "ouml",   L'ö', "szlig",  L'ß', "thorn",  L'þ', "uacute", L'ú',
152*7dd7cddfSDavid du Colombier "ucirc",  L'û', "ugrave", L'ù', "uuml",   L'ü', "yacute", L'ý', "yuml",   L'ÿ',
153*7dd7cddfSDavid du Colombier 0
154*7dd7cddfSDavid du Colombier };
155*7dd7cddfSDavid du Colombier 
156*7dd7cddfSDavid du Colombier int
157*7dd7cddfSDavid du Colombier cistrcmp(char *a, char *b)
158*7dd7cddfSDavid du Colombier {
159*7dd7cddfSDavid du Colombier 	int c, d;
160*7dd7cddfSDavid du Colombier 
161*7dd7cddfSDavid du Colombier 	for(;; a++, b++){
162*7dd7cddfSDavid du Colombier 		d = tolower(*a);
163*7dd7cddfSDavid du Colombier 		c = d - tolower(*b);
164*7dd7cddfSDavid du Colombier 		if(c)
165*7dd7cddfSDavid du Colombier 			break;
166*7dd7cddfSDavid du Colombier 		if(d == 0)
167*7dd7cddfSDavid du Colombier 			break;
168*7dd7cddfSDavid du Colombier 	}
169*7dd7cddfSDavid du Colombier 	return c;
170*7dd7cddfSDavid du Colombier }
171*7dd7cddfSDavid du Colombier 
172*7dd7cddfSDavid du Colombier int
173*7dd7cddfSDavid du Colombier readupto(char *buf, int n, char d, char notme)
174*7dd7cddfSDavid du Colombier {
175*7dd7cddfSDavid du Colombier 	char *p;
176*7dd7cddfSDavid du Colombier 	int c;
177*7dd7cddfSDavid du Colombier 
178*7dd7cddfSDavid du Colombier 	buf[0] = 0;
179*7dd7cddfSDavid du Colombier 	for(p = buf;; p++){
180*7dd7cddfSDavid du Colombier 		c = Bgetc(&in);
181*7dd7cddfSDavid du Colombier 		if(c < 0){
182*7dd7cddfSDavid du Colombier 			*p = 0;
183*7dd7cddfSDavid du Colombier 			return -1;
184*7dd7cddfSDavid du Colombier 		}
185*7dd7cddfSDavid du Colombier 		if(c == notme){
186*7dd7cddfSDavid du Colombier 			Bungetc(&in);
187*7dd7cddfSDavid du Colombier 			return -1;
188*7dd7cddfSDavid du Colombier 		}
189*7dd7cddfSDavid du Colombier 		if(c == d){
190*7dd7cddfSDavid du Colombier 			*p = 0;
191*7dd7cddfSDavid du Colombier 			return 0;
192*7dd7cddfSDavid du Colombier 		}
193*7dd7cddfSDavid du Colombier 		*p = c;
194*7dd7cddfSDavid du Colombier 		if(p == buf + n){
195*7dd7cddfSDavid du Colombier 			*p = 0;
196*7dd7cddfSDavid du Colombier 			Bprint(&out, "<%s", buf);
197*7dd7cddfSDavid du Colombier 			return -1;
198*7dd7cddfSDavid du Colombier 		}
199*7dd7cddfSDavid du Colombier 	}
200*7dd7cddfSDavid du Colombier }
201*7dd7cddfSDavid du Colombier 
202*7dd7cddfSDavid du Colombier void
203*7dd7cddfSDavid du Colombier dogoobie(void)
204*7dd7cddfSDavid du Colombier {
205*7dd7cddfSDavid du Colombier 	char *arg, *type;
206*7dd7cddfSDavid du Colombier 	Goobie *g;
207*7dd7cddfSDavid du Colombier 	char buf[1024];
208*7dd7cddfSDavid du Colombier 	int closing;
209*7dd7cddfSDavid du Colombier 
210*7dd7cddfSDavid du Colombier 	if(readupto(buf, sizeof(buf), '>', '<') < 0){
211*7dd7cddfSDavid du Colombier 		Bprint(&out, "<%s", buf);
212*7dd7cddfSDavid du Colombier 		return;
213*7dd7cddfSDavid du Colombier 	}
214*7dd7cddfSDavid du Colombier 	type = buf;
215*7dd7cddfSDavid du Colombier 	if(*type == '/'){
216*7dd7cddfSDavid du Colombier 		type++;
217*7dd7cddfSDavid du Colombier 		closing = 1;
218*7dd7cddfSDavid du Colombier 	} else
219*7dd7cddfSDavid du Colombier 		closing = 0;
220*7dd7cddfSDavid du Colombier 	arg = strchr(type, ' ');
221*7dd7cddfSDavid du Colombier 	if(arg == 0)
222*7dd7cddfSDavid du Colombier 		arg = strchr(type, '\r');
223*7dd7cddfSDavid du Colombier 	if(arg == 0)
224*7dd7cddfSDavid du Colombier 		arg = strchr(type, '\n');
225*7dd7cddfSDavid du Colombier 	if(arg)
226*7dd7cddfSDavid du Colombier 		*arg++ = 0;
227*7dd7cddfSDavid du Colombier 	for(g = gtab; g->name; g++)
228*7dd7cddfSDavid du Colombier 		if(cistrcmp(type, g->name) == 0){
229*7dd7cddfSDavid du Colombier 			if(closing){
230*7dd7cddfSDavid du Colombier 				if(g->ef){
231*7dd7cddfSDavid du Colombier 					(*g->ef)(g, arg);
232*7dd7cddfSDavid du Colombier 					return;
233*7dd7cddfSDavid du Colombier 				}
234*7dd7cddfSDavid du Colombier 			} else {
235*7dd7cddfSDavid du Colombier 				if(g->f){
236*7dd7cddfSDavid du Colombier 					(*g->f)(g, arg);
237*7dd7cddfSDavid du Colombier 					return;
238*7dd7cddfSDavid du Colombier 				}
239*7dd7cddfSDavid du Colombier 			}
240*7dd7cddfSDavid du Colombier 		}
241*7dd7cddfSDavid du Colombier 	if(closing)
242*7dd7cddfSDavid du Colombier 		type--;
243*7dd7cddfSDavid du Colombier 	if(arg)
244*7dd7cddfSDavid du Colombier 		Bprint(&out, "<%s %s>\n", type, arg);
245*7dd7cddfSDavid du Colombier 	else
246*7dd7cddfSDavid du Colombier 		Bprint(&out, "<%s>\n", type);
247*7dd7cddfSDavid du Colombier }
248*7dd7cddfSDavid du Colombier 
249*7dd7cddfSDavid du Colombier void
250*7dd7cddfSDavid du Colombier main(void)
251*7dd7cddfSDavid du Colombier {
252*7dd7cddfSDavid du Colombier 	int c;
253*7dd7cddfSDavid du Colombier 
254*7dd7cddfSDavid du Colombier 	Binit(&in, 0, OREAD);
255*7dd7cddfSDavid du Colombier 	Binit(&out, 1, OWRITE);
256*7dd7cddfSDavid du Colombier 
257*7dd7cddfSDavid du Colombier 	for(;;){
258*7dd7cddfSDavid du Colombier 		c = Bgetc(&in);
259*7dd7cddfSDavid du Colombier 		if(c < 0)
260*7dd7cddfSDavid du Colombier 			return;
261*7dd7cddfSDavid du Colombier 		switch(c){
262*7dd7cddfSDavid du Colombier 		case '<':
263*7dd7cddfSDavid du Colombier 			dogoobie();
264*7dd7cddfSDavid du Colombier 			break;
265*7dd7cddfSDavid du Colombier 		case '&':
266*7dd7cddfSDavid du Colombier 			escape();
267*7dd7cddfSDavid du Colombier 			break;
268*7dd7cddfSDavid du Colombier 		case '\r':
269*7dd7cddfSDavid du Colombier 			break;
270*7dd7cddfSDavid du Colombier 		case '\n':
271*7dd7cddfSDavid du Colombier 			if(quoting){
272*7dd7cddfSDavid du Colombier 				Bputc(&out, '"');
273*7dd7cddfSDavid du Colombier 				quoting = 0;
274*7dd7cddfSDavid du Colombier 			}
275*7dd7cddfSDavid du Colombier 			if(lastc != '\n')
276*7dd7cddfSDavid du Colombier 				Bputc(&out, '\n');
277*7dd7cddfSDavid du Colombier 			/* can't emit leading spaces in filled troff docs */
278*7dd7cddfSDavid du Colombier 			if (!inpre)
279*7dd7cddfSDavid du Colombier 				eatwhite();
280*7dd7cddfSDavid du Colombier 			lastc = c;
281*7dd7cddfSDavid du Colombier 			break;
282*7dd7cddfSDavid du Colombier 		default:
283*7dd7cddfSDavid du Colombier 			Bputc(&out, c);
284*7dd7cddfSDavid du Colombier 			lastc = c;
285*7dd7cddfSDavid du Colombier 			break;
286*7dd7cddfSDavid du Colombier 		}
287*7dd7cddfSDavid du Colombier 	}
288*7dd7cddfSDavid du Colombier }
289*7dd7cddfSDavid du Colombier 
290*7dd7cddfSDavid du Colombier void
291*7dd7cddfSDavid du Colombier escape(void)
292*7dd7cddfSDavid du Colombier {
293*7dd7cddfSDavid du Colombier 	Entity *e;
294*7dd7cddfSDavid du Colombier 	char buf[8];
295*7dd7cddfSDavid du Colombier 
296*7dd7cddfSDavid du Colombier 	if(readupto(buf, sizeof(buf), ';', '\n') < 0){
297*7dd7cddfSDavid du Colombier 		Bprint(&out, "&%s", buf);
298*7dd7cddfSDavid du Colombier 		return;
299*7dd7cddfSDavid du Colombier 	}
300*7dd7cddfSDavid du Colombier 	for(e = pl_entity; e->name; e++)
301*7dd7cddfSDavid du Colombier 		if(strcmp(buf, e->name) == 0){
302*7dd7cddfSDavid du Colombier 			Bprint(&out, "%C", e->value);
303*7dd7cddfSDavid du Colombier 			return;
304*7dd7cddfSDavid du Colombier 		}
305*7dd7cddfSDavid du Colombier 	Bprint(&out, "&%s;", buf);
306*7dd7cddfSDavid du Colombier }
307*7dd7cddfSDavid du Colombier 
308*7dd7cddfSDavid du Colombier /*
309*7dd7cddfSDavid du Colombier  * whitespace is not significant to HTML, but newlines
310*7dd7cddfSDavid du Colombier  * and leading spaces are significant to troff.
311*7dd7cddfSDavid du Colombier  */
312*7dd7cddfSDavid du Colombier void
313*7dd7cddfSDavid du Colombier eatwhite(void)
314*7dd7cddfSDavid du Colombier {
315*7dd7cddfSDavid du Colombier 	int c;
316*7dd7cddfSDavid du Colombier 
317*7dd7cddfSDavid du Colombier 	for(;;){
318*7dd7cddfSDavid du Colombier 		c = Bgetc(&in);
319*7dd7cddfSDavid du Colombier 		if(c < 0)
320*7dd7cddfSDavid du Colombier 			break;
321*7dd7cddfSDavid du Colombier 		if(!isspace(c)){
322*7dd7cddfSDavid du Colombier 			Bungetc(&in);
323*7dd7cddfSDavid du Colombier 			break;
324*7dd7cddfSDavid du Colombier 		}
325*7dd7cddfSDavid du Colombier 	}
326*7dd7cddfSDavid du Colombier }
327*7dd7cddfSDavid du Colombier 
328*7dd7cddfSDavid du Colombier /*
329*7dd7cddfSDavid du Colombier  *  print at start of line
330*7dd7cddfSDavid du Colombier  */
331*7dd7cddfSDavid du Colombier void
332*7dd7cddfSDavid du Colombier printsol(char *fmt, ...)
333*7dd7cddfSDavid du Colombier {
334*7dd7cddfSDavid du Colombier 	char buf[8*1024], *s;
335*7dd7cddfSDavid du Colombier 	va_list arg;
336*7dd7cddfSDavid du Colombier 
337*7dd7cddfSDavid du Colombier 	if(quoting){
338*7dd7cddfSDavid du Colombier 		Bputc(&out, '"');
339*7dd7cddfSDavid du Colombier 		quoting = 0;
340*7dd7cddfSDavid du Colombier 	}
341*7dd7cddfSDavid du Colombier 	if(lastc != '\n')
342*7dd7cddfSDavid du Colombier 		Bputc(&out, '\n');
343*7dd7cddfSDavid du Colombier 	va_start(arg, fmt);
344*7dd7cddfSDavid du Colombier 	s = doprint(buf, buf + (sizeof(buf)-1) / sizeof(*buf), fmt, arg);
345*7dd7cddfSDavid du Colombier 	va_end(arg);
346*7dd7cddfSDavid du Colombier 	Bwrite(&out, buf, s-buf);
347*7dd7cddfSDavid du Colombier 	lastc = *(s-1);
348*7dd7cddfSDavid du Colombier }
349*7dd7cddfSDavid du Colombier 
350*7dd7cddfSDavid du Colombier void
351*7dd7cddfSDavid du Colombier g_ignore(Goobie *g, char *arg)
352*7dd7cddfSDavid du Colombier {
353*7dd7cddfSDavid du Colombier 	USED(g, arg);
354*7dd7cddfSDavid du Colombier }
355*7dd7cddfSDavid du Colombier 
356*7dd7cddfSDavid du Colombier void
357*7dd7cddfSDavid du Colombier g_unexpected(Goobie *g, char *arg)
358*7dd7cddfSDavid du Colombier {
359*7dd7cddfSDavid du Colombier 	USED(arg);
360*7dd7cddfSDavid du Colombier 	fprint(2, "unexpected %s ending\n", g->name);
361*7dd7cddfSDavid du Colombier }
362*7dd7cddfSDavid du Colombier 
363*7dd7cddfSDavid du Colombier void
364*7dd7cddfSDavid du Colombier g_title(Goobie *g, char *arg)
365*7dd7cddfSDavid du Colombier {
366*7dd7cddfSDavid du Colombier 	USED(arg);
367*7dd7cddfSDavid du Colombier 	printsol(".TL\n", g->name);
368*7dd7cddfSDavid du Colombier }
369*7dd7cddfSDavid du Colombier 
370*7dd7cddfSDavid du Colombier void
371*7dd7cddfSDavid du Colombier g_p(Goobie *g, char *arg)
372*7dd7cddfSDavid du Colombier {
373*7dd7cddfSDavid du Colombier 	USED(arg);
374*7dd7cddfSDavid du Colombier 	printsol(".LP\n", g->name);
375*7dd7cddfSDavid du Colombier }
376*7dd7cddfSDavid du Colombier 
377*7dd7cddfSDavid du Colombier void
378*7dd7cddfSDavid du Colombier g_h(Goobie *g, char *arg)
379*7dd7cddfSDavid du Colombier {
380*7dd7cddfSDavid du Colombier 	USED(arg);
381*7dd7cddfSDavid du Colombier 	printsol(".SH %c\n", g->name[1]);
382*7dd7cddfSDavid du Colombier }
383*7dd7cddfSDavid du Colombier 
384*7dd7cddfSDavid du Colombier void
385*7dd7cddfSDavid du Colombier g_list(Goobie *g, char *arg)
386*7dd7cddfSDavid du Colombier {
387*7dd7cddfSDavid du Colombier 	USED(arg);
388*7dd7cddfSDavid du Colombier 
389*7dd7cddfSDavid du Colombier 	if(lsp != SSIZE){
390*7dd7cddfSDavid du Colombier 		switch(g->name[0]){
391*7dd7cddfSDavid du Colombier 		case 'o':
392*7dd7cddfSDavid du Colombier 			liststack[lsp].type  = Lordered;
393*7dd7cddfSDavid du Colombier 			liststack[lsp].ord = 0;
394*7dd7cddfSDavid du Colombier 			break;
395*7dd7cddfSDavid du Colombier 		default:
396*7dd7cddfSDavid du Colombier 			liststack[lsp].type = Lunordered;
397*7dd7cddfSDavid du Colombier 			break;
398*7dd7cddfSDavid du Colombier 		}
399*7dd7cddfSDavid du Colombier 	}
400*7dd7cddfSDavid du Colombier 	lsp++;
401*7dd7cddfSDavid du Colombier }
402*7dd7cddfSDavid du Colombier 
403*7dd7cddfSDavid du Colombier void
404*7dd7cddfSDavid du Colombier g_br(Goobie *g, char *arg)
405*7dd7cddfSDavid du Colombier {
406*7dd7cddfSDavid du Colombier 	USED(g, arg);
407*7dd7cddfSDavid du Colombier 	printsol(".br\n");
408*7dd7cddfSDavid du Colombier }
409*7dd7cddfSDavid du Colombier 
410*7dd7cddfSDavid du Colombier void
411*7dd7cddfSDavid du Colombier g_li(Goobie *g, char *arg)
412*7dd7cddfSDavid du Colombier {
413*7dd7cddfSDavid du Colombier 	USED(g, arg);
414*7dd7cddfSDavid du Colombier 	if(lsp <= 0 || lsp > SSIZE){
415*7dd7cddfSDavid du Colombier 		printsol(".IP \\(bu\n");
416*7dd7cddfSDavid du Colombier 		return;
417*7dd7cddfSDavid du Colombier 	}
418*7dd7cddfSDavid du Colombier 	switch(liststack[lsp-1].type){
419*7dd7cddfSDavid du Colombier 	case Lunordered:
420*7dd7cddfSDavid du Colombier 		printsol(".IP \\(bu\n");
421*7dd7cddfSDavid du Colombier 		break;
422*7dd7cddfSDavid du Colombier 	case Lordered:
423*7dd7cddfSDavid du Colombier 		printsol(".IP %d\n", ++liststack[lsp-1].ord);
424*7dd7cddfSDavid du Colombier 		break;
425*7dd7cddfSDavid du Colombier 	}
426*7dd7cddfSDavid du Colombier }
427*7dd7cddfSDavid du Colombier 
428*7dd7cddfSDavid du Colombier void
429*7dd7cddfSDavid du Colombier g_listend(Goobie *g, char *arg)
430*7dd7cddfSDavid du Colombier {
431*7dd7cddfSDavid du Colombier 	USED(g, arg);
432*7dd7cddfSDavid du Colombier 	if(--lsp < 0)
433*7dd7cddfSDavid du Colombier 		lsp = 0;
434*7dd7cddfSDavid du Colombier 	printsol(".LP\n");
435*7dd7cddfSDavid du Colombier }
436*7dd7cddfSDavid du Colombier 
437*7dd7cddfSDavid du Colombier void
438*7dd7cddfSDavid du Colombier g_display(Goobie *g, char *arg)
439*7dd7cddfSDavid du Colombier {
440*7dd7cddfSDavid du Colombier 	USED(g, arg);
441*7dd7cddfSDavid du Colombier 	printsol(".DS\n");
442*7dd7cddfSDavid du Colombier }
443*7dd7cddfSDavid du Colombier 
444*7dd7cddfSDavid du Colombier void
445*7dd7cddfSDavid du Colombier g_pre(Goobie *g, char *arg)
446*7dd7cddfSDavid du Colombier {
447*7dd7cddfSDavid du Colombier 	USED(g, arg);
448*7dd7cddfSDavid du Colombier 	printsol(".DS L\n");
449*7dd7cddfSDavid du Colombier 	inpre = 1;
450*7dd7cddfSDavid du Colombier }
451*7dd7cddfSDavid du Colombier 
452*7dd7cddfSDavid du Colombier void
453*7dd7cddfSDavid du Colombier g_displayend(Goobie *g, char *arg)
454*7dd7cddfSDavid du Colombier {
455*7dd7cddfSDavid du Colombier 	USED(g, arg);
456*7dd7cddfSDavid du Colombier 	printsol(".DE\n");
457*7dd7cddfSDavid du Colombier 	inpre = 0;
458*7dd7cddfSDavid du Colombier }
459*7dd7cddfSDavid du Colombier 
460*7dd7cddfSDavid du Colombier void
461*7dd7cddfSDavid du Colombier g_fpush(Goobie *g, char *arg)
462*7dd7cddfSDavid du Colombier {
463*7dd7cddfSDavid du Colombier 	USED(arg);
464*7dd7cddfSDavid du Colombier 	if(fsp < SSIZE)
465*7dd7cddfSDavid du Colombier 		fontstack[fsp] = font;
466*7dd7cddfSDavid du Colombier 	fsp++;
467*7dd7cddfSDavid du Colombier 	switch(g->name[0]){
468*7dd7cddfSDavid du Colombier 	case 'b':
469*7dd7cddfSDavid du Colombier 		font = "B";
470*7dd7cddfSDavid du Colombier 		break;
471*7dd7cddfSDavid du Colombier 	case 'i':
472*7dd7cddfSDavid du Colombier 		font = "I";
473*7dd7cddfSDavid du Colombier 		break;
474*7dd7cddfSDavid du Colombier 	case 'k':		/* kbd */
475*7dd7cddfSDavid du Colombier 	case 't':		/* tt */
476*7dd7cddfSDavid du Colombier 		font = "(CW";
477*7dd7cddfSDavid du Colombier 		break;
478*7dd7cddfSDavid du Colombier 	}
479*7dd7cddfSDavid du Colombier 	Bprint(&out, "\\f%s", font);
480*7dd7cddfSDavid du Colombier }
481*7dd7cddfSDavid du Colombier 
482*7dd7cddfSDavid du Colombier void
483*7dd7cddfSDavid du Colombier g_fpop(Goobie *g, char *arg)
484*7dd7cddfSDavid du Colombier {
485*7dd7cddfSDavid du Colombier 	USED(g, arg);
486*7dd7cddfSDavid du Colombier 	fsp--;
487*7dd7cddfSDavid du Colombier 	if(fsp < SSIZE)
488*7dd7cddfSDavid du Colombier 		font = fontstack[fsp];
489*7dd7cddfSDavid du Colombier 	else
490*7dd7cddfSDavid du Colombier 		font = "R";
491*7dd7cddfSDavid du Colombier 
492*7dd7cddfSDavid du Colombier 	Bprint(&out, "\\f%s", font);
493*7dd7cddfSDavid du Colombier }
494*7dd7cddfSDavid du Colombier 
495*7dd7cddfSDavid du Colombier void
496*7dd7cddfSDavid du Colombier g_indent(Goobie *g, char *arg)
497*7dd7cddfSDavid du Colombier {
498*7dd7cddfSDavid du Colombier 	USED(g, arg);
499*7dd7cddfSDavid du Colombier 	printsol(".RS\n");
500*7dd7cddfSDavid du Colombier }
501*7dd7cddfSDavid du Colombier 
502*7dd7cddfSDavid du Colombier void
503*7dd7cddfSDavid du Colombier g_exdent(Goobie *g, char *arg)
504*7dd7cddfSDavid du Colombier {
505*7dd7cddfSDavid du Colombier 	USED(g, arg);
506*7dd7cddfSDavid du Colombier 	printsol(".RE\n");
507*7dd7cddfSDavid du Colombier }
508*7dd7cddfSDavid du Colombier 
509*7dd7cddfSDavid du Colombier void
510*7dd7cddfSDavid du Colombier g_dt(Goobie *g, char *arg)
511*7dd7cddfSDavid du Colombier {
512*7dd7cddfSDavid du Colombier 	USED(g, arg);
513*7dd7cddfSDavid du Colombier 	printsol(".IP \"");
514*7dd7cddfSDavid du Colombier 	quoting = 1;
515*7dd7cddfSDavid du Colombier }
516*7dd7cddfSDavid du Colombier 
517*7dd7cddfSDavid du Colombier void
518*7dd7cddfSDavid du Colombier g_hr(Goobie *g, char *arg)
519*7dd7cddfSDavid du Colombier {
520*7dd7cddfSDavid du Colombier 	USED(g, arg);
521*7dd7cddfSDavid du Colombier 	printsol(".br\n");
522*7dd7cddfSDavid du Colombier 	printsol("\\l'5i'\n");
523*7dd7cddfSDavid du Colombier }
524*7dd7cddfSDavid du Colombier 
525*7dd7cddfSDavid du Colombier 
526*7dd7cddfSDavid du Colombier /*
527*7dd7cddfSDavid du Colombier <table border>
528*7dd7cddfSDavid du Colombier <caption><font size="+1"><b>Cumulative Class Data</b></font></caption>
529*7dd7cddfSDavid du Colombier <tr><th rowspan=2>DOSE<br>mg/kg</th><th colspan=2>PARALYSIS</th><th colspan=2>DEATH</th>
530*7dd7cddfSDavid du Colombier </tr>
531*7dd7cddfSDavid du Colombier <tr><th width=80>Number</th><th width=80>Percent</th><th width=80>Number</th><th width=80>Percent</th>
532*7dd7cddfSDavid du Colombier </tr>
533*7dd7cddfSDavid du Colombier <tr align=center>
534*7dd7cddfSDavid du Colombier <td>0.1</td><td><br></td> <td><br></td> <td><br></td> <td><br></td>
535*7dd7cddfSDavid du Colombier </tr>
536*7dd7cddfSDavid du Colombier <tr align=center>
537*7dd7cddfSDavid du Colombier <td>0.2</td><td><br></td> <td><br></td> <td><br></td> <td><br></td>
538*7dd7cddfSDavid du Colombier </tr>
539*7dd7cddfSDavid du Colombier <tr align=center>
540*7dd7cddfSDavid du Colombier <td>0.3</td><td><br></td> <td><br></td> <td><br></td> <td><br></td>
541*7dd7cddfSDavid du Colombier </tr>
542*7dd7cddfSDavid du Colombier <tr align=center>
543*7dd7cddfSDavid du Colombier <td>0.4</td><td><br></td> <td><br></td> <td><br></td> <td><br></td>
544*7dd7cddfSDavid du Colombier </tr>
545*7dd7cddfSDavid du Colombier <tr align=center>
546*7dd7cddfSDavid du Colombier <td>0.5</td><td><br></td> <td><br></td> <td><br></td> <td><br></td>
547*7dd7cddfSDavid du Colombier </tr>
548*7dd7cddfSDavid du Colombier <tr align=center>
549*7dd7cddfSDavid du Colombier <td>0.6</td><td><br></td> <td><br></td> <td><br></td> <td><br></td>
550*7dd7cddfSDavid du Colombier </tr>
551*7dd7cddfSDavid du Colombier <tr align=center>
552*7dd7cddfSDavid du Colombier <td>0.7</td><td><br></td> <td><br></td> <td><br></td> <td><br></td>
553*7dd7cddfSDavid du Colombier </tr>
554*7dd7cddfSDavid du Colombier <tr align=center>
555*7dd7cddfSDavid du Colombier <td>0.8</td><td><br></td> <td><br></td> <td><br></td> <td><br></td>
556*7dd7cddfSDavid du Colombier </tr>
557*7dd7cddfSDavid du Colombier <tr align=center>
558*7dd7cddfSDavid du Colombier <td>0.8 oral</td><td><br></td> <td><br></td> <td><br></td> <td><br></td>
559*7dd7cddfSDavid du Colombier </tr>
560*7dd7cddfSDavid du Colombier </table>
561*7dd7cddfSDavid du Colombier */
562*7dd7cddfSDavid du Colombier 
563*7dd7cddfSDavid du Colombier void
564*7dd7cddfSDavid du Colombier g_table(Goobie *g, char *arg)
565*7dd7cddfSDavid du Colombier {
566*7dd7cddfSDavid du Colombier 	USED(g, arg);
567*7dd7cddfSDavid du Colombier 	printsol(".TS\ncenter ;\n");
568*7dd7cddfSDavid du Colombier }
569*7dd7cddfSDavid du Colombier 
570*7dd7cddfSDavid du Colombier void
571*7dd7cddfSDavid du Colombier g_tableend(Goobie *g, char *arg)
572*7dd7cddfSDavid du Colombier {
573*7dd7cddfSDavid du Colombier 	USED(g, arg);
574*7dd7cddfSDavid du Colombier 	printsol(".TE\n");
575*7dd7cddfSDavid du Colombier }
576*7dd7cddfSDavid du Colombier 
577*7dd7cddfSDavid du Colombier void
578*7dd7cddfSDavid du Colombier g_caption(Goobie *g, char *arg)
579*7dd7cddfSDavid du Colombier {
580*7dd7cddfSDavid du Colombier 	USED(g, arg);
581*7dd7cddfSDavid du Colombier }
582*7dd7cddfSDavid du Colombier 
583*7dd7cddfSDavid du Colombier void
584*7dd7cddfSDavid du Colombier g_captionend(Goobie *g, char *arg)
585*7dd7cddfSDavid du Colombier {
586*7dd7cddfSDavid du Colombier 	USED(g, arg);
587*7dd7cddfSDavid du Colombier }
588