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