xref: /plan9/sys/src/cmd/ms2html.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <bio.h>
5 
6 enum
7 {
8 	SSIZE = 10,
9 
10 	Maxnh=	8,		/* highest NH level */
11 	HH=	4,		/* heading level used for SH and NH */
12 	Maxmstack=	10,	/* deepest macro/string nesting */
13 	Narg=	20,		/* max args to a macro */
14 	Maxsstack=	5,	/* deepest nesting of .so's */
15 	Nline=	1024,
16 	Maxget= 10,
17 	Maxif = 20,
18 
19 	/* list types */
20 	Lordered = 1,
21 	Lunordered,
22 	Ldef,
23 	Lother,
24 };
25 
26 char *delim = "$$";
27 int eqnmode;
28 
29 int 	quiet;
30 float	indent; /* from .in */
31 Biobuf	bout;
32 int	isup;
33 int	isdown;
34 int	debug;
35 
36 int nh[Maxnh];
37 int ifwastrue[Maxif];
38 
39 int list, listnum, example;
40 int hangingau, hangingdt, hanginghead;
41 int indirective, paragraph, sol, titleseen, ignore_nl, weBref;
42 
43 typedef struct Goobie Goobie;
44 typedef struct Goobieif Goobieif;
45 struct Goobie
46 {
47 	char *name;
48 	void (*f)(int, char**);
49 };
50 
51 typedef void F(int, char**);
52 typedef void Fif(char*, char*);
53 
54 struct Goobieif
55 {
56 	char *name;
57 	Fif *f;
58 };
59 
60 /* if, ie */
61 Fif g_as, g_ds, g_el, g_ie, g_if;
62 Goobieif gtabif[] = {
63 	{ "as", g_as },
64 	{ "ds", g_ds },
65 	{ "if", g_if },
66 	{ "ie", g_ie },
67 	{ "el", g_el },
68 	{ nil, nil },
69 	};
70 
71 /* pseudo ops */
72 F g_notyet, g_ignore, g_hrule, g_startgif;
73 
74 /* ms macros */
75 F g_AU, g_B, g_BI, g_CW, g_I, g_IP, g_LP, g_PP, g_SH, g_NH;
76 F g_P1, g_P2, g_TL, g_R, g_AB, g_AE, g_EQ, g_TS, g_TE, g_FS, g_FE;
77 F g_PY, g_IH, g_MH, g_HO, g_BX, g_QS, g_QE, g_RS, g_RE;
78 
79 /* pictures macro */
80 F g_BP;
81 
82 /* real troff */
83 F g_br, g_ft, g_sp, g_de, g_lf, g_so, g_rm, g_in;
84 F g_nr, g_ig, g_RT, g_BS, g_BE, g_LB, g_ta;
85 
86 /* macros to include ML in output */
87 F g__H, g__T;
88 
89 Goobie gtab[] =
90 {
91 	{ "_T", g__T, },
92 	{ "_H", g__H, },
93 	{ "1C",	g_ignore, },
94 	{ "2C",	g_ignore, },
95 	{ "AB", g_AB, },
96 	{ "AE", g_AE, },
97 	{ "AI", g_ignore, },
98 	{ "AU", g_AU, },
99 	{ "B",	g_B, },
100 	{ "B1", g_hrule, },
101 	{ "B2", g_hrule, },
102 	{ "BI",	g_BI, },
103 	{ "BP",	g_BP, },
104 	{ "BT",	g_ignore, },
105 	{ "BX",	g_BX, },
106 	{ "CW",	g_CW, },
107 	{ "CT",	g_ignore, },
108 	{ "DA",	g_ignore, },
109 	{ "DE",	g_P2, },
110 	{ "DS",	g_P1, },
111 	{ "EG",	g_ignore, },
112 	{ "EN",	g_ignore, },
113 	{ "EQ",	g_startgif, },
114 	{ "FE",	g_FE, },
115 	{ "FP",	g_ignore, },
116 	{ "FS",	g_FS, },
117 	{ "HO",	g_HO, },
118 	{ "I",	g_I, },
119 	{ "IH",	g_IH, },
120 	{ "IM",	g_ignore, },
121 	{ "IP",	g_IP, },
122 	{ "KE",	g_ignore, },
123 	{ "KF",	g_ignore, },
124 	{ "KS",	g_ignore, },
125 	{ "LG",	g_ignore, },
126 	{ "LP",	g_LP, },
127 	{ "LT",	g_ignore, },
128 	{ "MF",	g_ignore, },
129 	{ "MH",	g_MH, },
130 	{ "MR",	g_ignore, },
131 	{ "ND",	g_ignore, },
132 	{ "NH",	g_NH, },
133 	{ "NL",	g_ignore, },
134 	{ "P1",	g_P1, },
135 	{ "P2",	g_P2, },
136 	{ "PE",	g_ignore, },
137 	{ "PF",	g_ignore, },
138 	{ "PP",	g_PP, },
139 	{ "PS",	g_startgif, },
140 	{ "PY",	g_PY, },
141 	{ "QE",	g_QE, },
142 	{ "QP",	g_QS, },
143 	{ "QS",	g_QS, },
144 	{ "R",		g_R, },
145 	{ "RE",	g_RE, },
146 	{ "RP",	g_ignore, },
147 	{ "RS",	g_RS, },
148 	{ "SG",	g_ignore, },
149 	{ "SH",	g_SH, },
150 	{ "SM",	g_ignore, },
151 	{ "TA",	g_ignore, },
152 	{ "TE",	g_ignore, },
153 	{ "TH",	g_TL, },
154 	{ "TL",	g_TL, },
155 	{ "TM",	g_ignore, },
156 	{ "TR",	g_ignore, },
157 	{ "TS",	g_startgif, },
158 	{ "UL",	g_I, },
159 	{ "UX",	g_ignore, },
160 	{ "WH",	g_ignore, },
161 	{ "RT", 	g_RT, },
162 
163 	{ "br",	g_br, },
164 	{ "ti",		g_br, },
165 	{ "nf",	g_P1, },
166 	{ "fi",		g_P2, },
167 	{ "ft",		g_ft, },
168 	{ "sp", 	g_sp, },
169 	{ "rm", 	g_rm, },
170 	{ "de", 	g_de, },
171 	{ "am", 	g_de, },
172 	{ "lf", 	g_lf, },
173 	{ "so", 	g_so, },
174 	{ "ps", 	g_ignore },
175 	{ "vs", 	g_ignore },
176 	{ "nr", 	g_nr },
177 	{ "in", 	g_in },
178 	{ "ne", 	g_ignore },
179 	{ "ig", 	g_ig },
180 	{ "BS", 	g_BS },
181 	{ "BE", 	g_BE },
182 	{ "LB", 	g_LB },
183 	{ nil, nil },
184 };
185 
186 typedef struct Entity Entity;
187 struct Entity
188 {
189 	char *name;
190 	int value;
191 };
192 
193 Entity entity[] =
194 {
195 	{ "&#SPACE;",	L' ', },
196 	{ "&#RS;",		L'\n', },
197 	{ "&#RE;",		L'\r', },
198 	{ "&quot;",		L'"', },
199 	{ "&AElig;",	L'Æ', },
200 	{ "&Aacute;",	L'Á', },
201 	{ "&Acirc;",	L'Â', },
202 	{ "&Agrave;",	L'À', },
203 	{ "&Aring;",	L'Å', },
204 	{ "&Atilde;",	L'Ã', },
205 	{ "&Auml;",	L'Ä', },
206 	{ "&Ccedil;",	L'Ç', },
207 	{ "&ETH;",		L'Ð', },
208 	{ "&Eacute;",	L'É', },
209 	{ "&Ecirc;",	L'Ê', },
210 	{ "&Egrave;",	L'È', },
211 	{ "&Euml;",	L'Ë', },
212 	{ "&Iacute;",	L'Í', },
213 	{ "&Icirc;",		L'Î', },
214 	{ "&Igrave;",	L'Ì', },
215 	{ "&Iuml;",		L'Ï', },
216 	{ "&Ntilde;",	L'Ñ', },
217 	{ "&Oacute;",	L'Ó', },
218 	{ "&Ocirc;",	L'Ô', },
219 	{ "&Ograve;",	L'Ò', },
220 	{ "&Oslash;",	L'Ø', },
221 	{ "&Otilde;",	L'Õ', },
222 	{ "&Ouml;",	L'Ö', },
223 	{ "&THORN;",	L'Þ', },
224 	{ "&Uacute;",	L'Ú', },
225 	{ "&Ucirc;",	L'Û', },
226 	{ "&Ugrave;",	L'Ù', },
227 	{ "&Uuml;",	L'Ü', },
228 	{ "&Yacute;",	L'Ý', },
229 	{ "&aacute;",	L'á', },
230 	{ "&acirc;",	L'â', },
231 	{ "&aelig;",	L'æ', },
232 	{ "&agrave;",	L'à', },
233 	{ "&amp;",		L'&', },
234 	{ "&aring;",	L'å', },
235 	{ "&atilde;",	L'ã', },
236 	{ "&auml;",	L'ä', },
237 	{ "&ccedil;",	L'ç', },
238 	{ "&eacute;",	L'é', },
239 	{ "&ecirc;",	L'ê', },
240 	{ "&egrave;",	L'è', },
241 	{ "&eth;",		L'ð', },
242 	{ "&euml;",	L'ë', },
243 	{ "&gt;",		L'>', },
244 	{ "&iacute;",	L'í', },
245 	{ "&icirc;",		L'î', },
246 	{ "&igrave;",	L'ì', },
247 	{ "&iuml;",		L'ï', },
248 	{ "&lt;",		L'<', },
249 	{ "&ntilde;",	L'ñ', },
250 	{ "&oacute;",	L'ó', },
251 	{ "&ocirc;",	L'ô', },
252 	{ "&ograve;",	L'ò', },
253 	{ "&oslash;",	L'ø', },
254 	{ "&otilde;",	L'õ', },
255 	{ "&ouml;",	L'ö', },
256 	{ "&szlig;",		L'ß', },
257 	{ "&thorn;",	L'þ', },
258 	{ "&uacute;",	L'ú', },
259 	{ "&ucirc;",	L'û', },
260 	{ "&ugrave;",	L'ù', },
261 	{ "&uuml;",	L'ü', },
262 	{ "&yacute;",	L'ý', },
263 	{ "&yuml;",	L'ÿ', },
264 	{ "&#161;",	L'¡', },
265 	{ "&#162;",	L'¢', },
266 	{ "&#163;",	L'£', },
267 	{ "&#164;",	L'¤', },
268 	{ "&#165;",	L'¥', },
269 	{ "&#166;",	L'¦', },
270 	{ "&#167;",	L'§', },
271 	{ "&#168;",	L'¨', },
272 	{ "&#169;",	L'©', },
273 	{ "&#170;",	L'ª', },
274 	{ "&#171;",	L'«', },
275 	{ "&#172;",	L'¬', },
276 	{ "&#173;",	L'­', },
277 	{ "&#174;",	L'®', },
278 	{ "&#175;",	L'¯', },
279 	{ "&#176;",	L'°', },
280 	{ "&#177;",	L'±', },
281 	{ "&#178;",	L'²', },
282 	{ "&#179;",	L'³', },
283 	{ "&#180;",	L'´', },
284 	{ "&#181;",	L'µ', },
285 	{ "&#182;",	L'¶', },
286 	{ "&#183;",	L'·', },
287 	{ "&#184;",	L'¸', },
288 	{ "&#185;",	L'¹', },
289 	{ "&#186;",	L'º', },
290 	{ "&#187;",	L'»', },
291 	{ "&#188;",	L'¼', },
292 	{ "&#189;",	L'½', },
293 	{ "&#190;",	L'¾', },
294 	{ "&#191;",	L'¿', },
295 
296 	{ "*",			L'•', },
297 	{ "&#164;",	L'□', },
298 	{ "&#186;",	L'◊', },
299 	{ "(tm)",		L'™', },
300 	{"&#913;",		L'Α',},
301 	{"&#914;",		L'Β',},
302 	{"&#915;",		L'Γ',},
303 	{"&#916;",		L'Δ',},
304 	{"&#917;",		L'Ε',},
305 	{"&#918;",		L'Ζ',},
306 	{"&#919;",		L'Η',},
307 	{"&#920;",		L'Θ',},
308 	{"&#921;",		L'Ι',},
309 	{"&#922;",		L'Κ',},
310 	{"&#923;",		L'Λ',},
311 	{"&#924;",		L'Μ',},
312 	{"&#925;",		L'Ν',},
313 	{"&#926;",		L'Ξ',},
314 	{"&#927;",		L'Ο',},
315 	{"&#928;",		L'Π',},
316 	{"&#929;",		L'Ρ',},
317 	{"&#930;",		L'΢',},
318 	{"&#931;",		L'Σ',},
319 	{"&#932;",		L'Τ',},
320 	{"&#933;",		L'Υ',},
321 	{"&#934;",		L'Φ',},
322 	{"&#935;",		L'Χ',},
323 	{"&#936;",		L'Ψ',},
324 	{"&#937;",		L'Ω',},
325 	{"&#945;",		L'α',},
326 	{"&#946;",		L'β',},
327 	{"&#947;",		L'γ',},
328 	{"&#948;",		L'δ',},
329 	{"&#949;",		L'ε',},
330 	{"&#950;",		L'ζ',},
331 	{"&#951;",		L'η',},
332 	{"&#952;",		L'θ',},
333 	{"&#953;",		L'ι',},
334 	{"&#954;",		L'κ',},
335 	{"&#955;",		L'λ',},
336 	{"&#956;",		L'μ',},
337 	{"&#957;",		L'ν',},
338 	{"&#958;",		L'ξ',},
339 	{"&#959;",		L'ο',},
340 	{"&#960;",		L'π',},
341 	{"&#961;",		L'ρ',},
342 	{"&#962;",		L'ς',},
343 	{"&#963;",		L'σ',},
344 	{"&#964;",		L'τ',},
345 	{"&#965;",		L'υ',},
346 	{"&#966;",		L'φ',},
347 	{"&#967;",		L'χ',},
348 	{"&#968;",		L'ψ',},
349 	{"&#969;",		L'ω',},
350 
351 	{ "<-",		L'←', },
352 	{ "^",			L'↑', },
353 	{ "->",		L'→', },
354 	{ "v",			L'↓', },
355 	{ "!=",		L'≠', },
356 	{ "<=",		L'≤', },
357 	{ "...",		L'⋯', },
358 	{"&isin;",		L'∈', },
359 
360 	{"&#8211;",	L'–', },
361 	{"&#8212;",	L'—', },
362 
363 	{ "CYRILLIC XYZZY",	L'й', },
364 	{ "CYRILLIC XYZZY",	L'ъ', },
365 	{ "CYRILLIC Y",		L'ь', },
366 	{ "CYRILLIC YA",	L'я', },
367 	{ "CYRILLIC YA",	L'ё', },
368 	{ "&#191;",		L'ℱ', },
369 
370 	{ nil, 0 },
371 };
372 
373 typedef struct Troffspec Troffspec;
374 struct Troffspec
375 {
376 	char *name;
377 	char *value;
378 };
379 
380 Troffspec tspec[] =
381 {
382 	{ "A*", "&Aring;", },
383 	{ "o\"", "&ouml;", },
384 	{ "ff", "ff", },
385 	{ "fi", "fi", },
386 	{ "fl", "fl", },
387 	{ "Fi", "ffi", },
388 	{ "ru", "_", },
389 	{ "em", "&#173;", },
390 	{ "14", "&#188;", },
391 	{ "12", "&#189;", },
392 	{ "co", "&#169;", },
393 	{ "de", "&#176;", },
394 	{ "dg", "&#161;", },
395 	{ "fm", "&#180;", },
396 	{ "rg", "&#174;", },
397 	{ "bu", "*", },
398 	{ "sq", "&#164;", },
399 	{ "hy", "-", },
400 	{ "pl", "+", },
401 	{ "mi", "-", },
402 	{ "mu", "&#215;", },
403 	{ "di", "&#247;", },
404 	{ "eq", "=", },
405 	{ "==", "==", },
406 	{ ">=", ">=", },
407 	{ "<=", "<=", },
408 	{ "!=", "!=", },
409 	{ "+-", "&#177;", },
410 	{ "no", "&#172;", },
411 	{ "sl", "/", },
412 	{ "ap", "&", },
413 	{ "~=", "~=", },
414 	{ "pt", "oc", },
415 	{ "gr", "GRAD", },
416 	{ "->", "->", },
417 	{ "<-", "<-", },
418 	{ "ua", "^", },
419 	{ "da", "v", },
420 	{ "is", "Integral", },
421 	{ "pd", "DIV", },
422 	{ "if", "oo", },
423 	{ "sr", "-/", },
424 	{ "sb", "(~", },
425 	{ "sp", "~)", },
426 	{ "cu", "U", },
427 	{ "ca", "(^)", },
428 	{ "ib", "(=", },
429 	{ "ip", "=)", },
430 	{ "mo", "C", },
431 	{ "es", "&Oslash;", },
432 	{ "aa", "&#180;", },
433 	{ "ga", "`", },
434 	{ "ci", "O", },
435 	{ "L1", "DEATHSTAR", },
436 	{ "sc", "&#167;", },
437 	{ "dd", "++", },
438 	{ "lh", "<=", },
439 	{ "rh", "=>", },
440 	{ "lt", "(", },
441 	{ "rt", ")", },
442 	{ "lc", "|", },
443 	{ "rc", "|", },
444 	{ "lb", "(", },
445 	{ "rb", ")", },
446 	{ "lf", "|", },
447 	{ "rf", "|", },
448 	{ "lk", "|", },
449 	{ "rk", "|", },
450 	{ "bv", "|", },
451 	{ "ts", "s", },
452 	{ "br", "|", },
453 	{ "or", "|", },
454 	{ "ul", "_", },
455 	{ "rn", " ", },
456 	{ "**", "*", },
457 	{ "tm", "&#153", },
458 	{ nil, nil, },
459 };
460 
461 typedef struct Font Font;
462 struct Font
463 {
464 	char	*start;
465 	char	*end;
466 };
467 Font bfont = { "<B>", "</B>" };
468 Font ifont = { "<I>", "</I>" };
469 Font bifont = { "<B><I>", "</I></B>" };
470 Font cwfont = { "<TT>", "</TT>" };
471 Font *prevfont;
472 Font *curfont;
473 
474 typedef struct String String;
475 struct String
476 {
477 	String *next;
478 	char *name;
479 	char *val;
480 };
481 String *numregs, *strings;
482 char *strstack[Maxmstack];
483 char *mustfree[Maxmstack];
484 int strsp = -1;
485 int elsetop = -1;
486 
487 typedef struct Mstack Mstack;
488 struct Mstack
489 {
490 	char *ptr;
491 	char *argv[Narg+1];
492 };
493 String *macros;
494 Mstack mstack[Maxmstack];
495 int msp = -1;
496 
497 typedef struct Srcstack Srcstack;
498 struct Srcstack
499 {
500 	char	filename[256];
501 	int	fd;
502 	int	lno;
503 	int	rlno;
504 	Biobuf	in;
505 };
506 Srcstack sstack[Maxsstack];
507 Srcstack *ssp = &sstack[-1];
508 
509 char token[128];
510 
511 void	closel(void);
512 void	closefont(void);
513 
514 void*
515 emalloc(uint n)
516 {
517 	void *p;
518 
519 	p = mallocz(n, 1);
520 	if(p == nil){
521 		fprint(2, "ms2html: malloc failed: %r\n");
522 		exits("malloc");
523 	}
524 	return p;
525 }
526 
527 
528 /* define a string variable */
529 void
530 dsnr(char *name, char *val, String **l)
531 {
532 	String *s;
533 
534 	for(s = *l; s != nil; s = *l){
535 		if(strcmp(s->name, name) == 0)
536 			break;
537 		l = &s->next;
538 	}
539 	if(s == nil){
540 		s = emalloc(sizeof(String));
541 		*l = s;
542 		s->name = strdup(name);
543 	} else
544 		free(s->val);
545 	s->val = strdup(val);
546 }
547 
548 void
549 ds(char *name, char *val)
550 {
551 	dsnr(name, val, &strings);
552 }
553 
554 /* look up a defined string */
555 char*
556 getds(char *name)
557 {
558 	String *s;
559 
560 	for(s = strings; s != nil; s = s->next)
561 		if(strcmp(name, s->name) == 0)
562 			break;
563 	if(s != nil)
564 		return s->val;
565 	return "";
566 }
567 
568 char *
569 getnr(char *name)
570 {
571 	String *s;
572 
573 	for(s = numregs; s != nil; s = s->next)
574 		if(strcmp(name, s->name) == 0)
575 			break;
576 	if(s != nil)
577 		return s->val;
578 	return "0";
579 }
580 
581 void
582 pushstr(char *p)
583 {
584 	if(p == nil)
585 		return;
586 	if(strsp >= Maxmstack - 1)
587 		return;
588 	strstack[++strsp] = p;
589 }
590 
591 
592 /* lookup a defined macro */
593 char*
594 getmacro(char *name)
595 {
596 	String *s;
597 
598 	for(s = macros; s != nil; s = s->next)
599 		if(strcmp(name, s->name) == 0)
600 			return s->val;
601 	return nil;
602 }
603 
604 enum
605 {
606 	Dstring,
607 	Macro,
608 	Input,
609 };
610 int lastsrc;
611 
612 void
613 pushsrc(char *name)
614 {
615 	Dir *d;
616 	int fd;
617 
618 	if(ssp == &sstack[Maxsstack-1]){
619 		fprint(2, "ms2html: .so's too deep\n");
620 		return;
621 	}
622 	d = nil;
623 	if(name == nil){
624 		d = dirfstat(0);
625 		if(d == nil){
626 			fprint(2, "ms2html: can't stat %s: %r\n", name);
627 			return;
628 		}
629 		name = d->name;
630 		fd = 0;
631 	} else {
632 		fd = open(name, OREAD);
633 		if(fd < 0){
634 			fprint(2, "ms2html: can't open %s: %r\n", name);
635 			return;
636 		}
637 	}
638 	ssp++;
639 	ssp->fd = fd;
640 	Binit(&ssp->in, fd, OREAD);
641 	snprint(ssp->filename, sizeof(ssp->filename), "%s", name);
642 	ssp->lno = ssp->rlno = 1;
643 	free(d);
644 }
645 
646 /* get next logical byte.  from stdin or a defined string */
647 int
648 getrune(void)
649 {
650 	int i;
651 	Rune r;
652 	int c;
653 	Mstack *m;
654 
655 	while(strsp >= 0){
656 		i = chartorune(&r, strstack[strsp]);
657 		if(r != 0){
658 			strstack[strsp] += i;
659 			lastsrc = Dstring;
660 			return r;
661 		}
662 		if (mustfree[strsp]) {
663 			free(mustfree[strsp]);
664 			mustfree[strsp] = nil;
665 		}
666 		strsp--;
667  	}
668 	while(msp >= 0){
669 		m = &mstack[msp];
670 		i = chartorune(&r, m->ptr);
671 		if(r != 0){
672 			m->ptr += i;
673 			lastsrc = Macro;
674 			return r;
675 		}
676 		for(i = 0; m->argv[i] != nil; i++)
677 			free(m->argv[i]);
678 		msp--;
679  	}
680 
681 	lastsrc = Input;
682 	do {
683 		if(ssp < sstack)
684 			return -1;
685 		c = Bgetrune(&ssp->in);
686 		if(c >= 0){
687 			r = c;
688 			break;
689 		}
690 		close(ssp->fd);
691 		ssp--;
692 	} while(r < 0);
693 
694 	return r;
695 }
696 
697 void
698 ungetrune(void)
699 {
700 	switch(lastsrc){
701 	case Dstring:
702 		if(strsp >= 0)
703 			strstack[strsp]--;
704 		break;
705 	case Macro:
706 		if(msp >= 0)
707 			mstack[msp].ptr--;
708 		break;
709 	case Input:
710 		if(ssp >= sstack)
711 			Bungetrune(&ssp->in);
712 		break;
713 	}
714 }
715 
716 int vert;
717 
718 char*
719 changefont(Font *f)
720 {
721 	token[0] = 0;
722 	if(curfont != nil)
723 		strcpy(token, curfont->end);
724 	if(f != nil)
725 		strcat(token, f->start);
726 	prevfont = curfont;
727 	curfont = f;
728 	return token;
729 }
730 
731 char*
732 changesize(int amount)
733 {
734 	static int curamount;
735 	static char buf[200];
736 	int i;
737 
738 	buf[0] = 0;
739 	if (curamount >= 0)
740 		for (i = 0; i < curamount; i++)
741 			strcat(buf, "</big>");
742 	else
743 		for (i = 0; i < -curamount; i++)
744 			strcat(buf, "</small>");
745 	curamount = 0;
746 	if (amount >= 0)
747 		for (i = 0; i < amount; i++)
748 			strcat(buf, "<big>");
749 	else
750 		for (i = 0; i < -amount; i++)
751 			strcat(buf, "<small>");
752 	curamount = amount;
753 	return buf;
754 }
755 
756 /* get next logical character.  expand it with escapes */
757 char*
758 getnext(void)
759 {
760 	int r;
761 	Entity *e;
762 	Troffspec *t;
763 	Rune R;
764 	char str[4];
765 	static char buf[8];
766 
767 	r = getrune();
768 	if(r < 0)
769 		return nil;
770 	if(r > 128 || r == '<' || r == '>'){
771 		for(e = entity; e->name; e++)
772 			if(e->value == r)
773 				return e->name;
774 		sprint(buf, "&#%d;", r);
775 		return buf;
776 	}
777 
778 	if (r == delim[eqnmode]){
779 		if (eqnmode == 0){
780 			eqnmode = 1;
781 			return changefont(&ifont);
782 		}
783 		eqnmode = 0;
784 		return changefont(prevfont);
785 	}
786 	switch(r){
787 	case '\\':
788 		r = getrune();
789 		if(r < 0)
790 			return nil;
791 		switch(r){
792 		case ' ':
793 			return " ";
794 
795 		/* chars to ignore */
796 		case '&':
797 		case '|':
798 		case '%':
799 			return "";
800 
801 		/* small space in troff, nothing in nroff */
802 		case '^':
803 			return getnext();
804 
805 		/* ignore arg */
806 		case 'k':
807 			getrune();
808 			return getnext();
809 
810 		/* comment */
811 		case '"':
812 			while(getrune() != '\n')
813 				;
814 			return "\n";
815 		/* ignore line */
816 		case '!':
817 			while(getrune() != '\n')
818 				;
819 			ungetrune();
820 			return getnext();
821 
822 		/* defined strings */
823 		case '*':
824 			r = getrune();
825 			if(r == '('){
826 				str[0] = getrune();
827 				str[1] = getrune();
828 				str[2] = 0;
829 			} else {
830 				str[0] = r;
831 				str[1] = 0;
832 			}
833 			pushstr(getds(str));
834 			return getnext();
835 
836 		/* macro args */
837 		case '$':
838 			r = getrune();
839 			if(r < '1' || r > '9'){
840 				token[0] = '\\';
841 				token[1] = '$';
842 				token[2] = r;
843 				token[3] = 0;
844 				return token;
845 			}
846 			r -= '0';
847 			if(msp >= 0)
848 				pushstr(mstack[msp].argv[r]);
849 			return getnext();
850 
851 		/* special chars */
852 		case '(':
853 			token[0] = getrune();
854 			token[1] = getrune();
855 			token[2] = 0;
856 			for(t = tspec; t->name; t++)
857 				if(strcmp(token, t->name) == 0)
858 					return t->value;
859 			return "&#191;";
860 
861 		/* ignore immediately following newline */
862 		case 'c':
863 			r = getrune();
864 			if (r == '\n') {
865 				sol = ignore_nl = 1;
866 				if (indirective)
867 					break;
868 				}
869 			else
870 				ungetrune();
871 			return getnext();
872 
873 		/* escape backslash */
874 		case 'e':
875 			return "\\";
876 			break;
877 
878 		/* font change */
879 		case 'f':
880 			r = getrune();
881 			switch(r){
882 			case '(':
883 				str[0] = getrune();
884 				str[1] = getrune();
885 				str[2] = 0;
886 				token[0] = 0;
887 				if(strcmp("BI", str) == 0)
888 					return changefont(&bifont);
889 				else if(strcmp("CW", str) == 0)
890 					return changefont(&cwfont);
891 				else
892 					return changefont(nil);
893 			case '3':
894 			case 'B':
895 				return changefont(&bfont);
896 			case '2':
897 			case 'I':
898 				return changefont(&ifont);
899 			case '4':
900 				return changefont(&bifont);
901 			case '5':
902 				return changefont(&cwfont);
903 			case 'P':
904 				return changefont(prevfont);
905 			case 'R':
906 			default:
907 				return changefont(nil);
908 			}
909 
910 		/* number register */
911 		case 'n':
912 			r = getrune();
913 			if (r == '(') /*)*/ {
914 				r = getrune();
915 				if (r < 0)
916 					return nil;
917 				str[0] = r;
918 				r = getrune();
919 				if (r < 0)
920 					return nil;
921 				str[1] = r;
922 				str[2] = 0;
923 				}
924 			else {
925 				str[0] = r;
926 				str[1] = 0;
927 				}
928 			pushstr(getnr(str));
929 			return getnext();
930 
931 		/* font size */
932 		case 's':
933 			r = getrune();
934 			switch(r){
935 			case '0':
936 				return changesize(0);
937 			case '-':
938 				r = getrune();
939 				if (!isdigit(r))
940 					return getnext();
941 				return changesize(-(r - '0'));
942 			case '+':
943 				r = getrune();
944 				if (!isdigit(r))
945 					return getnext();
946 				return changesize(r - '0');
947 			}
948 			return getnext();
949 		/* vertical movement */
950 		case 'v':
951 			r = getrune();
952 			if(r != '\''){
953 				ungetrune();
954 				return getnext();
955 			}
956 			r = getrune();
957 			if(r != '-')
958 				vert--;
959 			else
960 				vert++;
961 			while(r != '\'' && r != '\n')
962 				r = getrune();
963 			if(r != '\'')
964 				ungetrune();
965 
966 			if(vert > 0)
967 				return "^";
968 			return getnext();
969 
970 
971 		/* horizontal line */
972 		case 'l':
973 			r = getrune();
974 			if(r != '\''){
975 				ungetrune();
976 				return "<HR>";
977 			}
978 			while(getrune() != '\'')
979 				;
980 			return "<HR>";
981 
982 		/* character height and slant */
983 		case 'S':
984 		case 'H':
985 			r = getrune();
986 			if(r != '\''){
987 				ungetrune();
988 				return "<HR>";
989 			}
990 			while(getrune() != '\'')
991 				;
992 			return getnext();
993 
994 		/* digit-width space */
995 		case '0':
996 			return " ";
997 
998 		/*for .if, .ie, .el */
999 		case '{':
1000 			return "\\{"; /*}*/
1001 		case '}':
1002 			return "";
1003 		/* up and down */
1004 		case 'u':
1005 			if (isdown) {
1006 				isdown = 0;
1007 				return "</sub>";
1008 			}
1009 			isup = 1;
1010 			return "<sup>";
1011 		case 'd':
1012 			if (isup) {
1013 				isup = 0;
1014 				return "</sup>";
1015 			}
1016 			isdown = 1;
1017 			return "<sub>";
1018 		}
1019 		break;
1020 	case '&':
1021 		if(msp >= 0 || strsp >= 0)
1022 			return "&";
1023 		return "&amp;";
1024 	case '<':
1025 		if(msp >= 0 || strsp >= 0)
1026 			return "<";
1027 		return "&#60;";
1028 	case '>':
1029 		if(msp >= 0 || strsp >= 0)
1030 			return ">";
1031 		return "&#62;";
1032 	}
1033 	if (r < Runeself) {
1034 		token[0] = r;
1035 		token[1] = 0;
1036 		}
1037 	else {
1038 		R = r;
1039 		token[runetochar(token,&R)] = 0;
1040 	}
1041 	return token;
1042 }
1043 
1044 char*
1045 copyline(char *p, char *e, int arg0)
1046 {
1047 	int c;
1048 	Rune r;
1049 	char *p1;
1050 
1051 	while((c = getrune()) == ' ' || c == '\t')
1052 		;
1053 	for(indirective = 1; p < e; c = getrune()) {
1054 		if (c < 0)
1055 			goto done;
1056 		switch(c) {
1057 		case '\\':
1058 			break;
1059 		case '\n':
1060 			if (arg0)
1061 				ungetrune();
1062 			goto done;
1063 		case ' ':
1064 		case '\t':
1065 			if (arg0)
1066 				goto done;
1067 		default:
1068 			r = c;
1069 			p += runetochar(p,&r);
1070 			continue;
1071 		}
1072 		ungetrune();
1073 		p1 = getnext();
1074 		if (p1 == nil)
1075 			goto done;
1076 		if (*p1 == '\n') {
1077 			if (arg0)
1078 				ungetrune();
1079 			break;
1080 		}
1081 		while((*p = *p1++) && p < e)
1082 			p++;
1083 	}
1084 done:
1085 	indirective = 0;
1086 	*p++ = 0;
1087 	return p;
1088 }
1089 
1090 char*
1091 copyarg(char *p, char *e, int *nullarg)
1092 {
1093 	int c, quoted;
1094 	Rune r;
1095 	char *p1;
1096 
1097 	*nullarg = 0;
1098 	quoted = 0;
1099 	do{
1100 		c = getrune();
1101 	} while(c == ' ' || c == '\t');
1102 
1103 	if(c == '"'){
1104 		quoted = 1;
1105 		*nullarg = 1;
1106 		c = getrune();
1107 	}
1108 
1109 	if(c == '\n')
1110 		goto done;
1111 
1112 	for(; p < e; c = getrune()) {
1113 		if (c < 0)
1114 			break;
1115 		switch(c) {
1116 		case '\\':
1117 			break;
1118 		case '\n':
1119 			ungetrune();
1120 			goto done;
1121 		case ' ':
1122 		case '\t':
1123 			if(!quoted)
1124 				goto done;
1125 			r = c;
1126 			p += runetochar(p,&r);
1127 			continue;
1128 		case '"':
1129 			if(quoted)
1130 				goto done;
1131 			r = c;
1132 			p += runetochar(p,&r);
1133 			continue;
1134 		default:
1135 			r = c;
1136 			p += runetochar(p,&r);
1137 			continue;
1138 		}
1139 		ungetrune();
1140 		p1 = getnext();
1141 		if(p1 == nil)
1142 			break;
1143 		if(*p1 == '\n')
1144 			break;
1145 		while((*p = *p1++) && p < e)
1146 			p++;
1147 	}
1148 done:
1149 	*p++ = 0;
1150 	return p;
1151 
1152 }
1153 
1154 int
1155 parseargs(char *p, char *e, char **argv)
1156 {
1157 	int argc;
1158 	char *np;
1159 	int nullarg;
1160 
1161 	indirective = 1;
1162 	*p++ = 0;
1163 	for(argc = 1; argc < Narg; argc++){
1164 		np = copyarg(p, e, &nullarg);
1165 		if(nullarg==0 && np == p+1)
1166 			break;
1167 		argv[argc] = p;
1168 		p = np;
1169 	}
1170 	argv[argc] = nil;
1171 	indirective = 0;
1172 
1173 
1174 	return argc;
1175 }
1176 
1177 void
1178 dodirective(void)
1179 {
1180 	char *p, *e;
1181 	Goobie *g;
1182 	Goobieif *gif;
1183 	char line[Nline], *line1;
1184 	int i, argc;
1185 	char *argv[Narg];
1186 	Mstack *m;
1187 
1188 	/* read line, translate special bytes */
1189 	e = line + sizeof(line) - UTFmax - 1;
1190 	line1 = copyline(line, e, 1);
1191 	if (!line[0])
1192 		return;
1193 	argv[0] = line;
1194 
1195 	/* first look through user defined macros */
1196 	p = getmacro(argv[0]);
1197 	if(p != nil){
1198 		if(msp == Maxmstack-1){
1199 			fprint(2, "ms2html: macro stack overflow\n");
1200 			return;
1201 		}
1202 		argc = parseargs(line1, e, argv);
1203 		m = &mstack[++msp];
1204 		m->ptr = p;
1205 		memset(m->argv, 0, sizeof(m->argv));
1206 		for(i = 0; i < argc; i++)
1207 			m->argv[i] = strdup(argv[i]);
1208 		return;
1209 	}
1210 
1211 	/* check for .if or .ie */
1212 	for(gif = gtabif; gif->name; gif++)
1213 		if(strcmp(gif->name, argv[0]) == 0){
1214 			(*gif->f)(line1, e);
1215 			return;
1216 		}
1217 
1218 	argc = parseargs(line1, e, argv);
1219 
1220 	/* try standard ms macros */
1221 	for(g = gtab; g->name; g++)
1222 		if(strcmp(g->name, argv[0]) == 0){
1223 			(*g->f)(argc, argv);
1224 			return;
1225 		}
1226 
1227 	if(debug)
1228 		fprint(2, "stdin %d(%s:%d): unknown directive %s\n",
1229 			ssp->lno, ssp->filename, ssp->rlno, line);
1230 }
1231 
1232 void
1233 printarg(char *a)
1234 {
1235 	char *e, *p;
1236 
1237 	e = a + strlen(a);
1238 	pushstr(a);
1239 	while(strsp >= 0 && strstack[strsp] >= a && strstack[strsp] < e){
1240 		p = getnext();
1241 		if(p == nil)
1242 			return;
1243 		Bprint(&bout, "%s", p);
1244 	}
1245 }
1246 
1247 void
1248 printargs(int argc, char **argv)
1249 {
1250 	argc--;
1251 	argv++;
1252 	while(--argc > 0){
1253 		printarg(*argv++);
1254 		Bprint(&bout, " ");
1255 	}
1256 	if(argc == 0)
1257 		printarg(*argv);
1258 }
1259 
1260 void
1261 dohangingdt(void)
1262 {
1263 	switch(hangingdt){
1264 	case 3:
1265 		hangingdt--;
1266 		break;
1267 	case 2:
1268 		Bprint(&bout, "<dd>");
1269 		hangingdt = 0;
1270 		break;
1271 	}
1272 
1273 }
1274 
1275 void
1276 dohangingau(void)
1277 {
1278 	if(hangingau == 0)
1279 		return;
1280 	Bprint(&bout, "</I></DL>\n");
1281 	hangingau = 0;
1282 }
1283 
1284 void
1285 dohanginghead(void)
1286 {
1287 	if(hanginghead == 0)
1288 		return;
1289 	Bprint(&bout, "</H%d>\n", hanginghead);
1290 	hanginghead = 0;
1291 }
1292 
1293 /*
1294  *  convert a man page to html and output
1295  */
1296 void
1297 doconvert(void)
1298 {
1299 	char c, *p;
1300 	Tm *t;
1301 
1302 	pushsrc(nil);
1303 
1304 	sol = 1;
1305 	Bprint(&bout, "<html>\n");
1306 	Bflush(&bout);
1307 	for(;;){
1308 		p = getnext();
1309 		if(p == nil)
1310 			break;
1311 		c = *p;
1312 		if(c == '.' && sol){
1313 			dodirective();
1314 			dohangingdt();
1315 			ssp->lno++;
1316 			ssp->rlno++;
1317 			sol = 1;
1318 		} else if(c == '\n'){
1319 			if (ignore_nl)
1320 				ignore_nl = 0;
1321 			else {
1322 				if(hangingau)
1323 					Bprint(&bout, "<br>\n");
1324 				else
1325 					Bprint(&bout, "%s", p);
1326 				dohangingdt();
1327 				}
1328 			ssp->lno++;
1329 			ssp->rlno++;
1330 			sol = 1;
1331 		} else{
1332 			Bprint(&bout, "%s", p);
1333 			ignore_nl = sol = 0;
1334 		}
1335 	}
1336 	dohanginghead();
1337 	dohangingdt();
1338 	closel();
1339 	if(curfont)
1340 		Bprint(&bout, "%s", curfont->end);
1341 	Bprint(&bout, "<br>&#32;<br>\n");
1342 	Bprint(&bout, "<A href=http://www.lucent.com/copyright.html>\n");
1343 	t = localtime(time(nil));
1344 	Bprint(&bout, "Copyright</A> &#169; %d Lucent Technologies Inc.  All rights reserved.\n",
1345 			t->year+1900);
1346 	Bprint(&bout, "</body></html>\n");
1347 }
1348 
1349 static void
1350 usage(void)
1351 {
1352 	sysfatal("Usage: %s\n", argv0);
1353 }
1354 
1355 void
1356 main(int argc, char **argv)
1357 {
1358 	quiet = 1;
1359 	ARGBEGIN {
1360 	case 'q':
1361 		quiet = 0;
1362 		break;
1363 	case 'd':
1364 		delim = EARGF(usage());
1365 		break;
1366 	case '?':
1367 	default:
1368 		usage();
1369 	} ARGEND;
1370 
1371 	Binit(&bout, 1, OWRITE);
1372 
1373 	ds("R", "&#174;");
1374 
1375 	doconvert();
1376 	exits(nil);
1377 }
1378 
1379 void
1380 g_notyet(int, char **argv)
1381 {
1382 	fprint(2, "ms2html: .%s not yet supported\n", argv[0]);
1383 }
1384 
1385 void
1386 g_ignore(int, char **argv)
1387 {
1388 	if(quiet)
1389 		return;
1390 	fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]);
1391 }
1392 
1393 void
1394 g_PP(int, char**)
1395 {
1396 	dohanginghead();
1397 	closel();
1398 	closefont();
1399 	Bprint(&bout, "<P>\n");
1400 	paragraph = 1;
1401 }
1402 
1403 void
1404 g_LP(int, char**)
1405 {
1406 	dohanginghead();
1407 	closel();
1408 	closefont();
1409 	Bprint(&bout, "<br>&#32;<br>\n");
1410 }
1411 
1412 /* close a list */
1413 void
1414 closel(void)
1415 {
1416 	g_P2(1, nil);
1417 	dohangingau();
1418 	if(paragraph){
1419 		Bprint(&bout, "</P>\n");
1420 		paragraph = 0;
1421 	}
1422 	switch(list){
1423 	case Lordered:
1424 		Bprint(&bout, "</ol>\n");
1425 		break;
1426 	case Lunordered:
1427 		Bprint(&bout, "</ul>\n");
1428 		break;
1429 	case Lother:
1430 	case Ldef:
1431 		Bprint(&bout, "</dl>\n");
1432 		break;
1433 	}
1434 	list = 0;
1435 
1436 }
1437 
1438 
1439 void
1440 g_IP(int argc, char **argv)
1441 {
1442 	switch(list){
1443 	default:
1444 		closel();
1445 		if(argc > 1){
1446 			if(strcmp(argv[1], "1") == 0){
1447 				list = Lordered;
1448 				listnum = 1;
1449 				Bprint(&bout, "<OL>\n");
1450 			} else if(strcmp(argv[1], "\\(bu") == 0){
1451 				list = Lunordered;
1452 				Bprint(&bout, "<UL>\n");
1453 			} else {
1454 				list = Lother;
1455 				Bprint(&bout, "<DL COMPACT>\n");
1456 			}
1457 		} else {
1458 			list = Lother;
1459 			Bprint(&bout, "<DL>\n");
1460 		}
1461 		break;
1462 	case Lother:
1463 	case Lordered:
1464 	case Lunordered:
1465 		break;
1466 	}
1467 
1468 	switch(list){
1469 	case Lother:
1470 		Bprint(&bout, "<DT>");
1471 		if(argc > 1)
1472 			printarg(argv[1]);
1473 		else
1474 			Bprint(&bout, "<DT>&#32;");
1475 		Bprint(&bout, "<DD>\n");
1476 		break;
1477 	case Lordered:
1478 	case Lunordered:
1479 		Bprint(&bout, "<LI>\n");
1480 		break;
1481 	}
1482 }
1483 
1484 /*
1485  *  .5i is one <DL><DT><DD>
1486  */
1487 void
1488 g_in(int argc, char **argv)
1489 {
1490 	float	f;
1491 	int	delta, x;
1492 	char	*p;
1493 
1494 	f = indent/0.5;
1495 	delta = f;
1496 	if(argc <= 1){
1497 		indent = 0.0;
1498 	} else {
1499 		f = strtod(argv[1], &p);
1500 		switch(*p){
1501 		case 'i':
1502 			break;
1503 		case 'c':
1504 			f = f / 2.54;
1505 			break;
1506 		case 'P':
1507 			f = f / 6;
1508 			break;
1509 		default:
1510 		case 'u':
1511 		case 'm':
1512 			f = f * (12 / 72);
1513 			break;
1514 		case 'n':
1515 			f = f * (6 / 72);
1516 			break;
1517 		case 'p':
1518 			f = f / 72.0;
1519 			break;
1520 		}
1521 		switch(argv[1][0]){
1522 		case '+':
1523 		case '-':
1524 			indent += f;
1525 			break;
1526 		default:
1527 			indent = f;
1528 			break;
1529 		}
1530 	}
1531 	if(indent < 0.0)
1532 		indent = 0.0;
1533 	f = (indent/0.5);
1534 	x = f;
1535 	delta = x - delta;
1536 	while(delta < 0){
1537 		Bprint(&bout, "</DL>\n");
1538 		delta++;
1539 	}
1540 	while(delta > 0){
1541 		Bprint(&bout, "<DL><DT><DD>\n");
1542 		delta--;
1543 	}
1544 }
1545 
1546 void
1547 g_HP(int, char**)
1548 {
1549 	switch(list){
1550 	default:
1551 		closel();
1552 		list = Ldef;
1553 		hangingdt = 1;
1554 		Bprint(&bout, "<DL><DT>\n");
1555 		break;
1556 	case Ldef:
1557 		if(hangingdt)
1558 			Bprint(&bout, "<DD>");
1559 		Bprint(&bout, "<DT>");
1560 		hangingdt = 1;
1561 		break;
1562 	}
1563 }
1564 
1565 void
1566 g_SH(int, char**)
1567 {
1568 	dohanginghead();
1569 	closel();
1570 	closefont();
1571 	Bprint(&bout, "<H%d>", HH);
1572 	hanginghead = HH;
1573 }
1574 
1575 void
1576 g_NH(int argc, char **argv)
1577 {
1578 	int i, level;
1579 
1580 	closel();
1581 	closefont();
1582 
1583 	if(argc == 1)
1584 		level = 0;
1585 	else {
1586 		level = atoi(argv[1])-1;
1587 		if(level < 0 || level >= Maxnh)
1588 			level = Maxnh - 1;
1589 	}
1590 	nh[level]++;
1591 
1592 	Bprint(&bout, "<H%d>", HH);
1593 	hanginghead = HH;
1594 
1595 	Bprint(&bout, "%d", nh[0]);
1596 	for(i = 1; i <= level; i++)
1597 		Bprint(&bout, ".%d", nh[i]);
1598 	Bprint(&bout, " ");
1599 
1600 	for(i = level+1; i < Maxnh; i++)
1601 		nh[i] = 0;
1602 }
1603 
1604 void
1605 g_TL(int, char**)
1606 {
1607 	char *p, *np;
1608 	char name[128];
1609 
1610 	closefont();
1611 
1612 	if(!titleseen){
1613 		/* get base part of filename */
1614 		p = strrchr(ssp->filename, '/');
1615 		if(p == nil)
1616 			p = ssp->filename;
1617 		else
1618 			p++;
1619 		strncpy(name, p, sizeof(name));
1620 		name[sizeof(name)-1] = 0;
1621 
1622 		/* dump any extensions */
1623 		np = strchr(name, '.');
1624 		if(np)
1625 			*np = 0;
1626 
1627 		Bprint(&bout, "<title>\n");
1628 		Bprint(&bout, "%s\n", p);
1629 		Bprint(&bout, "</title>\n");
1630 		Bprint(&bout, "<body BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#330088\" ALINK=\"#FF0044\">\n");
1631 		titleseen = 1;
1632 	}
1633 
1634 	Bprint(&bout, "<H%d>", 1);
1635 	hanginghead = 1;
1636 }
1637 
1638 void
1639 g_AU(int, char**)
1640 {
1641 	closel();
1642 	dohanginghead();
1643 	Bprint(&bout, "<DL><DD><I>");
1644 	hangingau = 1;
1645 }
1646 
1647 void
1648 setfont(Font *f)
1649 {
1650 	if(curfont != nil)
1651 		Bprint(&bout, "%s", curfont->end);
1652 	prevfont = curfont;
1653 	if(f != nil)
1654 		Bprint(&bout, "%s", f->start);
1655 	curfont = f;
1656 }
1657 
1658 /*
1659  *  for 3 args print arg3 \fxarg1\fP arg2
1660  *  for 2 args print arg1 \fxarg2\fP
1661  *  for 1 args print \fxarg1\fP
1662  */
1663 void
1664 font(Font *f, int argc, char **argv)
1665 {
1666 	Font *prev;
1667 
1668 	if(argc == 1){
1669 		setfont(nil);
1670 		return;
1671 	}
1672 	if(argc > 3)
1673 		printarg(argv[3]);
1674 	prev = prevfont;
1675 	setfont(f);
1676 	printarg(argv[1]);
1677 	setfont(prevfont);
1678 	prevfont = prev;
1679 	if(argc > 2)
1680 		printarg(argv[2]);
1681 	Bprint(&bout, "\n");
1682 }
1683 
1684 void
1685 closefont(void)
1686 {
1687 	if(curfont != nil)
1688 		Bprint(&bout, "%s", curfont->end);
1689 	curfont = nil;
1690 	prevfont = nil;
1691 }
1692 
1693 void
1694 g_B(int argc, char **argv)
1695 {
1696 	font(&bfont, argc, argv);
1697 }
1698 
1699 void
1700 g_R(int argc, char **argv)
1701 {
1702 	font(nil, argc, argv);
1703 }
1704 
1705 void
1706 g_BI(int argc, char **argv)
1707 {
1708 	font(&bifont, argc, argv);
1709 }
1710 
1711 void
1712 g_CW(int argc, char **argv)
1713 {
1714 	font(&cwfont, argc, argv);
1715 }
1716 
1717 char*
1718 lower(char *p)
1719 {
1720 	char *x;
1721 
1722 	for(x = p; *x; x++)
1723 		if(*x >= 'A' && *x <= 'Z')
1724 			*x -= 'A' - 'a';
1725 	return p;
1726 }
1727 
1728 void
1729 g_I(int argc, char **argv)
1730 {
1731 	int anchor;
1732 	char *p;
1733 
1734 	anchor = 0;
1735 	if(argc > 2){
1736 		p = argv[2];
1737 		if(p[0] == '(')
1738 		if(p[1] >= '0' && p[1] <= '9')
1739 		if(p[2] == ')'){
1740 			anchor = 1;
1741 			Bprint(&bout, "<A href=\"/magic/man2html/%c/%s\">",
1742 				p[1], lower(argv[1]));
1743 		}
1744 	}
1745 	font(&ifont, argc, argv);
1746 	if(anchor)
1747 		Bprint(&bout, "</A>");
1748 }
1749 
1750 void
1751 g_br(int, char**)
1752 {
1753 	if(hangingdt){
1754 		Bprint(&bout, "<dd>");
1755 		hangingdt = 0;
1756 	}else
1757 		Bprint(&bout, "<br>\n");
1758 }
1759 
1760 void
1761 g_P1(int, char**)
1762 {
1763 	if(example == 0){
1764 		example = 1;
1765 		Bprint(&bout, "<DL><DT><DD><TT><PRE>\n");
1766 	}
1767 }
1768 
1769 void
1770 g_P2(int, char**)
1771 {
1772 	if(example){
1773 		example = 0;
1774 		Bprint(&bout, "</PRE></TT></DL>\n");
1775 	}
1776 }
1777 
1778 void
1779 g_SM(int, char **argv)
1780 {
1781 	Bprint(&bout, "%s", argv[1]);
1782 }
1783 
1784 void
1785 g_ft(int argc, char **argv)
1786 {
1787 	if(argc < 2){
1788 		setfont(nil);
1789 		return;
1790 	}
1791 
1792 	switch(argv[1][0]){
1793 	case '3':
1794 	case 'B':
1795 		setfont(&bfont);
1796 		break;
1797 	case '2':
1798 	case 'I':
1799 		setfont(&ifont);
1800 		break;
1801 	case '4':
1802 		setfont(&bifont);
1803 		break;
1804 	case '5':
1805 		setfont(&cwfont);
1806 		break;
1807 	case 'P':
1808 		setfont(prevfont);
1809 		break;
1810 	case 'R':
1811 	default:
1812 		setfont(nil);
1813 		break;
1814 	}
1815 }
1816 
1817 void
1818 g_sp(int argc, char **argv)
1819 {
1820 	int n;
1821 
1822 	n = 1;
1823 	if(argc > 1){
1824 		n = atoi(argv[1]);
1825 		if(n < 1)
1826 			n = 1;
1827 		if(argv[1][strlen(argv[1])-1] == 'i')
1828 			n *= 4;
1829 	}
1830 	if(n > 5){
1831 		Bprint(&bout, "<br>&#32;<br>\n");
1832 		Bprint(&bout, "<HR>\n");
1833 		Bprint(&bout, "<br>&#32;<br>\n");
1834 	} else
1835 		for(; n > 0; n--)
1836 			Bprint(&bout, "<br>&#32;<br>\n");
1837 }
1838 
1839  void
1840 rm_loop(char *name, String **l)
1841 {
1842 	String *s;
1843 	for(s = *l; s != nil; s = *l){
1844 		if(strcmp(name, s->name) == 0){
1845 			*l = s->next;
1846 			free(s->name);
1847 			free(s->val);
1848 			free(s);
1849 			break;
1850 			}
1851 		l = &s->next;
1852 		}
1853 	}
1854 
1855 void
1856 g_rm(int argc, char **argv)
1857 {
1858 	Goobie *g;
1859 	char *name;
1860 	int i;
1861 
1862 	for(i = 1; i < argc; i++) {
1863 		name = argv[i];
1864 		rm_loop(name, &strings);
1865 		rm_loop(name, &macros);
1866 		for(g = gtab; g->name; g++)
1867 			if (strcmp(g->name, name) == 0) {
1868 				g->f = g_ignore;
1869 				break;
1870 				}
1871 		}
1872 	}
1873 
1874 void
1875 g_AB(int, char**)
1876 {
1877 	closel();
1878 	Bprint(&bout, "<DL><DD><H4>ABSTRACT</H4>\n");
1879 }
1880 
1881 void
1882 g_AE(int, char**)
1883 {
1884 	Bprint(&bout, "</DL>\n");
1885 }
1886 
1887 void
1888 g_FS(int, char **)
1889 {
1890 	char *argv[3];
1891 
1892 	argv[0] = "IP";
1893 	argv[1] = nil;
1894 	argv[2] = nil;
1895 	g_IP(1, argv);
1896 	Bprint(&bout, "NOTE:<I> ");
1897 }
1898 
1899 void
1900 g_FE(int, char **)
1901 {
1902 	Bprint(&bout, "</I><DT>&#32;<DD>");
1903 	closel();
1904 	Bprint(&bout, "<br>\n");
1905 }
1906 
1907 void
1908 g_de(int argc, char **argv)
1909 {
1910 	int r;
1911 	char *p, *cp;
1912 	String *m;
1913 	int len;
1914 
1915 	if(argc < 2)
1916 		return;
1917 
1918 	m = nil;
1919 	len = 0;
1920 	if(strcmp(argv[0], "am") == 0){
1921 		for(m = macros; m != nil; m = m->next)
1922 			if(strcmp(argv[1], m->name) == 0){
1923 				len = strlen(m->val);
1924 				break;
1925 			}
1926 
1927 		if(m == nil){
1928 			/* nothing to append to */
1929 			for(;;){
1930 				p = Brdline(&ssp->in, '\n');
1931 				if(p == nil)
1932 					break;
1933 				p[Blinelen(&ssp->in)-1] = 0;
1934 				if(strcmp(p, "..") == 0)
1935 					break;
1936 			}
1937 			return;
1938 		}
1939 	}
1940 
1941 	if(m == nil){
1942 		m = emalloc(sizeof(*m));
1943 		m->next = macros;
1944 		macros = m;
1945 		m->name = strdup(argv[1]);
1946 		m->val = nil;
1947 		len = 0;
1948 	}
1949 
1950 	/* read up to a .. removing double backslashes */
1951 	for(;;){
1952 		p = Brdline(&ssp->in, '\n');
1953 		if(p == nil)
1954 			break;
1955 		p[Blinelen(&ssp->in)-1] = 0;
1956 		if(strcmp(p, "..") == 0)
1957 			break;
1958 		m->val = realloc(m->val, len + Blinelen(&ssp->in)+1);
1959 		cp = m->val + len;
1960 		while(*p){
1961 			r = *p++;
1962 			if(r == '\\' && *p == '\\')
1963 				p++;
1964 			*cp++ = r;
1965 		}
1966 		*cp++ = '\n';
1967 		len = cp - m->val;
1968 		*cp = 0;
1969 	}
1970 }
1971 
1972 void
1973 g_hrule(int, char**)
1974 {
1975 	Bprint(&bout, "<HR>\n");
1976 }
1977 
1978 void
1979 g_BX(int argc, char **argv)
1980 {
1981 	Bprint(&bout, "<HR>\n");
1982 	printargs(argc, argv);
1983 	Bprint(&bout, "<HR>\n");
1984 }
1985 
1986 void
1987 g_IH(int, char**)
1988 {
1989 	Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n");
1990 }
1991 
1992 void
1993 g_MH(int, char**)
1994 {
1995 	Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n");
1996 }
1997 
1998 void
1999 g_PY(int, char**)
2000 {
2001 	Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n");
2002 }
2003 
2004 void
2005 g_HO(int, char**)
2006 {
2007 	Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n");
2008 }
2009 
2010 void
2011 g_QS(int, char**)
2012 {
2013 	Bprint(&bout, "<BLOCKQUOTE>\n");
2014 }
2015 
2016 void
2017 g_QE(int, char**)
2018 {
2019 	Bprint(&bout, "</BLOCKQUOTE>\n");
2020 }
2021 
2022 void
2023 g_RS(int, char**)
2024 {
2025 	Bprint(&bout, "<DL><DD>\n");
2026 }
2027 
2028 void
2029 g_RE(int, char**)
2030 {
2031 	Bprint(&bout, "</DL>\n");
2032 }
2033 
2034 int gif;
2035 
2036 void
2037 g_startgif(int, char **argv)
2038 {
2039 	int fd;
2040 	int pfd[2];
2041 	char *e, *p;
2042 	char name[32];
2043 	Dir *d;
2044 
2045 	if(strcmp(argv[0], "EQ") == 0)
2046 		e = ".EN";
2047 	else if(strcmp(argv[0], "TS") == 0)
2048 		e = ".TE";
2049 	else if(strcmp(argv[0], "PS") == 0)
2050 		e = ".PE";
2051 	else
2052 		return;
2053 
2054 	p = strrchr(sstack[0].filename, '/');
2055 	if(p != nil)
2056 		p++;
2057 	else
2058 		p = sstack[0].filename;
2059 	snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++);
2060 	fd = create(name, OWRITE, 0664);
2061 	if(fd < 0){
2062 		fprint(2, "ms2html: can't create %s: %r\n", name);
2063 		return;
2064 	}
2065 
2066 	if(pipe(pfd) < 0){
2067 		fprint(2, "ms2html: can't create pipe: %r\n");
2068 		close(fd);
2069 		return;
2070 	}
2071 	switch(rfork(RFFDG|RFPROC)){
2072 	case -1:
2073 		fprint(2, "ms2html: can't fork: %r\n");
2074 		close(fd);
2075 		return;
2076 	case 0:
2077 		dup(fd, 1);
2078 		close(fd);
2079 		dup(pfd[0], 0);
2080 		close(pfd[0]);
2081 		close(pfd[1]);
2082 		execl("/bin/troff2gif", "troff2gif", 0);
2083 		fprint(2, "ms2html: couldn't exec troff2gif: %r\n");
2084 		_exits(nil);
2085 	default:
2086 		close(fd);
2087 		close(pfd[0]);
2088 		fprint(pfd[1], ".ll 7i\n");
2089 		fprint(pfd[1], ".EQ\ndelim %s\n.EN\n", delim);
2090 		fprint(pfd[1], ".%s\n", argv[0]);
2091 		for(;;){
2092 			p = Brdline(&ssp->in, '\n');
2093 			if(p == nil)
2094 				break;
2095 			ssp->lno++;
2096 			ssp->rlno++;
2097 			if(write(pfd[1], p, Blinelen(&ssp->in)) < 0)
2098 				break;
2099 			if(strncmp(p, e, 3) == 0)
2100 				break;
2101 		}
2102 		close(pfd[1]);
2103 		waitpid();
2104 		d = dirstat(name);
2105 		if(d == nil)
2106 			break;
2107 		if(d->length == 0){
2108 			remove(name);
2109 			free(d);
2110 			break;
2111 		}
2112 		free(d);
2113 		fprint(2, "ms2html: created auxiliary file %s\n", name);
2114 		Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2115 		break;
2116 	}
2117 }
2118 
2119 void
2120 g_lf(int argc, char **argv)
2121 {
2122 	if(argc > 2)
2123 		snprint(ssp->filename, sizeof(ssp->filename), argv[2]);
2124 	if(argc > 1)
2125 		ssp->rlno = atoi(argv[1]);
2126 }
2127 
2128 void
2129 g_so(int argc, char **argv)
2130 {
2131 	ssp->lno++;
2132 	ssp->rlno++;
2133 	if(argc > 1)
2134 		pushsrc(argv[1]);
2135 }
2136 
2137 
2138 void
2139 g_BP(int argc, char **argv)
2140 {
2141 	int fd;
2142 	char *p, *ext;
2143 	char name[32];
2144 	Dir *d;
2145 
2146 	if(argc < 2)
2147 		return;
2148 
2149 	p = strrchr(argv[1], '/');
2150 	if(p != nil)
2151 		p++;
2152 	else
2153 		p = argv[1];
2154 
2155 
2156 	ext = strrchr(p, '.');
2157 	if(ext){
2158 		if(strcmp(ext, ".jpeg") == 0
2159 		|| strcmp(ext, ".gif") == 0){
2160 			Bprint(&bout, "<br><img src=\"%s\"><br>\n", argv[1]);
2161 			return;
2162 		}
2163 	}
2164 
2165 
2166 	snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++);
2167 	fd = create(name, OWRITE, 0664);
2168 	if(fd < 0){
2169 		fprint(2, "ms2html: can't create %s: %r\n", name);
2170 		return;
2171 	}
2172 
2173 	switch(rfork(RFFDG|RFPROC)){
2174 	case -1:
2175 		fprint(2, "ms2html: can't fork: %r\n");
2176 		close(fd);
2177 		return;
2178 	case 0:
2179 		dup(fd, 1);
2180 		close(fd);
2181 		execl("/bin/ps2gif", "ps2gif", argv[1], 0);
2182 		fprint(2, "ms2html: couldn't exec ps2gif: %r\n");
2183 		_exits(nil);
2184 	default:
2185 		close(fd);
2186 		waitpid();
2187 		d = dirstat(name);
2188 		if(d == nil)
2189 			break;
2190 		if(d->length == 0){
2191 			remove(name);
2192 			free(d);
2193 			break;
2194 		}
2195 		free(d);
2196 		fprint(2, "ms2html: created auxiliary file %s\n", name);
2197 		Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2198 		break;
2199 	}
2200 }
2201 
2202 /* insert straight HTML into output */
2203 void
2204 g__H(int argc, char **argv)
2205 {
2206 	int i;
2207 
2208 	for(i = 1; i < argc; i++)
2209 		Bprint(&bout, "%s ", argv[i]);
2210 	Bprint(&bout, "\n");
2211 }
2212 
2213 /* HTML page title */
2214 void
2215 g__T(int argc, char **argv)
2216 {
2217 	if(titleseen)
2218 		return;
2219 
2220 	Bprint(&bout, "<title>\n");
2221 	printargs(argc, argv);
2222 	Bprint(&bout, "</title></head><body>\n");
2223 	titleseen = 1;
2224 }
2225 
2226 void
2227 g_nr(int argc, char **argv)
2228 {
2229 	char *val;
2230 
2231 	if (argc > 1) {
2232 		if (argc == 2)
2233 			val = "0";
2234 		else
2235 			val = argv[2];
2236 		dsnr(argv[1], val, &numregs);
2237 	}
2238 }
2239 
2240 void
2241 zerodivide(void)
2242 {
2243 	fprint(2, "stdin %d(%s:%d): division by 0\n",
2244 		ssp->lno, ssp->filename, ssp->rlno);
2245 }
2246 
2247 int
2248 numval(char **pline, int recur)
2249 {
2250 	char *p;
2251 	int neg, x, y;
2252 
2253 	x = neg = 0;
2254 	p = *pline;
2255 	while(*p == '-') {
2256 		neg = 1 - neg;
2257 		p++;
2258 	}
2259 	if (*p == '(') {
2260 		p++;
2261 		x = numval(&p, 1);
2262 		if (*p != ')')
2263 			goto done;
2264 		p++;
2265 	}
2266 	else while(*p >= '0' && *p <= '9')
2267 		x = 10*x + *p++ - '0';
2268 	if (neg)
2269 		x = -x;
2270 	if (recur)
2271 	    for(;;) {
2272 		switch(*p++) {
2273 		case '+':
2274 			x += numval(&p, 0);
2275 			continue;
2276 		case '-':
2277 			x -= numval(&p, 0);
2278 			continue;
2279 		case '*':
2280 			x *= numval(&p, 0);
2281 			continue;
2282 		case '/':
2283 			y = numval(&p, 0);
2284 			if (y == 0) {
2285 				zerodivide();
2286 				x = 0;
2287 				goto done;
2288 			}
2289 			x /= y;
2290 			continue;
2291 		case '<':
2292 			if (*p == '=') {
2293 				p++;
2294 				x = x <= numval(&p, 0);
2295 				continue;
2296 			}
2297 			x = x < numval(&p, 0);
2298 			continue;
2299 		case '>':
2300 			if (*p == '=') {
2301 				p++;
2302 				x = x >= numval(&p, 0);
2303 				continue;
2304 			}
2305 			x = x > numval(&p, 0);
2306 			continue;
2307 		case '=':
2308 			if (*p == '=')
2309 				p++;
2310 			x = x == numval(&p, 0);
2311 			continue;
2312 		case '&':
2313 			x &= numval(&p, 0);
2314 			continue;
2315 		case ':':
2316 			x |= numval(&p, 0);
2317 			continue;
2318 		case '%':
2319 			y = numval(&p, 0);
2320 			if (!y) {
2321 				zerodivide();
2322 				goto done;
2323 			}
2324 			x %= y;
2325 			continue;
2326 		}
2327 		--p;
2328 		break;
2329 	}
2330  done:
2331 	*pline = p;
2332 	return x;
2333 }
2334 
2335 int
2336 iftest(char *p, char **bp)
2337 {
2338 	char *p1;
2339 	int c, neg, rv;
2340 
2341 	rv = neg = 0;
2342 	if (*p == '!') {
2343 		neg = 1;
2344 		p++;
2345 	}
2346 	c = *p;
2347 	if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '('/*)*/) {
2348 		if (numval(&p,1) >= 1)
2349 			rv = 1;
2350 		goto done;
2351 	}
2352 	switch(c) {
2353 	case 't':
2354 	case 'o':
2355 		rv = 1;
2356 	case 'n':
2357 	case 'e':
2358 		p++;
2359 		goto done;
2360 	}
2361 	for(p1 = ++p; *p != c; p++)
2362 		if (!*p)
2363 			goto done;
2364 	for(p++;;) {
2365 		if (*p != *p1++) {
2366 			while(*p && *p++ != c);
2367 			goto done;
2368 		}
2369 		if (*p++ == c)
2370 			break;
2371 	}
2372 	rv = 1;
2373  done:
2374 	if (neg)
2375 		rv = 1 - rv;
2376 	while(*p == ' ' || *p == '\t')
2377 		p++;
2378 	*bp = p;
2379 	return rv;
2380 }
2381 
2382 void
2383 scanline(char *p, char *e, int wantnl)
2384 {
2385 	int c;
2386 	Rune r;
2387 
2388 	while((c = getrune()) == ' ' || c == '\t') ;
2389 	while(p < e) {
2390 		if (c < 0)
2391 			break;
2392 		if (c < Runeself) {
2393 			if (c == '\n') {
2394 				if (wantnl)
2395 					*p++ = c;
2396 				break;
2397 			}
2398 			*p++ = c;
2399 		}
2400 		else {
2401 			r = c;
2402 			p += runetochar(p, &r);
2403 		}
2404 		c = getrune();
2405 	}
2406 	*p = 0;
2407 }
2408 
2409 void
2410 pushbody(char *line)
2411 {
2412 	char *b;
2413 
2414 	if (line[0] == '\\' && line[1] == '{' /*}*/ )
2415 		line += 2;
2416 	if (strsp < Maxmstack - 1) {
2417 		pushstr(b = strdup(line));
2418 		mustfree[strsp] = b;
2419 	}
2420 }
2421 
2422 void
2423 skipbody(char *line)
2424 {
2425 	int c, n;
2426 
2427 	if (line[0] != '\\' || line[1] != '{' /*}*/ )
2428 		return;
2429 	for(n = 1;;) {
2430 		while((c = getrune()) != '\\')
2431 			if (c < 0)
2432 				return;
2433 		c = getrune();
2434 		if (c == '{')
2435 			n++;
2436 		else if ((c == '}' && (c = getrune()) == '\n' && !--n)
2437 			|| c < 0)
2438 			return;
2439 	}
2440 }
2441 
2442 int
2443 ifstart(char *line, char *e, char **bp)
2444 {
2445 	int it;
2446 	char *b;
2447 
2448 	b = copyline(line, e, 1);
2449 	ungetrune();
2450 	b[-1] = getrune();
2451 	scanline(b, e, 1);
2452 	it = iftest(line, bp);
2453 	return it;
2454 }
2455 
2456 void
2457 g_ie(char *line, char *e)
2458 {
2459 	char *b;
2460 
2461 	if (elsetop >= Maxif-1) {
2462 		fprint(2, "ms2html: .ie's too deep\n");
2463 		return;
2464 	}
2465 	if (ifwastrue[++elsetop] = ifstart(line, e, &b))
2466 		pushbody(b);
2467 	else
2468 		skipbody(b);
2469 }
2470 
2471 void
2472 g_if(char *line, char *e)
2473 {
2474 	char *b;
2475 
2476 	if (ifstart(line, e, &b))
2477 		pushbody(b);
2478 	else
2479 		skipbody(b);
2480 }
2481 
2482 void
2483 g_el(char *line, char *e)
2484 {
2485 	if (elsetop < 0)
2486 		return;
2487 	scanline(line, e, 1);
2488 	if (ifwastrue[elsetop--])
2489 		skipbody(line);
2490 	else
2491 		pushbody(line);
2492 }
2493 
2494 void
2495 g_ig(int argc, char **argv)
2496 {
2497 	char *p, *s;
2498 
2499 	s = "..";
2500 	if (argc > 1)
2501 		s = argv[1];
2502 	for(;;) {
2503 		p = Brdline(&ssp->in, '\n');
2504 		if(p == nil)
2505 			break;
2506 		p[Blinelen(&ssp->in)-1] = 0;
2507 		if(strcmp(p, s) == 0)
2508 			break;
2509 	}
2510 }
2511 
2512 void
2513 g_ds(char *line, char *e)
2514 {
2515 	char *b;
2516 
2517 	b = copyline(line, e, 1);
2518 	if (b > line) {
2519 		copyline(b, e, 0);
2520 		if (*b == '"')
2521 			b++;
2522 		ds(line, b);
2523 	}
2524 }
2525 
2526 void
2527 g_as(char *line, char *e)
2528 {
2529 	String *s;
2530 	char *b;
2531 
2532 	b = copyline(line, e, 1);
2533 	if (b == line)
2534 		return;
2535 	copyline(b, e, 0);
2536 	if (*b == '"')
2537 		b++;
2538 	for(s = strings; s != nil; s = s->next)
2539 		if(strcmp(line, s->name) == 0)
2540 			break;
2541 
2542 	if(s == nil){
2543 		ds(line, b);
2544 		return;
2545 	}
2546 
2547 	s->val = realloc(s->val, strlen(s->val) + strlen(b) + 1);
2548 	strcat(s->val, b);
2549 }
2550 
2551 void
2552 g_BS(int argc, char **argv)
2553 {
2554 	int i;
2555 
2556 	if (argc > 1 && !weBref) {
2557 		Bprint(&bout, "<a href=\"%s\"", argv[1]);
2558 		for(i = 2; i < argc; i++)
2559 			Bprint(&bout, " %s", argv[i]);
2560 		Bprint(&bout, ">");
2561 		weBref = 1;
2562 	}
2563 }
2564 
2565 void
2566 g_BE(int, char**)
2567 {
2568 	if (weBref) {
2569 		Bprint(&bout, "</a>");
2570 		weBref = 0;
2571 	}
2572 }
2573 
2574 void
2575 g_LB(int argc, char **argv)
2576 {
2577 	if (argc > 1) {
2578 		if (weBref)
2579 			g_BE(0,nil);
2580 		Bprint(&bout, "<a name=\"%s\"></a>", argv[1]);
2581 	}
2582 }
2583 
2584 void
2585 g_RT(int, char**)
2586 {
2587 	g_BE(0,nil);
2588 	dohanginghead();
2589 	closel();
2590 	closefont();
2591 }
2592