xref: /plan9/sys/src/cmd/troff2html/troff2html.c (revision 0fb05a454311ea792de3070b4d1cc4de5db23659)
1223a736eSDavid du Colombier #include <u.h>
2223a736eSDavid du Colombier #include <libc.h>
3223a736eSDavid du Colombier #include <bio.h>
4223a736eSDavid du Colombier 
5223a736eSDavid du Colombier enum{
6223a736eSDavid du Colombier 	Nfont = 11,
7223a736eSDavid du Colombier 	Wid = 20,	/* tmac.anhtml sets page width to 20" so we can recognize .nf text */
8223a736eSDavid du Colombier };
9223a736eSDavid du Colombier 
10223a736eSDavid du Colombier typedef ulong Char;
11223a736eSDavid du Colombier typedef struct Troffchar Troffchar;
12223a736eSDavid du Colombier typedef struct Htmlchar Htmlchar;
13223a736eSDavid du Colombier typedef struct Font Font;
14223a736eSDavid du Colombier typedef struct HTMLfont HTMLfont;
15223a736eSDavid du Colombier 
16223a736eSDavid du Colombier /* a Char is 32 bits. low 16 bits are the rune. higher are attributes */
17223a736eSDavid du Colombier enum
18223a736eSDavid du Colombier {
19223a736eSDavid du Colombier 	Italic	=	16,
20*0fb05a45SDavid du Colombier 	Bold,
21*0fb05a45SDavid du Colombier 	CW,
22*0fb05a45SDavid du Colombier 	Indent1,
23*0fb05a45SDavid du Colombier 	Indent2,
24*0fb05a45SDavid du Colombier 	Indent3,
25223a736eSDavid du Colombier 	Heading =	25,
26223a736eSDavid du Colombier 	Anchor =	26,	/* must be last */
27223a736eSDavid du Colombier };
28223a736eSDavid du Colombier 
29*0fb05a45SDavid du Colombier enum	/* magic emissions */
30*0fb05a45SDavid du Colombier {
31*0fb05a45SDavid du Colombier 	Estring = 0,
32*0fb05a45SDavid du Colombier 	Epp = 1<<16,
33*0fb05a45SDavid du Colombier };
34*0fb05a45SDavid du Colombier 
35*0fb05a45SDavid du Colombier int attrorder[] = { Indent1, Indent2, Indent3, Heading, Anchor, Italic, Bold, CW };
36*0fb05a45SDavid du Colombier 
37*0fb05a45SDavid du Colombier int nest[10];
38*0fb05a45SDavid du Colombier int nnest;
39*0fb05a45SDavid du Colombier 
40223a736eSDavid du Colombier struct Troffchar
41223a736eSDavid du Colombier {
42223a736eSDavid du Colombier 	char *name;
43223a736eSDavid du Colombier 	char *value;
44223a736eSDavid du Colombier };
45223a736eSDavid du Colombier 
46223a736eSDavid du Colombier struct Htmlchar
47223a736eSDavid du Colombier {
48*0fb05a45SDavid du Colombier 	char *utf;
49223a736eSDavid du Colombier 	char *name;
50*0fb05a45SDavid du Colombier 	int value;
51223a736eSDavid du Colombier };
52223a736eSDavid du Colombier 
53223a736eSDavid du Colombier #include "chars.h"
54223a736eSDavid du Colombier 
55223a736eSDavid du Colombier struct Font{
56223a736eSDavid du Colombier 	char		*name;
57223a736eSDavid du Colombier 	HTMLfont	*htmlfont;
58223a736eSDavid du Colombier };
59223a736eSDavid du Colombier 
60223a736eSDavid du Colombier struct HTMLfont{
61223a736eSDavid du Colombier 	char	*name;
62223a736eSDavid du Colombier 	char	*htmlname;
63223a736eSDavid du Colombier 	int	bit;
64223a736eSDavid du Colombier };
65223a736eSDavid du Colombier 
66223a736eSDavid du Colombier /* R must be first; it's the default representation for fonts we don't recognize */
67223a736eSDavid du Colombier HTMLfont htmlfonts[] =
68223a736eSDavid du Colombier {
69223a736eSDavid du Colombier 	"R",			nil,		0,
70223a736eSDavid du Colombier 	"LucidaSans",	nil,		0,
71*0fb05a45SDavid du Colombier 	"I",			"i",	Italic,
72*0fb05a45SDavid du Colombier 	"LucidaSansI",	"i",	Italic,
73*0fb05a45SDavid du Colombier 	"CW",		"tt",		CW,
74*0fb05a45SDavid du Colombier 	"LucidaCW",	"tt",		CW,
75223a736eSDavid du Colombier 	nil,	nil,
76223a736eSDavid du Colombier };
77223a736eSDavid du Colombier 
78*0fb05a45SDavid du Colombier #define TABLE "<table border=0 cellpadding=0 cellspacing=0>"
79*0fb05a45SDavid du Colombier 
80223a736eSDavid du Colombier char*
81*0fb05a45SDavid du Colombier onattr[8*sizeof(ulong)] =
82*0fb05a45SDavid du Colombier {
83*0fb05a45SDavid du Colombier 	0, 0, 0, 0, 0, 0, 0, 0,
84*0fb05a45SDavid du Colombier 	0, 0, 0, 0, 0, 0, 0, 0,
85*0fb05a45SDavid du Colombier 	"<i>",	/* italic */
86*0fb05a45SDavid du Colombier 	"<b>",	/* bold */
87*0fb05a45SDavid du Colombier 	"<tt><font size=+1>",	/* cw */
88*0fb05a45SDavid du Colombier 	"<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",	/* indent1 */
89*0fb05a45SDavid du Colombier 	"<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",	/* indent2 */
90*0fb05a45SDavid du Colombier 	"<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",	/* indent3 */
91*0fb05a45SDavid du Colombier 	0,
92*0fb05a45SDavid du Colombier 	0,
93*0fb05a45SDavid du Colombier 	0,
94*0fb05a45SDavid du Colombier 	"<p><font size=+1><b>",	/* heading 25 */
95*0fb05a45SDavid du Colombier 	"<unused>",	/* anchor 26 */
96223a736eSDavid du Colombier };
97223a736eSDavid du Colombier 
98223a736eSDavid du Colombier char*
99*0fb05a45SDavid du Colombier offattr[8*sizeof(ulong)] =
100*0fb05a45SDavid du Colombier {
101*0fb05a45SDavid du Colombier 	0, 0, 0, 0, 0, 0, 0, 0,
102*0fb05a45SDavid du Colombier 	0, 0, 0, 0, 0, 0, 0, 0,
103*0fb05a45SDavid du Colombier 	"</i>",	/* italic */
104*0fb05a45SDavid du Colombier 	"</b>",	/* bold */
105*0fb05a45SDavid du Colombier 	"</font></tt>",	/* cw */
106*0fb05a45SDavid du Colombier 	"<-/table>",	/* indent1 */
107*0fb05a45SDavid du Colombier 	"<-/table>",	/* indent2 */
108*0fb05a45SDavid du Colombier 	"<-/table>",	/* indent3 */
109*0fb05a45SDavid du Colombier 	0,
110*0fb05a45SDavid du Colombier 	0,
111*0fb05a45SDavid du Colombier 	0,
112*0fb05a45SDavid du Colombier 	"</b></font>",	/* heading 25 */
113*0fb05a45SDavid du Colombier 	"</a>",	/* anchor 26 */
114223a736eSDavid du Colombier };
115223a736eSDavid du Colombier 
116223a736eSDavid du Colombier Font *font[Nfont];
117223a736eSDavid du Colombier 
118223a736eSDavid du Colombier Biobuf bout;
119223a736eSDavid du Colombier int	debug = 0;
120223a736eSDavid du Colombier 
121223a736eSDavid du Colombier /* troff state */
122223a736eSDavid du Colombier int	page = 1;
123223a736eSDavid du Colombier int	ft = 1;
124223a736eSDavid du Colombier int	vp = 0;
125223a736eSDavid du Colombier int	hp = 0;
126223a736eSDavid du Colombier int	ps = 1;
127223a736eSDavid du Colombier int	res = 720;
128223a736eSDavid du Colombier 
129223a736eSDavid du Colombier int		didP = 0;
130223a736eSDavid du Colombier int		atnewline = 1;
131223a736eSDavid du Colombier int		prevlineH = 0;
132223a736eSDavid du Colombier ulong	attr = 0;	/* or'ed into each Char */
133223a736eSDavid du Colombier 
134223a736eSDavid du Colombier Char		*chars;
135223a736eSDavid du Colombier int		nchars;
136223a736eSDavid du Colombier int		nalloc;
137223a736eSDavid du Colombier char**	anchors;	/* allocated in order */
138223a736eSDavid du Colombier int		nanchors;
139223a736eSDavid du Colombier 
140223a736eSDavid du Colombier char	*filename;
141223a736eSDavid du Colombier int	cno;
142223a736eSDavid du Colombier char	buf[8192];
143223a736eSDavid du Colombier char	*title = "Plan 9 man page";
144223a736eSDavid du Colombier 
145223a736eSDavid du Colombier void	process(Biobuf*, char*);
146223a736eSDavid du Colombier void	mountfont(int, char*);
147223a736eSDavid du Colombier void	switchfont(int);
148223a736eSDavid du Colombier void	header(char*);
149223a736eSDavid du Colombier void	flush(void);
150223a736eSDavid du Colombier void	trailer(void);
151223a736eSDavid du Colombier 
152223a736eSDavid du Colombier void*
153223a736eSDavid du Colombier emalloc(ulong n)
154223a736eSDavid du Colombier {
155223a736eSDavid du Colombier 	void *p;
156223a736eSDavid du Colombier 
157223a736eSDavid du Colombier 	p = malloc(n);
158223a736eSDavid du Colombier 	if(p == nil)
159223a736eSDavid du Colombier 		sysfatal("malloc failed: %r");
160223a736eSDavid du Colombier 	return p;
161223a736eSDavid du Colombier }
162223a736eSDavid du Colombier 
163223a736eSDavid du Colombier void*
164223a736eSDavid du Colombier erealloc(void *p, ulong n)
165223a736eSDavid du Colombier {
166223a736eSDavid du Colombier 
167223a736eSDavid du Colombier 	p = realloc(p, n);
168223a736eSDavid du Colombier 	if(p == nil)
169223a736eSDavid du Colombier 		sysfatal("realloc failed: %r");
170223a736eSDavid du Colombier 	return p;
171223a736eSDavid du Colombier }
172223a736eSDavid du Colombier 
173223a736eSDavid du Colombier char*
174223a736eSDavid du Colombier estrdup(char *s)
175223a736eSDavid du Colombier {
176223a736eSDavid du Colombier 	char *t;
177223a736eSDavid du Colombier 
178223a736eSDavid du Colombier 	t = strdup(s);
179223a736eSDavid du Colombier 	if(t == nil)
180223a736eSDavid du Colombier 		sysfatal("strdup failed: %r");
181223a736eSDavid du Colombier 	return t;
182223a736eSDavid du Colombier }
183223a736eSDavid du Colombier 
184223a736eSDavid du Colombier void
185223a736eSDavid du Colombier usage(void)
186223a736eSDavid du Colombier {
187*0fb05a45SDavid du Colombier 	fprint(2, "usage: troff2html [-d] [-t title] [file ...]\n");
188223a736eSDavid du Colombier 	exits("usage");
189223a736eSDavid du Colombier }
190223a736eSDavid du Colombier 
191*0fb05a45SDavid du Colombier int
192*0fb05a45SDavid du Colombier hccmp(const void *va, const void *vb)
193*0fb05a45SDavid du Colombier {
194*0fb05a45SDavid du Colombier 	Htmlchar *a, *b;
195*0fb05a45SDavid du Colombier 
196*0fb05a45SDavid du Colombier 	a = (Htmlchar*)va;
197*0fb05a45SDavid du Colombier 	b = (Htmlchar*)vb;
198*0fb05a45SDavid du Colombier 	return a->value - b->value;
199*0fb05a45SDavid du Colombier }
200*0fb05a45SDavid du Colombier 
201223a736eSDavid du Colombier void
202223a736eSDavid du Colombier main(int argc, char *argv[])
203223a736eSDavid du Colombier {
204223a736eSDavid du Colombier 	int i;
205223a736eSDavid du Colombier 	Biobuf in, *inp;
206*0fb05a45SDavid du Colombier 	Rune r;
207*0fb05a45SDavid du Colombier 
208*0fb05a45SDavid du Colombier 	for(i=0; i<nelem(htmlchars); i++){
209*0fb05a45SDavid du Colombier 		chartorune(&r, htmlchars[i].utf);
210*0fb05a45SDavid du Colombier 		htmlchars[i].value = r;
211*0fb05a45SDavid du Colombier 	}
212*0fb05a45SDavid du Colombier 	qsort(htmlchars, nelem(htmlchars), sizeof(htmlchars[0]), hccmp);
213223a736eSDavid du Colombier 
214223a736eSDavid du Colombier 	ARGBEGIN{
215223a736eSDavid du Colombier 	case 't':
216223a736eSDavid du Colombier 		title = ARGF();
217223a736eSDavid du Colombier 		if(title == nil)
218223a736eSDavid du Colombier 			usage();
219223a736eSDavid du Colombier 		break;
220223a736eSDavid du Colombier 	case 'd':
221223a736eSDavid du Colombier 		debug++;
222223a736eSDavid du Colombier 		break;
223223a736eSDavid du Colombier 	default:
224223a736eSDavid du Colombier 		usage();
225223a736eSDavid du Colombier 	}ARGEND
226*0fb05a45SDavid du Colombier 
227223a736eSDavid du Colombier 	Binit(&bout, 1, OWRITE);
228223a736eSDavid du Colombier 	if(argc == 0){
229223a736eSDavid du Colombier 		header(title);
230223a736eSDavid du Colombier 		Binit(&in, 0, OREAD);
231223a736eSDavid du Colombier 		process(&in, "<stdin>");
232223a736eSDavid du Colombier 	}else{
233223a736eSDavid du Colombier 		header(title);
234223a736eSDavid du Colombier 		for(i=0; i<argc; i++){
235223a736eSDavid du Colombier 			inp = Bopen(argv[i], OREAD);
236223a736eSDavid du Colombier 			if(inp == nil)
237223a736eSDavid du Colombier 				sysfatal("can't open %s: %r", argv[i]);
238223a736eSDavid du Colombier 			process(inp, argv[i]);
239223a736eSDavid du Colombier 			Bterm(inp);
240223a736eSDavid du Colombier 		}
241223a736eSDavid du Colombier 	}
242223a736eSDavid du Colombier 	flush();
243223a736eSDavid du Colombier 	trailer();
244223a736eSDavid du Colombier 	exits(nil);
245223a736eSDavid du Colombier }
246223a736eSDavid du Colombier 
247223a736eSDavid du Colombier void
248223a736eSDavid du Colombier emitul(ulong ul)
249223a736eSDavid du Colombier {
250223a736eSDavid du Colombier 	if(nalloc == nchars){
251223a736eSDavid du Colombier 		nalloc += 10000;
252223a736eSDavid du Colombier 		chars = realloc(chars, nalloc*sizeof(chars[0]));
253223a736eSDavid du Colombier 		if(chars == nil)
254223a736eSDavid du Colombier 			sysfatal("malloc failed: %r");
255223a736eSDavid du Colombier 	}
256223a736eSDavid du Colombier 	chars[nchars++] = ul;
257223a736eSDavid du Colombier }
258223a736eSDavid du Colombier 
259223a736eSDavid du Colombier void
260223a736eSDavid du Colombier emit(Rune r)
261223a736eSDavid du Colombier {
262223a736eSDavid du Colombier 	emitul(r | attr);
263*0fb05a45SDavid du Colombier 	/*
264*0fb05a45SDavid du Colombier 	 * Close man page references early, so that
265*0fb05a45SDavid du Colombier 	 * .IR proof (1),
266*0fb05a45SDavid du Colombier 	 * doesn't make the comma part of the link.
267*0fb05a45SDavid du Colombier 	 */
268*0fb05a45SDavid du Colombier 	if(r == ')')
269*0fb05a45SDavid du Colombier 		attr &= ~(1<<Anchor);
270223a736eSDavid du Colombier }
271223a736eSDavid du Colombier 
272223a736eSDavid du Colombier void
273223a736eSDavid du Colombier emitstr(char *s)
274223a736eSDavid du Colombier {
275*0fb05a45SDavid du Colombier 	emitul(Estring);
276223a736eSDavid du Colombier 	emitul((ulong)s);
277223a736eSDavid du Colombier }
278223a736eSDavid du Colombier 
279*0fb05a45SDavid du Colombier int indentlevel;
280*0fb05a45SDavid du Colombier int linelen;
281*0fb05a45SDavid du Colombier 
282*0fb05a45SDavid du Colombier void
283*0fb05a45SDavid du Colombier iputrune(Biobuf *b, Rune r)
284*0fb05a45SDavid du Colombier {
285*0fb05a45SDavid du Colombier 	int i;
286*0fb05a45SDavid du Colombier 
287*0fb05a45SDavid du Colombier 	if(linelen++ > 60 && r == ' ')
288*0fb05a45SDavid du Colombier 		r = '\n';
289*0fb05a45SDavid du Colombier 	Bputrune(b, r);
290*0fb05a45SDavid du Colombier 	if(r == '\n'){
291*0fb05a45SDavid du Colombier 		for(i=0; i<indentlevel; i++)
292*0fb05a45SDavid du Colombier 			Bprint(b, "    ");
293*0fb05a45SDavid du Colombier 		linelen = 0;
294*0fb05a45SDavid du Colombier 	}
295*0fb05a45SDavid du Colombier }
296*0fb05a45SDavid du Colombier 
297*0fb05a45SDavid du Colombier void
298*0fb05a45SDavid du Colombier iputs(Biobuf *b, char *s)
299*0fb05a45SDavid du Colombier {
300*0fb05a45SDavid du Colombier 	if(s[0]=='<' && s[1]=='+'){
301*0fb05a45SDavid du Colombier 		iputrune(b, '\n');
302*0fb05a45SDavid du Colombier 		Bprint(b, "<%s", s+2);
303*0fb05a45SDavid du Colombier 		indentlevel++;
304*0fb05a45SDavid du Colombier 		iputrune(b, '\n');
305*0fb05a45SDavid du Colombier 	}else if(s[0]=='<' && s[1]=='-'){
306*0fb05a45SDavid du Colombier 		indentlevel--;
307*0fb05a45SDavid du Colombier 		iputrune(b, '\n');
308*0fb05a45SDavid du Colombier 		Bprint(b, "<%s", s+2);
309*0fb05a45SDavid du Colombier 		iputrune(b, '\n');
310*0fb05a45SDavid du Colombier 	}else
311*0fb05a45SDavid du Colombier 		Bprint(b, "%s", s);
312*0fb05a45SDavid du Colombier }
313*0fb05a45SDavid du Colombier 
314*0fb05a45SDavid du Colombier void
315*0fb05a45SDavid du Colombier setattr(ulong a)
316*0fb05a45SDavid du Colombier {
317*0fb05a45SDavid du Colombier 	int on, off, i, j;
318*0fb05a45SDavid du Colombier 
319*0fb05a45SDavid du Colombier 	on = a & ~attr;
320*0fb05a45SDavid du Colombier 	off = attr & ~a;
321*0fb05a45SDavid du Colombier 
322*0fb05a45SDavid du Colombier 	/* walk up the nest stack until we reach something we need to turn off. */
323*0fb05a45SDavid du Colombier 	for(i=0; i<nnest; i++)
324*0fb05a45SDavid du Colombier 		if(off&(1<<nest[i]))
325*0fb05a45SDavid du Colombier 			break;
326*0fb05a45SDavid du Colombier 
327*0fb05a45SDavid du Colombier 	/* turn off everything above that */
328*0fb05a45SDavid du Colombier 	for(j=nnest-1; j>=i; j--)
329*0fb05a45SDavid du Colombier 		iputs(&bout, offattr[nest[j]]);
330*0fb05a45SDavid du Colombier 
331*0fb05a45SDavid du Colombier 	/* turn on everything we just turned off but didn't want to */
332*0fb05a45SDavid du Colombier 	for(j=i; j<nnest; j++)
333*0fb05a45SDavid du Colombier 		if(a&(1<<nest[j]))
334*0fb05a45SDavid du Colombier 			iputs(&bout, onattr[nest[j]]);
335*0fb05a45SDavid du Colombier 		else
336*0fb05a45SDavid du Colombier 			nest[j] = 0;
337*0fb05a45SDavid du Colombier 
338*0fb05a45SDavid du Colombier 	/* shift the zeros (turned off things) up */
339*0fb05a45SDavid du Colombier 	for(i=j=0; i<nnest; i++)
340*0fb05a45SDavid du Colombier 		if(nest[i] != 0)
341*0fb05a45SDavid du Colombier 			nest[j++] = nest[i];
342*0fb05a45SDavid du Colombier 	nnest = j;
343*0fb05a45SDavid du Colombier 
344*0fb05a45SDavid du Colombier 	/* now turn on the new attributes */
345*0fb05a45SDavid du Colombier 	for(i=0; i<nelem(attrorder); i++){
346*0fb05a45SDavid du Colombier 		j = attrorder[i];
347*0fb05a45SDavid du Colombier 		if(on&(1<<j)){
348*0fb05a45SDavid du Colombier 			if(j == Anchor)
349*0fb05a45SDavid du Colombier 				onattr[j] = anchors[nanchors++];
350*0fb05a45SDavid du Colombier 			iputs(&bout, onattr[j]);
351*0fb05a45SDavid du Colombier 			nest[nnest++] = j;
352*0fb05a45SDavid du Colombier 		}
353*0fb05a45SDavid du Colombier 	}
354*0fb05a45SDavid du Colombier 	attr = a;
355*0fb05a45SDavid du Colombier }
356*0fb05a45SDavid du Colombier 
357223a736eSDavid du Colombier void
358223a736eSDavid du Colombier flush(void)
359223a736eSDavid du Colombier {
360*0fb05a45SDavid du Colombier 	int i;
361*0fb05a45SDavid du Colombier 	ulong c, a;
362223a736eSDavid du Colombier 
363*0fb05a45SDavid du Colombier 	nanchors = 0;
364223a736eSDavid du Colombier 	for(i=0; i<nchars; i++){
365223a736eSDavid du Colombier 		c = chars[i];
366*0fb05a45SDavid du Colombier 		if(c == Estring){
367223a736eSDavid du Colombier 			/* next word is string to print */
368*0fb05a45SDavid du Colombier 			iputs(&bout, (char*)chars[++i]);
369223a736eSDavid du Colombier 			continue;
370223a736eSDavid du Colombier 		}
371*0fb05a45SDavid du Colombier 		if(c == Epp){
372*0fb05a45SDavid du Colombier 			iputrune(&bout, '\n');
373*0fb05a45SDavid du Colombier 			iputs(&bout, TABLE "<tr height=5><td></table>");
374*0fb05a45SDavid du Colombier 			iputrune(&bout, '\n');
375*0fb05a45SDavid du Colombier 			continue;
376223a736eSDavid du Colombier 		}
377*0fb05a45SDavid du Colombier 		a = c & ~0xFFFF;
378*0fb05a45SDavid du Colombier 		c &= 0xFFFF;
379*0fb05a45SDavid du Colombier 		/*
380*0fb05a45SDavid du Colombier 		 * If we're going to something off after a space,
381*0fb05a45SDavid du Colombier 		 * let's just turn it off before.
382*0fb05a45SDavid du Colombier 		 */
383*0fb05a45SDavid du Colombier 		if(c == ' ' && i<nchars-1 && (chars[i+1]&0xFFFF) >= 32)
384*0fb05a45SDavid du Colombier 			a ^= a & ~chars[i+1];
385*0fb05a45SDavid du Colombier 		setattr(a);
386*0fb05a45SDavid du Colombier 		iputrune(&bout, c & 0xFFFF);
387223a736eSDavid du Colombier 	}
388223a736eSDavid du Colombier }
389223a736eSDavid du Colombier 
390223a736eSDavid du Colombier void
391223a736eSDavid du Colombier header(char *s)
392223a736eSDavid du Colombier {
393*0fb05a45SDavid du Colombier 	Bprint(&bout, "<head>\n");
394*0fb05a45SDavid du Colombier 	Bprint(&bout, "<title>%s</title>\n", s);
395*0fb05a45SDavid du Colombier 	Bprint(&bout, "<meta content=\"text/html; charset=utf-8\" http-equiv=Content-Type>\n");
396*0fb05a45SDavid du Colombier 	Bprint(&bout, "</head>\n");
397*0fb05a45SDavid du Colombier 	Bprint(&bout, "<body bgcolor=#ffffff>\n");
398223a736eSDavid du Colombier }
399223a736eSDavid du Colombier 
400223a736eSDavid du Colombier void
401223a736eSDavid du Colombier trailer(void)
402223a736eSDavid du Colombier {
4039a747e4fSDavid du Colombier 
404*0fb05a45SDavid du Colombier #ifdef LUCENT
4059a747e4fSDavid du Colombier 	t = localtime(time(nil));
406*0fb05a45SDavid du Colombier 	Bprint(&bout, TABLE "<tr height=20><td></table>\n");
407*0fb05a45SDavid du Colombier 	Bprint(&bout, "<font size=-1><a href=\"http://www.lucent.com/copyright.html\">\n");
408*0fb05a45SDavid du Colombier 	Bprint(&bout, "Copyright</A> &#169; %d Lucent Technologies.  All rights reserved.</font>\n", t->year+1900);
409*0fb05a45SDavid du Colombier #endif
410*0fb05a45SDavid du Colombier 	Bprint(&bout, "</body></html>\n");
411223a736eSDavid du Colombier }
412223a736eSDavid du Colombier 
413223a736eSDavid du Colombier int
414223a736eSDavid du Colombier getc(Biobuf *b)
415223a736eSDavid du Colombier {
416223a736eSDavid du Colombier 	cno++;
417223a736eSDavid du Colombier 	return Bgetrune(b);
418223a736eSDavid du Colombier }
419223a736eSDavid du Colombier 
420223a736eSDavid du Colombier void
421223a736eSDavid du Colombier ungetc(Biobuf *b)
422223a736eSDavid du Colombier {
423223a736eSDavid du Colombier 	cno--;
424223a736eSDavid du Colombier 	Bungetrune(b);
425223a736eSDavid du Colombier }
426223a736eSDavid du Colombier 
427223a736eSDavid du Colombier char*
428223a736eSDavid du Colombier getline(Biobuf *b)
429223a736eSDavid du Colombier {
430223a736eSDavid du Colombier 	int i, c;
431223a736eSDavid du Colombier 
432223a736eSDavid du Colombier 	for(i=0; i<sizeof buf; i++){
433223a736eSDavid du Colombier 		c = getc(b);
434223a736eSDavid du Colombier 		if(c == Beof)
435223a736eSDavid du Colombier 			return nil;
436223a736eSDavid du Colombier 		buf[i] = c;
437223a736eSDavid du Colombier 		if(c == '\n'){
438223a736eSDavid du Colombier 			buf[i] = '\0';
439223a736eSDavid du Colombier 			break;
440223a736eSDavid du Colombier 		}
441223a736eSDavid du Colombier 	}
442223a736eSDavid du Colombier 	return buf;
443223a736eSDavid du Colombier }
444223a736eSDavid du Colombier 
445223a736eSDavid du Colombier int
446223a736eSDavid du Colombier getnum(Biobuf *b)
447223a736eSDavid du Colombier {
448223a736eSDavid du Colombier 	int i, c;
449223a736eSDavid du Colombier 
450223a736eSDavid du Colombier 	i = 0;
451223a736eSDavid du Colombier 	for(;;){
452223a736eSDavid du Colombier 		c = getc(b);
453223a736eSDavid du Colombier 		if(c<'0' || '9'<c){
454223a736eSDavid du Colombier 			ungetc(b);
455223a736eSDavid du Colombier 			break;
456223a736eSDavid du Colombier 		}
457223a736eSDavid du Colombier 		i = i*10 + (c-'0');
458223a736eSDavid du Colombier 	}
459223a736eSDavid du Colombier 	return i;
460223a736eSDavid du Colombier }
461223a736eSDavid du Colombier 
462223a736eSDavid du Colombier char*
463223a736eSDavid du Colombier getstr(Biobuf *b)
464223a736eSDavid du Colombier {
465223a736eSDavid du Colombier 	int i, c;
466223a736eSDavid du Colombier 
467223a736eSDavid du Colombier 	for(i=0; i<sizeof buf; i++){
468223a736eSDavid du Colombier 		/* must get bytes not runes */
469223a736eSDavid du Colombier 		cno++;
470223a736eSDavid du Colombier 		c = Bgetc(b);
471223a736eSDavid du Colombier 		if(c == Beof)
472223a736eSDavid du Colombier 			return nil;
473223a736eSDavid du Colombier 		buf[i] = c;
474223a736eSDavid du Colombier 		if(c == '\n' || c==' ' || c=='\t'){
475223a736eSDavid du Colombier 			ungetc(b);
476223a736eSDavid du Colombier 			buf[i] = '\0';
477223a736eSDavid du Colombier 			break;
478223a736eSDavid du Colombier 		}
479223a736eSDavid du Colombier 	}
480223a736eSDavid du Colombier 	return buf;
481223a736eSDavid du Colombier }
482223a736eSDavid du Colombier 
483223a736eSDavid du Colombier int
484223a736eSDavid du Colombier setnum(Biobuf *b, char *name, int min, int max)
485223a736eSDavid du Colombier {
486223a736eSDavid du Colombier 	int i;
487223a736eSDavid du Colombier 
488223a736eSDavid du Colombier 	i = getnum(b);
489223a736eSDavid du Colombier 	if(debug > 2)
490223a736eSDavid du Colombier 		fprint(2, "set %s = %d\n", name, i);
491223a736eSDavid du Colombier 	if(min<=i && i<max)
492223a736eSDavid du Colombier 		return i;
493223a736eSDavid du Colombier 	sysfatal("value of %s is %d; min %d max %d at %s:#%d", name, i, min, max, filename, cno);
494223a736eSDavid du Colombier 	return i;
495223a736eSDavid du Colombier }
496223a736eSDavid du Colombier 
497223a736eSDavid du Colombier void
498223a736eSDavid du Colombier xcmd(Biobuf *b)
499223a736eSDavid du Colombier {
500*0fb05a45SDavid du Colombier 	char *p, *fld[16], buf[1024];
501*0fb05a45SDavid du Colombier 
502223a736eSDavid du Colombier 	int i, nfld;
503223a736eSDavid du Colombier 
504223a736eSDavid du Colombier 	p = getline(b);
505223a736eSDavid du Colombier 	if(p == nil)
506223a736eSDavid du Colombier 		sysfatal("xcmd error: %r");
507223a736eSDavid du Colombier 	if(debug)
508223a736eSDavid du Colombier 		fprint(2, "x command '%s'\n", p);
509223a736eSDavid du Colombier 	nfld = tokenize(p, fld, nelem(fld));
510223a736eSDavid du Colombier 	if(nfld == 0)
511223a736eSDavid du Colombier 		return;
512223a736eSDavid du Colombier 	switch(fld[0][0]){
513223a736eSDavid du Colombier 	case 'f':
514223a736eSDavid du Colombier 		/* mount font */
515223a736eSDavid du Colombier 		if(nfld != 3)
516223a736eSDavid du Colombier 			break;
517223a736eSDavid du Colombier 		i = atoi(fld[1]);
518223a736eSDavid du Colombier 		if(i<0 || Nfont<=i)
519223a736eSDavid du Colombier 			sysfatal("font %d out of range at %s:#%d", i, filename, cno);
520223a736eSDavid du Colombier 		mountfont(i, fld[2]);
521223a736eSDavid du Colombier 		return;
522223a736eSDavid du Colombier 	case 'i':
523223a736eSDavid du Colombier 		/* init */
524223a736eSDavid du Colombier 		return;
525223a736eSDavid du Colombier 	case 'r':
526223a736eSDavid du Colombier 		if(nfld<2 || atoi(fld[1])!=res)
527223a736eSDavid du Colombier 			sysfatal("typesetter has unexpected resolution %s", fld[1]? fld[1] : "<unspecified>");
528223a736eSDavid du Colombier 		return;
529223a736eSDavid du Colombier 	case 's':
530223a736eSDavid du Colombier 		/* stop */
531223a736eSDavid du Colombier 		return;
532223a736eSDavid du Colombier 	case 't':
533223a736eSDavid du Colombier 		/* trailer */
534223a736eSDavid du Colombier 		return;
535223a736eSDavid du Colombier 	case 'T':
536223a736eSDavid du Colombier 		if(nfld!=2 || strcmp(fld[1], "utf")!=0)
537223a736eSDavid du Colombier 			sysfatal("output for unknown typesetter type %s", fld[1]);
538223a736eSDavid du Colombier 		return;
539223a736eSDavid du Colombier 	case 'X':
540223a736eSDavid du Colombier 		if(nfld<3 || strcmp(fld[1], "html")!=0)
541223a736eSDavid du Colombier 			break;
542223a736eSDavid du Colombier 		/* is it a man reference of the form cp(1)? */
543223a736eSDavid du Colombier 		/* X manref start/end cp (1) */
544223a736eSDavid du Colombier 		if(nfld==6 && strcmp(fld[2], "manref")==0){
545223a736eSDavid du Colombier 			/* was the right macro; is it the right form? */
546223a736eSDavid du Colombier 			if(strlen(fld[5])>=3 &&
547223a736eSDavid du Colombier 			   fld[5][0]=='(' && fld[5][2]==')' &&
548223a736eSDavid du Colombier 			   '0'<=fld[5][1] && fld[5][1]<='9'){
549223a736eSDavid du Colombier 				if(strcmp(fld[3], "start") == 0){
550223a736eSDavid du Colombier 					/* set anchor attribute and remember string */
551223a736eSDavid du Colombier 					attr |= (1<<Anchor);
552223a736eSDavid du Colombier 					snprint(buf, sizeof buf,
553*0fb05a45SDavid du Colombier 						"<a href=\"/magic/man2html/%c/%s\">",
554223a736eSDavid du Colombier 						fld[5][1], fld[4]);
555223a736eSDavid du Colombier 					nanchors++;
556223a736eSDavid du Colombier 					anchors = erealloc(anchors, nanchors*sizeof(char*));
557223a736eSDavid du Colombier 					anchors[nanchors-1] = estrdup(buf);
558223a736eSDavid du Colombier 				}else if(strcmp(fld[3], "end") == 0)
559223a736eSDavid du Colombier 					attr &= ~(1<<Anchor);
560223a736eSDavid du Colombier 			}
561*0fb05a45SDavid du Colombier 		}else if(strcmp(fld[2], "manPP") == 0){
562*0fb05a45SDavid du Colombier 			didP = 1;
563*0fb05a45SDavid du Colombier 			emitul(Epp);
564223a736eSDavid du Colombier 		}else if(nfld<4 || strcmp(fld[2], "manref")!=0){
565223a736eSDavid du Colombier 			if(nfld>2 && strcmp(fld[2], "<P>")==0){	/* avoid triggering extra <br> */
566223a736eSDavid du Colombier 				didP = 1;
567223a736eSDavid du Colombier 				/* clear all font attributes before paragraph */
568223a736eSDavid du Colombier 				emitul(' ' | (attr & ~(0xFFFF|((1<<Italic)|(1<<Bold)|(1<<CW)))));
569223a736eSDavid du Colombier 				emitstr("<P>");
570223a736eSDavid du Colombier 				/* next emittec char will turn font attributes back on */
571223a736eSDavid du Colombier 			}else if(nfld>2 && strcmp(fld[2], "<H4>")==0)
572223a736eSDavid du Colombier 				attr |= (1<<Heading);
573223a736eSDavid du Colombier 			else if(nfld>2 && strcmp(fld[2], "</H4>")==0)
574223a736eSDavid du Colombier 				attr &= ~(1<<Heading);
575223a736eSDavid du Colombier 			else if(debug)
576223a736eSDavid du Colombier 				fprint(2, "unknown in-line html %s... at %s:%#d\n",
577223a736eSDavid du Colombier 					fld[2], filename, cno);
578223a736eSDavid du Colombier 		}
579223a736eSDavid du Colombier 		return;
580223a736eSDavid du Colombier 	}
581223a736eSDavid du Colombier 	if(debug)
582223a736eSDavid du Colombier 		fprint(2, "unknown or badly formatted x command %s\n", fld[0]);
583223a736eSDavid du Colombier }
584223a736eSDavid du Colombier 
585223a736eSDavid du Colombier int
586223a736eSDavid du Colombier lookup(int c, Htmlchar tab[], int ntab)
587223a736eSDavid du Colombier {
588223a736eSDavid du Colombier 	int low, high, mid;
589223a736eSDavid du Colombier 
590223a736eSDavid du Colombier 	low = 0;
591223a736eSDavid du Colombier 	high = ntab - 1;
592223a736eSDavid du Colombier 	while(low <= high){
593223a736eSDavid du Colombier 		mid = (low+high)/2;
594223a736eSDavid du Colombier 		if(c < tab[mid].value)
595223a736eSDavid du Colombier 			high = mid - 1;
596223a736eSDavid du Colombier 		else if(c > tab[mid].value)
597223a736eSDavid du Colombier 			low = mid + 1;
598223a736eSDavid du Colombier 		else
599223a736eSDavid du Colombier 			return mid;
600223a736eSDavid du Colombier 	}
601223a736eSDavid du Colombier 	return -1;	/* no match */
602223a736eSDavid du Colombier }
603223a736eSDavid du Colombier 
604223a736eSDavid du Colombier void
605223a736eSDavid du Colombier emithtmlchar(int r)
606223a736eSDavid du Colombier {
607223a736eSDavid du Colombier 	static char buf[10];
608223a736eSDavid du Colombier 	int i;
609223a736eSDavid du Colombier 
610223a736eSDavid du Colombier 	i = lookup(r, htmlchars, nelem(htmlchars));
611223a736eSDavid du Colombier 	if(i >= 0)
612223a736eSDavid du Colombier 		emitstr(htmlchars[i].name);
613223a736eSDavid du Colombier 	else
614223a736eSDavid du Colombier 		emit(r);
615223a736eSDavid du Colombier }
616223a736eSDavid du Colombier 
617223a736eSDavid du Colombier char*
618223a736eSDavid du Colombier troffchar(char *s)
619223a736eSDavid du Colombier {
620223a736eSDavid du Colombier 	int i;
621223a736eSDavid du Colombier 
622223a736eSDavid du Colombier 	for(i=0; troffchars[i].name!=nil; i++)
623223a736eSDavid du Colombier 		if(strcmp(s, troffchars[i].name) == 0)
624223a736eSDavid du Colombier 			return troffchars[i].value;
625223a736eSDavid du Colombier 	return "??";
626223a736eSDavid du Colombier }
627223a736eSDavid du Colombier 
628223a736eSDavid du Colombier void
629223a736eSDavid du Colombier indent(void)
630223a736eSDavid du Colombier {
631223a736eSDavid du Colombier 	int nind;
632223a736eSDavid du Colombier 
633223a736eSDavid du Colombier 	didP = 0;
634223a736eSDavid du Colombier 	if(atnewline){
635223a736eSDavid du Colombier 		if(hp != prevlineH){
636223a736eSDavid du Colombier 			prevlineH = hp;
637223a736eSDavid du Colombier 			/* these most peculiar numbers appear in the troff -man output */
638223a736eSDavid du Colombier 			nind = ((prevlineH-1*res)+323)/324;
639223a736eSDavid du Colombier 			attr &= ~((1<<Indent1)|(1<<Indent2)|(1<<Indent3));
640223a736eSDavid du Colombier 			if(nind >= 1)
641223a736eSDavid du Colombier 				attr |= (1<<Indent1);
642223a736eSDavid du Colombier 			if(nind >= 2)
643223a736eSDavid du Colombier 				attr |= (1<<Indent2);
644*0fb05a45SDavid du Colombier 			if(nind >= 3)
645223a736eSDavid du Colombier 				attr |= (1<<Indent3);
646223a736eSDavid du Colombier 		}
647223a736eSDavid du Colombier 		atnewline = 0;
648223a736eSDavid du Colombier 	}
649223a736eSDavid du Colombier }
650223a736eSDavid du Colombier 
651223a736eSDavid du Colombier void
652223a736eSDavid du Colombier process(Biobuf *b, char *name)
653223a736eSDavid du Colombier {
654223a736eSDavid du Colombier 	int c, r, v, i;
655223a736eSDavid du Colombier 	char *p;
656223a736eSDavid du Colombier 
657223a736eSDavid du Colombier 	cno = 0;
658223a736eSDavid du Colombier 	prevlineH = res;
659223a736eSDavid du Colombier 	filename = name;
660223a736eSDavid du Colombier 	for(;;){
661223a736eSDavid du Colombier 		c = getc(b);
662223a736eSDavid du Colombier 		switch(c){
663223a736eSDavid du Colombier 		case Beof:
664223a736eSDavid du Colombier 			/* go to ground state */
665223a736eSDavid du Colombier 			attr = 0;
666223a736eSDavid du Colombier 			emit('\n');
667223a736eSDavid du Colombier 			return;
668223a736eSDavid du Colombier 		case '\n':
669223a736eSDavid du Colombier 			break;
670223a736eSDavid du Colombier 		case '0': case '1': case '2': case '3': case '4':
671223a736eSDavid du Colombier 		case '5': case '6': case '7': case '8': case '9':
672223a736eSDavid du Colombier 			v = c-'0';
673223a736eSDavid du Colombier 			c = getc(b);
674223a736eSDavid du Colombier 			if(c<'0' || '9'<c)
675223a736eSDavid du Colombier 				sysfatal("illegal character motion at %s:#%d", filename, cno);
676223a736eSDavid du Colombier 			v = v*10 + (c-'0');
677223a736eSDavid du Colombier 			hp += v;
678223a736eSDavid du Colombier 			/* fall through to character case */
679223a736eSDavid du Colombier 		case 'c':
680223a736eSDavid du Colombier 			indent();
681223a736eSDavid du Colombier 			r = getc(b);
682223a736eSDavid du Colombier 			emithtmlchar(r);
683223a736eSDavid du Colombier 			break;
684b7b24591SDavid du Colombier 		case 'D':
685b7b24591SDavid du Colombier 			/* draw line; ignore */
686b7b24591SDavid du Colombier 			do
687b7b24591SDavid du Colombier 				c = getc(b);
688b7b24591SDavid du Colombier 			while(c!='\n' && c!= Beof);
689b7b24591SDavid du Colombier 			break;
690223a736eSDavid du Colombier 		case 'f':
691223a736eSDavid du Colombier 			v = setnum(b, "font", 0, Nfont);
692223a736eSDavid du Colombier 			switchfont(v);
693223a736eSDavid du Colombier 			break;
694223a736eSDavid du Colombier 		case 'h':
695223a736eSDavid du Colombier 			v = setnum(b, "hpos", -20000, 20000);
696223a736eSDavid du Colombier 			/* generate spaces if motion is large and within a line */
697223a736eSDavid du Colombier 			if(!atnewline && v>2*72)
698223a736eSDavid du Colombier 				for(i=0; i<v; i+=72)
699223a736eSDavid du Colombier 					emitstr("&nbsp;");
700223a736eSDavid du Colombier 			hp += v;
701223a736eSDavid du Colombier 			break;
702223a736eSDavid du Colombier 		case 'n':
703223a736eSDavid du Colombier 			setnum(b, "n1", -10000, 10000);
704223a736eSDavid du Colombier 			//Bprint(&bout, " N1=%d", v);
705223a736eSDavid du Colombier 			getc(b);	/* space separates */
706223a736eSDavid du Colombier 			setnum(b, "n2", -10000, 10000);
707223a736eSDavid du Colombier 			atnewline = 1;
708223a736eSDavid du Colombier 			if(!didP && hp < (Wid-1)*res)	/* if line is less than 19" long, probably need a line break */
709223a736eSDavid du Colombier 				emitstr("<br>");
710223a736eSDavid du Colombier 			emit('\n');
711223a736eSDavid du Colombier 			break;
712223a736eSDavid du Colombier 		case 'p':
713223a736eSDavid du Colombier 			page = setnum(b, "ps", -10000, 10000);
714223a736eSDavid du Colombier 			break;
715223a736eSDavid du Colombier 		case 's':
716223a736eSDavid du Colombier 			ps = setnum(b, "ps", 1, 1000);
717223a736eSDavid du Colombier 			break;
718223a736eSDavid du Colombier 		case 'v':
719223a736eSDavid du Colombier 			vp += setnum(b, "vpos", -10000, 10000);
720223a736eSDavid du Colombier 			/* BUG: ignore motion */
721223a736eSDavid du Colombier 			break;
722223a736eSDavid du Colombier 		case 'x':
723223a736eSDavid du Colombier 			xcmd(b);
724223a736eSDavid du Colombier 			break;
725223a736eSDavid du Colombier 		case 'w':
726223a736eSDavid du Colombier 			emit(' ');
727223a736eSDavid du Colombier 			break;
728223a736eSDavid du Colombier 		case 'C':
729223a736eSDavid du Colombier 			indent();
730223a736eSDavid du Colombier 			p = getstr(b);
731223a736eSDavid du Colombier 			emitstr(troffchar(p));
732223a736eSDavid du Colombier 			break;
733223a736eSDavid du Colombier 		case 'H':
734223a736eSDavid du Colombier 			hp = setnum(b, "hpos", 0, 20000);
735223a736eSDavid du Colombier 			//Bprint(&bout, " H=%d ", hp);
736223a736eSDavid du Colombier 			break;
737223a736eSDavid du Colombier 		case 'V':
738223a736eSDavid du Colombier 			vp = setnum(b, "vpos", 0, 10000);
739223a736eSDavid du Colombier 			break;
740223a736eSDavid du Colombier 		default:
741223a736eSDavid du Colombier 			fprint(2, "dhtml: unknown directive %c(0x%.2ux) at %s:#%d\n", c, c, filename, cno);
742223a736eSDavid du Colombier 			return;
743223a736eSDavid du Colombier 		}
744223a736eSDavid du Colombier 	}
745223a736eSDavid du Colombier }
746223a736eSDavid du Colombier 
747223a736eSDavid du Colombier HTMLfont*
748223a736eSDavid du Colombier htmlfont(char *name)
749223a736eSDavid du Colombier {
750223a736eSDavid du Colombier 	int i;
751223a736eSDavid du Colombier 
752223a736eSDavid du Colombier 	for(i=0; htmlfonts[i].name!=nil; i++)
753223a736eSDavid du Colombier 		if(strcmp(name, htmlfonts[i].name) == 0)
754223a736eSDavid du Colombier 			return &htmlfonts[i];
755223a736eSDavid du Colombier 	return &htmlfonts[0];
756223a736eSDavid du Colombier }
757223a736eSDavid du Colombier 
758223a736eSDavid du Colombier void
759223a736eSDavid du Colombier mountfont(int pos, char *name)
760223a736eSDavid du Colombier {
761223a736eSDavid du Colombier 	if(debug)
762223a736eSDavid du Colombier 		fprint(2, "mount font %s on %d\n", name, pos);
763223a736eSDavid du Colombier 	if(font[pos] != nil){
764223a736eSDavid du Colombier 		free(font[pos]->name);
765223a736eSDavid du Colombier 		free(font[pos]);
766223a736eSDavid du Colombier 	}
767223a736eSDavid du Colombier 	font[pos] = emalloc(sizeof(Font));
768223a736eSDavid du Colombier 	font[pos]->name = estrdup(name);
769223a736eSDavid du Colombier 	font[pos]->htmlfont = htmlfont(name);
770223a736eSDavid du Colombier }
771223a736eSDavid du Colombier 
772223a736eSDavid du Colombier void
773223a736eSDavid du Colombier switchfont(int pos)
774223a736eSDavid du Colombier {
775223a736eSDavid du Colombier 	HTMLfont *hf;
776223a736eSDavid du Colombier 
777223a736eSDavid du Colombier 	if(debug)
778223a736eSDavid du Colombier 		fprint(2, "font change from %d (%s) to %d (%s)\n", ft, font[ft]->name, pos, font[pos]->name);
779223a736eSDavid du Colombier 	if(pos == ft)
780223a736eSDavid du Colombier 		return;
781223a736eSDavid du Colombier 	hf = font[ft]->htmlfont;
782223a736eSDavid du Colombier 	if(hf->bit != 0)
783223a736eSDavid du Colombier 		attr &= ~(1<<hf->bit);
784223a736eSDavid du Colombier 	ft = pos;
785223a736eSDavid du Colombier 	hf = font[ft]->htmlfont;
786223a736eSDavid du Colombier 	if(hf->bit != 0)
787223a736eSDavid du Colombier 		attr |= (1<<hf->bit);
788223a736eSDavid du Colombier }
789