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