xref: /plan9-contrib/sys/src/cmd/ms2html.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
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 	if(name == nil){
573 		dirfstat(0, &d);
574 		name = d.name;
575 		fd = 0;
576 	} else {
577 		fd = open(name, OREAD);
578 		if(fd < 0){
579 			fprint(2, "ms2html: .can't open %s: %r\n", name);
580 			return;
581 		}
582 	}
583 	ssp++;
584 	ssp->fd = fd;
585 	Binit(&ssp->in, fd, OREAD);
586 	snprint(ssp->filename, sizeof(ssp->filename), "%s", name);
587 	ssp->lno = ssp->rlno = 1;
588 }
589 
590 /* get next logical byte.  from stdin or a defined string */
591 int
592 getrune(void)
593 {
594 	int i;
595 	Rune r;
596 	int c;
597 	Mstack *m;
598 
599 	while(strsp >= 0){
600 		i = chartorune(&r, strstack[strsp]);
601 		if(r != 0){
602 			strstack[strsp] += i;
603 			lastsrc = Dstring;
604 			return r;
605 		}
606 		if (mustfree[strsp]) {
607 			free(mustfree[strsp]);
608 			mustfree[strsp] = nil;
609 		}
610 		strsp--;
611  	}
612 	while(msp >= 0){
613 		m = &mstack[msp];
614 		i = chartorune(&r, m->ptr);
615 		if(r != 0){
616 			m->ptr += i;
617 			lastsrc = Macro;
618 			return r;
619 		}
620 		for(i = 0; m->argv[i] != nil; i++)
621 			free(m->argv[i]);
622 		msp--;
623  	}
624 
625 	lastsrc = Input;
626 	do {
627 		if(ssp < sstack)
628 			return -1;
629 		c = Bgetrune(&ssp->in);
630 		if(c >= 0){
631 			r = c;
632 			break;
633 		}
634 		close(ssp->fd);
635 		ssp--;
636 	} while(r < 0);
637 
638 	return r;
639 }
640 
641 void
642 ungetrune(void)
643 {
644 	switch(lastsrc){
645 	case Dstring:
646 		if(strsp >= 0)
647 			strstack[strsp]--;
648 		break;
649 	case Macro:
650 		if(msp >= 0)
651 			mstack[msp].ptr--;
652 		break;
653 	case Input:
654 		if(ssp >= sstack)
655 			Bungetrune(&ssp->in);
656 		break;
657 	}
658 }
659 
660 int vert;
661 
662 char*
663 changefont(Font *f)
664 {
665 	token[0] = 0;
666 	if(curfont != nil)
667 		strcpy(token, curfont->end);
668 	if(f != nil)
669 		strcat(token, f->start);
670 	prevfont = curfont;
671 	curfont = f;
672 	return token;
673 }
674 
675 /* get next logical byteacter.  expand it with escapes */
676 char*
677 getnext(void)
678 {
679 	int r, mult, size;
680 	Entity *e;
681 	Troffspec *t;
682 	Rune R;
683 	char str[4];
684 
685 	r = getrune();
686 	if(r < 0)
687 		return nil;
688 	if(r > 128){
689 		for(e = entity; e->name; e++)
690 			if(e->value == r)
691 				return e->name;
692 		return "&#191;";
693 	}
694 
695 	switch(r){
696 	case '\\':
697 		r = getrune();
698 		if(r < 0)
699 			return nil;
700 		switch(r){
701 		case ' ':
702 			return " ";
703 
704 		/* chars to ignore */
705 		case '&':
706 		case '|':
707 		case '%':
708 			return "";
709 
710 		/* small space in troff, nothing in nroff */
711 		case '^':
712 			return getnext();
713 
714 		/* ignore arg */
715 		case 'k':
716 			getrune();
717 			return getnext();
718 
719 		/* comment */
720 		case '"':
721 			while(getrune() != '\n')
722 				;
723 			return "\n";
724 		/* ignore line */
725 		case '!':
726 			while(getrune() != '\n')
727 				;
728 			ungetrune();
729 			return getnext();
730 
731 		/* defined strings */
732 		case '*':
733 			r = getrune();
734 			if(r == '('){
735 				str[0] = getrune();
736 				str[1] = getrune();
737 				str[2] = 0;
738 			} else {
739 				str[0] = r;
740 				str[1] = 0;
741 			}
742 			pushstr(getds(str));
743 			return getnext();
744 
745 		/* macro args */
746 		case '$':
747 			r = getrune();
748 			if(r < '1' || r > '9'){
749 				token[0] = '\\';
750 				token[1] = '$';
751 				token[2] = r;
752 				token[3] = 0;
753 				return token;
754 			}
755 			r -= '0';
756 			if(msp >= 0)
757 				pushstr(mstack[msp].argv[r]);
758 			return getnext();
759 
760 		/* special chars */
761 		case '(':
762 			token[0] = getrune();
763 			token[1] = getrune();
764 			token[2] = 0;
765 			for(t = tspec; t->name; t++)
766 				if(strcmp(token, t->name) == 0)
767 					return t->value;
768 			return "&#191;";
769 
770 		/* ignore immediately following newline */
771 		case 'c':
772 			r = getrune();
773 			if (r == '\n') {
774 				sol = ignore_nl = 1;
775 				if (indirective)
776 					break;
777 				}
778 			else
779 				ungetrune();
780 			return getnext();
781 
782 		/* escape backslash */
783 		case 'e':
784 			return "\\";
785 			break;
786 
787 		/* font change */
788 		case 'f':
789 			r = getrune();
790 			switch(r){
791 			case '(':
792 				str[0] = getrune();
793 				str[1] = getrune();
794 				str[2] = 0;
795 				token[0] = 0;
796 				if(strcmp("BI", str) == 0)
797 					return changefont(&bifont);
798 				else if(strcmp("CW", str) == 0)
799 					return changefont(&cwfont);
800 				else
801 					return changefont(nil);
802 			case '3':
803 			case 'B':
804 				return changefont(&bfont);
805 			case '2':
806 			case 'I':
807 				return changefont(&ifont);
808 			case '4':
809 				return changefont(&bifont);
810 			case '5':
811 				return changefont(&cwfont);
812 			case 'P':
813 				return changefont(prevfont);
814 			case 'R':
815 			default:
816 				return changefont(nil);
817 			}
818 
819 		/* number register */
820 		case 'n':
821 			r = getrune();
822 			if (r == '(') /*)*/ {
823 				r = getrune();
824 				if (r < 0)
825 					return nil;
826 				str[0] = r;
827 				r = getrune();
828 				if (r < 0)
829 					return nil;
830 				str[1] = r;
831 				str[2] = 0;
832 				}
833 			else {
834 				str[0] = r;
835 				str[1] = 0;
836 				}
837 			pushstr(getnr(str));
838 			return getnext();
839 
840 		/* font size */
841 		case 's':
842 			mult = 1;
843 			size = 0;
844 			for(;;){
845 				r = getrune();
846 				if(r < 0)
847 					return nil;
848 				if(r == '+' || r == '(')
849 					;
850 				else if(r == '-')
851 					mult *= -1;
852 				else if(r >= '0' && r <= '9')
853 					size = size*10 + (r-'0');
854 				else{
855 					ungetrune();
856 					return getnext();
857 				}
858 			}
859 			break;
860 
861 		/* vertical movement */
862 		case 'v':
863 			r = getrune();
864 			if(r != '\''){
865 				ungetrune();
866 				return getnext();
867 			}
868 			r = getrune();
869 			if(r != '-')
870 				vert--;
871 			else
872 				vert++;
873 			while(r != '\'' && r != '\n')
874 				r = getrune();
875 			if(r != '\'')
876 				ungetrune();
877 
878 			if(vert > 0)
879 				return "^";
880 			return getnext();
881 
882 
883 		/* horizontal line */
884 		case 'l':
885 			r = getrune();
886 			if(r != '\''){
887 				ungetrune();
888 				return "<HR>";
889 			}
890 			while(getrune() != '\'')
891 				;
892 			return "<HR>";
893 
894 		/* character height and slant */
895 		case 'S':
896 		case 'H':
897 			r = getrune();
898 			if(r != '\''){
899 				ungetrune();
900 				return "<HR>";
901 			}
902 			while(getrune() != '\'')
903 				;
904 			return getnext();
905 
906 		/* digit-width space */
907 		case '0':
908 			return " ";
909 
910 		/*for .if, .ie, .el */
911 		case '{':
912 			return "\\{"; /*}*/
913 		case '}':
914 			return "";
915 		/* up and down */
916 		case 'u':
917 			if (isdown) {
918 				isdown = 0;
919 				return "</sub>";
920 			}
921 			isup = 1;
922 			return "<sup>";
923 		case 'd':
924 			if (isup) {
925 				isup = 0;
926 				return "</sup>";
927 			}
928 			isdown = 1;
929 			return "<sub>";
930 		}
931 		break;
932 	case '&':
933 		if(msp >= 0 || strsp >= 0)
934 			return "&";
935 		return "&amp;";
936 	case '<':
937 		if(msp >= 0 || strsp >= 0)
938 			return "<";
939 		return "&#60;";
940 	case '>':
941 		if(msp >= 0 || strsp >= 0)
942 			return ">";
943 		return "&#62;";
944 	}
945 	if (r < Runeself) {
946 		token[0] = r;
947 		token[1] = 0;
948 		}
949 	else {
950 		R = r;
951 		token[runetochar(token,&R)] = 0;
952 	}
953 	return token;
954 }
955 
956 char*
957 copyline(char *p, char *e, int arg0)
958 {
959 	int c;
960 	Rune r;
961 	char *p1;
962 
963 	while((c = getrune()) == ' ' || c == '\t')
964 		;
965 	for(indirective = 1; p < e; c = getrune()) {
966 		if (c < 0)
967 			goto done;
968 		switch(c) {
969 		case '\\':
970 			break;
971 		case '\n':
972 			if (arg0)
973 				ungetrune();
974 			goto done;
975 		case ' ':
976 		case '\t':
977 			if (arg0)
978 				goto done;
979 		default:
980 			r = c;
981 			p += runetochar(p,&r);
982 			continue;
983 		}
984 		ungetrune();
985 		p1 = getnext();
986 		if (p1 == nil)
987 			goto done;
988 		if (*p1 == '\n') {
989 			if (arg0)
990 				ungetrune();
991 			break;
992 		}
993 		while((*p = *p1++) && p < e)
994 			p++;
995 	}
996 done:
997 	indirective = 0;
998 	*p++ = 0;
999 	return p;
1000 }
1001 
1002 char*
1003 copyarg(char *p, char *e, int *nullarg)
1004 {
1005 	int c, quoted;
1006 	Rune r;
1007 	char *p1;
1008 
1009 	*nullarg = 0;
1010 	quoted = 0;
1011 	do{
1012 		c = getrune();
1013 	} while(c == ' ' || c == '\t');
1014 
1015 	if(c == '"'){
1016 		quoted = 1;
1017 		*nullarg = 1;
1018 		c = getrune();
1019 	}
1020 
1021 	if(c == '\n')
1022 		goto done;
1023 
1024 	for(; p < e; c = getrune()) {
1025 		if (c < 0)
1026 			break;
1027 		switch(c) {
1028 		case '\\':
1029 			break;
1030 		case '\n':
1031 			ungetrune();
1032 			goto done;
1033 		case ' ':
1034 		case '\t':
1035 			if(!quoted)
1036 				goto done;
1037 			r = c;
1038 			p += runetochar(p,&r);
1039 			continue;
1040 		case '"':
1041 			if(quoted)
1042 				goto done;
1043 			r = c;
1044 			p += runetochar(p,&r);
1045 			continue;
1046 		default:
1047 			r = c;
1048 			p += runetochar(p,&r);
1049 			continue;
1050 		}
1051 		ungetrune();
1052 		p1 = getnext();
1053 		if(p1 == nil)
1054 			break;
1055 		if(*p1 == '\n')
1056 			break;
1057 		while((*p = *p1++) && p < e)
1058 			p++;
1059 	}
1060 done:
1061 	*p++ = 0;
1062 	return p;
1063 
1064 }
1065 
1066 int
1067 parseargs(char *p, char *e, char **argv)
1068 {
1069 	int argc;
1070 	char *np;
1071 	int nullarg;
1072 
1073 	indirective = 1;
1074 	*p++ = 0;
1075 	for(argc = 1; argc < Narg; argc++){
1076 		np = copyarg(p, e, &nullarg);
1077 		if(nullarg==0 && np == p+1)
1078 			break;
1079 		argv[argc] = p;
1080 		p = np;
1081 	}
1082 	argv[argc] = nil;
1083 	indirective = 0;
1084 
1085 
1086 	return argc;
1087 }
1088 
1089 void
1090 dodirective(void)
1091 {
1092 	char *p, *e;
1093 	Goobie *g;
1094 	Goobieif *gif;
1095 	char line[Nline], *line1;
1096 	int i, argc;
1097 	char *argv[Narg];
1098 	Mstack *m;
1099 
1100 	/* read line, translate special bytes */
1101 	e = line + sizeof(line) - UTFmax - 1;
1102 	line1 = copyline(line, e, 1);
1103 	if (!line[0])
1104 		return;
1105 	argv[0] = line;
1106 
1107 	/* first look through user defined macros */
1108 	p = getmacro(argv[0]);
1109 	if(p != nil){
1110 		if(msp == Maxmstack-1){
1111 			fprint(2, "ms2html: macro stack overflow\n");
1112 			return;
1113 		}
1114 		argc = parseargs(line1, e, argv);
1115 		m = &mstack[++msp];
1116 		m->ptr = p;
1117 		memset(m->argv, 0, sizeof(m->argv));
1118 		for(i = 0; i < argc; i++)
1119 			m->argv[i] = strdup(argv[i]);
1120 		return;
1121 	}
1122 
1123 	/* check for .if or .ie */
1124 	for(gif = gtabif; gif->name; gif++)
1125 		if(strcmp(gif->name, argv[0]) == 0){
1126 			(*gif->f)(line1, e);
1127 			return;
1128 		}
1129 
1130 	argc = parseargs(line1, e, argv);
1131 
1132 	/* try standard ms macros */
1133 	for(g = gtab; g->name; g++)
1134 		if(strcmp(g->name, argv[0]) == 0){
1135 			(*g->f)(argc, argv);
1136 			return;
1137 		}
1138 
1139 	if(debug)
1140 		fprint(2, "stdin %d(%s:%d): unknown directive %s\n",
1141 			ssp->lno, ssp->filename, ssp->rlno, line);
1142 }
1143 
1144 void
1145 printarg(char *a)
1146 {
1147 	char *e, *p;
1148 
1149 	e = a + strlen(a);
1150 	pushstr(a);
1151 	while(strsp >= 0 && strstack[strsp] >= a && strstack[strsp] < e){
1152 		p = getnext();
1153 		if(p == nil)
1154 			return;
1155 		Bprint(&bout, "%s", p);
1156 	}
1157 }
1158 
1159 void
1160 printargs(int argc, char **argv)
1161 {
1162 	argc--;
1163 	argv++;
1164 	while(--argc > 0){
1165 		printarg(*argv++);
1166 		Bprint(&bout, " ");
1167 	}
1168 	if(argc == 0)
1169 		printarg(*argv);
1170 }
1171 
1172 void
1173 dohangingdt(void)
1174 {
1175 	switch(hangingdt){
1176 	case 3:
1177 		hangingdt--;
1178 		break;
1179 	case 2:
1180 		Bprint(&bout, "<dd>");
1181 		hangingdt = 0;
1182 		break;
1183 	}
1184 
1185 }
1186 
1187 void
1188 dohangingau(void)
1189 {
1190 	if(hangingau == 0)
1191 		return;
1192 	Bprint(&bout, "</I></DL>\n");
1193 	hangingau = 0;
1194 }
1195 
1196 void
1197 dohanginghead(void)
1198 {
1199 	if(hanginghead == 0)
1200 		return;
1201 	Bprint(&bout, "</H%d>\n", hanginghead);
1202 	hanginghead = 0;
1203 }
1204 
1205 /*
1206  *  convert a man page to html and output
1207  */
1208 void
1209 doconvert(void)
1210 {
1211 	char c, *p;
1212 	Tm *t;
1213 
1214 	pushsrc(nil);
1215 
1216 	sol = 1;
1217 	Bprint(&bout, "<html>\n");
1218 	Bflush(&bout);
1219 	for(;;){
1220 		p = getnext();
1221 		if(p == nil)
1222 			break;
1223 		c = *p;
1224 		if(c == '.' && sol){
1225 			dodirective();
1226 			dohangingdt();
1227 			ssp->lno++;
1228 			ssp->rlno++;
1229 			sol = 1;
1230 		} else if(c == '\n'){
1231 			if (ignore_nl)
1232 				ignore_nl = 0;
1233 			else {
1234 				if(hangingau)
1235 					Bprint(&bout, "<br>\n");
1236 				else
1237 					Bprint(&bout, "%s", p);
1238 				dohangingdt();
1239 				}
1240 			ssp->lno++;
1241 			ssp->rlno++;
1242 			sol = 1;
1243 		} else{
1244 			Bprint(&bout, "%s", p);
1245 			ignore_nl = sol = 0;
1246 		}
1247 	}
1248 	dohanginghead();
1249 	dohangingdt();
1250 	closel();
1251 	if(curfont)
1252 		Bprint(&bout, "%s", curfont->end);
1253 	Bprint(&bout, "<br>&#32;<br>\n");
1254 	Bprint(&bout, "<A href=http://www.lucent.com/copyright.html>\n");
1255 	t = localtime(time(nil));
1256 	Bprint(&bout, "Copyright</A> &#169; %d Lucent Technologies Inc.  All rights reserved.\n",
1257 			t->year+1900);
1258 	Bprint(&bout, "</body></html>\n");
1259 }
1260 
1261 void
1262 main(int argc, char **argv)
1263 {
1264 	quiet = 1;
1265 	if(argc > 1)
1266 		if(strcmp(argv[1], "-v") == 0)
1267 			quiet = 0;
1268 
1269 	Binit(&bout, 1, OWRITE);
1270 
1271 	ds("R", "&#174;");
1272 
1273 	doconvert();
1274 	exits(nil);
1275 }
1276 
1277 void
1278 g_notyet(int, char **argv)
1279 {
1280 	fprint(2, "ms2html: .%s not yet supported\n", argv[0]);
1281 }
1282 
1283 void
1284 g_ignore(int, char **argv)
1285 {
1286 	if(quiet)
1287 		return;
1288 	fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]);
1289 }
1290 
1291 void
1292 g_PP(int, char**)
1293 {
1294 	dohanginghead();
1295 	closel();
1296 	closefont();
1297 	Bprint(&bout, "<P>\n");
1298 	paragraph = 1;
1299 }
1300 
1301 void
1302 g_LP(int, char**)
1303 {
1304 	dohanginghead();
1305 	closel();
1306 	closefont();
1307 	Bprint(&bout, "<br>&#32;<br>\n");
1308 }
1309 
1310 /* close a list */
1311 void
1312 closel(void)
1313 {
1314 	g_P2(1, nil);
1315 	dohangingau();
1316 	if(paragraph){
1317 		Bprint(&bout, "</P>\n");
1318 		paragraph = 0;
1319 	}
1320 	switch(list){
1321 	case Lordered:
1322 		Bprint(&bout, "</ol>\n");
1323 		break;
1324 	case Lunordered:
1325 		Bprint(&bout, "</ul>\n");
1326 		break;
1327 	case Lother:
1328 	case Ldef:
1329 		Bprint(&bout, "</dl>\n");
1330 		break;
1331 	}
1332 	list = 0;
1333 
1334 }
1335 
1336 
1337 void
1338 g_IP(int argc, char **argv)
1339 {
1340 	switch(list){
1341 	default:
1342 		closel();
1343 		if(argc > 1){
1344 			if(strcmp(argv[1], "1") == 0){
1345 				list = Lordered;
1346 				listnum = 1;
1347 				Bprint(&bout, "<OL>\n");
1348 			} else if(strcmp(argv[1], "\\(bu") == 0){
1349 				list = Lunordered;
1350 				Bprint(&bout, "<UL>\n");
1351 			} else {
1352 				list = Lother;
1353 				Bprint(&bout, "<DL COMPACT>\n");
1354 			}
1355 		} else {
1356 			list = Lother;
1357 			Bprint(&bout, "<DL>\n");
1358 		}
1359 		break;
1360 	case Lother:
1361 	case Lordered:
1362 	case Lunordered:
1363 		break;
1364 	}
1365 
1366 	switch(list){
1367 	case Lother:
1368 		Bprint(&bout, "<DT>");
1369 		if(argc > 1)
1370 			printarg(argv[1]);
1371 		else
1372 			Bprint(&bout, "<DT>&#32;");
1373 		Bprint(&bout, "<DD>\n");
1374 		break;
1375 	case Lordered:
1376 	case Lunordered:
1377 		Bprint(&bout, "<LI>\n");
1378 		break;
1379 	}
1380 }
1381 
1382 /*
1383  *  .5i is one <DL><DT><DD>
1384  */
1385 void
1386 g_in(int argc, char **argv)
1387 {
1388 	float	f;
1389 	int	delta, x;
1390 	char	*p;
1391 
1392 	f = indent/0.5;
1393 	delta = f;
1394 	if(argc <= 1){
1395 		indent = 0.0;
1396 	} else {
1397 		f = strtod(argv[1], &p);
1398 		switch(*p){
1399 		case 'i':
1400 			break;
1401 		case 'c':
1402 			f = f / 2.54;
1403 			break;
1404 		case 'P':
1405 			f = f / 6;
1406 			break;
1407 		default:
1408 		case 'u':
1409 		case 'm':
1410 			f = f * (12 / 72);
1411 			break;
1412 		case 'n':
1413 			f = f * (6 / 72);
1414 			break;
1415 		case 'p':
1416 			f = f / 72.0;
1417 			break;
1418 		}
1419 		switch(argv[1][0]){
1420 		case '+':
1421 		case '-':
1422 			indent += f;
1423 			break;
1424 		default:
1425 			indent = f;
1426 			break;
1427 		}
1428 	}
1429 	if(indent < 0.0)
1430 		indent = 0.0;
1431 	f = (indent/0.5);
1432 	x = f;
1433 	delta = x - delta;
1434 	while(delta < 0){
1435 		Bprint(&bout, "</DL>\n");
1436 		delta++;
1437 	}
1438 	while(delta > 0){
1439 		Bprint(&bout, "<DL><DT><DD>\n");
1440 		delta--;
1441 	}
1442 }
1443 
1444 void
1445 g_HP(int, char**)
1446 {
1447 	switch(list){
1448 	default:
1449 		closel();
1450 		list = Ldef;
1451 		hangingdt = 1;
1452 		Bprint(&bout, "<DL><DT>\n");
1453 		break;
1454 	case Ldef:
1455 		if(hangingdt)
1456 			Bprint(&bout, "<DD>");
1457 		Bprint(&bout, "<DT>");
1458 		hangingdt = 1;
1459 		break;
1460 	}
1461 }
1462 
1463 void
1464 g_SH(int, char**)
1465 {
1466 	dohanginghead();
1467 	closel();
1468 	closefont();
1469 	Bprint(&bout, "<H%d>", HH);
1470 	hanginghead = HH;
1471 }
1472 
1473 void
1474 g_NH(int argc, char **argv)
1475 {
1476 	int i, level;
1477 
1478 	closel();
1479 	closefont();
1480 
1481 	if(argc == 1)
1482 		level = 0;
1483 	else {
1484 		level = atoi(argv[1])-1;
1485 		if(level < 0 || level >= Maxnh)
1486 			level = Maxnh - 1;
1487 	}
1488 	nh[level]++;
1489 
1490 	Bprint(&bout, "<H%d>", HH);
1491 	hanginghead = HH;
1492 
1493 	Bprint(&bout, "%d", nh[0]);
1494 	for(i = 1; i <= level; i++)
1495 		Bprint(&bout, ".%d", nh[i]);
1496 	Bprint(&bout, " ");
1497 
1498 	for(i = level+1; i < Maxnh; i++)
1499 		nh[i] = 0;
1500 }
1501 
1502 void
1503 g_TL(int, char**)
1504 {
1505 	char *p, *np;
1506 	char name[128];
1507 
1508 	closefont();
1509 
1510 	if(!titleseen){
1511 		/* get base part of filename */
1512 		p = strrchr(ssp->filename, '/');
1513 		if(p == nil)
1514 			p = ssp->filename;
1515 		else
1516 			p++;
1517 		strncpy(name, p, sizeof(name));
1518 		name[sizeof(name)-1] = 0;
1519 
1520 		/* dump any extensions */
1521 		np = strchr(name, '.');
1522 		if(np)
1523 			*np = 0;
1524 
1525 		Bprint(&bout, "<title>\n");
1526 		Bprint(&bout, "%s\n", p);
1527 		Bprint(&bout, "</title>\n");
1528 		Bprint(&bout, "<body BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#330088\" ALINK=\"#FF0044\">\n");
1529 		titleseen = 1;
1530 	}
1531 
1532 	Bprint(&bout, "<H%d>", 1);
1533 	hanginghead = 1;
1534 }
1535 
1536 void
1537 g_AU(int, char**)
1538 {
1539 	closel();
1540 	dohanginghead();
1541 	Bprint(&bout, "<DL><DD><I>");
1542 	hangingau = 1;
1543 }
1544 
1545 void
1546 setfont(Font *f)
1547 {
1548 	if(curfont != nil)
1549 		Bprint(&bout, "%s", curfont->end);
1550 	prevfont = curfont;
1551 	if(f != nil)
1552 		Bprint(&bout, "%s", f->start);
1553 	curfont = f;
1554 }
1555 
1556 /*
1557  *  for 3 args print arg3 \fxarg1\fP arg2
1558  *  for 2 args print arg1 \fxarg2\fP
1559  *  for 1 args print \fxarg1\fP
1560  */
1561 void
1562 font(Font *f, int argc, char **argv)
1563 {
1564 	Font *prev;
1565 
1566 	if(argc == 1){
1567 		setfont(nil);
1568 		return;
1569 	}
1570 	if(argc > 3)
1571 		printarg(argv[3]);
1572 	prev = prevfont;
1573 	setfont(f);
1574 	printarg(argv[1]);
1575 	setfont(prevfont);
1576 	prevfont = prev;
1577 	if(argc > 2)
1578 		printarg(argv[2]);
1579 	Bprint(&bout, "\n");
1580 }
1581 
1582 void
1583 closefont(void)
1584 {
1585 	if(curfont != nil)
1586 		Bprint(&bout, "%s", curfont->end);
1587 	curfont = nil;
1588 	prevfont = nil;
1589 }
1590 
1591 void
1592 g_B(int argc, char **argv)
1593 {
1594 	font(&bfont, argc, argv);
1595 }
1596 
1597 void
1598 g_R(int argc, char **argv)
1599 {
1600 	font(nil, argc, argv);
1601 }
1602 
1603 void
1604 g_BI(int argc, char **argv)
1605 {
1606 	font(&bifont, argc, argv);
1607 }
1608 
1609 void
1610 g_CW(int argc, char **argv)
1611 {
1612 	font(&cwfont, argc, argv);
1613 }
1614 
1615 char*
1616 lower(char *p)
1617 {
1618 	char *x;
1619 
1620 	for(x = p; *x; x++)
1621 		if(*x >= 'A' && *x <= 'Z')
1622 			*x -= 'A' - 'a';
1623 	return p;
1624 }
1625 
1626 void
1627 g_I(int argc, char **argv)
1628 {
1629 	int anchor;
1630 	char *p;
1631 
1632 	anchor = 0;
1633 	if(argc > 2){
1634 		p = argv[2];
1635 		if(p[0] == '(')
1636 		if(p[1] >= '0' && p[1] <= '9')
1637 		if(p[2] == ')'){
1638 			anchor = 1;
1639 			Bprint(&bout, "<A href=\"/magic/man2html/%c/%s\">",
1640 				p[1], lower(argv[1]));
1641 		}
1642 	}
1643 	font(&ifont, argc, argv);
1644 	if(anchor)
1645 		Bprint(&bout, "</A>");
1646 }
1647 
1648 void
1649 g_br(int, char**)
1650 {
1651 	if(hangingdt){
1652 		Bprint(&bout, "<dd>");
1653 		hangingdt = 0;
1654 	}else
1655 		Bprint(&bout, "<br>\n");
1656 }
1657 
1658 void
1659 g_P1(int, char**)
1660 {
1661 	if(example == 0){
1662 		example = 1;
1663 		Bprint(&bout, "<DL><DT><DD><TT><PRE>\n");
1664 	}
1665 }
1666 
1667 void
1668 g_P2(int, char**)
1669 {
1670 	if(example){
1671 		example = 0;
1672 		Bprint(&bout, "</PRE></TT></DL>\n");
1673 	}
1674 }
1675 
1676 void
1677 g_SM(int, char **argv)
1678 {
1679 	Bprint(&bout, "%s", argv[1]);
1680 }
1681 
1682 void
1683 g_ft(int argc, char **argv)
1684 {
1685 	if(argc < 2){
1686 		setfont(nil);
1687 		return;
1688 	}
1689 
1690 	switch(argv[1][0]){
1691 	case '3':
1692 	case 'B':
1693 		setfont(&bfont);
1694 		break;
1695 	case '2':
1696 	case 'I':
1697 		setfont(&ifont);
1698 		break;
1699 	case '4':
1700 		setfont(&bifont);
1701 		break;
1702 	case '5':
1703 		setfont(&cwfont);
1704 		break;
1705 	case 'P':
1706 		setfont(prevfont);
1707 		break;
1708 	case 'R':
1709 	default:
1710 		setfont(nil);
1711 		break;
1712 	}
1713 }
1714 
1715 void
1716 g_sp(int argc, char **argv)
1717 {
1718 	int n;
1719 
1720 	n = 1;
1721 	if(argc > 1){
1722 		n = atoi(argv[1]);
1723 		if(n < 1)
1724 			n = 1;
1725 		if(argv[1][strlen(argv[1])-1] == 'i')
1726 			n *= 4;
1727 	}
1728 	if(n > 5){
1729 		Bprint(&bout, "<br>&#32;<br>\n");
1730 		Bprint(&bout, "<HR>\n");
1731 		Bprint(&bout, "<br>&#32;<br>\n");
1732 	} else
1733 		for(; n > 0; n--)
1734 			Bprint(&bout, "<br>&#32;<br>\n");
1735 }
1736 
1737  void
1738 rm_loop(char *name, String **l)
1739 {
1740 	String *s;
1741 	for(s = *l; s != nil; s = *l){
1742 		if(strcmp(name, s->name) == 0){
1743 			*l = s->next;
1744 			free(s->name);
1745 			free(s->val);
1746 			free(s);
1747 			break;
1748 			}
1749 		l = &s->next;
1750 		}
1751 	}
1752 
1753 void
1754 g_rm(int argc, char **argv)
1755 {
1756 	Goobie *g;
1757 	char *name;
1758 	int i;
1759 
1760 	for(i = 1; i < argc; i++) {
1761 		name = argv[i];
1762 		rm_loop(name, &strings);
1763 		rm_loop(name, &macros);
1764 		for(g = gtab; g->name; g++)
1765 			if (strcmp(g->name, name) == 0) {
1766 				g->f = g_ignore;
1767 				break;
1768 				}
1769 		}
1770 	}
1771 
1772 void
1773 g_AB(int, char**)
1774 {
1775 	closel();
1776 	Bprint(&bout, "<DL><DD><H4>ABSTRACT</H4>\n");
1777 }
1778 
1779 void
1780 g_AE(int, char**)
1781 {
1782 	Bprint(&bout, "</DL>\n");
1783 }
1784 
1785 void
1786 g_FS(int, char **)
1787 {
1788 	char *argv[3];
1789 
1790 	argv[0] = "IP";
1791 	argv[1] = nil;
1792 	argv[2] = nil;
1793 	g_IP(1, argv);
1794 	Bprint(&bout, "NOTE:<I> ");
1795 }
1796 
1797 void
1798 g_FE(int, char **)
1799 {
1800 	Bprint(&bout, "</I><DT>&#32;<DD>");
1801 	closel();
1802 	Bprint(&bout, "<br>\n");
1803 }
1804 
1805 void
1806 g_de(int argc, char **argv)
1807 {
1808 	int r;
1809 	char *p, *cp;
1810 	String *m;
1811 	int len;
1812 
1813 	if(argc < 2)
1814 		return;
1815 
1816 	m = nil;
1817 	len = 0;
1818 	if(strcmp(argv[0], "am") == 0){
1819 		for(m = macros; m != nil; m = m->next)
1820 			if(strcmp(argv[1], m->name) == 0){
1821 				len = strlen(m->val);
1822 				break;
1823 			}
1824 
1825 		if(m == nil){
1826 			/* nothing to append to */
1827 			for(;;){
1828 				p = Brdline(&ssp->in, '\n');
1829 				if(p == nil)
1830 					break;
1831 				p[Blinelen(&ssp->in)-1] = 0;
1832 				if(strcmp(p, "..") == 0)
1833 					break;
1834 			}
1835 			return;
1836 		}
1837 	}
1838 
1839 	if(m == nil){
1840 		m = emalloc(sizeof(*m));
1841 		m->next = macros;
1842 		macros = m;
1843 		m->name = strdup(argv[1]);
1844 		m->val = nil;
1845 		len = 0;
1846 	}
1847 
1848 	/* read up to a .. removing double backslashes */
1849 	for(;;){
1850 		p = Brdline(&ssp->in, '\n');
1851 		if(p == nil)
1852 			break;
1853 		p[Blinelen(&ssp->in)-1] = 0;
1854 		if(strcmp(p, "..") == 0)
1855 			break;
1856 		m->val = realloc(m->val, len + Blinelen(&ssp->in)+1);
1857 		cp = m->val + len;
1858 		while(*p){
1859 			r = *p++;
1860 			if(r == '\\' && *p == '\\')
1861 				p++;
1862 			*cp++ = r;
1863 		}
1864 		*cp++ = '\n';
1865 		len = cp - m->val;
1866 		*cp = 0;
1867 	}
1868 }
1869 
1870 void
1871 g_hrule(int, char**)
1872 {
1873 	Bprint(&bout, "<HR>\n");
1874 }
1875 
1876 void
1877 g_BX(int argc, char **argv)
1878 {
1879 	Bprint(&bout, "<HR>\n");
1880 	printargs(argc, argv);
1881 	Bprint(&bout, "<HR>\n");
1882 }
1883 
1884 void
1885 g_IH(int, char**)
1886 {
1887 	Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n");
1888 }
1889 
1890 void
1891 g_MH(int, char**)
1892 {
1893 	Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n");
1894 }
1895 
1896 void
1897 g_PY(int, char**)
1898 {
1899 	Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n");
1900 }
1901 
1902 void
1903 g_HO(int, char**)
1904 {
1905 	Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n");
1906 }
1907 
1908 void
1909 g_QS(int, char**)
1910 {
1911 	Bprint(&bout, "<BLOCKQUOTE>\n");
1912 }
1913 
1914 void
1915 g_QE(int, char**)
1916 {
1917 	Bprint(&bout, "</BLOCKQUOTE>\n");
1918 }
1919 
1920 void
1921 g_RS(int, char**)
1922 {
1923 	Bprint(&bout, "<DL><DD>\n");
1924 }
1925 
1926 void
1927 g_RE(int, char**)
1928 {
1929 	Bprint(&bout, "</DL>\n");
1930 }
1931 
1932 int gif;
1933 
1934 void
1935 g_startgif(int, char **argv)
1936 {
1937 	int fd;
1938 	int pfd[2];
1939 	char *e, *p;
1940 	char name[32];
1941 	Dir d;
1942 
1943 	if(strcmp(argv[0], "EQ") == 0)
1944 		e = ".EN";
1945 	else if(strcmp(argv[0], "TS") == 0)
1946 		e = ".TE";
1947 	else if(strcmp(argv[0], "PS") == 0)
1948 		e = ".PE";
1949 	else
1950 		return;
1951 
1952 	p = strrchr(sstack[0].filename, '/');
1953 	if(p != nil)
1954 		p++;
1955 	else
1956 		p = sstack[0].filename;
1957 	snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++);
1958 	fd = create(name, OWRITE, 0664);
1959 	if(fd < 0){
1960 		fprint(2, "ms2html: can't create %s: %r\n", name);
1961 		return;
1962 	}
1963 
1964 	if(pipe(pfd) < 0){
1965 		fprint(2, "ms2html: can't create pipe: %r\n");
1966 		close(fd);
1967 		return;
1968 	}
1969 	switch(rfork(RFFDG|RFPROC)){
1970 	case -1:
1971 		fprint(2, "ms2html: can't fork: %r\n");
1972 		close(fd);
1973 		return;
1974 	case 0:
1975 		dup(fd, 1);
1976 		close(fd);
1977 		dup(pfd[0], 0);
1978 		close(pfd[0]);
1979 		close(pfd[1]);
1980 		execl("/bin/troff2gif", "troff2gif", 0);
1981 		fprint(2, "ms2html: couldn't exec troff2gif: %r\n");
1982 		_exits(nil);
1983 	default:
1984 		close(fd);
1985 		close(pfd[0]);
1986 		fprint(pfd[1], ".ll 7i\n");
1987 		for(;;){
1988 			p = Brdline(&ssp->in, '\n');
1989 			if(p == nil)
1990 				break;
1991 			ssp->lno++;
1992 			ssp->rlno++;
1993 			if(strncmp(p, e, 3) == 0)
1994 				break;
1995 			if(write(pfd[1], p, Blinelen(&ssp->in)) < 0)
1996 				break;
1997 		}
1998 		close(pfd[1]);
1999 		wait(nil);
2000 		if(dirstat(name, &d) < 0)
2001 			break;
2002 		if(d.length == 0){
2003 			remove(name);
2004 			break;
2005 		}
2006 		fprint(2, "ms2html: created auxiliary file %s\n", name);
2007 		Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2008 		break;
2009 	}
2010 }
2011 
2012 void
2013 g_lf(int argc, char **argv)
2014 {
2015 	if(argc > 2)
2016 		snprint(ssp->filename, sizeof(ssp->filename), argv[2]);
2017 	if(argc > 1)
2018 		ssp->rlno = atoi(argv[1]);
2019 }
2020 
2021 void
2022 g_so(int argc, char **argv)
2023 {
2024 	ssp->lno++;
2025 	ssp->rlno++;
2026 	if(argc > 1)
2027 		pushsrc(argv[1]);
2028 }
2029 
2030 
2031 void
2032 g_BP(int argc, char **argv)
2033 {
2034 	int fd;
2035 	char *p, *ext;
2036 	char name[32];
2037 	Dir d;
2038 
2039 	if(argc < 2)
2040 		return;
2041 
2042 	p = strrchr(argv[1], '/');
2043 	if(p != nil)
2044 		p++;
2045 	else
2046 		p = argv[1];
2047 
2048 
2049 	ext = strrchr(p, '.');
2050 	if(ext){
2051 		if(strcmp(ext, ".jpeg") == 0
2052 		|| strcmp(ext, ".gif") == 0){
2053 			Bprint(&bout, "<br><img src=\"%s\"><br>\n", argv[1]);
2054 			return;
2055 		}
2056 	}
2057 
2058 
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 	switch(rfork(RFFDG|RFPROC)){
2067 	case -1:
2068 		fprint(2, "ms2html: can't fork: %r\n");
2069 		close(fd);
2070 		return;
2071 	case 0:
2072 		dup(fd, 1);
2073 		close(fd);
2074 		execl("/bin/ps2gif", "ps2gif", argv[1], 0);
2075 		fprint(2, "ms2html: couldn't exec ps2gif: %r\n");
2076 		_exits(nil);
2077 	default:
2078 		close(fd);
2079 		wait(nil);
2080 		if(dirstat(name, &d) < 0)
2081 			break;
2082 		if(d.length == 0){
2083 			remove(name);
2084 			break;
2085 		}
2086 		fprint(2, "ms2html: created auxiliary file %s\n", name);
2087 		Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2088 		break;
2089 	}
2090 }
2091 
2092 /* insert straight HTML into output */
2093 void
2094 g__H(int argc, char **argv)
2095 {
2096 	int i;
2097 
2098 	for(i = 1; i < argc; i++)
2099 		Bprint(&bout, "%s ", argv[i]);
2100 	Bprint(&bout, "\n");
2101 }
2102 
2103 /* HTML page title */
2104 void
2105 g__T(int argc, char **argv)
2106 {
2107 	if(titleseen)
2108 		return;
2109 
2110 	Bprint(&bout, "<title>\n");
2111 	printargs(argc, argv);
2112 	Bprint(&bout, "</title></head><body>\n");
2113 	titleseen = 1;
2114 }
2115 
2116 void
2117 g_nr(int argc, char **argv)
2118 {
2119 	char *val;
2120 
2121 	if (argc > 1) {
2122 		if (argc == 2)
2123 			val = "0";
2124 		else
2125 			val = argv[2];
2126 		dsnr(argv[1], val, &numregs);
2127 	}
2128 }
2129 
2130 void
2131 zerodivide(void)
2132 {
2133 	fprint(2, "stdin %d(%s:%d): division by 0\n",
2134 		ssp->lno, ssp->filename, ssp->rlno);
2135 }
2136 
2137 int
2138 numval(char **pline, int recur)
2139 {
2140 	char *p;
2141 	int neg, x, y;
2142 
2143 	x = neg = 0;
2144 	p = *pline;
2145 	while(*p == '-') {
2146 		neg = 1 - neg;
2147 		p++;
2148 	}
2149 	if (*p == '(') {
2150 		p++;
2151 		x = numval(&p, 1);
2152 		if (*p != ')')
2153 			goto done;
2154 		p++;
2155 	}
2156 	else while(*p >= '0' && *p <= '9')
2157 		x = 10*x + *p++ - '0';
2158 	if (neg)
2159 		x = -x;
2160 	if (recur)
2161 	    for(;;) {
2162 		switch(*p++) {
2163 		case '+':
2164 			x += numval(&p, 0);
2165 			continue;
2166 		case '-':
2167 			x -= numval(&p, 0);
2168 			continue;
2169 		case '*':
2170 			x *= numval(&p, 0);
2171 			continue;
2172 		case '/':
2173 			y = numval(&p, 0);
2174 			if (y == 0) {
2175 				zerodivide();
2176 				x = 0;
2177 				goto done;
2178 			}
2179 			x /= y;
2180 			continue;
2181 		case '<':
2182 			if (*p == '=') {
2183 				p++;
2184 				x = x <= numval(&p, 0);
2185 				continue;
2186 			}
2187 			x = x < numval(&p, 0);
2188 			continue;
2189 		case '>':
2190 			if (*p == '=') {
2191 				p++;
2192 				x = x >= numval(&p, 0);
2193 				continue;
2194 			}
2195 			x = x > numval(&p, 0);
2196 			continue;
2197 		case '=':
2198 			if (*p == '=')
2199 				p++;
2200 			x = x == numval(&p, 0);
2201 			continue;
2202 		case '&':
2203 			x &= numval(&p, 0);
2204 			continue;
2205 		case ':':
2206 			x |= numval(&p, 0);
2207 			continue;
2208 		case '%':
2209 			y = numval(&p, 0);
2210 			if (!y) {
2211 				zerodivide();
2212 				goto done;
2213 			}
2214 			x %= y;
2215 			continue;
2216 		}
2217 		--p;
2218 		break;
2219 	}
2220  done:
2221 	*pline = p;
2222 	return x;
2223 }
2224 
2225 int
2226 iftest(char *p, char **bp)
2227 {
2228 	char *p1;
2229 	int c, neg, rv;
2230 
2231 	rv = neg = 0;
2232 	if (*p == '!') {
2233 		neg = 1;
2234 		p++;
2235 	}
2236 	c = *p;
2237 	if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '('/*)*/) {
2238 		if (numval(&p,1) >= 1)
2239 			rv = 1;
2240 		goto done;
2241 	}
2242 	switch(c) {
2243 	case 't':
2244 	case 'o':
2245 		rv = 1;
2246 	case 'n':
2247 	case 'e':
2248 		p++;
2249 		goto done;
2250 	}
2251 	for(p1 = ++p; *p != c; p++)
2252 		if (!*p)
2253 			goto done;
2254 	for(p++;;) {
2255 		if (*p != *p1++) {
2256 			while(*p && *p++ != c);
2257 			goto done;
2258 		}
2259 		if (*p++ == c)
2260 			break;
2261 	}
2262 	rv = 1;
2263  done:
2264 	if (neg)
2265 		rv = 1 - rv;
2266 	while(*p == ' ' || *p == '\t')
2267 		p++;
2268 	*bp = p;
2269 	return rv;
2270 }
2271 
2272 void
2273 scanline(char *p, char *e, int wantnl)
2274 {
2275 	int c;
2276 	Rune r;
2277 
2278 	while((c = getrune()) == ' ' || c == '\t') ;
2279 	while(p < e) {
2280 		if (c < 0)
2281 			break;
2282 		if (c < Runeself) {
2283 			if (c == '\n') {
2284 				if (wantnl)
2285 					*p++ = c;
2286 				break;
2287 			}
2288 			*p++ = c;
2289 		}
2290 		else {
2291 			r = c;
2292 			p += runetochar(p, &r);
2293 		}
2294 		c = getrune();
2295 	}
2296 	*p = 0;
2297 }
2298 
2299 void
2300 pushbody(char *line)
2301 {
2302 	char *b;
2303 
2304 	if (line[0] == '\\' && line[1] == '{' /*}*/ )
2305 		line += 2;
2306 	if (strsp < Maxmstack - 1) {
2307 		pushstr(b = strdup(line));
2308 		mustfree[strsp] = b;
2309 	}
2310 }
2311 
2312 void
2313 skipbody(char *line)
2314 {
2315 	int c, n;
2316 
2317 	if (line[0] != '\\' || line[1] != '{' /*}*/ )
2318 		return;
2319 	for(n = 1;;) {
2320 		while((c = getrune()) != '\\')
2321 			if (c < 0)
2322 				return;
2323 		c = getrune();
2324 		if (c == '{')
2325 			n++;
2326 		else if ((c == '}' && (c = getrune()) == '\n' && !--n)
2327 			|| c < 0)
2328 			return;
2329 	}
2330 }
2331 
2332 int
2333 ifstart(char *line, char *e, char **bp)
2334 {
2335 	int it;
2336 	char *b;
2337 
2338 	b = copyline(line, e, 1);
2339 	ungetrune();
2340 	b[-1] = getrune();
2341 	scanline(b, e, 1);
2342 	it = iftest(line, bp);
2343 	return it;
2344 }
2345 
2346 void
2347 g_ie(char *line, char *e)
2348 {
2349 	char *b;
2350 
2351 	if (elsetop >= Maxif-1) {
2352 		fprint(2, "ms2html: .ie's too deep\n");
2353 		return;
2354 	}
2355 	if (ifwastrue[++elsetop] = ifstart(line, e, &b))
2356 		pushbody(b);
2357 	else
2358 		skipbody(b);
2359 }
2360 
2361 void
2362 g_if(char *line, char *e)
2363 {
2364 	char *b;
2365 
2366 	if (ifstart(line, e, &b))
2367 		pushbody(b);
2368 	else
2369 		skipbody(b);
2370 }
2371 
2372 void
2373 g_el(char *line, char *e)
2374 {
2375 	if (elsetop < 0)
2376 		return;
2377 	scanline(line, e, 1);
2378 	if (ifwastrue[elsetop--])
2379 		skipbody(line);
2380 	else
2381 		pushbody(line);
2382 }
2383 
2384 void
2385 g_ig(int argc, char **argv)
2386 {
2387 	char *p, *s;
2388 
2389 	s = "..";
2390 	if (argc > 1)
2391 		s = argv[1];
2392 	for(;;) {
2393 		p = Brdline(&ssp->in, '\n');
2394 		if(p == nil)
2395 			break;
2396 		p[Blinelen(&ssp->in)-1] = 0;
2397 		if(strcmp(p, s) == 0)
2398 			break;
2399 	}
2400 }
2401 
2402 void
2403 g_ds(char *line, char *e)
2404 {
2405 	char *b;
2406 
2407 	b = copyline(line, e, 1);
2408 	if (b > line) {
2409 		copyline(b, e, 0);
2410 		if (*b == '"')
2411 			b++;
2412 		ds(line, b);
2413 	}
2414 }
2415 
2416 void
2417 g_as(char *line, char *e)
2418 {
2419 	String *s;
2420 	char *b;
2421 
2422 	b = copyline(line, e, 1);
2423 	if (b == line)
2424 		return;
2425 	copyline(b, e, 0);
2426 	if (*b == '"')
2427 		b++;
2428 	for(s = strings; s != nil; s = s->next)
2429 		if(strcmp(line, s->name) == 0)
2430 			break;
2431 
2432 	if(s == nil){
2433 		ds(line, b);
2434 		return;
2435 	}
2436 
2437 	s->val = realloc(s->val, strlen(s->val) + strlen(b) + 1);
2438 	strcat(s->val, b);
2439 }
2440 
2441 void
2442 g_BS(int argc, char **argv)
2443 {
2444 	int i;
2445 
2446 	if (argc > 1 && !weBref) {
2447 		Bprint(&bout, "<a href=\"%s\"", argv[1]);
2448 		for(i = 2; i < argc; i++)
2449 			Bprint(&bout, " %s", argv[i]);
2450 		Bprint(&bout, ">");
2451 		weBref = 1;
2452 	}
2453 }
2454 
2455 void
2456 g_BE(int, char**)
2457 {
2458 	if (weBref) {
2459 		Bprint(&bout, "</a>");
2460 		weBref = 0;
2461 	}
2462 }
2463 
2464 void
2465 g_LB(int argc, char **argv)
2466 {
2467 	if (argc > 1) {
2468 		if (weBref)
2469 			g_BE(0,nil);
2470 		Bprint(&bout, "<a name=\"%s\"></a>", argv[1]);
2471 	}
2472 }
2473 
2474 void
2475 g_RT(int, char**)
2476 {
2477 	g_BE(0,nil);
2478 	dohanginghead();
2479 	closel();
2480 	closefont();
2481 }
2482