xref: /plan9/sys/src/cmd/ms2html.c (revision 6f8e93f6894df6375fc490745e7cb8df51855166)
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*
emalloc(uint n)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
dsnr(char * name,char * val,String ** l)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
ds(char * name,char * val)553 ds(char *name, char *val)
554 {
555 	dsnr(name, val, &strings);
556 }
557 
558 /* look up a defined string */
559 char*
getds(char * name)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 *
getnr(char * name)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
pushstr(char * p)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*
getmacro(char * name)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
pushsrc(char * name)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
getrune(void)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 	for(;;) {
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 	}
697 
698 	return r;
699 }
700 
701 void
ungetrune(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*
changefont(Font * f)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*
changebackfont(void)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*
changesize(int amount)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*
getnext(void)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 
896 		/* font change */
897 		case 'f':
898 			r = getrune();
899 			switch(r){
900 			case '(':
901 				str[0] = getrune();
902 				str[1] = getrune();
903 				str[2] = 0;
904 				token[0] = 0;
905 				if(strcmp("BI", str) == 0)
906 					return changefont(&bifont);
907 				else if(strcmp("CW", str) == 0)
908 					return changefont(&cwfont);
909 				else
910 					return changefont(nil);
911 			case '3':
912 			case 'B':
913 				return changefont(&bfont);
914 			case '2':
915 			case 'I':
916 				return changefont(&ifont);
917 			case '4':
918 				return changefont(&bifont);
919 			case '5':
920 				return changefont(&cwfont);
921 			case 'P':
922 				return changebackfont();
923 			case 'R':
924 			default:
925 				return changefont(nil);
926 			}
927 
928 		/* number register */
929 		case 'n':
930 			r = getrune();
931 			if (r == '(') /*)*/ {
932 				r = getrune();
933 				if (r < 0)
934 					return nil;
935 				str[0] = r;
936 				r = getrune();
937 				if (r < 0)
938 					return nil;
939 				str[1] = r;
940 				str[2] = 0;
941 				}
942 			else {
943 				str[0] = r;
944 				str[1] = 0;
945 				}
946 			pushstr(getnr(str));
947 			return getnext();
948 
949 		/* font size */
950 		case 's':
951 			r = getrune();
952 			switch(r){
953 			case '0':
954 				return changesize(0);
955 			case '-':
956 				r = getrune();
957 				if (!isdigit(r))
958 					return getnext();
959 				return changesize(-(r - '0'));
960 			case '+':
961 				r = getrune();
962 				if (!isdigit(r))
963 					return getnext();
964 				return changesize(r - '0');
965 			}
966 			return getnext();
967 		/* vertical movement */
968 		case 'v':
969 			r = getrune();
970 			if(r != '\''){
971 				ungetrune();
972 				return getnext();
973 			}
974 			r = getrune();
975 			if(r != '-')
976 				vert--;
977 			else
978 				vert++;
979 			while(r != '\'' && r != '\n')
980 				r = getrune();
981 			if(r != '\'')
982 				ungetrune();
983 
984 			if(vert > 0)
985 				return "^";
986 			return getnext();
987 
988 
989 		/* horizontal line */
990 		case 'l':
991 			r = getrune();
992 			if(r != '\''){
993 				ungetrune();
994 				return "<HR>";
995 			}
996 			while(getrune() != '\'')
997 				;
998 			return "<HR>";
999 
1000 		/* character height and slant */
1001 		case 'S':
1002 		case 'H':
1003 			r = getrune();
1004 			if(r != '\''){
1005 				ungetrune();
1006 				return "<HR>";
1007 			}
1008 			while(getrune() != '\'')
1009 				;
1010 			return getnext();
1011 
1012 		/* digit-width space */
1013 		case '0':
1014 			return " ";
1015 
1016 		/*for .if, .ie, .el */
1017 		case '{':
1018 			return "\\{"; /*}*/
1019 		case '}':
1020 			return "";
1021 		/* up and down */
1022 		case 'u':
1023 			if (isdown) {
1024 				isdown = 0;
1025 				return "</sub>";
1026 			}
1027 			isup = 1;
1028 			return "<sup>";
1029 		case 'd':
1030 			if (isup) {
1031 				isup = 0;
1032 				return "</sup>";
1033 			}
1034 			isdown = 1;
1035 			return "<sub>";
1036 		}
1037 		break;
1038 	case '&':
1039 		if(msp >= 0 || strsp >= 0)
1040 			return "&";
1041 		return "&amp;";
1042 	case '<':
1043 		if(msp >= 0 || strsp >= 0)
1044 			return "<";
1045 		return "&#60;";
1046 	case '>':
1047 		if(msp >= 0 || strsp >= 0)
1048 			return ">";
1049 		return "&#62;";
1050 	}
1051 	if (r < Runeself) {
1052 		token[0] = r;
1053 		token[1] = 0;
1054 		}
1055 	else {
1056 		R = r;
1057 		token[runetochar(token,&R)] = 0;
1058 	}
1059 	return token;
1060 }
1061 
1062 /* if arg0 is set, read up to (and expand) to the next whitespace, else to the end of line */
1063 char*
copyline(char * p,char * e,int arg0)1064 copyline(char *p, char *e, int arg0)
1065 {
1066 	int c;
1067 	Rune r;
1068 	char *p1;
1069 
1070 	while((c = getrune()) == ' ' || c == '\t')
1071 		;
1072 	for(indirective = 1; p < e; c = getrune()) {
1073 		if (c < 0)
1074 			goto done;
1075 		switch(c) {
1076 		case '\\':
1077 			break;
1078 		case '\n':
1079 			if (arg0)
1080 				ungetrune();
1081 			goto done;
1082 		case ' ':
1083 		case '\t':
1084 			if (arg0)
1085 				goto done;
1086 		default:
1087 			r = c;
1088 			p += runetochar(p,&r);
1089 			continue;
1090 		}
1091 		ungetrune();
1092 		p1 = getnext();
1093 		if (p1 == nil)
1094 			goto done;
1095 		if (*p1 == '\n') {
1096 			if (arg0)
1097 				ungetrune();
1098 			break;
1099 		}
1100 		while((*p = *p1++) && p < e)
1101 			p++;
1102 	}
1103 done:
1104 	indirective = 0;
1105 	*p++ = 0;
1106 	return p;
1107 }
1108 
1109 char*
copyarg(char * p,char * e,int * nullarg)1110 copyarg(char *p, char *e, int *nullarg)
1111 {
1112 	int c, quoted, last;
1113 	Rune r;
1114 
1115 	*nullarg = 0;
1116 	quoted = 0;
1117 	do{
1118 		c = getrune();
1119 	} while(c == ' ' || c == '\t');
1120 
1121 	if(c == '"'){
1122 		quoted = 1;
1123 		*nullarg = 1;
1124 		c = getrune();
1125 	}
1126 
1127 	if(c == '\n')
1128 		goto done;
1129 
1130 	last = 0;
1131 	for(; p < e; c = getrune()) {
1132 		if (c < 0)
1133 			break;
1134 		switch(c) {
1135 		case '\n':
1136 			ungetrune();
1137 			goto done;
1138 		case '\\':
1139 			r = c;
1140 			p += runetochar(p,&r);
1141 			if(last == '\\')
1142 				r = 0;
1143 			break;
1144 		case ' ':
1145 		case '\t':
1146 			if(!quoted && last != '\\')
1147 				goto done;
1148 			r = c;
1149 			p += runetochar(p,&r);
1150 			break;
1151 		case '"':
1152 			if(quoted && last != '\\')
1153 				goto done;
1154 			r = c;
1155 			p += runetochar(p,&r);
1156 			break;
1157 		default:
1158 			r = c;
1159 			p += runetochar(p,&r);
1160 			break;
1161 		}
1162 		last = r;
1163 	}
1164 done:
1165 	*p++ = 0;
1166 	return p;
1167 
1168 }
1169 
1170 int
parseargs(char * p,char * e,char ** argv)1171 parseargs(char *p, char *e, char **argv)
1172 {
1173 	int argc;
1174 	char *np;
1175 	int nullarg;
1176 
1177 	indirective = 1;
1178 	*p++ = 0;
1179 	for(argc = 1; argc < Narg; argc++){
1180 		np = copyarg(p, e, &nullarg);
1181 		if(nullarg==0 && np == p+1)
1182 			break;
1183 		argv[argc] = p;
1184 		p = np;
1185 	}
1186 	argv[argc] = nil;
1187 	indirective = 0;
1188 
1189 
1190 	return argc;
1191 }
1192 
1193 void
dodirective(void)1194 dodirective(void)
1195 {
1196 	char *p, *e;
1197 	Goobie *g;
1198 	Goobieif *gif;
1199 	char line[Nline], *line1;
1200 	int i, argc;
1201 	char *argv[Narg];
1202 	Mstack *m;
1203 
1204 	/* read line, translate special bytes */
1205 	e = line + sizeof(line) - UTFmax - 1;
1206 	line1 = copyline(line, e, 1);
1207 	if (!line[0])
1208 		return;
1209 	argv[0] = line;
1210 
1211 	/* first look through user defined macros */
1212 	p = getmacro(argv[0]);
1213 	if(p != nil){
1214 		if(msp == Maxmstack-1){
1215 			fprint(2, "ms2html: macro stack overflow\n");
1216 			return;
1217 		}
1218 		argc = parseargs(line1, e, argv);
1219 		m = &mstack[++msp];
1220 		m->ptr = p;
1221 		memset(m->argv, 0, sizeof(m->argv));
1222 		for(i = 0; i < argc; i++)
1223 			m->argv[i] = strdup(argv[i]);
1224 		return;
1225 	}
1226 
1227 	/* check for .if or .ie */
1228 	for(gif = gtabif; gif->name; gif++)
1229 		if(strcmp(gif->name, argv[0]) == 0){
1230 			(*gif->f)(line1, e);
1231 			return;
1232 		}
1233 
1234 	argc = parseargs(line1, e, argv);
1235 
1236 	/* try standard ms macros */
1237 	for(g = gtab; g->name; g++)
1238 		if(strcmp(g->name, argv[0]) == 0){
1239 			(*g->f)(argc, argv);
1240 			return;
1241 		}
1242 
1243 	if(debug)
1244 		fprint(2, "stdin %d(%s:%d): unknown directive %s\n",
1245 			ssp->lno, ssp->filename, ssp->rlno, line);
1246 }
1247 
1248 void
printarg(char * a)1249 printarg(char *a)
1250 {
1251 	char *e, *p;
1252 
1253 	e = a + strlen(a);
1254 	pushstr(a);
1255 	while(strsp >= 0 && strstack[strsp] >= a && strstack[strsp] < e){
1256 		p = getnext();
1257 		if(p == nil)
1258 			return;
1259 		Bprint(&bout, "%s", p);
1260 	}
1261 }
1262 
1263 void
printargs(int argc,char ** argv)1264 printargs(int argc, char **argv)
1265 {
1266 	argc--;
1267 	argv++;
1268 	while(--argc > 0){
1269 		printarg(*argv++);
1270 		Bprint(&bout, " ");
1271 	}
1272 	if(argc == 0)
1273 		printarg(*argv);
1274 }
1275 
1276 void
dohangingdt(void)1277 dohangingdt(void)
1278 {
1279 	switch(hangingdt){
1280 	case 3:
1281 		hangingdt--;
1282 		break;
1283 	case 2:
1284 		Bprint(&bout, "<dd>");
1285 		hangingdt = 0;
1286 		break;
1287 	}
1288 
1289 }
1290 
1291 void
dohangingau(void)1292 dohangingau(void)
1293 {
1294 	if(hangingau == 0)
1295 		return;
1296 	Bprint(&bout, "</I></DL>\n");
1297 	hangingau = 0;
1298 }
1299 
1300 void
dohanginghead(void)1301 dohanginghead(void)
1302 {
1303 	if(hanginghead == 0)
1304 		return;
1305 	Bprint(&bout, "</H%d>\n", hanginghead);
1306 	hanginghead = 0;
1307 }
1308 
1309 /*
1310  *  convert a man page to html and output
1311  */
1312 void
doconvert(void)1313 doconvert(void)
1314 {
1315 	char c, *p;
1316 	Tm *t;
1317 
1318 	pushsrc(nil);
1319 
1320 	sol = 1;
1321 	Bprint(&bout, "<html>\n");
1322 	Bflush(&bout);
1323 	for(;;){
1324 		p = getnext();
1325 		if(p == nil)
1326 			break;
1327 		c = *p;
1328 		if(c == '.' && sol){
1329 			dodirective();
1330 			dohangingdt();
1331 			ssp->lno++;
1332 			ssp->rlno++;
1333 			sol = 1;
1334 		} else if(c == '\n'){
1335 			if (ignore_nl)
1336 				ignore_nl = 0;
1337 			else {
1338 				if(hangingau)
1339 					Bprint(&bout, "<br>\n");
1340 				else
1341 					Bprint(&bout, "%s", p);
1342 				dohangingdt();
1343 				}
1344 			ssp->lno++;
1345 			ssp->rlno++;
1346 			sol = 1;
1347 		} else{
1348 			Bprint(&bout, "%s", p);
1349 			ignore_nl = sol = 0;
1350 		}
1351 	}
1352 	dohanginghead();
1353 	dohangingdt();
1354 	closel();
1355 	if(fsp >= 0 && fstack[fsp])
1356 		Bprint(&bout, "%s", fstack[fsp]->end);
1357 	Bprint(&bout, "<br>&#32;<br>\n");
1358 	Bprint(&bout, "<A href=http://www.lucent.com/copyright.html>\n");
1359 	t = localtime(time(nil));
1360 	Bprint(&bout, "Copyright</A> &#169; %d Alcatel-Lucent Inc.  All rights reserved.\n",
1361 			t->year+1900);
1362 	Bprint(&bout, "</body></html>\n");
1363 }
1364 
1365 static void
usage(void)1366 usage(void)
1367 {
1368 	sysfatal("usage: ms2html [-q] [-b basename] [-d '$$'] [-t title]");
1369 }
1370 
1371 void
main(int argc,char ** argv)1372 main(int argc, char **argv)
1373 {
1374 	quiet = 1;
1375 	ARGBEGIN {
1376 	case 't':
1377 		title = EARGF(usage());
1378 		break;
1379 	case 'b':
1380 		basename = EARGF(usage());
1381 		break;
1382 	case 'q':
1383 		quiet = 0;
1384 		break;
1385 	case 'd':
1386 		delim = EARGF(usage());
1387 		break;
1388 	case '?':
1389 	default:
1390 		usage();
1391 	} ARGEND;
1392 
1393 	Binit(&bout, 1, OWRITE);
1394 
1395 	ds("R", "&#174;");
1396 
1397 	doconvert();
1398 	exits(nil);
1399 }
1400 
1401 void
g_notyet(int,char ** argv)1402 g_notyet(int, char **argv)
1403 {
1404 	fprint(2, "ms2html: .%s not yet supported\n", argv[0]);
1405 }
1406 
1407 void
g_ignore(int,char ** argv)1408 g_ignore(int, char **argv)
1409 {
1410 	if(quiet)
1411 		return;
1412 	fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]);
1413 }
1414 
1415 void
g_PP(int,char **)1416 g_PP(int, char**)
1417 {
1418 	dohanginghead();
1419 	closel();
1420 	closefont();
1421 	Bprint(&bout, "<P>\n");
1422 	paragraph = 1;
1423 }
1424 
1425 void
g_LP(int,char **)1426 g_LP(int, char**)
1427 {
1428 	dohanginghead();
1429 	closel();
1430 	closefont();
1431 	Bprint(&bout, "<br>&#32;<br>\n");
1432 }
1433 
1434 /* close a list */
1435 void
closel(void)1436 closel(void)
1437 {
1438 	g_P2(1, nil);
1439 	dohangingau();
1440 	if(paragraph){
1441 		Bprint(&bout, "</P>\n");
1442 		paragraph = 0;
1443 	}
1444 	switch(list){
1445 	case Lordered:
1446 		Bprint(&bout, "</ol>\n");
1447 		break;
1448 	case Lunordered:
1449 		Bprint(&bout, "</ul>\n");
1450 		break;
1451 	case Lother:
1452 	case Ldef:
1453 		Bprint(&bout, "</dl>\n");
1454 		break;
1455 	}
1456 	list = 0;
1457 
1458 }
1459 
1460 
1461 void
g_IP(int argc,char ** argv)1462 g_IP(int argc, char **argv)
1463 {
1464 	dohanginghead();
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
g_in(int argc,char ** argv)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
g_HP(int,char **)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
g_SH(int,char **)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
g_NH(int argc,char ** argv)1600 g_NH(int argc, char **argv)
1601 {
1602 	int i, level;
1603 
1604 	closel();
1605 	closefont();
1606 
1607 	dohanginghead();
1608 	dohangingcenter();
1609 	if(argc == 1)
1610 		level = 0;
1611 	else {
1612 		level = atoi(argv[1])-1;
1613 		if(level < 0 || level >= Maxnh)
1614 			level = Maxnh - 1;
1615 	}
1616 	nh[level]++;
1617 
1618 	Bprint(&bout, "<H%d>", HH);
1619 	hanginghead = HH;
1620 
1621 	Bprint(&bout, "%d", nh[0]);
1622 	for(i = 1; i <= level; i++)
1623 		Bprint(&bout, ".%d", nh[i]);
1624 	Bprint(&bout, " ");
1625 
1626 	for(i = level+1; i < Maxnh; i++)
1627 		nh[i] = 0;
1628 }
1629 
1630 void
g_TL(int,char **)1631 g_TL(int, char**)
1632 {
1633 	char *p, *np;
1634 	char name[128];
1635 
1636 	closefont();
1637 
1638 	if(!titleseen){
1639 		if(!title){
1640 			/* get base part of filename */
1641 			p = strrchr(ssp->filename, '/');
1642 			if(p == nil)
1643 				p = ssp->filename;
1644 			else
1645 				p++;
1646 			strncpy(name, p, sizeof(name));
1647 			name[sizeof(name)-1] = 0;
1648 
1649 			/* dump any extensions */
1650 			np = strchr(name, '.');
1651 			if(np)
1652 				*np = 0;
1653 			title = p;
1654 		}
1655 		Bprint(&bout, "<title>\n");
1656 		Bprint(&bout, "%s\n", title);
1657 		Bprint(&bout, "</title>\n");
1658 		Bprint(&bout, "<body BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#330088\" ALINK=\"#FF0044\">\n");
1659 		titleseen = 1;
1660 	}
1661 
1662 	Bprint(&bout, "<center>");
1663 	hangingcenter = 1;
1664 	Bprint(&bout, "<H%d>", 1);
1665 	hanginghead = 1;
1666 }
1667 
1668 void
dohangingcenter(void)1669 dohangingcenter(void)
1670 {
1671 	if(hangingcenter){
1672 		Bprint(&bout, "</center>");
1673 		hangingcenter = 1;
1674 	}
1675 }
1676 
1677 void
g_AU(int,char **)1678 g_AU(int, char**)
1679 {
1680 	closel();
1681 	dohanginghead();
1682 	Bprint(&bout, "<DL><DD><I>");
1683 	hangingau = 1;
1684 }
1685 
1686 void
pushfont(Font * f)1687 pushfont(Font *f)
1688 {
1689 	if(fsp == Maxfsp)
1690 		return;
1691 	if(fsp >= 0 && fstack[fsp])
1692 		Bprint(&bout, "%s", fstack[fsp]->end);
1693 	if(f != nil)
1694 		Bprint(&bout, "%s", f->start);
1695 	fstack[++fsp] = f;
1696 }
1697 
1698 void
popfont(void)1699 popfont(void)
1700 {
1701 	if(fsp >= 0){
1702 		if(fstack[fsp])
1703 			Bprint(&bout, "%s", fstack[fsp]->end);
1704 		fsp--;
1705 	}
1706 }
1707 
1708 /*
1709  *  for 3 args print arg3 \fxarg1\fP arg2
1710  *  for 2 args print arg1 \fxarg2\fP
1711  *  for 1 args print \fxarg1\fP
1712  */
1713 void
font(Font * f,int argc,char ** argv)1714 font(Font *f, int argc, char **argv)
1715 {
1716 	if(argc == 1){
1717 		pushfont(nil);
1718 		return;
1719 	}
1720 	if(argc > 3)
1721 		printarg(argv[3]);
1722 	pushfont(f);
1723 	printarg(argv[1]);
1724 	popfont();
1725 	if(argc > 2)
1726 		printarg(argv[2]);
1727 	Bprint(&bout, "\n");
1728 }
1729 
1730 void
closefont(void)1731 closefont(void)
1732 {
1733 	if(fsp >= 0 && fstack[fsp])
1734 		Bprint(&bout, "%s", fstack[fsp]->end);
1735 	fsp = -1;
1736 }
1737 
1738 void
g_B(int argc,char ** argv)1739 g_B(int argc, char **argv)
1740 {
1741 	font(&bfont, argc, argv);
1742 }
1743 
1744 void
g_R(int argc,char ** argv)1745 g_R(int argc, char **argv)
1746 {
1747 	font(nil, argc, argv);
1748 }
1749 
1750 void
g_BI(int argc,char ** argv)1751 g_BI(int argc, char **argv)
1752 {
1753 	font(&bifont, argc, argv);
1754 }
1755 
1756 void
g_CW(int argc,char ** argv)1757 g_CW(int argc, char **argv)
1758 {
1759 	font(&cwfont, argc, argv);
1760 }
1761 
1762 char*
lower(char * p)1763 lower(char *p)
1764 {
1765 	char *x;
1766 
1767 	for(x = p; *x; x++)
1768 		if(*x >= 'A' && *x <= 'Z')
1769 			*x -= 'A' - 'a';
1770 	return p;
1771 }
1772 
1773 void
g_I(int argc,char ** argv)1774 g_I(int argc, char **argv)
1775 {
1776 	int anchor;
1777 	char *p;
1778 
1779 	anchor = 0;
1780 	if(argc > 2){
1781 		p = argv[2];
1782 		if(p[0] == '(')
1783 		if(p[1] >= '0' && p[1] <= '9')
1784 		if(p[2] == ')'){
1785 			anchor = 1;
1786 			Bprint(&bout, "<A href=\"/magic/man2html/%c/%s\">",
1787 				p[1], lower(argv[1]));
1788 		}
1789 	}
1790 	font(&ifont, argc, argv);
1791 	if(anchor)
1792 		Bprint(&bout, "</A>");
1793 }
1794 
1795 void
g_br(int,char **)1796 g_br(int, char**)
1797 {
1798 	if(hangingdt){
1799 		Bprint(&bout, "<dd>");
1800 		hangingdt = 0;
1801 	}else
1802 		Bprint(&bout, "<br>\n");
1803 }
1804 
1805 void
g_P1(int,char **)1806 g_P1(int, char**)
1807 {
1808 	if(example == 0){
1809 		example = 1;
1810 		Bprint(&bout, "<DL><DT><DD><TT><PRE>\n");
1811 	}
1812 }
1813 
1814 void
g_P2(int,char **)1815 g_P2(int, char**)
1816 {
1817 	if(example){
1818 		example = 0;
1819 		Bprint(&bout, "</PRE></TT></DL>\n");
1820 	}
1821 }
1822 
1823 void
g_SM(int,char ** argv)1824 g_SM(int, char **argv)
1825 {
1826 	Bprint(&bout, "%s", argv[1]);
1827 }
1828 
1829 void
g_ft(int argc,char ** argv)1830 g_ft(int argc, char **argv)
1831 {
1832 	if(argc < 2){
1833 		pushfont(nil);
1834 		return;
1835 	}
1836 
1837 	switch(argv[1][0]){
1838 	case '3':
1839 	case 'B':
1840 		pushfont(&bfont);
1841 		break;
1842 	case '2':
1843 	case 'I':
1844 		pushfont(&ifont);
1845 		break;
1846 	case '4':
1847 		pushfont(&bifont);
1848 		break;
1849 	case '5':
1850 		pushfont(&cwfont);
1851 		break;
1852 	case 'P':
1853 		popfont();
1854 		break;
1855 	case 'R':
1856 	default:
1857 		pushfont(nil);
1858 		break;
1859 	}
1860 }
1861 
1862 void
g_sp(int argc,char ** argv)1863 g_sp(int argc, char **argv)
1864 {
1865 	int n;
1866 
1867 	n = 1;
1868 	if(argc > 1){
1869 		n = atoi(argv[1]);
1870 		if(n < 1)
1871 			n = 1;
1872 		if(argv[1][strlen(argv[1])-1] == 'i')
1873 			n *= 4;
1874 	}
1875 	if(n > 5){
1876 		Bprint(&bout, "<br>&#32;<br>\n");
1877 		Bprint(&bout, "<HR>\n");
1878 		Bprint(&bout, "<br>&#32;<br>\n");
1879 	} else
1880 		for(; n > 0; n--)
1881 			Bprint(&bout, "<br>&#32;<br>\n");
1882 }
1883 
1884  void
rm_loop(char * name,String ** l)1885 rm_loop(char *name, String **l)
1886 {
1887 	String *s;
1888 	for(s = *l; s != nil; s = *l){
1889 		if(strcmp(name, s->name) == 0){
1890 			*l = s->next;
1891 			free(s->name);
1892 			free(s->val);
1893 			free(s);
1894 			break;
1895 			}
1896 		l = &s->next;
1897 		}
1898 	}
1899 
1900 void
g_rm(int argc,char ** argv)1901 g_rm(int argc, char **argv)
1902 {
1903 	Goobie *g;
1904 	char *name;
1905 	int i;
1906 
1907 	for(i = 1; i < argc; i++) {
1908 		name = argv[i];
1909 		rm_loop(name, &strings);
1910 		rm_loop(name, &macros);
1911 		for(g = gtab; g->name; g++)
1912 			if (strcmp(g->name, name) == 0) {
1913 				g->f = g_ignore;
1914 				break;
1915 				}
1916 		}
1917 	}
1918 
1919 void
g_AB(int,char **)1920 g_AB(int, char**)
1921 {
1922 	closel();
1923 	dohangingcenter();
1924 	Bprint(&bout, "<center><H4>ABSTRACT</H4></center><DL><DD>\n");
1925 }
1926 
1927 void
g_AE(int,char **)1928 g_AE(int, char**)
1929 {
1930 	Bprint(&bout, "</DL>\n");
1931 }
1932 
1933 void
g_FS(int,char **)1934 g_FS(int, char **)
1935 {
1936 	char *argv[3];
1937 
1938 	argv[0] = "IP";
1939 	argv[1] = nil;
1940 	argv[2] = nil;
1941 	g_IP(1, argv);
1942 	Bprint(&bout, "NOTE:<I> ");
1943 }
1944 
1945 void
g_FE(int,char **)1946 g_FE(int, char **)
1947 {
1948 	Bprint(&bout, "</I><DT>&#32;<DD>");
1949 	closel();
1950 	Bprint(&bout, "<br>\n");
1951 }
1952 
1953 void
g_de(int argc,char ** argv)1954 g_de(int argc, char **argv)
1955 {
1956 	int r;
1957 	char *p, *cp;
1958 	String *m;
1959 	int len;
1960 
1961 	if(argc < 2)
1962 		return;
1963 
1964 	m = nil;
1965 	len = 0;
1966 	if(strcmp(argv[0], "am") == 0){
1967 		for(m = macros; m != nil; m = m->next)
1968 			if(strcmp(argv[1], m->name) == 0){
1969 				len = strlen(m->val);
1970 				break;
1971 			}
1972 
1973 		if(m == nil){
1974 			/* nothing to append to */
1975 			for(;;){
1976 				p = Brdline(&ssp->in, '\n');
1977 				if(p == nil)
1978 					break;
1979 				p[Blinelen(&ssp->in)-1] = 0;
1980 				if(strcmp(p, "..") == 0)
1981 					break;
1982 			}
1983 			return;
1984 		}
1985 	}
1986 
1987 	if(m == nil){
1988 		m = emalloc(sizeof(*m));
1989 		m->next = macros;
1990 		macros = m;
1991 		m->name = strdup(argv[1]);
1992 		m->val = nil;
1993 		len = 0;
1994 	}
1995 
1996 	/* read up to a .. removing double backslashes */
1997 	for(;;){
1998 		p = Brdline(&ssp->in, '\n');
1999 		if(p == nil)
2000 			break;
2001 		p[Blinelen(&ssp->in)-1] = 0;
2002 		if(strcmp(p, "..") == 0)
2003 			break;
2004 		m->val = realloc(m->val, len + Blinelen(&ssp->in)+1);
2005 		cp = m->val + len;
2006 		while(*p){
2007 			r = *p++;
2008 			if(r == '\\' && *p == '\\')
2009 				p++;
2010 			*cp++ = r;
2011 		}
2012 		*cp++ = '\n';
2013 		len = cp - m->val;
2014 		*cp = 0;
2015 	}
2016 }
2017 
2018 void
g_hrule(int,char **)2019 g_hrule(int, char**)
2020 {
2021 	Bprint(&bout, "<HR>\n");
2022 }
2023 
2024 void
g_BX(int argc,char ** argv)2025 g_BX(int argc, char **argv)
2026 {
2027 	Bprint(&bout, "<HR>\n");
2028 	printargs(argc, argv);
2029 	Bprint(&bout, "<HR>\n");
2030 }
2031 
2032 void
g_IH(int,char **)2033 g_IH(int, char**)
2034 {
2035 	Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n");
2036 }
2037 
2038 void
g_MH(int,char **)2039 g_MH(int, char**)
2040 {
2041 	Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n");
2042 }
2043 
2044 void
g_PY(int,char **)2045 g_PY(int, char**)
2046 {
2047 	Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n");
2048 }
2049 
2050 void
g_HO(int,char **)2051 g_HO(int, char**)
2052 {
2053 	Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n");
2054 }
2055 
2056 void
g_QS(int,char **)2057 g_QS(int, char**)
2058 {
2059 	Bprint(&bout, "<BLOCKQUOTE>\n");
2060 }
2061 
2062 void
g_QE(int,char **)2063 g_QE(int, char**)
2064 {
2065 	Bprint(&bout, "</BLOCKQUOTE>\n");
2066 }
2067 
2068 void
g_RS(int,char **)2069 g_RS(int, char**)
2070 {
2071 	Bprint(&bout, "<DL><DD>\n");
2072 }
2073 
2074 void
g_RE(int,char **)2075 g_RE(int, char**)
2076 {
2077 	Bprint(&bout, "</DL>\n");
2078 }
2079 
2080 int gif;
2081 
2082 void
g_startgif(int,char ** argv)2083 g_startgif(int, char **argv)
2084 {
2085 	int fd;
2086 	int pfd[2];
2087 	char *e, *p;
2088 	char name[32];
2089 	Dir *d;
2090 
2091 	if(strcmp(argv[0], "EQ") == 0)
2092 		e = ".EN";
2093 	else if(strcmp(argv[0], "TS") == 0)
2094 		e = ".TE";
2095 	else if(strcmp(argv[0], "PS") == 0)
2096 		e = ".PE";
2097 	else
2098 		return;
2099 
2100 	if(basename)
2101 		p = basename;
2102 	else{
2103 		p = strrchr(sstack[0].filename, '/');
2104 		if(p != nil)
2105 			p++;
2106 		else
2107 			p = sstack[0].filename;
2108 	}
2109 	snprint(name, sizeof(name), "%s.%d.gif", p, gif++);
2110 	fd = create(name, OWRITE, 0664);
2111 	if(fd < 0){
2112 		fprint(2, "ms2html: can't create %s: %r\n", name);
2113 		return;
2114 	}
2115 
2116 	if(pipe(pfd) < 0){
2117 		fprint(2, "ms2html: can't create pipe: %r\n");
2118 		close(fd);
2119 		return;
2120 	}
2121 	switch(rfork(RFFDG|RFPROC)){
2122 	case -1:
2123 		fprint(2, "ms2html: can't fork: %r\n");
2124 		close(fd);
2125 		return;
2126 	case 0:
2127 		dup(fd, 1);
2128 		close(fd);
2129 		dup(pfd[0], 0);
2130 		close(pfd[0]);
2131 		close(pfd[1]);
2132 		execl("/bin/troff2gif", "troff2gif", nil);
2133 		fprint(2, "ms2html: couldn't exec troff2gif: %r\n");
2134 		_exits(nil);
2135 	default:
2136 		close(fd);
2137 		close(pfd[0]);
2138 		fprint(pfd[1], ".ll 7i\n");
2139 	/*	fprint(pfd[1], ".EQ\ndelim %s\n.EN\n", delim); */
2140 	/*	fprint(pfd[1], ".%s\n", argv[0]); */
2141 		for(;;){
2142 			p = Brdline(&ssp->in, '\n');
2143 			if(p == nil)
2144 				break;
2145 			ssp->lno++;
2146 			ssp->rlno++;
2147 			if(write(pfd[1], p, Blinelen(&ssp->in)) < 0)
2148 				break;
2149 			if(strncmp(p, e, 3) == 0)
2150 				break;
2151 		}
2152 		close(pfd[1]);
2153 		waitpid();
2154 		d = dirstat(name);
2155 		if(d == nil)
2156 			break;
2157 		if(d->length == 0){
2158 			remove(name);
2159 			free(d);
2160 			break;
2161 		}
2162 		free(d);
2163 		fprint(2, "ms2html: created auxiliary file %s\n", name);
2164 		Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2165 		break;
2166 	}
2167 }
2168 
2169 void
g_lf(int argc,char ** argv)2170 g_lf(int argc, char **argv)
2171 {
2172 	if(argc > 2)
2173 		snprint(ssp->filename, sizeof(ssp->filename), argv[2]);
2174 	if(argc > 1)
2175 		ssp->rlno = atoi(argv[1]);
2176 }
2177 
2178 void
g_so(int argc,char ** argv)2179 g_so(int argc, char **argv)
2180 {
2181 	ssp->lno++;
2182 	ssp->rlno++;
2183 	if(argc > 1)
2184 		pushsrc(argv[1]);
2185 }
2186 
2187 
2188 void
g_BP(int argc,char ** argv)2189 g_BP(int argc, char **argv)
2190 {
2191 	int fd;
2192 	char *p, *ext;
2193 	char name[32];
2194 	Dir *d;
2195 
2196 	if(argc < 2)
2197 		return;
2198 
2199 	p = strrchr(argv[1], '/');
2200 	if(p != nil)
2201 		p++;
2202 	else
2203 		p = argv[1];
2204 
2205 
2206 	ext = strrchr(p, '.');
2207 	if(ext){
2208 		if(strcmp(ext, ".jpeg") == 0
2209 		|| strcmp(ext, ".gif") == 0){
2210 			Bprint(&bout, "<br><img src=\"%s\"><br>\n", argv[1]);
2211 			return;
2212 		}
2213 	}
2214 
2215 
2216 	snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++);
2217 	fd = create(name, OWRITE, 0664);
2218 	if(fd < 0){
2219 		fprint(2, "ms2html: can't create %s: %r\n", name);
2220 		return;
2221 	}
2222 
2223 	switch(rfork(RFFDG|RFPROC)){
2224 	case -1:
2225 		fprint(2, "ms2html: can't fork: %r\n");
2226 		close(fd);
2227 		return;
2228 	case 0:
2229 		dup(fd, 1);
2230 		close(fd);
2231 		execl("/bin/ps2gif", "ps2gif", argv[1], nil);
2232 		fprint(2, "ms2html: couldn't exec ps2gif: %r\n");
2233 		_exits(nil);
2234 	default:
2235 		close(fd);
2236 		waitpid();
2237 		d = dirstat(name);
2238 		if(d == nil)
2239 			break;
2240 		if(d->length == 0){
2241 			remove(name);
2242 			free(d);
2243 			break;
2244 		}
2245 		free(d);
2246 		fprint(2, "ms2html: created auxiliary file %s\n", name);
2247 		Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2248 		break;
2249 	}
2250 }
2251 
2252 /* insert straight HTML into output */
2253 void
g__H(int argc,char ** argv)2254 g__H(int argc, char **argv)
2255 {
2256 	int i;
2257 
2258 	for(i = 1; i < argc; i++)
2259 		Bprint(&bout, "%s ", argv[i]);
2260 	Bprint(&bout, "\n");
2261 }
2262 
2263 /* HTML page title */
2264 void
g__T(int argc,char ** argv)2265 g__T(int argc, char **argv)
2266 {
2267 	if(titleseen)
2268 		return;
2269 
2270 	Bprint(&bout, "<title>\n");
2271 	printargs(argc, argv);
2272 	Bprint(&bout, "</title></head><body>\n");
2273 	titleseen = 1;
2274 }
2275 
2276 void
g_nr(int argc,char ** argv)2277 g_nr(int argc, char **argv)
2278 {
2279 	char *val;
2280 
2281 	if (argc > 1) {
2282 		if (argc == 2)
2283 			val = "0";
2284 		else
2285 			val = argv[2];
2286 		dsnr(argv[1], val, &numregs);
2287 	}
2288 }
2289 
2290 void
zerodivide(void)2291 zerodivide(void)
2292 {
2293 	fprint(2, "stdin %d(%s:%d): division by 0\n",
2294 		ssp->lno, ssp->filename, ssp->rlno);
2295 }
2296 
2297 int
numval(char ** pline,int recur)2298 numval(char **pline, int recur)
2299 {
2300 	char *p;
2301 	int neg, x, y;
2302 
2303 	x = neg = 0;
2304 	p = *pline;
2305 	while(*p == '-') {
2306 		neg = 1 - neg;
2307 		p++;
2308 	}
2309 	if (*p == '(') {
2310 		p++;
2311 		x = numval(&p, 1);
2312 		if (*p != ')')
2313 			goto done;
2314 		p++;
2315 	}
2316 	else while(*p >= '0' && *p <= '9')
2317 		x = 10*x + *p++ - '0';
2318 	if (neg)
2319 		x = -x;
2320 	if (recur)
2321 	    for(;;) {
2322 		switch(*p++) {
2323 		case '+':
2324 			x += numval(&p, 0);
2325 			continue;
2326 		case '-':
2327 			x -= numval(&p, 0);
2328 			continue;
2329 		case '*':
2330 			x *= numval(&p, 0);
2331 			continue;
2332 		case '/':
2333 			y = numval(&p, 0);
2334 			if (y == 0) {
2335 				zerodivide();
2336 				x = 0;
2337 				goto done;
2338 			}
2339 			x /= y;
2340 			continue;
2341 		case '<':
2342 			if (*p == '=') {
2343 				p++;
2344 				x = x <= numval(&p, 0);
2345 				continue;
2346 			}
2347 			x = x < numval(&p, 0);
2348 			continue;
2349 		case '>':
2350 			if (*p == '=') {
2351 				p++;
2352 				x = x >= numval(&p, 0);
2353 				continue;
2354 			}
2355 			x = x > numval(&p, 0);
2356 			continue;
2357 		case '=':
2358 			if (*p == '=')
2359 				p++;
2360 			x = x == numval(&p, 0);
2361 			continue;
2362 		case '&':
2363 			x &= numval(&p, 0);
2364 			continue;
2365 		case ':':
2366 			x |= numval(&p, 0);
2367 			continue;
2368 		case '%':
2369 			y = numval(&p, 0);
2370 			if (!y) {
2371 				zerodivide();
2372 				goto done;
2373 			}
2374 			x %= y;
2375 			continue;
2376 		}
2377 		--p;
2378 		break;
2379 	}
2380  done:
2381 	*pline = p;
2382 	return x;
2383 }
2384 
2385 int
iftest(char * p,char ** bp)2386 iftest(char *p, char **bp)
2387 {
2388 	char *p1;
2389 	int c, neg, rv;
2390 
2391 	rv = neg = 0;
2392 	if (*p == '!') {
2393 		neg = 1;
2394 		p++;
2395 	}
2396 	c = *p;
2397 	if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '('/*)*/) {
2398 		if (numval(&p,1) >= 1)
2399 			rv = 1;
2400 		goto done;
2401 	}
2402 	switch(c) {
2403 	case 't':
2404 	case 'o':
2405 		rv = 1;
2406 	case 'n':
2407 	case 'e':
2408 		p++;
2409 		goto done;
2410 	}
2411 	for(p1 = ++p; *p != c; p++)
2412 		if (!*p)
2413 			goto done;
2414 	for(p++;;) {
2415 		if (*p != *p1++) {
2416 			while(*p && *p++ != c);
2417 			goto done;
2418 		}
2419 		if (*p++ == c)
2420 			break;
2421 	}
2422 	rv = 1;
2423  done:
2424 	if (neg)
2425 		rv = 1 - rv;
2426 	while(*p == ' ' || *p == '\t')
2427 		p++;
2428 	*bp = p;
2429 	return rv;
2430 }
2431 
2432 void
scanline(char * p,char * e,int wantnl)2433 scanline(char *p, char *e, int wantnl)
2434 {
2435 	int c;
2436 	Rune r;
2437 
2438 	while((c = getrune()) == ' ' || c == '\t') ;
2439 	while(p < e) {
2440 		if (c < 0)
2441 			break;
2442 		if (c < Runeself) {
2443 			if (c == '\n') {
2444 				if (wantnl)
2445 					*p++ = c;
2446 				break;
2447 			}
2448 			*p++ = c;
2449 		}
2450 		else {
2451 			r = c;
2452 			p += runetochar(p, &r);
2453 		}
2454 		c = getrune();
2455 	}
2456 	*p = 0;
2457 }
2458 
2459 void
pushbody(char * line)2460 pushbody(char *line)
2461 {
2462 	char *b;
2463 
2464 	if (line[0] == '\\' && line[1] == '{' /*}*/ )
2465 		line += 2;
2466 	if (strsp < Maxmstack - 1) {
2467 		pushstr(b = strdup(line));
2468 		mustfree[strsp] = b;
2469 	}
2470 }
2471 
2472 void
skipbody(char * line)2473 skipbody(char *line)
2474 {
2475 	int c, n;
2476 
2477 	if (line[0] != '\\' || line[1] != '{' /*}*/ )
2478 		return;
2479 	for(n = 1;;) {
2480 		while((c = getrune()) != '\\')
2481 			if (c < 0)
2482 				return;
2483 		c = getrune();
2484 		if (c == '{')
2485 			n++;
2486 		else if ((c == '}' && (c = getrune()) == '\n' && !--n)
2487 			|| c < 0)
2488 			return;
2489 	}
2490 }
2491 
2492 int
ifstart(char * line,char * e,char ** bp)2493 ifstart(char *line, char *e, char **bp)
2494 {
2495 	int it;
2496 	char *b;
2497 
2498 	b = copyline(line, e, 1);
2499 	ungetrune();
2500 	b[-1] = getrune();
2501 	scanline(b, e, 1);
2502 	it = iftest(line, bp);
2503 	return it;
2504 }
2505 
2506 void
g_ie(char * line,char * e)2507 g_ie(char *line, char *e)
2508 {
2509 	char *b;
2510 
2511 	if (elsetop >= Maxif-1) {
2512 		fprint(2, "ms2html: .ie's too deep\n");
2513 		return;
2514 	}
2515 	if (ifwastrue[++elsetop] = ifstart(line, e, &b))
2516 		pushbody(b);
2517 	else
2518 		skipbody(b);
2519 }
2520 
2521 void
g_if(char * line,char * e)2522 g_if(char *line, char *e)
2523 {
2524 	char *b;
2525 
2526 	if (ifstart(line, e, &b))
2527 		pushbody(b);
2528 	else
2529 		skipbody(b);
2530 }
2531 
2532 void
g_el(char * line,char * e)2533 g_el(char *line, char *e)
2534 {
2535 	if (elsetop < 0)
2536 		return;
2537 	scanline(line, e, 1);
2538 	if (ifwastrue[elsetop--])
2539 		skipbody(line);
2540 	else
2541 		pushbody(line);
2542 }
2543 
2544 void
g_ig(int argc,char ** argv)2545 g_ig(int argc, char **argv)
2546 {
2547 	char *p, *s;
2548 
2549 	s = "..";
2550 	if (argc > 1)
2551 		s = argv[1];
2552 	for(;;) {
2553 		p = Brdline(&ssp->in, '\n');
2554 		if(p == nil)
2555 			break;
2556 		p[Blinelen(&ssp->in)-1] = 0;
2557 		if(strcmp(p, s) == 0)
2558 			break;
2559 	}
2560 }
2561 
2562 void
g_ds(char * line,char * e)2563 g_ds(char *line, char *e)
2564 {
2565 	char *b;
2566 
2567 	b = copyline(line, e, 1);
2568 	if (b > line) {
2569 		copyline(b, e, 0);
2570 		if (*b == '"')
2571 			b++;
2572 		ds(line, b);
2573 	}
2574 }
2575 
2576 void
g_as(char * line,char * e)2577 g_as(char *line, char *e)
2578 {
2579 	String *s;
2580 	char *b;
2581 
2582 	b = copyline(line, e, 1);
2583 	if (b == line)
2584 		return;
2585 	copyline(b, e, 0);
2586 	if (*b == '"')
2587 		b++;
2588 	for(s = strings; s != nil; s = s->next)
2589 		if(strcmp(line, s->name) == 0)
2590 			break;
2591 
2592 	if(s == nil){
2593 		ds(line, b);
2594 		return;
2595 	}
2596 
2597 	s->val = realloc(s->val, strlen(s->val) + strlen(b) + 1);
2598 	strcat(s->val, b);
2599 }
2600 
2601 void
g_BS(int argc,char ** argv)2602 g_BS(int argc, char **argv)
2603 {
2604 	int i;
2605 
2606 	if (argc > 1 && !weBref) {
2607 		Bprint(&bout, "<a href=\"%s\"", argv[1]);
2608 		for(i = 2; i < argc; i++)
2609 			Bprint(&bout, " %s", argv[i]);
2610 		Bprint(&bout, ">");
2611 		weBref = 1;
2612 	}
2613 }
2614 
2615 void
g_BE(int,char **)2616 g_BE(int, char**)
2617 {
2618 	if (weBref) {
2619 		Bprint(&bout, "</a>");
2620 		weBref = 0;
2621 	}
2622 }
2623 
2624 void
g_LB(int argc,char ** argv)2625 g_LB(int argc, char **argv)
2626 {
2627 	if (argc > 1) {
2628 		if (weBref)
2629 			g_BE(0,nil);
2630 		Bprint(&bout, "<a name=\"%s\"></a>", argv[1]);
2631 	}
2632 }
2633 
2634 void
g_RT(int,char **)2635 g_RT(int, char**)
2636 {
2637 	g_BE(0,nil);
2638 	dohanginghead();
2639 	closel();
2640 	closefont();
2641 }
2642