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