xref: /plan9/sys/src/cmd/troff2html/troff2html.c (revision b09c09c56326c85e62a02c7f2061cadadc3e620b)
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 
1073e742d7SDavid du Colombier typedef uintptr 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 
1673e742d7SDavid du Colombier /*
1773e742d7SDavid du Colombier  * a Char is >= 32 bits. low 16 bits are the rune. higher are attributes.
1873e742d7SDavid du Colombier  * must be able to hold a pointer.
1973e742d7SDavid du Colombier  */
20223a736eSDavid du Colombier enum
21223a736eSDavid du Colombier {
22223a736eSDavid du Colombier 	Italic	=	16,
230fb05a45SDavid du Colombier 	Bold,
240fb05a45SDavid du Colombier 	CW,
250fb05a45SDavid du Colombier 	Indent1,
260fb05a45SDavid du Colombier 	Indent2,
270fb05a45SDavid du Colombier 	Indent3,
28223a736eSDavid du Colombier 	Heading =	25,
29223a736eSDavid du Colombier 	Anchor =	26,	/* must be last */
30223a736eSDavid du Colombier };
31223a736eSDavid du Colombier 
320fb05a45SDavid du Colombier enum	/* magic emissions */
330fb05a45SDavid du Colombier {
340fb05a45SDavid du Colombier 	Estring = 0,
350fb05a45SDavid du Colombier 	Epp = 1<<16,
360fb05a45SDavid du Colombier };
370fb05a45SDavid du Colombier 
380fb05a45SDavid du Colombier int attrorder[] = { Indent1, Indent2, Indent3, Heading, Anchor, Italic, Bold, CW };
390fb05a45SDavid du Colombier 
400fb05a45SDavid du Colombier int nest[10];
410fb05a45SDavid du Colombier int nnest;
420fb05a45SDavid du Colombier 
43223a736eSDavid du Colombier struct Troffchar
44223a736eSDavid du Colombier {
45223a736eSDavid du Colombier 	char *name;
46223a736eSDavid du Colombier 	char *value;
47223a736eSDavid du Colombier };
48223a736eSDavid du Colombier 
49223a736eSDavid du Colombier struct Htmlchar
50223a736eSDavid du Colombier {
510fb05a45SDavid du Colombier 	char *utf;
52223a736eSDavid du Colombier 	char *name;
530fb05a45SDavid du Colombier 	int value;
54223a736eSDavid du Colombier };
55223a736eSDavid du Colombier 
56223a736eSDavid du Colombier #include "chars.h"
57223a736eSDavid du Colombier 
58223a736eSDavid du Colombier struct Font{
59223a736eSDavid du Colombier 	char		*name;
60223a736eSDavid du Colombier 	HTMLfont	*htmlfont;
61223a736eSDavid du Colombier };
62223a736eSDavid du Colombier 
63223a736eSDavid du Colombier struct HTMLfont{
64223a736eSDavid du Colombier 	char	*name;
65223a736eSDavid du Colombier 	char	*htmlname;
66223a736eSDavid du Colombier 	int	bit;
67223a736eSDavid du Colombier };
68223a736eSDavid du Colombier 
69223a736eSDavid du Colombier /* R must be first; it's the default representation for fonts we don't recognize */
70223a736eSDavid du Colombier HTMLfont htmlfonts[] =
71223a736eSDavid du Colombier {
72223a736eSDavid du Colombier 	"R",		nil,	0,
73223a736eSDavid du Colombier 	"LucidaSans",	nil,	0,
740fb05a45SDavid du Colombier 	"I",		"i",	Italic,
750fb05a45SDavid du Colombier 	"LucidaSansI",	"i",	Italic,
760fb05a45SDavid du Colombier 	"CW",		"tt",	CW,
770fb05a45SDavid du Colombier 	"LucidaCW",	"tt",	CW,
78223a736eSDavid du Colombier 	nil,	nil,
79223a736eSDavid du Colombier };
80223a736eSDavid du Colombier 
810fb05a45SDavid du Colombier #define TABLE "<table border=0 cellpadding=0 cellspacing=0>"
820fb05a45SDavid du Colombier 
83223a736eSDavid du Colombier char*
8473e742d7SDavid du Colombier onattr[8*sizeof(int)] =
850fb05a45SDavid du Colombier {
860fb05a45SDavid du Colombier 	0, 0, 0, 0, 0, 0, 0, 0,
870fb05a45SDavid du Colombier 	0, 0, 0, 0, 0, 0, 0, 0,
880fb05a45SDavid du Colombier 	"<i>",			/* italic */
890fb05a45SDavid du Colombier 	"<b>",			/* bold */
900fb05a45SDavid du Colombier 	"<tt><font size=+1>",	/* cw */
910fb05a45SDavid du Colombier 	"<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",		/* indent1 */
920fb05a45SDavid du Colombier 	"<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",		/* indent2 */
930fb05a45SDavid du Colombier 	"<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",		/* indent3 */
940fb05a45SDavid du Colombier 	0,
950fb05a45SDavid du Colombier 	0,
960fb05a45SDavid du Colombier 	0,
970fb05a45SDavid du Colombier 	"<p><font size=+1><b>",	/* heading 25 */
980fb05a45SDavid du Colombier 	"<unused>",		/* anchor 26 */
99223a736eSDavid du Colombier };
100223a736eSDavid du Colombier 
101223a736eSDavid du Colombier char*
10273e742d7SDavid du Colombier offattr[8*sizeof(int)] =
1030fb05a45SDavid du Colombier {
1040fb05a45SDavid du Colombier 	0, 0, 0, 0, 0, 0, 0, 0,
1050fb05a45SDavid du Colombier 	0, 0, 0, 0, 0, 0, 0, 0,
1060fb05a45SDavid du Colombier 	"</i>",			/* italic */
1070fb05a45SDavid du Colombier 	"</b>",			/* bold */
1080fb05a45SDavid du Colombier 	"</font></tt>",		/* cw */
1090fb05a45SDavid du Colombier 	"<-/table>",		/* indent1 */
1100fb05a45SDavid du Colombier 	"<-/table>",		/* indent2 */
1110fb05a45SDavid du Colombier 	"<-/table>",		/* indent3 */
1120fb05a45SDavid du Colombier 	0,
1130fb05a45SDavid du Colombier 	0,
1140fb05a45SDavid du Colombier 	0,
1150fb05a45SDavid du Colombier 	"</b></font>",		/* heading 25 */
1160fb05a45SDavid du Colombier 	"</a>",			/* anchor 26 */
117223a736eSDavid du Colombier };
118223a736eSDavid du Colombier 
119223a736eSDavid du Colombier Font	*font[Nfont];
120223a736eSDavid du Colombier 
121223a736eSDavid du Colombier Biobuf	bout;
122223a736eSDavid du Colombier int	debug = 0;
123223a736eSDavid du Colombier 
124223a736eSDavid du Colombier /* troff state */
125223a736eSDavid du Colombier int	page = 1;
126223a736eSDavid du Colombier int	ft = 1;
127223a736eSDavid du Colombier int	vp = 0;
128223a736eSDavid du Colombier int	hp = 0;
129223a736eSDavid du Colombier int	ps = 1;
130223a736eSDavid du Colombier int	res = 720;
131223a736eSDavid du Colombier 
132223a736eSDavid du Colombier int	didP = 0;
133223a736eSDavid du Colombier int	atnewline = 1;
134223a736eSDavid du Colombier int	prevlineH = 0;
13573e742d7SDavid du Colombier Char	attr = 0;	/* or'ed into each Char */
136223a736eSDavid du Colombier 
137223a736eSDavid du Colombier Char	*chars;
138223a736eSDavid du Colombier int	nchars;
139223a736eSDavid du Colombier int	nalloc;
140223a736eSDavid du Colombier char**	anchors;	/* allocated in order */
141223a736eSDavid du Colombier int	nanchors;
142223a736eSDavid du Colombier 
143223a736eSDavid du Colombier char	*filename;
144223a736eSDavid du Colombier int	cno;
145223a736eSDavid du Colombier char	buf[8192];
146223a736eSDavid du Colombier char	*title = "Plan 9 man page";
147223a736eSDavid du Colombier 
148223a736eSDavid du Colombier void	process(Biobuf*, char*);
149223a736eSDavid du Colombier void	mountfont(int, char*);
150223a736eSDavid du Colombier void	switchfont(int);
151223a736eSDavid du Colombier void	header(char*);
152223a736eSDavid du Colombier void	flush(void);
153223a736eSDavid du Colombier void	trailer(void);
154223a736eSDavid du Colombier 
155223a736eSDavid du Colombier void*
emalloc(ulong n)156223a736eSDavid du Colombier emalloc(ulong n)
157223a736eSDavid du Colombier {
158223a736eSDavid du Colombier 	void *p;
159223a736eSDavid du Colombier 
160223a736eSDavid du Colombier 	p = malloc(n);
161223a736eSDavid du Colombier 	if(p == nil)
162223a736eSDavid du Colombier 		sysfatal("malloc failed: %r");
163223a736eSDavid du Colombier 	return p;
164223a736eSDavid du Colombier }
165223a736eSDavid du Colombier 
166223a736eSDavid du Colombier void*
erealloc(void * p,ulong n)167223a736eSDavid du Colombier erealloc(void *p, ulong n)
168223a736eSDavid du Colombier {
169223a736eSDavid du Colombier 
170223a736eSDavid du Colombier 	p = realloc(p, n);
171223a736eSDavid du Colombier 	if(p == nil)
172223a736eSDavid du Colombier 		sysfatal("realloc failed: %r");
173223a736eSDavid du Colombier 	return p;
174223a736eSDavid du Colombier }
175223a736eSDavid du Colombier 
176223a736eSDavid du Colombier char*
estrdup(char * s)177223a736eSDavid du Colombier estrdup(char *s)
178223a736eSDavid du Colombier {
179223a736eSDavid du Colombier 	char *t;
180223a736eSDavid du Colombier 
181223a736eSDavid du Colombier 	t = strdup(s);
182223a736eSDavid du Colombier 	if(t == nil)
183223a736eSDavid du Colombier 		sysfatal("strdup failed: %r");
184223a736eSDavid du Colombier 	return t;
185223a736eSDavid du Colombier }
186223a736eSDavid du Colombier 
187223a736eSDavid du Colombier void
usage(void)188223a736eSDavid du Colombier usage(void)
189223a736eSDavid du Colombier {
1900fb05a45SDavid du Colombier 	fprint(2, "usage: troff2html [-d] [-t title] [file ...]\n");
191223a736eSDavid du Colombier 	exits("usage");
192223a736eSDavid du Colombier }
193223a736eSDavid du Colombier 
1940fb05a45SDavid du Colombier int
hccmp(const void * va,const void * vb)1950fb05a45SDavid du Colombier hccmp(const void *va, const void *vb)
1960fb05a45SDavid du Colombier {
1970fb05a45SDavid du Colombier 	Htmlchar *a, *b;
1980fb05a45SDavid du Colombier 
1990fb05a45SDavid du Colombier 	a = (Htmlchar*)va;
2000fb05a45SDavid du Colombier 	b = (Htmlchar*)vb;
2010fb05a45SDavid du Colombier 	return a->value - b->value;
2020fb05a45SDavid du Colombier }
2030fb05a45SDavid du Colombier 
204223a736eSDavid du Colombier void
main(int argc,char * argv[])205223a736eSDavid du Colombier main(int argc, char *argv[])
206223a736eSDavid du Colombier {
207223a736eSDavid du Colombier 	int i;
208223a736eSDavid du Colombier 	Biobuf in, *inp;
2090fb05a45SDavid du Colombier 	Rune r;
2100fb05a45SDavid du Colombier 
2110fb05a45SDavid du Colombier 	for(i=0; i<nelem(htmlchars); i++){
2120fb05a45SDavid du Colombier 		chartorune(&r, htmlchars[i].utf);
2130fb05a45SDavid du Colombier 		htmlchars[i].value = r;
2140fb05a45SDavid du Colombier 	}
2150fb05a45SDavid du Colombier 	qsort(htmlchars, nelem(htmlchars), sizeof(htmlchars[0]), hccmp);
216223a736eSDavid du Colombier 
217223a736eSDavid du Colombier 	ARGBEGIN{
218223a736eSDavid du Colombier 	case 't':
219223a736eSDavid du Colombier 		title = ARGF();
220223a736eSDavid du Colombier 		if(title == nil)
221223a736eSDavid du Colombier 			usage();
222223a736eSDavid du Colombier 		break;
223223a736eSDavid du Colombier 	case 'd':
224223a736eSDavid du Colombier 		debug++;
225223a736eSDavid du Colombier 		break;
226223a736eSDavid du Colombier 	default:
227223a736eSDavid du Colombier 		usage();
228223a736eSDavid du Colombier 	}ARGEND
2290fb05a45SDavid du Colombier 
230223a736eSDavid du Colombier 	Binit(&bout, 1, OWRITE);
231223a736eSDavid du Colombier 	if(argc == 0){
232223a736eSDavid du Colombier 		header(title);
233223a736eSDavid du Colombier 		Binit(&in, 0, OREAD);
234223a736eSDavid du Colombier 		process(&in, "<stdin>");
235223a736eSDavid du Colombier 	}else{
236223a736eSDavid du Colombier 		header(title);
237223a736eSDavid du Colombier 		for(i=0; i<argc; i++){
238223a736eSDavid du Colombier 			inp = Bopen(argv[i], OREAD);
239223a736eSDavid du Colombier 			if(inp == nil)
240223a736eSDavid du Colombier 				sysfatal("can't open %s: %r", argv[i]);
241223a736eSDavid du Colombier 			process(inp, argv[i]);
242223a736eSDavid du Colombier 			Bterm(inp);
243223a736eSDavid du Colombier 		}
244223a736eSDavid du Colombier 	}
245223a736eSDavid du Colombier 	flush();
246223a736eSDavid du Colombier 	trailer();
247223a736eSDavid du Colombier 	exits(nil);
248223a736eSDavid du Colombier }
249223a736eSDavid du Colombier 
250223a736eSDavid du Colombier void
emitchar(Char c)25173e742d7SDavid du Colombier emitchar(Char c)
252223a736eSDavid du Colombier {
253223a736eSDavid du Colombier 	if(nalloc == nchars){
254223a736eSDavid du Colombier 		nalloc += 10000;
255223a736eSDavid du Colombier 		chars = realloc(chars, nalloc*sizeof(chars[0]));
256223a736eSDavid du Colombier 		if(chars == nil)
257223a736eSDavid du Colombier 			sysfatal("malloc failed: %r");
258223a736eSDavid du Colombier 	}
25973e742d7SDavid du Colombier 	chars[nchars++] = c;
260223a736eSDavid du Colombier }
261223a736eSDavid du Colombier 
262223a736eSDavid du Colombier void
emit(Rune r)263223a736eSDavid du Colombier emit(Rune r)
264223a736eSDavid du Colombier {
26573e742d7SDavid du Colombier 	emitchar(r | attr);
2660fb05a45SDavid du Colombier 	/*
2670fb05a45SDavid du Colombier 	 * Close man page references early, so that
2680fb05a45SDavid du Colombier 	 * .IR proof (1),
2690fb05a45SDavid du Colombier 	 * doesn't make the comma part of the link.
2700fb05a45SDavid du Colombier 	 */
2710fb05a45SDavid du Colombier 	if(r == ')')
2720fb05a45SDavid du Colombier 		attr &= ~(1<<Anchor);
273223a736eSDavid du Colombier }
274223a736eSDavid du Colombier 
275223a736eSDavid du Colombier void
emitstr(char * s)276223a736eSDavid du Colombier emitstr(char *s)
277223a736eSDavid du Colombier {
27873e742d7SDavid du Colombier 	emitchar(Estring);
27973e742d7SDavid du Colombier 	emitchar((Char)s);
280223a736eSDavid du Colombier }
281223a736eSDavid du Colombier 
2820fb05a45SDavid du Colombier int indentlevel;
2830fb05a45SDavid du Colombier int linelen;
2840fb05a45SDavid du Colombier 
2850fb05a45SDavid du Colombier void
iputrune(Biobuf * b,Rune r)2860fb05a45SDavid du Colombier iputrune(Biobuf *b, Rune r)
2870fb05a45SDavid du Colombier {
2880fb05a45SDavid du Colombier 	int i;
2890fb05a45SDavid du Colombier 
2900fb05a45SDavid du Colombier 	if(linelen++ > 60 && r == ' ')
2910fb05a45SDavid du Colombier 		r = '\n';
2920fb05a45SDavid du Colombier 	Bputrune(b, r);
2930fb05a45SDavid du Colombier 	if(r == '\n'){
2940fb05a45SDavid du Colombier 		for(i=0; i<indentlevel; i++)
2950fb05a45SDavid du Colombier 			Bprint(b, "    ");
2960fb05a45SDavid du Colombier 		linelen = 0;
2970fb05a45SDavid du Colombier 	}
2980fb05a45SDavid du Colombier }
2990fb05a45SDavid du Colombier 
3000fb05a45SDavid du Colombier void
iputs(Biobuf * b,char * s)3010fb05a45SDavid du Colombier iputs(Biobuf *b, char *s)
3020fb05a45SDavid du Colombier {
3030fb05a45SDavid du Colombier 	if(s[0]=='<' && s[1]=='+'){
3040fb05a45SDavid du Colombier 		iputrune(b, '\n');
3050fb05a45SDavid du Colombier 		Bprint(b, "<%s", s+2);
3060fb05a45SDavid du Colombier 		indentlevel++;
3070fb05a45SDavid du Colombier 		iputrune(b, '\n');
3080fb05a45SDavid du Colombier 	}else if(s[0]=='<' && s[1]=='-'){
3090fb05a45SDavid du Colombier 		indentlevel--;
3100fb05a45SDavid du Colombier 		iputrune(b, '\n');
3110fb05a45SDavid du Colombier 		Bprint(b, "<%s", s+2);
3120fb05a45SDavid du Colombier 		iputrune(b, '\n');
3130fb05a45SDavid du Colombier 	}else
3140fb05a45SDavid du Colombier 		Bprint(b, "%s", s);
3150fb05a45SDavid du Colombier }
3160fb05a45SDavid du Colombier 
3170fb05a45SDavid du Colombier void
setattr(Char a)31873e742d7SDavid du Colombier setattr(Char a)
3190fb05a45SDavid du Colombier {
32073e742d7SDavid du Colombier 	Char on, off;
32173e742d7SDavid du Colombier 	int i, j;
3220fb05a45SDavid du Colombier 
3230fb05a45SDavid du Colombier 	on = a & ~attr;
3240fb05a45SDavid du Colombier 	off = attr & ~a;
3250fb05a45SDavid du Colombier 
3260fb05a45SDavid du Colombier 	/* walk up the nest stack until we reach something we need to turn off. */
3270fb05a45SDavid du Colombier 	for(i=0; i<nnest; i++)
3280fb05a45SDavid du Colombier 		if(off&(1<<nest[i]))
3290fb05a45SDavid du Colombier 			break;
3300fb05a45SDavid du Colombier 
3310fb05a45SDavid du Colombier 	/* turn off everything above that */
3320fb05a45SDavid du Colombier 	for(j=nnest-1; j>=i; j--)
3330fb05a45SDavid du Colombier 		iputs(&bout, offattr[nest[j]]);
3340fb05a45SDavid du Colombier 
3350fb05a45SDavid du Colombier 	/* turn on everything we just turned off but didn't want to */
3360fb05a45SDavid du Colombier 	for(j=i; j<nnest; j++)
3370fb05a45SDavid du Colombier 		if(a&(1<<nest[j]))
3380fb05a45SDavid du Colombier 			iputs(&bout, onattr[nest[j]]);
3390fb05a45SDavid du Colombier 		else
3400fb05a45SDavid du Colombier 			nest[j] = 0;
3410fb05a45SDavid du Colombier 
3420fb05a45SDavid du Colombier 	/* shift the zeros (turned off things) up */
3430fb05a45SDavid du Colombier 	for(i=j=0; i<nnest; i++)
3440fb05a45SDavid du Colombier 		if(nest[i] != 0)
3450fb05a45SDavid du Colombier 			nest[j++] = nest[i];
3460fb05a45SDavid du Colombier 	nnest = j;
3470fb05a45SDavid du Colombier 
3480fb05a45SDavid du Colombier 	/* now turn on the new attributes */
3490fb05a45SDavid du Colombier 	for(i=0; i<nelem(attrorder); i++){
3500fb05a45SDavid du Colombier 		j = attrorder[i];
3510fb05a45SDavid du Colombier 		if(on&(1<<j)){
3520fb05a45SDavid du Colombier 			if(j == Anchor)
3530fb05a45SDavid du Colombier 				onattr[j] = anchors[nanchors++];
3540fb05a45SDavid du Colombier 			iputs(&bout, onattr[j]);
35573e742d7SDavid du Colombier 			if(nnest >= nelem(nest))
35673e742d7SDavid du Colombier 				sysfatal("nesting too deep");
3570fb05a45SDavid du Colombier 			nest[nnest++] = j;
3580fb05a45SDavid du Colombier 		}
3590fb05a45SDavid du Colombier 	}
3600fb05a45SDavid du Colombier 	attr = a;
3610fb05a45SDavid du Colombier }
3620fb05a45SDavid du Colombier 
363223a736eSDavid du Colombier void
flush(void)364223a736eSDavid du Colombier flush(void)
365223a736eSDavid du Colombier {
3660fb05a45SDavid du Colombier 	int i;
36773e742d7SDavid du Colombier 	Char c, a;
368223a736eSDavid du Colombier 
3690fb05a45SDavid du Colombier 	nanchors = 0;
370223a736eSDavid du Colombier 	for(i=0; i<nchars; i++){
371223a736eSDavid du Colombier 		c = chars[i];
3720fb05a45SDavid du Colombier 		if(c == Estring){
373223a736eSDavid du Colombier 			/* next word is string to print */
3740fb05a45SDavid du Colombier 			iputs(&bout, (char*)chars[++i]);
375223a736eSDavid du Colombier 			continue;
376223a736eSDavid du Colombier 		}
3770fb05a45SDavid du Colombier 		if(c == Epp){
3780fb05a45SDavid du Colombier 			iputrune(&bout, '\n');
3790fb05a45SDavid du Colombier 			iputs(&bout, TABLE "<tr height=5><td></table>");
3800fb05a45SDavid du Colombier 			iputrune(&bout, '\n');
3810fb05a45SDavid du Colombier 			continue;
382223a736eSDavid du Colombier 		}
3830fb05a45SDavid du Colombier 		a = c & ~0xFFFF;
3840fb05a45SDavid du Colombier 		c &= 0xFFFF;
3850fb05a45SDavid du Colombier 		/*
3860fb05a45SDavid du Colombier 		 * If we're going to something off after a space,
3870fb05a45SDavid du Colombier 		 * let's just turn it off before.
3880fb05a45SDavid du Colombier 		 */
3890fb05a45SDavid du Colombier 		if(c == ' ' && i<nchars-1 && (chars[i+1]&0xFFFF) >= 32)
3900fb05a45SDavid du Colombier 			a ^= a & ~chars[i+1];
3910fb05a45SDavid du Colombier 		setattr(a);
3920fb05a45SDavid du Colombier 		iputrune(&bout, c & 0xFFFF);
393223a736eSDavid du Colombier 	}
394223a736eSDavid du Colombier }
395223a736eSDavid du Colombier 
396223a736eSDavid du Colombier void
header(char * s)397223a736eSDavid du Colombier header(char *s)
398223a736eSDavid du Colombier {
3990fb05a45SDavid du Colombier 	Bprint(&bout, "<head>\n");
4000fb05a45SDavid du Colombier 	Bprint(&bout, "<title>%s</title>\n", s);
4010fb05a45SDavid du Colombier 	Bprint(&bout, "<meta content=\"text/html; charset=utf-8\" http-equiv=Content-Type>\n");
4020fb05a45SDavid du Colombier 	Bprint(&bout, "</head>\n");
4030fb05a45SDavid du Colombier 	Bprint(&bout, "<body bgcolor=#ffffff>\n");
404223a736eSDavid du Colombier }
405223a736eSDavid du Colombier 
406223a736eSDavid du Colombier void
trailer(void)407223a736eSDavid du Colombier trailer(void)
408223a736eSDavid du Colombier {
4099a747e4fSDavid du Colombier 
4100fb05a45SDavid du Colombier #ifdef LUCENT
411daafdc08SDavid du Colombier 	Tm *t;
4129a747e4fSDavid du Colombier 	t = localtime(time(nil));
4130fb05a45SDavid du Colombier 	Bprint(&bout, TABLE "<tr height=20><td></table>\n");
4140fb05a45SDavid du Colombier 	Bprint(&bout, "<font size=-1><a href=\"http://www.lucent.com/copyright.html\">\n");
415*b09c09c5SDavid du Colombier 	Bprint(&bout, "Copyright</A> &#169; %d Alcatel-Lucent.  All rights reserved.</font>\n", t->year+1900);
4160fb05a45SDavid du Colombier #endif
4170fb05a45SDavid du Colombier 	Bprint(&bout, "</body></html>\n");
418223a736eSDavid du Colombier }
419223a736eSDavid du Colombier 
420223a736eSDavid du Colombier int
getc(Biobuf * b)421223a736eSDavid du Colombier getc(Biobuf *b)
422223a736eSDavid du Colombier {
423223a736eSDavid du Colombier 	cno++;
424223a736eSDavid du Colombier 	return Bgetrune(b);
425223a736eSDavid du Colombier }
426223a736eSDavid du Colombier 
427223a736eSDavid du Colombier void
ungetc(Biobuf * b)428223a736eSDavid du Colombier ungetc(Biobuf *b)
429223a736eSDavid du Colombier {
430223a736eSDavid du Colombier 	cno--;
431223a736eSDavid du Colombier 	Bungetrune(b);
432223a736eSDavid du Colombier }
433223a736eSDavid du Colombier 
434223a736eSDavid du Colombier char*
getline(Biobuf * b)435223a736eSDavid du Colombier getline(Biobuf *b)
436223a736eSDavid du Colombier {
437223a736eSDavid du Colombier 	int i, c;
438223a736eSDavid du Colombier 
439223a736eSDavid du Colombier 	for(i=0; i<sizeof buf; i++){
440223a736eSDavid du Colombier 		c = getc(b);
441223a736eSDavid du Colombier 		if(c == Beof)
442223a736eSDavid du Colombier 			return nil;
443223a736eSDavid du Colombier 		buf[i] = c;
444223a736eSDavid du Colombier 		if(c == '\n'){
445223a736eSDavid du Colombier 			buf[i] = '\0';
446223a736eSDavid du Colombier 			break;
447223a736eSDavid du Colombier 		}
448223a736eSDavid du Colombier 	}
449223a736eSDavid du Colombier 	return buf;
450223a736eSDavid du Colombier }
451223a736eSDavid du Colombier 
452223a736eSDavid du Colombier int
getnum(Biobuf * b)453223a736eSDavid du Colombier getnum(Biobuf *b)
454223a736eSDavid du Colombier {
455223a736eSDavid du Colombier 	int i, c;
456223a736eSDavid du Colombier 
457223a736eSDavid du Colombier 	i = 0;
458223a736eSDavid du Colombier 	for(;;){
459223a736eSDavid du Colombier 		c = getc(b);
460223a736eSDavid du Colombier 		if(c<'0' || '9'<c){
461223a736eSDavid du Colombier 			ungetc(b);
462223a736eSDavid du Colombier 			break;
463223a736eSDavid du Colombier 		}
464223a736eSDavid du Colombier 		i = i*10 + (c-'0');
465223a736eSDavid du Colombier 	}
466223a736eSDavid du Colombier 	return i;
467223a736eSDavid du Colombier }
468223a736eSDavid du Colombier 
469223a736eSDavid du Colombier char*
getstr(Biobuf * b)470223a736eSDavid du Colombier getstr(Biobuf *b)
471223a736eSDavid du Colombier {
472223a736eSDavid du Colombier 	int i, c;
473223a736eSDavid du Colombier 
474223a736eSDavid du Colombier 	for(i=0; i<sizeof buf; i++){
475223a736eSDavid du Colombier 		/* must get bytes not runes */
476223a736eSDavid du Colombier 		cno++;
477223a736eSDavid du Colombier 		c = Bgetc(b);
478223a736eSDavid du Colombier 		if(c == Beof)
479223a736eSDavid du Colombier 			return nil;
480223a736eSDavid du Colombier 		buf[i] = c;
481223a736eSDavid du Colombier 		if(c == '\n' || c==' ' || c=='\t'){
482223a736eSDavid du Colombier 			ungetc(b);
483223a736eSDavid du Colombier 			buf[i] = '\0';
484223a736eSDavid du Colombier 			break;
485223a736eSDavid du Colombier 		}
486223a736eSDavid du Colombier 	}
487223a736eSDavid du Colombier 	return buf;
488223a736eSDavid du Colombier }
489223a736eSDavid du Colombier 
490223a736eSDavid du Colombier int
setnum(Biobuf * b,char * name,int min,int max)491223a736eSDavid du Colombier setnum(Biobuf *b, char *name, int min, int max)
492223a736eSDavid du Colombier {
493223a736eSDavid du Colombier 	int i;
494223a736eSDavid du Colombier 
495223a736eSDavid du Colombier 	i = getnum(b);
496223a736eSDavid du Colombier 	if(debug > 2)
497223a736eSDavid du Colombier 		fprint(2, "set %s = %d\n", name, i);
498223a736eSDavid du Colombier 	if(min<=i && i<max)
499223a736eSDavid du Colombier 		return i;
500223a736eSDavid du Colombier 	sysfatal("value of %s is %d; min %d max %d at %s:#%d", name, i, min, max, filename, cno);
501223a736eSDavid du Colombier 	return i;
502223a736eSDavid du Colombier }
503223a736eSDavid du Colombier 
504223a736eSDavid du Colombier void
xcmd(Biobuf * b)505223a736eSDavid du Colombier xcmd(Biobuf *b)
506223a736eSDavid du Colombier {
5070fb05a45SDavid du Colombier 	char *p, *fld[16], buf[1024];
5080fb05a45SDavid du Colombier 
509223a736eSDavid du Colombier 	int i, nfld;
510223a736eSDavid du Colombier 
511223a736eSDavid du Colombier 	p = getline(b);
512223a736eSDavid du Colombier 	if(p == nil)
513223a736eSDavid du Colombier 		sysfatal("xcmd error: %r");
514223a736eSDavid du Colombier 	if(debug)
515223a736eSDavid du Colombier 		fprint(2, "x command '%s'\n", p);
516223a736eSDavid du Colombier 	nfld = tokenize(p, fld, nelem(fld));
517223a736eSDavid du Colombier 	if(nfld == 0)
518223a736eSDavid du Colombier 		return;
519223a736eSDavid du Colombier 	switch(fld[0][0]){
520223a736eSDavid du Colombier 	case 'f':
521223a736eSDavid du Colombier 		/* mount font */
522223a736eSDavid du Colombier 		if(nfld != 3)
523223a736eSDavid du Colombier 			break;
524223a736eSDavid du Colombier 		i = atoi(fld[1]);
525223a736eSDavid du Colombier 		if(i<0 || Nfont<=i)
526223a736eSDavid du Colombier 			sysfatal("font %d out of range at %s:#%d", i, filename, cno);
527223a736eSDavid du Colombier 		mountfont(i, fld[2]);
528223a736eSDavid du Colombier 		return;
529223a736eSDavid du Colombier 	case 'i':
530223a736eSDavid du Colombier 		/* init */
531223a736eSDavid du Colombier 		return;
532223a736eSDavid du Colombier 	case 'r':
533223a736eSDavid du Colombier 		if(nfld<2 || atoi(fld[1])!=res)
534223a736eSDavid du Colombier 			sysfatal("typesetter has unexpected resolution %s", fld[1]? fld[1] : "<unspecified>");
535223a736eSDavid du Colombier 		return;
536223a736eSDavid du Colombier 	case 's':
537223a736eSDavid du Colombier 		/* stop */
538223a736eSDavid du Colombier 		return;
539223a736eSDavid du Colombier 	case 't':
540223a736eSDavid du Colombier 		/* trailer */
541223a736eSDavid du Colombier 		return;
542223a736eSDavid du Colombier 	case 'T':
543223a736eSDavid du Colombier 		if(nfld!=2 || strcmp(fld[1], "utf")!=0)
544223a736eSDavid du Colombier 			sysfatal("output for unknown typesetter type %s", fld[1]);
545223a736eSDavid du Colombier 		return;
546223a736eSDavid du Colombier 	case 'X':
547223a736eSDavid du Colombier 		if(nfld<3 || strcmp(fld[1], "html")!=0)
548223a736eSDavid du Colombier 			break;
549223a736eSDavid du Colombier 		/* is it a man reference of the form cp(1)? */
550223a736eSDavid du Colombier 		/* X manref start/end cp (1) */
551223a736eSDavid du Colombier 		if(nfld==6 && strcmp(fld[2], "manref")==0){
552223a736eSDavid du Colombier 			/* was the right macro; is it the right form? */
553223a736eSDavid du Colombier 			if(strlen(fld[5])>=3 &&
554223a736eSDavid du Colombier 			   fld[5][0]=='(' && fld[5][2]==')' &&
555223a736eSDavid du Colombier 			   '0'<=fld[5][1] && fld[5][1]<='9'){
556223a736eSDavid du Colombier 				if(strcmp(fld[3], "start") == 0){
557223a736eSDavid du Colombier 					/* set anchor attribute and remember string */
558223a736eSDavid du Colombier 					attr |= (1<<Anchor);
559223a736eSDavid du Colombier 					snprint(buf, sizeof buf,
5600fb05a45SDavid du Colombier 						"<a href=\"/magic/man2html/%c/%s\">",
561223a736eSDavid du Colombier 						fld[5][1], fld[4]);
562223a736eSDavid du Colombier 					nanchors++;
563223a736eSDavid du Colombier 					anchors = erealloc(anchors, nanchors*sizeof(char*));
564223a736eSDavid du Colombier 					anchors[nanchors-1] = estrdup(buf);
565223a736eSDavid du Colombier 				}else if(strcmp(fld[3], "end") == 0)
566223a736eSDavid du Colombier 					attr &= ~(1<<Anchor);
567223a736eSDavid du Colombier 			}
5680fb05a45SDavid du Colombier 		}else if(strcmp(fld[2], "manPP") == 0){
5690fb05a45SDavid du Colombier 			didP = 1;
57073e742d7SDavid du Colombier 			emitchar(Epp);
571223a736eSDavid du Colombier 		}else if(nfld<4 || strcmp(fld[2], "manref")!=0){
572223a736eSDavid du Colombier 			if(nfld>2 && strcmp(fld[2], "<P>")==0){	/* avoid triggering extra <br> */
573223a736eSDavid du Colombier 				didP = 1;
574223a736eSDavid du Colombier 				/* clear all font attributes before paragraph */
57573e742d7SDavid du Colombier 				emitchar(' ' | (attr & ~(0xFFFF|((1<<Italic)|(1<<Bold)|(1<<CW)))));
576223a736eSDavid du Colombier 				emitstr("<P>");
577223a736eSDavid du Colombier 				/* next emittec char will turn font attributes back on */
578223a736eSDavid du Colombier 			}else if(nfld>2 && strcmp(fld[2], "<H4>")==0)
579223a736eSDavid du Colombier 				attr |= (1<<Heading);
580223a736eSDavid du Colombier 			else if(nfld>2 && strcmp(fld[2], "</H4>")==0)
581223a736eSDavid du Colombier 				attr &= ~(1<<Heading);
582223a736eSDavid du Colombier 			else if(debug)
583223a736eSDavid du Colombier 				fprint(2, "unknown in-line html %s... at %s:%#d\n",
584223a736eSDavid du Colombier 					fld[2], filename, cno);
585223a736eSDavid du Colombier 		}
586223a736eSDavid du Colombier 		return;
587223a736eSDavid du Colombier 	}
588223a736eSDavid du Colombier 	if(debug)
589223a736eSDavid du Colombier 		fprint(2, "unknown or badly formatted x command %s\n", fld[0]);
590223a736eSDavid du Colombier }
591223a736eSDavid du Colombier 
592223a736eSDavid du Colombier int
lookup(int c,Htmlchar tab[],int ntab)593223a736eSDavid du Colombier lookup(int c, Htmlchar tab[], int ntab)
594223a736eSDavid du Colombier {
595223a736eSDavid du Colombier 	int low, high, mid;
596223a736eSDavid du Colombier 
597223a736eSDavid du Colombier 	low = 0;
598223a736eSDavid du Colombier 	high = ntab - 1;
599223a736eSDavid du Colombier 	while(low <= high){
600223a736eSDavid du Colombier 		mid = (low+high)/2;
601223a736eSDavid du Colombier 		if(c < tab[mid].value)
602223a736eSDavid du Colombier 			high = mid - 1;
603223a736eSDavid du Colombier 		else if(c > tab[mid].value)
604223a736eSDavid du Colombier 			low = mid + 1;
605223a736eSDavid du Colombier 		else
606223a736eSDavid du Colombier 			return mid;
607223a736eSDavid du Colombier 	}
608223a736eSDavid du Colombier 	return -1;	/* no match */
609223a736eSDavid du Colombier }
610223a736eSDavid du Colombier 
611223a736eSDavid du Colombier void
emithtmlchar(int r)612223a736eSDavid du Colombier emithtmlchar(int r)
613223a736eSDavid du Colombier {
614223a736eSDavid du Colombier 	static char buf[10];
615223a736eSDavid du Colombier 	int i;
616223a736eSDavid du Colombier 
617223a736eSDavid du Colombier 	i = lookup(r, htmlchars, nelem(htmlchars));
618223a736eSDavid du Colombier 	if(i >= 0)
619223a736eSDavid du Colombier 		emitstr(htmlchars[i].name);
620223a736eSDavid du Colombier 	else
621223a736eSDavid du Colombier 		emit(r);
622223a736eSDavid du Colombier }
623223a736eSDavid du Colombier 
624223a736eSDavid du Colombier char*
troffchar(char * s)625223a736eSDavid du Colombier troffchar(char *s)
626223a736eSDavid du Colombier {
627223a736eSDavid du Colombier 	int i;
628223a736eSDavid du Colombier 
629223a736eSDavid du Colombier 	for(i=0; troffchars[i].name!=nil; i++)
630223a736eSDavid du Colombier 		if(strcmp(s, troffchars[i].name) == 0)
631223a736eSDavid du Colombier 			return troffchars[i].value;
632223a736eSDavid du Colombier 	return "??";
633223a736eSDavid du Colombier }
634223a736eSDavid du Colombier 
635223a736eSDavid du Colombier void
indent(void)636223a736eSDavid du Colombier indent(void)
637223a736eSDavid du Colombier {
638223a736eSDavid du Colombier 	int nind;
639223a736eSDavid du Colombier 
640223a736eSDavid du Colombier 	didP = 0;
641223a736eSDavid du Colombier 	if(atnewline){
642223a736eSDavid du Colombier 		if(hp != prevlineH){
643223a736eSDavid du Colombier 			prevlineH = hp;
644223a736eSDavid du Colombier 			/* these most peculiar numbers appear in the troff -man output */
645223a736eSDavid du Colombier 			nind = ((prevlineH-1*res)+323)/324;
646223a736eSDavid du Colombier 			attr &= ~((1<<Indent1)|(1<<Indent2)|(1<<Indent3));
647223a736eSDavid du Colombier 			if(nind >= 1)
648223a736eSDavid du Colombier 				attr |= (1<<Indent1);
649223a736eSDavid du Colombier 			if(nind >= 2)
650223a736eSDavid du Colombier 				attr |= (1<<Indent2);
6510fb05a45SDavid du Colombier 			if(nind >= 3)
652223a736eSDavid du Colombier 				attr |= (1<<Indent3);
653223a736eSDavid du Colombier 		}
654223a736eSDavid du Colombier 		atnewline = 0;
655223a736eSDavid du Colombier 	}
656223a736eSDavid du Colombier }
657223a736eSDavid du Colombier 
658223a736eSDavid du Colombier void
process(Biobuf * b,char * name)659223a736eSDavid du Colombier process(Biobuf *b, char *name)
660223a736eSDavid du Colombier {
661223a736eSDavid du Colombier 	int c, r, v, i;
662223a736eSDavid du Colombier 	char *p;
663223a736eSDavid du Colombier 
664223a736eSDavid du Colombier 	cno = 0;
665223a736eSDavid du Colombier 	prevlineH = res;
666223a736eSDavid du Colombier 	filename = name;
667223a736eSDavid du Colombier 	for(;;){
668223a736eSDavid du Colombier 		c = getc(b);
669223a736eSDavid du Colombier 		switch(c){
670223a736eSDavid du Colombier 		case Beof:
671223a736eSDavid du Colombier 			/* go to ground state */
672223a736eSDavid du Colombier 			attr = 0;
673223a736eSDavid du Colombier 			emit('\n');
674223a736eSDavid du Colombier 			return;
675223a736eSDavid du Colombier 		case '\n':
676223a736eSDavid du Colombier 			break;
677223a736eSDavid du Colombier 		case '0': case '1': case '2': case '3': case '4':
678223a736eSDavid du Colombier 		case '5': case '6': case '7': case '8': case '9':
679223a736eSDavid du Colombier 			v = c-'0';
680223a736eSDavid du Colombier 			c = getc(b);
681223a736eSDavid du Colombier 			if(c<'0' || '9'<c)
682223a736eSDavid du Colombier 				sysfatal("illegal character motion at %s:#%d", filename, cno);
683223a736eSDavid du Colombier 			v = v*10 + (c-'0');
684223a736eSDavid du Colombier 			hp += v;
685223a736eSDavid du Colombier 			/* fall through to character case */
686223a736eSDavid du Colombier 		case 'c':
687223a736eSDavid du Colombier 			indent();
688223a736eSDavid du Colombier 			r = getc(b);
689223a736eSDavid du Colombier 			emithtmlchar(r);
690223a736eSDavid du Colombier 			break;
691b7b24591SDavid du Colombier 		case 'D':
692b7b24591SDavid du Colombier 			/* draw line; ignore */
693b7b24591SDavid du Colombier 			do
694b7b24591SDavid du Colombier 				c = getc(b);
695b7b24591SDavid du Colombier 			while(c!='\n' && c!= Beof);
696b7b24591SDavid du Colombier 			break;
697223a736eSDavid du Colombier 		case 'f':
698223a736eSDavid du Colombier 			v = setnum(b, "font", 0, Nfont);
699223a736eSDavid du Colombier 			switchfont(v);
700223a736eSDavid du Colombier 			break;
701223a736eSDavid du Colombier 		case 'h':
702223a736eSDavid du Colombier 			v = setnum(b, "hpos", -20000, 20000);
703223a736eSDavid du Colombier 			/* generate spaces if motion is large and within a line */
704223a736eSDavid du Colombier 			if(!atnewline && v>2*72)
705223a736eSDavid du Colombier 				for(i=0; i<v; i+=72)
706223a736eSDavid du Colombier 					emitstr("&nbsp;");
707223a736eSDavid du Colombier 			hp += v;
708223a736eSDavid du Colombier 			break;
709223a736eSDavid du Colombier 		case 'n':
710223a736eSDavid du Colombier 			setnum(b, "n1", -10000, 10000);
711223a736eSDavid du Colombier 			//Bprint(&bout, " N1=%d", v);
712223a736eSDavid du Colombier 			getc(b);	/* space separates */
713223a736eSDavid du Colombier 			setnum(b, "n2", -10000, 10000);
714223a736eSDavid du Colombier 			atnewline = 1;
715223a736eSDavid du Colombier 			if(!didP && hp < (Wid-1)*res)	/* if line is less than 19" long, probably need a line break */
716223a736eSDavid du Colombier 				emitstr("<br>");
717223a736eSDavid du Colombier 			emit('\n');
718223a736eSDavid du Colombier 			break;
719223a736eSDavid du Colombier 		case 'p':
720223a736eSDavid du Colombier 			page = setnum(b, "ps", -10000, 10000);
721223a736eSDavid du Colombier 			break;
722223a736eSDavid du Colombier 		case 's':
723223a736eSDavid du Colombier 			ps = setnum(b, "ps", 1, 1000);
724223a736eSDavid du Colombier 			break;
725223a736eSDavid du Colombier 		case 'v':
726223a736eSDavid du Colombier 			vp += setnum(b, "vpos", -10000, 10000);
727223a736eSDavid du Colombier 			/* BUG: ignore motion */
728223a736eSDavid du Colombier 			break;
729223a736eSDavid du Colombier 		case 'x':
730223a736eSDavid du Colombier 			xcmd(b);
731223a736eSDavid du Colombier 			break;
732223a736eSDavid du Colombier 		case 'w':
733223a736eSDavid du Colombier 			emit(' ');
734223a736eSDavid du Colombier 			break;
735223a736eSDavid du Colombier 		case 'C':
736223a736eSDavid du Colombier 			indent();
737223a736eSDavid du Colombier 			p = getstr(b);
738223a736eSDavid du Colombier 			emitstr(troffchar(p));
739223a736eSDavid du Colombier 			break;
740223a736eSDavid du Colombier 		case 'H':
741223a736eSDavid du Colombier 			hp = setnum(b, "hpos", 0, 20000);
742223a736eSDavid du Colombier 			//Bprint(&bout, " H=%d ", hp);
743223a736eSDavid du Colombier 			break;
744223a736eSDavid du Colombier 		case 'V':
745223a736eSDavid du Colombier 			vp = setnum(b, "vpos", 0, 10000);
746223a736eSDavid du Colombier 			break;
747223a736eSDavid du Colombier 		default:
748223a736eSDavid du Colombier 			fprint(2, "dhtml: unknown directive %c(0x%.2ux) at %s:#%d\n", c, c, filename, cno);
749223a736eSDavid du Colombier 			return;
750223a736eSDavid du Colombier 		}
751223a736eSDavid du Colombier 	}
752223a736eSDavid du Colombier }
753223a736eSDavid du Colombier 
754223a736eSDavid du Colombier HTMLfont*
htmlfont(char * name)755223a736eSDavid du Colombier htmlfont(char *name)
756223a736eSDavid du Colombier {
757223a736eSDavid du Colombier 	int i;
758223a736eSDavid du Colombier 
759223a736eSDavid du Colombier 	for(i=0; htmlfonts[i].name!=nil; i++)
760223a736eSDavid du Colombier 		if(strcmp(name, htmlfonts[i].name) == 0)
761223a736eSDavid du Colombier 			return &htmlfonts[i];
762223a736eSDavid du Colombier 	return &htmlfonts[0];
763223a736eSDavid du Colombier }
764223a736eSDavid du Colombier 
765223a736eSDavid du Colombier void
mountfont(int pos,char * name)766223a736eSDavid du Colombier mountfont(int pos, char *name)
767223a736eSDavid du Colombier {
768223a736eSDavid du Colombier 	if(debug)
769223a736eSDavid du Colombier 		fprint(2, "mount font %s on %d\n", name, pos);
770223a736eSDavid du Colombier 	if(font[pos] != nil){
771223a736eSDavid du Colombier 		free(font[pos]->name);
772223a736eSDavid du Colombier 		free(font[pos]);
773223a736eSDavid du Colombier 	}
774223a736eSDavid du Colombier 	font[pos] = emalloc(sizeof(Font));
775223a736eSDavid du Colombier 	font[pos]->name = estrdup(name);
776223a736eSDavid du Colombier 	font[pos]->htmlfont = htmlfont(name);
777223a736eSDavid du Colombier }
778223a736eSDavid du Colombier 
779223a736eSDavid du Colombier void
switchfont(int pos)780223a736eSDavid du Colombier switchfont(int pos)
781223a736eSDavid du Colombier {
782223a736eSDavid du Colombier 	HTMLfont *hf;
783223a736eSDavid du Colombier 
784223a736eSDavid du Colombier 	if(debug)
785223a736eSDavid du Colombier 		fprint(2, "font change from %d (%s) to %d (%s)\n", ft, font[ft]->name, pos, font[pos]->name);
786223a736eSDavid du Colombier 	if(pos == ft)
787223a736eSDavid du Colombier 		return;
788223a736eSDavid du Colombier 	hf = font[ft]->htmlfont;
789223a736eSDavid du Colombier 	if(hf->bit != 0)
790223a736eSDavid du Colombier 		attr &= ~(1<<hf->bit);
791223a736eSDavid du Colombier 	ft = pos;
792223a736eSDavid du Colombier 	hf = font[ft]->htmlfont;
793223a736eSDavid du Colombier 	if(hf->bit != 0)
794223a736eSDavid du Colombier 		attr |= (1<<hf->bit);
795223a736eSDavid du Colombier }
796