xref: /plan9/sys/src/cmd/htmlroff/roff.c (revision 426d2b71458df9b491ba6c167f699b3f1f7b0428)
1*426d2b71SDavid du Colombier #include "a.h"
2*426d2b71SDavid du Colombier 
3*426d2b71SDavid du Colombier enum
4*426d2b71SDavid du Colombier {
5*426d2b71SDavid du Colombier 	MAXREQ = 100,
6*426d2b71SDavid du Colombier 	MAXRAW = 40,
7*426d2b71SDavid du Colombier 	MAXESC = 60,
8*426d2b71SDavid du Colombier 	MAXLINE = 1024,
9*426d2b71SDavid du Colombier 	MAXIF = 20,
10*426d2b71SDavid du Colombier 	MAXARG = 10,
11*426d2b71SDavid du Colombier };
12*426d2b71SDavid du Colombier 
13*426d2b71SDavid du Colombier typedef struct Esc Esc;
14*426d2b71SDavid du Colombier typedef struct Req Req;
15*426d2b71SDavid du Colombier typedef struct Raw Raw;
16*426d2b71SDavid du Colombier 
17*426d2b71SDavid du Colombier /* escape sequence handler, like for \c */
18*426d2b71SDavid du Colombier struct Esc
19*426d2b71SDavid du Colombier {
20*426d2b71SDavid du Colombier 	Rune r;
21*426d2b71SDavid du Colombier 	int (*f)(void);
22*426d2b71SDavid du Colombier 	int mode;
23*426d2b71SDavid du Colombier };
24*426d2b71SDavid du Colombier 
25*426d2b71SDavid du Colombier /* raw request handler, like for .ie */
26*426d2b71SDavid du Colombier struct Raw
27*426d2b71SDavid du Colombier {
28*426d2b71SDavid du Colombier 	Rune *name;
29*426d2b71SDavid du Colombier 	void (*f)(Rune*);
30*426d2b71SDavid du Colombier };
31*426d2b71SDavid du Colombier 
32*426d2b71SDavid du Colombier /* regular request handler, like for .ft */
33*426d2b71SDavid du Colombier struct Req
34*426d2b71SDavid du Colombier {
35*426d2b71SDavid du Colombier 	int argc;
36*426d2b71SDavid du Colombier 	Rune *name;
37*426d2b71SDavid du Colombier 	void (*f)(int, Rune**);
38*426d2b71SDavid du Colombier };
39*426d2b71SDavid du Colombier 
40*426d2b71SDavid du Colombier int		dot = '.';
41*426d2b71SDavid du Colombier int		tick = '\'';
42*426d2b71SDavid du Colombier int		backslash = '\\';
43*426d2b71SDavid du Colombier 
44*426d2b71SDavid du Colombier int		inputmode;
45*426d2b71SDavid du Colombier Req		req[MAXREQ];
46*426d2b71SDavid du Colombier int		nreq;
47*426d2b71SDavid du Colombier Raw		raw[MAXRAW];
48*426d2b71SDavid du Colombier int		nraw;
49*426d2b71SDavid du Colombier Esc		esc[MAXESC];
50*426d2b71SDavid du Colombier int		nesc;
51*426d2b71SDavid du Colombier int		iftrue[MAXIF];
52*426d2b71SDavid du Colombier int		niftrue;
53*426d2b71SDavid du Colombier 
54*426d2b71SDavid du Colombier int isoutput;
55*426d2b71SDavid du Colombier int linepos;
56*426d2b71SDavid du Colombier 
57*426d2b71SDavid du Colombier 
58*426d2b71SDavid du Colombier void
addraw(Rune * name,void (* f)(Rune *))59*426d2b71SDavid du Colombier addraw(Rune *name, void (*f)(Rune*))
60*426d2b71SDavid du Colombier {
61*426d2b71SDavid du Colombier 	Raw *r;
62*426d2b71SDavid du Colombier 
63*426d2b71SDavid du Colombier 	if(nraw >= nelem(raw)){
64*426d2b71SDavid du Colombier 		fprint(2, "too many raw requets\n");
65*426d2b71SDavid du Colombier 		return;
66*426d2b71SDavid du Colombier 	}
67*426d2b71SDavid du Colombier 	r = &raw[nraw++];
68*426d2b71SDavid du Colombier 	r->name = erunestrdup(name);
69*426d2b71SDavid du Colombier 	r->f = f;
70*426d2b71SDavid du Colombier }
71*426d2b71SDavid du Colombier 
72*426d2b71SDavid du Colombier void
delraw(Rune * name)73*426d2b71SDavid du Colombier delraw(Rune *name)
74*426d2b71SDavid du Colombier {
75*426d2b71SDavid du Colombier 	int i;
76*426d2b71SDavid du Colombier 
77*426d2b71SDavid du Colombier 	for(i=0; i<nraw; i++){
78*426d2b71SDavid du Colombier 		if(runestrcmp(raw[i].name, name) == 0){
79*426d2b71SDavid du Colombier 			if(i != --nraw){
80*426d2b71SDavid du Colombier 				free(raw[i].name);
81*426d2b71SDavid du Colombier 				raw[i] = raw[nraw];
82*426d2b71SDavid du Colombier 			}
83*426d2b71SDavid du Colombier 			return;
84*426d2b71SDavid du Colombier 		}
85*426d2b71SDavid du Colombier 	}
86*426d2b71SDavid du Colombier }
87*426d2b71SDavid du Colombier 
88*426d2b71SDavid du Colombier void
renraw(Rune * from,Rune * to)89*426d2b71SDavid du Colombier renraw(Rune *from, Rune *to)
90*426d2b71SDavid du Colombier {
91*426d2b71SDavid du Colombier 	int i;
92*426d2b71SDavid du Colombier 
93*426d2b71SDavid du Colombier 	delraw(to);
94*426d2b71SDavid du Colombier 	for(i=0; i<nraw; i++)
95*426d2b71SDavid du Colombier 		if(runestrcmp(raw[i].name, from) == 0){
96*426d2b71SDavid du Colombier 			free(raw[i].name);
97*426d2b71SDavid du Colombier 			raw[i].name = erunestrdup(to);
98*426d2b71SDavid du Colombier 			return;
99*426d2b71SDavid du Colombier 		}
100*426d2b71SDavid du Colombier }
101*426d2b71SDavid du Colombier 
102*426d2b71SDavid du Colombier 
103*426d2b71SDavid du Colombier void
addreq(Rune * s,void (* f)(int,Rune **),int argc)104*426d2b71SDavid du Colombier addreq(Rune *s, void (*f)(int, Rune**), int argc)
105*426d2b71SDavid du Colombier {
106*426d2b71SDavid du Colombier 	Req *r;
107*426d2b71SDavid du Colombier 
108*426d2b71SDavid du Colombier 	if(nreq >= nelem(req)){
109*426d2b71SDavid du Colombier 		fprint(2, "too many requests\n");
110*426d2b71SDavid du Colombier 		return;
111*426d2b71SDavid du Colombier 	}
112*426d2b71SDavid du Colombier 	r = &req[nreq++];
113*426d2b71SDavid du Colombier 	r->name = erunestrdup(s);
114*426d2b71SDavid du Colombier 	r->f = f;
115*426d2b71SDavid du Colombier 	r->argc = argc;
116*426d2b71SDavid du Colombier }
117*426d2b71SDavid du Colombier 
118*426d2b71SDavid du Colombier void
delreq(Rune * name)119*426d2b71SDavid du Colombier delreq(Rune *name)
120*426d2b71SDavid du Colombier {
121*426d2b71SDavid du Colombier 	int i;
122*426d2b71SDavid du Colombier 
123*426d2b71SDavid du Colombier 	for(i=0; i<nreq; i++){
124*426d2b71SDavid du Colombier 		if(runestrcmp(req[i].name, name) == 0){
125*426d2b71SDavid du Colombier 			if(i != --nreq){
126*426d2b71SDavid du Colombier 				free(req[i].name);
127*426d2b71SDavid du Colombier 				req[i] = req[nreq];
128*426d2b71SDavid du Colombier 			}
129*426d2b71SDavid du Colombier 			return;
130*426d2b71SDavid du Colombier 		}
131*426d2b71SDavid du Colombier 	}
132*426d2b71SDavid du Colombier }
133*426d2b71SDavid du Colombier 
134*426d2b71SDavid du Colombier void
renreq(Rune * from,Rune * to)135*426d2b71SDavid du Colombier renreq(Rune *from, Rune *to)
136*426d2b71SDavid du Colombier {
137*426d2b71SDavid du Colombier 	int i;
138*426d2b71SDavid du Colombier 
139*426d2b71SDavid du Colombier 	delreq(to);
140*426d2b71SDavid du Colombier 	for(i=0; i<nreq; i++)
141*426d2b71SDavid du Colombier 		if(runestrcmp(req[i].name, from) == 0){
142*426d2b71SDavid du Colombier 			free(req[i].name);
143*426d2b71SDavid du Colombier 			req[i].name = erunestrdup(to);
144*426d2b71SDavid du Colombier 			return;
145*426d2b71SDavid du Colombier 		}
146*426d2b71SDavid du Colombier }
147*426d2b71SDavid du Colombier 
148*426d2b71SDavid du Colombier void
addesc(Rune r,int (* f)(void),int mode)149*426d2b71SDavid du Colombier addesc(Rune r, int (*f)(void), int mode)
150*426d2b71SDavid du Colombier {
151*426d2b71SDavid du Colombier 	Esc *e;
152*426d2b71SDavid du Colombier 
153*426d2b71SDavid du Colombier 	if(nesc >= nelem(esc)){
154*426d2b71SDavid du Colombier 		fprint(2, "too many escapes\n");
155*426d2b71SDavid du Colombier 		return;
156*426d2b71SDavid du Colombier 	}
157*426d2b71SDavid du Colombier 	e = &esc[nesc++];
158*426d2b71SDavid du Colombier 	e->r = r;
159*426d2b71SDavid du Colombier 	e->f = f;
160*426d2b71SDavid du Colombier 	e->mode = mode;
161*426d2b71SDavid du Colombier }
162*426d2b71SDavid du Colombier 
163*426d2b71SDavid du Colombier /*
164*426d2b71SDavid du Colombier  * Get the next logical character in the input stream.
165*426d2b71SDavid du Colombier  */
166*426d2b71SDavid du Colombier int
getnext(void)167*426d2b71SDavid du Colombier getnext(void)
168*426d2b71SDavid du Colombier {
169*426d2b71SDavid du Colombier 	int i, r;
170*426d2b71SDavid du Colombier 
171*426d2b71SDavid du Colombier next:
172*426d2b71SDavid du Colombier 	r = getrune();
173*426d2b71SDavid du Colombier 	if(r < 0)
174*426d2b71SDavid du Colombier 		return -1;
175*426d2b71SDavid du Colombier 	if(r == Uformatted){
176*426d2b71SDavid du Colombier 		br();
177*426d2b71SDavid du Colombier 		assert(!isoutput);
178*426d2b71SDavid du Colombier 		while((r = getrune()) >= 0 && r != Uunformatted){
179*426d2b71SDavid du Colombier 			if(r == Uformatted)
180*426d2b71SDavid du Colombier 				continue;
181*426d2b71SDavid du Colombier 			outrune(r);
182*426d2b71SDavid du Colombier 		}
183*426d2b71SDavid du Colombier 		goto next;
184*426d2b71SDavid du Colombier 	}
185*426d2b71SDavid du Colombier 	if(r == Uunformatted)
186*426d2b71SDavid du Colombier 		goto next;
187*426d2b71SDavid du Colombier 	if(r == backslash){
188*426d2b71SDavid du Colombier 		r = getrune();
189*426d2b71SDavid du Colombier 		if(r < 0)
190*426d2b71SDavid du Colombier 			return -1;
191*426d2b71SDavid du Colombier 		for(i=0; i<nesc; i++){
192*426d2b71SDavid du Colombier 			if(r == esc[i].r && (inputmode&esc[i].mode)==inputmode){
193*426d2b71SDavid du Colombier 				if(esc[i].f == e_warn)
194*426d2b71SDavid du Colombier 					warn("ignoring %C%C", backslash, r);
195*426d2b71SDavid du Colombier 				r = esc[i].f();
196*426d2b71SDavid du Colombier 				if(r <= 0)
197*426d2b71SDavid du Colombier 					goto next;
198*426d2b71SDavid du Colombier 				return r;
199*426d2b71SDavid du Colombier 			}
200*426d2b71SDavid du Colombier 		}
201*426d2b71SDavid du Colombier 		if(inputmode&(ArgMode|CopyMode)){
202*426d2b71SDavid du Colombier 			ungetrune(r);
203*426d2b71SDavid du Colombier 			r = backslash;
204*426d2b71SDavid du Colombier 		}
205*426d2b71SDavid du Colombier 	}
206*426d2b71SDavid du Colombier 	return r;
207*426d2b71SDavid du Colombier }
208*426d2b71SDavid du Colombier 
209*426d2b71SDavid du Colombier void
ungetnext(Rune r)210*426d2b71SDavid du Colombier ungetnext(Rune r)
211*426d2b71SDavid du Colombier {
212*426d2b71SDavid du Colombier 	/*
213*426d2b71SDavid du Colombier 	 * really we want to undo the getrunes that led us here,
214*426d2b71SDavid du Colombier 	 * since the call after ungetnext might be getrune!
215*426d2b71SDavid du Colombier 	 */
216*426d2b71SDavid du Colombier 	ungetrune(r);
217*426d2b71SDavid du Colombier }
218*426d2b71SDavid du Colombier 
219*426d2b71SDavid du Colombier int
_readx(Rune * p,int n,int nmode,int line)220*426d2b71SDavid du Colombier _readx(Rune *p, int n, int nmode, int line)
221*426d2b71SDavid du Colombier {
222*426d2b71SDavid du Colombier 	int c, omode;
223*426d2b71SDavid du Colombier 	Rune *e;
224*426d2b71SDavid du Colombier 
225*426d2b71SDavid du Colombier 	while((c = getrune()) == ' ' || c == '\t')
226*426d2b71SDavid du Colombier 		;
227*426d2b71SDavid du Colombier 	ungetrune(c);
228*426d2b71SDavid du Colombier 	omode = inputmode;
229*426d2b71SDavid du Colombier 	inputmode = nmode;
230*426d2b71SDavid du Colombier 	e = p+n-1;
231*426d2b71SDavid du Colombier 	for(c=getnext(); p<e; c=getnext()){
232*426d2b71SDavid du Colombier 		if(c < 0)
233*426d2b71SDavid du Colombier 			break;
234*426d2b71SDavid du Colombier 		if(!line && (c == ' ' || c == '\t'))
235*426d2b71SDavid du Colombier 			break;
236*426d2b71SDavid du Colombier 		if(c == '\n'){
237*426d2b71SDavid du Colombier 			if(!line)
238*426d2b71SDavid du Colombier 				ungetnext(c);
239*426d2b71SDavid du Colombier 			break;
240*426d2b71SDavid du Colombier 		}
241*426d2b71SDavid du Colombier 		*p++ = c;
242*426d2b71SDavid du Colombier 	}
243*426d2b71SDavid du Colombier 	inputmode = omode;
244*426d2b71SDavid du Colombier 	*p = 0;
245*426d2b71SDavid du Colombier 	if(c < 0)
246*426d2b71SDavid du Colombier 		return -1;
247*426d2b71SDavid du Colombier 	return 0;
248*426d2b71SDavid du Colombier }
249*426d2b71SDavid du Colombier 
250*426d2b71SDavid du Colombier /*
251*426d2b71SDavid du Colombier  * Get the next argument from the current line.
252*426d2b71SDavid du Colombier  */
253*426d2b71SDavid du Colombier Rune*
copyarg(void)254*426d2b71SDavid du Colombier copyarg(void)
255*426d2b71SDavid du Colombier {
256*426d2b71SDavid du Colombier 	static Rune buf[MaxLine];
257*426d2b71SDavid du Colombier 	int c;
258*426d2b71SDavid du Colombier 	Rune *r;
259*426d2b71SDavid du Colombier 
260*426d2b71SDavid du Colombier 	if(_readx(buf, sizeof buf, ArgMode, 0) < 0)
261*426d2b71SDavid du Colombier 		return nil;
262*426d2b71SDavid du Colombier 	r = runestrstr(buf, L("\\\""));
263*426d2b71SDavid du Colombier 	if(r){
264*426d2b71SDavid du Colombier 		*r = 0;
265*426d2b71SDavid du Colombier 		while((c = getrune()) >= 0 && c != '\n')
266*426d2b71SDavid du Colombier 			;
267*426d2b71SDavid du Colombier 		ungetrune('\n');
268*426d2b71SDavid du Colombier 	}
269*426d2b71SDavid du Colombier 	r = erunestrdup(buf);
270*426d2b71SDavid du Colombier 	return r;
271*426d2b71SDavid du Colombier }
272*426d2b71SDavid du Colombier 
273*426d2b71SDavid du Colombier /*
274*426d2b71SDavid du Colombier  * Read the current line in given mode.  Newline not kept.
275*426d2b71SDavid du Colombier  * Uses different buffer from copyarg!
276*426d2b71SDavid du Colombier  */
277*426d2b71SDavid du Colombier Rune*
readline(int m)278*426d2b71SDavid du Colombier readline(int m)
279*426d2b71SDavid du Colombier {
280*426d2b71SDavid du Colombier 	static Rune buf[MaxLine];
281*426d2b71SDavid du Colombier 	Rune *r;
282*426d2b71SDavid du Colombier 
283*426d2b71SDavid du Colombier 	if(_readx(buf, sizeof buf, m, 1) < 0)
284*426d2b71SDavid du Colombier 		return nil;
285*426d2b71SDavid du Colombier 	r = erunestrdup(buf);
286*426d2b71SDavid du Colombier 	return r;
287*426d2b71SDavid du Colombier }
288*426d2b71SDavid du Colombier 
289*426d2b71SDavid du Colombier /*
290*426d2b71SDavid du Colombier  * Given the argument line (already read in copy+arg mode),
291*426d2b71SDavid du Colombier  * parse into arguments.  Note that \" has been left in place
292*426d2b71SDavid du Colombier  * during copy+arg mode parsing, so comments still need to be stripped.
293*426d2b71SDavid du Colombier  */
294*426d2b71SDavid du Colombier int
parseargs(Rune * p,Rune ** argv)295*426d2b71SDavid du Colombier parseargs(Rune *p, Rune **argv)
296*426d2b71SDavid du Colombier {
297*426d2b71SDavid du Colombier 	int argc;
298*426d2b71SDavid du Colombier 	Rune *w;
299*426d2b71SDavid du Colombier 
300*426d2b71SDavid du Colombier 	for(argc=0; argc<MAXARG; argc++){
301*426d2b71SDavid du Colombier 		while(*p == ' ' || *p == '\t')
302*426d2b71SDavid du Colombier 			p++;
303*426d2b71SDavid du Colombier 		if(*p == 0)
304*426d2b71SDavid du Colombier 			break;
305*426d2b71SDavid du Colombier 		argv[argc] = p;
306*426d2b71SDavid du Colombier 		if(*p == '"'){
307*426d2b71SDavid du Colombier 			/* quoted argument */
308*426d2b71SDavid du Colombier 			if(*(p+1) == '"'){
309*426d2b71SDavid du Colombier 				/* empty argument */
310*426d2b71SDavid du Colombier 				*p = 0;
311*426d2b71SDavid du Colombier 				p += 2;
312*426d2b71SDavid du Colombier 			}else{
313*426d2b71SDavid du Colombier 				/* parse quoted string */
314*426d2b71SDavid du Colombier 				w = p++;
315*426d2b71SDavid du Colombier 				for(; *p; p++){
316*426d2b71SDavid du Colombier 					if(*p == '"' && *(p+1) == '"')
317*426d2b71SDavid du Colombier 						*w++ = '"';
318*426d2b71SDavid du Colombier 					else if(*p == '"'){
319*426d2b71SDavid du Colombier 						p++;
320*426d2b71SDavid du Colombier 						break;
321*426d2b71SDavid du Colombier 					}else
322*426d2b71SDavid du Colombier 						*w++ = *p;
323*426d2b71SDavid du Colombier 				}
324*426d2b71SDavid du Colombier 				*w = 0;
325*426d2b71SDavid du Colombier 			}
326*426d2b71SDavid du Colombier 		}else{
327*426d2b71SDavid du Colombier 			/* unquoted argument - need to watch out for \" comment */
328*426d2b71SDavid du Colombier 			for(; *p; p++){
329*426d2b71SDavid du Colombier 				if(*p == ' ' || *p == '\t'){
330*426d2b71SDavid du Colombier 					*p++ = 0;
331*426d2b71SDavid du Colombier 					break;
332*426d2b71SDavid du Colombier 				}
333*426d2b71SDavid du Colombier 				if(*p == '\\' && *(p+1) == '"'){
334*426d2b71SDavid du Colombier 					*p = 0;
335*426d2b71SDavid du Colombier 					if(p != argv[argc])
336*426d2b71SDavid du Colombier 						argc++;
337*426d2b71SDavid du Colombier 					return argc;
338*426d2b71SDavid du Colombier 				}
339*426d2b71SDavid du Colombier 			}
340*426d2b71SDavid du Colombier 		}
341*426d2b71SDavid du Colombier 	}
342*426d2b71SDavid du Colombier 	return argc;
343*426d2b71SDavid du Colombier }
344*426d2b71SDavid du Colombier 
345*426d2b71SDavid du Colombier /*
346*426d2b71SDavid du Colombier  * Process a dot line.  The dot has been read.
347*426d2b71SDavid du Colombier  */
348*426d2b71SDavid du Colombier void
dotline(int dot)349*426d2b71SDavid du Colombier dotline(int dot)
350*426d2b71SDavid du Colombier {
351*426d2b71SDavid du Colombier 	int argc, i;
352*426d2b71SDavid du Colombier 	Rune *a, *argv[1+MAXARG];
353*426d2b71SDavid du Colombier 
354*426d2b71SDavid du Colombier 	/*
355*426d2b71SDavid du Colombier 	 * Read request/macro name
356*426d2b71SDavid du Colombier 	 */
357*426d2b71SDavid du Colombier 	a = copyarg();
358*426d2b71SDavid du Colombier 	if(a == nil || a[0] == 0){
359*426d2b71SDavid du Colombier 		free(a);
360*426d2b71SDavid du Colombier 		getrune();	/* \n */
361*426d2b71SDavid du Colombier 		return;
362*426d2b71SDavid du Colombier 	}
363*426d2b71SDavid du Colombier 	argv[0] = a;
364*426d2b71SDavid du Colombier 	/*
365*426d2b71SDavid du Colombier 	 * Check for .if, .ie, and others with special parsing.
366*426d2b71SDavid du Colombier 	 */
367*426d2b71SDavid du Colombier 	for(i=0; i<nraw; i++){
368*426d2b71SDavid du Colombier 		if(runestrcmp(raw[i].name, a) == 0){
369*426d2b71SDavid du Colombier 			raw[i].f(raw[i].name);
370*426d2b71SDavid du Colombier 			free(a);
371*426d2b71SDavid du Colombier 			return;
372*426d2b71SDavid du Colombier 		}
373*426d2b71SDavid du Colombier 	}
374*426d2b71SDavid du Colombier 
375*426d2b71SDavid du Colombier 	/*
376*426d2b71SDavid du Colombier 	 * Read rest of line in copy mode, invoke regular request.
377*426d2b71SDavid du Colombier 	 */
378*426d2b71SDavid du Colombier 	a = readline(ArgMode);
379*426d2b71SDavid du Colombier 	if(a == nil){
380*426d2b71SDavid du Colombier 		free(argv[0]);
381*426d2b71SDavid du Colombier 		return;
382*426d2b71SDavid du Colombier 	}
383*426d2b71SDavid du Colombier 	argc = 1+parseargs(a, argv+1);
384*426d2b71SDavid du Colombier 	for(i=0; i<nreq; i++){
385*426d2b71SDavid du Colombier 		if(runestrcmp(req[i].name, argv[0]) == 0){
386*426d2b71SDavid du Colombier 			if(req[i].argc != -1){
387*426d2b71SDavid du Colombier 				if(argc < 1+req[i].argc){
388*426d2b71SDavid du Colombier 					warn("not enough arguments for %C%S", dot, req[i].name);
389*426d2b71SDavid du Colombier 					free(argv[0]);
390*426d2b71SDavid du Colombier 					free(a);
391*426d2b71SDavid du Colombier 					return;
392*426d2b71SDavid du Colombier 				}
393*426d2b71SDavid du Colombier 				if(argc > 1+req[i].argc)
394*426d2b71SDavid du Colombier 					warn("too many arguments for %C%S", dot, req[i].name);
395*426d2b71SDavid du Colombier 			}
396*426d2b71SDavid du Colombier 			req[i].f(argc, argv);
397*426d2b71SDavid du Colombier 			free(argv[0]);
398*426d2b71SDavid du Colombier 			free(a);
399*426d2b71SDavid du Colombier 			return;
400*426d2b71SDavid du Colombier 		}
401*426d2b71SDavid du Colombier 	}
402*426d2b71SDavid du Colombier 
403*426d2b71SDavid du Colombier 	/*
404*426d2b71SDavid du Colombier 	 * Invoke user-defined macros.
405*426d2b71SDavid du Colombier 	 */
406*426d2b71SDavid du Colombier 	runmacro(dot, argc, argv);
407*426d2b71SDavid du Colombier 	free(argv[0]);
408*426d2b71SDavid du Colombier 	free(a);
409*426d2b71SDavid du Colombier }
410*426d2b71SDavid du Colombier 
411*426d2b71SDavid du Colombier /*
412*426d2b71SDavid du Colombier  * newlines are magical in various ways.
413*426d2b71SDavid du Colombier  */
414*426d2b71SDavid du Colombier int bol;
415*426d2b71SDavid du Colombier void
newline(void)416*426d2b71SDavid du Colombier newline(void)
417*426d2b71SDavid du Colombier {
418*426d2b71SDavid du Colombier 	int n;
419*426d2b71SDavid du Colombier 
420*426d2b71SDavid du Colombier 	if(bol)
421*426d2b71SDavid du Colombier 		sp(eval(L("1v")));
422*426d2b71SDavid du Colombier 	bol = 1;
423*426d2b71SDavid du Colombier 	if((n=getnr(L(".ce"))) > 0){
424*426d2b71SDavid du Colombier 		nr(L(".ce"), n-1);
425*426d2b71SDavid du Colombier 		br();
426*426d2b71SDavid du Colombier 	}
427*426d2b71SDavid du Colombier 	if(getnr(L(".fi")) == 0)
428*426d2b71SDavid du Colombier 		br();
429*426d2b71SDavid du Colombier 	outrune('\n');
430*426d2b71SDavid du Colombier }
431*426d2b71SDavid du Colombier 
432*426d2b71SDavid du Colombier void
startoutput(void)433*426d2b71SDavid du Colombier startoutput(void)
434*426d2b71SDavid du Colombier {
435*426d2b71SDavid du Colombier 	char *align;
436*426d2b71SDavid du Colombier 	double ps, vs, lm, rm, ti;
437*426d2b71SDavid du Colombier 	Rune buf[200];
438*426d2b71SDavid du Colombier 
439*426d2b71SDavid du Colombier 	if(isoutput)
440*426d2b71SDavid du Colombier 		return;
441*426d2b71SDavid du Colombier 	isoutput = 1;
442*426d2b71SDavid du Colombier 
443*426d2b71SDavid du Colombier 	if(getnr(L(".paragraph")) == 0)
444*426d2b71SDavid du Colombier 		return;
445*426d2b71SDavid du Colombier 
446*426d2b71SDavid du Colombier 	nr(L(".ns"), 0);
447*426d2b71SDavid du Colombier 	isoutput = 1;
448*426d2b71SDavid du Colombier 	ps = getnr(L(".s"));
449*426d2b71SDavid du Colombier 	if(ps <= 1)
450*426d2b71SDavid du Colombier 		ps = 10;
451*426d2b71SDavid du Colombier 	ps /= 72.0;
452*426d2b71SDavid du Colombier 	USED(ps);
453*426d2b71SDavid du Colombier 
454*426d2b71SDavid du Colombier 	vs = getnr(L(".v"))*getnr(L(".ls")) * 1.0/UPI;
455*426d2b71SDavid du Colombier 	vs /= (10.0/72.0);	/* ps */
456*426d2b71SDavid du Colombier 	if(vs == 0)
457*426d2b71SDavid du Colombier 		vs = 1.2;
458*426d2b71SDavid du Colombier 
459*426d2b71SDavid du Colombier 	lm = (getnr(L(".o"))+getnr(L(".i"))) * 1.0/UPI;
460*426d2b71SDavid du Colombier 	ti = getnr(L(".ti")) * 1.0/UPI;
461*426d2b71SDavid du Colombier 	nr(L(".ti"), 0);
462*426d2b71SDavid du Colombier 
463*426d2b71SDavid du Colombier 	rm = 8.0 - getnr(L(".l"))*1.0/UPI - getnr(L(".o"))*1.0/UPI;
464*426d2b71SDavid du Colombier 	if(rm < 0)
465*426d2b71SDavid du Colombier 		rm = 0;
466*426d2b71SDavid du Colombier 	switch(getnr(L(".j"))){
467*426d2b71SDavid du Colombier 	default:
468*426d2b71SDavid du Colombier 	case 0:
469*426d2b71SDavid du Colombier 		align = "left";
470*426d2b71SDavid du Colombier 		break;
471*426d2b71SDavid du Colombier 	case 1:
472*426d2b71SDavid du Colombier 		align = "justify";
473*426d2b71SDavid du Colombier 		break;
474*426d2b71SDavid du Colombier 	case 3:
475*426d2b71SDavid du Colombier 		align = "center";
476*426d2b71SDavid du Colombier 		break;
477*426d2b71SDavid du Colombier 	case 5:
478*426d2b71SDavid du Colombier 		align = "right";
479*426d2b71SDavid du Colombier 		break;
480*426d2b71SDavid du Colombier 	}
481*426d2b71SDavid du Colombier 	if(getnr(L(".ce")))
482*426d2b71SDavid du Colombier 		align = "center";
483*426d2b71SDavid du Colombier 	if(!getnr(L(".margin")))
484*426d2b71SDavid du Colombier 		runesnprint(buf, nelem(buf), "<p style=\"line-height: %.1fem; text-indent: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\n",
485*426d2b71SDavid du Colombier 			vs, ti, align);
486*426d2b71SDavid du Colombier 	else
487*426d2b71SDavid du Colombier 		runesnprint(buf, nelem(buf), "<p style=\"line-height: %.1fem; margin-left: %.2fin; text-indent: %.2fin; margin-right: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\n",
488*426d2b71SDavid du Colombier 			vs, lm, ti, rm, align);
489*426d2b71SDavid du Colombier 	outhtml(buf);
490*426d2b71SDavid du Colombier }
491*426d2b71SDavid du Colombier void
br(void)492*426d2b71SDavid du Colombier br(void)
493*426d2b71SDavid du Colombier {
494*426d2b71SDavid du Colombier 	if(!isoutput)
495*426d2b71SDavid du Colombier 		return;
496*426d2b71SDavid du Colombier 	isoutput = 0;
497*426d2b71SDavid du Colombier 
498*426d2b71SDavid du Colombier 	nr(L(".dv"), 0);
499*426d2b71SDavid du Colombier 	dv(0);
500*426d2b71SDavid du Colombier 	hideihtml();
501*426d2b71SDavid du Colombier 	if(getnr(L(".paragraph")))
502*426d2b71SDavid du Colombier 		outhtml(L("</p>"));
503*426d2b71SDavid du Colombier }
504*426d2b71SDavid du Colombier 
505*426d2b71SDavid du Colombier void
r_margin(int argc,Rune ** argv)506*426d2b71SDavid du Colombier r_margin(int argc, Rune **argv)
507*426d2b71SDavid du Colombier {
508*426d2b71SDavid du Colombier 	USED(argc);
509*426d2b71SDavid du Colombier 
510*426d2b71SDavid du Colombier 	nr(L(".margin"), eval(argv[1]));
511*426d2b71SDavid du Colombier }
512*426d2b71SDavid du Colombier 
513*426d2b71SDavid du Colombier int inrequest;
514*426d2b71SDavid du Colombier void
runinput(void)515*426d2b71SDavid du Colombier runinput(void)
516*426d2b71SDavid du Colombier {
517*426d2b71SDavid du Colombier 	int c;
518*426d2b71SDavid du Colombier 
519*426d2b71SDavid du Colombier 	bol = 1;
520*426d2b71SDavid du Colombier 	for(;;){
521*426d2b71SDavid du Colombier 		c = getnext();
522*426d2b71SDavid du Colombier 		if(c < 0)
523*426d2b71SDavid du Colombier 			break;
524*426d2b71SDavid du Colombier 		if((c == dot || c == tick) && bol){
525*426d2b71SDavid du Colombier 			inrequest = 1;
526*426d2b71SDavid du Colombier 			dotline(c);
527*426d2b71SDavid du Colombier 			bol = 1;
528*426d2b71SDavid du Colombier 			inrequest = 0;
529*426d2b71SDavid du Colombier 		}else if(c == '\n'){
530*426d2b71SDavid du Colombier 			newline();
531*426d2b71SDavid du Colombier 			itrap();
532*426d2b71SDavid du Colombier 			linepos = 0;
533*426d2b71SDavid du Colombier 		}else{
534*426d2b71SDavid du Colombier 			outtrap();
535*426d2b71SDavid du Colombier 			startoutput();
536*426d2b71SDavid du Colombier 			showihtml();
537*426d2b71SDavid du Colombier 			if(c == '\t'){
538*426d2b71SDavid du Colombier 				/* XXX do better */
539*426d2b71SDavid du Colombier 				outrune(' ');
540*426d2b71SDavid du Colombier 				while(++linepos%4)
541*426d2b71SDavid du Colombier 					outrune(' ');
542*426d2b71SDavid du Colombier 			}else{
543*426d2b71SDavid du Colombier 				outrune(c);
544*426d2b71SDavid du Colombier 				linepos++;
545*426d2b71SDavid du Colombier 			}
546*426d2b71SDavid du Colombier 			bol = 0;
547*426d2b71SDavid du Colombier 		}
548*426d2b71SDavid du Colombier 	}
549*426d2b71SDavid du Colombier }
550*426d2b71SDavid du Colombier 
551*426d2b71SDavid du Colombier void
run(void)552*426d2b71SDavid du Colombier run(void)
553*426d2b71SDavid du Colombier {
554*426d2b71SDavid du Colombier 	t1init();
555*426d2b71SDavid du Colombier 	t2init();
556*426d2b71SDavid du Colombier 	t3init();
557*426d2b71SDavid du Colombier 	t4init();
558*426d2b71SDavid du Colombier 	t5init();
559*426d2b71SDavid du Colombier 	t6init();
560*426d2b71SDavid du Colombier 	t7init();
561*426d2b71SDavid du Colombier 	t8init();
562*426d2b71SDavid du Colombier 	/* t9init(); t9.c */
563*426d2b71SDavid du Colombier 	t10init();
564*426d2b71SDavid du Colombier 	t11init();
565*426d2b71SDavid du Colombier 	/* t12init(); t12.c */
566*426d2b71SDavid du Colombier 	t13init();
567*426d2b71SDavid du Colombier 	t14init();
568*426d2b71SDavid du Colombier 	t15init();
569*426d2b71SDavid du Colombier 	t16init();
570*426d2b71SDavid du Colombier 	t17init();
571*426d2b71SDavid du Colombier 	t18init();
572*426d2b71SDavid du Colombier 	t19init();
573*426d2b71SDavid du Colombier 	t20init();
574*426d2b71SDavid du Colombier 	htmlinit();
575*426d2b71SDavid du Colombier 	hideihtml();
576*426d2b71SDavid du Colombier 
577*426d2b71SDavid du Colombier 	addreq(L("margin"), r_margin, 1);
578*426d2b71SDavid du Colombier 	nr(L(".margin"), 1);
579*426d2b71SDavid du Colombier 	nr(L(".paragraph"), 1);
580*426d2b71SDavid du Colombier 
581*426d2b71SDavid du Colombier 	runinput();
582*426d2b71SDavid du Colombier 	while(popinput())
583*426d2b71SDavid du Colombier 		;
584*426d2b71SDavid du Colombier 	dot = '.';
585*426d2b71SDavid du Colombier 	if(verbose)
586*426d2b71SDavid du Colombier 		fprint(2, "eof\n");
587*426d2b71SDavid du Colombier 	runmacro1(L("eof"));
588*426d2b71SDavid du Colombier 	closehtml();
589*426d2b71SDavid du Colombier }
590*426d2b71SDavid du Colombier 
591*426d2b71SDavid du Colombier void
out(Rune * s)592*426d2b71SDavid du Colombier out(Rune *s)
593*426d2b71SDavid du Colombier {
594*426d2b71SDavid du Colombier 	if(s == nil)
595*426d2b71SDavid du Colombier 		return;
596*426d2b71SDavid du Colombier 	for(; *s; s++)
597*426d2b71SDavid du Colombier 		outrune(*s);
598*426d2b71SDavid du Colombier }
599*426d2b71SDavid du Colombier 
600*426d2b71SDavid du Colombier void (*outcb)(Rune);
601*426d2b71SDavid du Colombier 
602*426d2b71SDavid du Colombier void
inroman(Rune r)603*426d2b71SDavid du Colombier inroman(Rune r)
604*426d2b71SDavid du Colombier {
605*426d2b71SDavid du Colombier 	int f;
606*426d2b71SDavid du Colombier 
607*426d2b71SDavid du Colombier 	f = getnr(L(".f"));
608*426d2b71SDavid du Colombier 	nr(L(".f"), 1);
609*426d2b71SDavid du Colombier 	runmacro1(L("font"));
610*426d2b71SDavid du Colombier 	outrune(r);
611*426d2b71SDavid du Colombier 	nr(L(".f"), f);
612*426d2b71SDavid du Colombier 	runmacro1(L("font"));
613*426d2b71SDavid du Colombier }
614*426d2b71SDavid du Colombier 
615*426d2b71SDavid du Colombier void
Brune(Rune r)616*426d2b71SDavid du Colombier Brune(Rune r)
617*426d2b71SDavid du Colombier {
618*426d2b71SDavid du Colombier 	if(r == '&')
619*426d2b71SDavid du Colombier 		Bprint(&bout, "&amp;");
620*426d2b71SDavid du Colombier 	else if(r == '<')
621*426d2b71SDavid du Colombier 		Bprint(&bout, "&lt;");
622*426d2b71SDavid du Colombier 	else if(r == '>')
623*426d2b71SDavid du Colombier 		Bprint(&bout, "&gt;");
624*426d2b71SDavid du Colombier 	else if(r < Runeself || utf8)
625*426d2b71SDavid du Colombier 		Bprint(&bout, "%C", r);
626*426d2b71SDavid du Colombier 	else
627*426d2b71SDavid du Colombier 		Bprint(&bout, "%S", rune2html(r));
628*426d2b71SDavid du Colombier }
629*426d2b71SDavid du Colombier 
630*426d2b71SDavid du Colombier void
outhtml(Rune * s)631*426d2b71SDavid du Colombier outhtml(Rune *s)
632*426d2b71SDavid du Colombier {
633*426d2b71SDavid du Colombier 	Rune r;
634*426d2b71SDavid du Colombier 
635*426d2b71SDavid du Colombier 	for(; *s; s++){
636*426d2b71SDavid du Colombier 		switch(r = *s){
637*426d2b71SDavid du Colombier 		case '<':
638*426d2b71SDavid du Colombier 			r = Ult;
639*426d2b71SDavid du Colombier 			break;
640*426d2b71SDavid du Colombier 		case '>':
641*426d2b71SDavid du Colombier 			r = Ugt;
642*426d2b71SDavid du Colombier 			break;
643*426d2b71SDavid du Colombier 		case '&':
644*426d2b71SDavid du Colombier 			r = Uamp;
645*426d2b71SDavid du Colombier 			break;
646*426d2b71SDavid du Colombier 		case ' ':
647*426d2b71SDavid du Colombier 			r = Uspace;
648*426d2b71SDavid du Colombier 			break;
649*426d2b71SDavid du Colombier 		}
650*426d2b71SDavid du Colombier 		outrune(r);
651*426d2b71SDavid du Colombier 	}
652*426d2b71SDavid du Colombier }
653*426d2b71SDavid du Colombier 
654*426d2b71SDavid du Colombier void
outrune(Rune r)655*426d2b71SDavid du Colombier outrune(Rune r)
656*426d2b71SDavid du Colombier {
657*426d2b71SDavid du Colombier 	switch(r){
658*426d2b71SDavid du Colombier 	case ' ':
659*426d2b71SDavid du Colombier 		if(getnr(L(".fi")) == 0)
660*426d2b71SDavid du Colombier 			r = Unbsp;
661*426d2b71SDavid du Colombier 		break;
662*426d2b71SDavid du Colombier 	case Uformatted:
663*426d2b71SDavid du Colombier 	case Uunformatted:
664*426d2b71SDavid du Colombier 		abort();
665*426d2b71SDavid du Colombier 	}
666*426d2b71SDavid du Colombier 	if(outcb){
667*426d2b71SDavid du Colombier 		if(r == ' ')
668*426d2b71SDavid du Colombier 			r = Uspace;
669*426d2b71SDavid du Colombier 		outcb(r);
670*426d2b71SDavid du Colombier 		return;
671*426d2b71SDavid du Colombier 	}
672*426d2b71SDavid du Colombier 	/* writing to bout */
673*426d2b71SDavid du Colombier 	switch(r){
674*426d2b71SDavid du Colombier 	case Uempty:
675*426d2b71SDavid du Colombier 		return;
676*426d2b71SDavid du Colombier 	case Upl:
677*426d2b71SDavid du Colombier 		inroman('+');
678*426d2b71SDavid du Colombier 		return;
679*426d2b71SDavid du Colombier 	case Ueq:
680*426d2b71SDavid du Colombier 		inroman('=');
681*426d2b71SDavid du Colombier 		return;
682*426d2b71SDavid du Colombier 	case Umi:
683*426d2b71SDavid du Colombier 		inroman(0x2212);
684*426d2b71SDavid du Colombier 		return;
685*426d2b71SDavid du Colombier 	case Utick:
686*426d2b71SDavid du Colombier 		r = '\'';
687*426d2b71SDavid du Colombier 		break;
688*426d2b71SDavid du Colombier 	case Ubtick:
689*426d2b71SDavid du Colombier 		r = '`';
690*426d2b71SDavid du Colombier 		break;
691*426d2b71SDavid du Colombier 	case Uminus:
692*426d2b71SDavid du Colombier 		r = '-';
693*426d2b71SDavid du Colombier 		break;
694*426d2b71SDavid du Colombier 	case '\'':
695*426d2b71SDavid du Colombier 		Bprint(&bout, "&rsquo;");
696*426d2b71SDavid du Colombier 		return;
697*426d2b71SDavid du Colombier 	case '`':
698*426d2b71SDavid du Colombier 		Bprint(&bout, "&lsquo;");
699*426d2b71SDavid du Colombier 		return;
700*426d2b71SDavid du Colombier 	case Uamp:
701*426d2b71SDavid du Colombier 		Bputrune(&bout, '&');
702*426d2b71SDavid du Colombier 		return;
703*426d2b71SDavid du Colombier 	case Ult:
704*426d2b71SDavid du Colombier 		Bputrune(&bout, '<');
705*426d2b71SDavid du Colombier 		return;
706*426d2b71SDavid du Colombier 	case Ugt:
707*426d2b71SDavid du Colombier 		Bputrune(&bout, '>');
708*426d2b71SDavid du Colombier 		return;
709*426d2b71SDavid du Colombier 	case Uspace:
710*426d2b71SDavid du Colombier 		Bputrune(&bout, ' ');
711*426d2b71SDavid du Colombier 		return;
712*426d2b71SDavid du Colombier 	case 0x2032:
713*426d2b71SDavid du Colombier 		/*
714*426d2b71SDavid du Colombier 		 * In Firefox, at least, the prime is not
715*426d2b71SDavid du Colombier 		 * a superscript by default.
716*426d2b71SDavid du Colombier 		 */
717*426d2b71SDavid du Colombier 		Bprint(&bout, "<sup>");
718*426d2b71SDavid du Colombier 		Brune(r);
719*426d2b71SDavid du Colombier 		Bprint(&bout, "</sup>");
720*426d2b71SDavid du Colombier 		return;
721*426d2b71SDavid du Colombier 	}
722*426d2b71SDavid du Colombier 	Brune(r);
723*426d2b71SDavid du Colombier }
724*426d2b71SDavid du Colombier 
725*426d2b71SDavid du Colombier void
r_nop(int argc,Rune ** argv)726*426d2b71SDavid du Colombier r_nop(int argc, Rune **argv)
727*426d2b71SDavid du Colombier {
728*426d2b71SDavid du Colombier 	USED(argc);
729*426d2b71SDavid du Colombier 	USED(argv);
730*426d2b71SDavid du Colombier }
731*426d2b71SDavid du Colombier 
732*426d2b71SDavid du Colombier void
r_warn(int argc,Rune ** argv)733*426d2b71SDavid du Colombier r_warn(int argc, Rune **argv)
734*426d2b71SDavid du Colombier {
735*426d2b71SDavid du Colombier 	USED(argc);
736*426d2b71SDavid du Colombier 	warn("ignoring %C%S", dot, argv[0]);
737*426d2b71SDavid du Colombier }
738*426d2b71SDavid du Colombier 
739*426d2b71SDavid du Colombier int
e_warn(void)740*426d2b71SDavid du Colombier e_warn(void)
741*426d2b71SDavid du Colombier {
742*426d2b71SDavid du Colombier 	/* dispatch loop prints a warning for us */
743*426d2b71SDavid du Colombier 	return 0;
744*426d2b71SDavid du Colombier }
745*426d2b71SDavid du Colombier 
746*426d2b71SDavid du Colombier int
e_nop(void)747*426d2b71SDavid du Colombier e_nop(void)
748*426d2b71SDavid du Colombier {
749*426d2b71SDavid du Colombier 	return 0;
750*426d2b71SDavid du Colombier }
751