1 /*
2  * This program was written by Richard Verhoeven (NL:5482ZX35)
3  * at the Eindhoven University of Technology. Email: rcb5@win.tue.nl
4  *
5  * Permission is granted to distribute, modify and use this program as long
6  * as this comment is not removed or changed.
7  *
8  * THIS IS A MODIFIED VERSION.  IT WAS MODIFIED BY chet@po.cwru.edu FOR
9  * USE BY BASH.
10  */
11 
12 /*
13  * man2html will add links to the converted manpages. The function add_links
14  * is used for that. At the moment it will add links as follows, where
15  *     indicates what should match to start with:
16  * ^^^
17  * Recognition           Item            Link
18  * ----------------------------------------------------------
19  * name(*)               Manpage         ../man?/name.*
20  *     ^
21  * name@hostname         Email address   mailto:name@hostname
22  *     ^
23  * method://string       URL             method://string
24  *       ^^^
25  * www.host.name         WWW server      http://www.host.name
26  * ^^^^
27  * ftp.host.name         FTP server      ftp://ftp.host.name
28  * ^^^^
29  * <file.h>              Include file    file:/usr/include/file.h
30  *      ^^^
31  *
32  * Since man2html does not check if manpages, hosts or email addresses exist,
33  * some links might not work. For manpages, some extra checks are performed
34  * to make sure not every () pair creates a link. Also out of date pages
35  * might point to incorrect places.
36  *
37  * The program will not allow users to get system specific files, such as
38  * /etc/passwd. It will check that "man" is part of the specified file and
39  * that  "/../" isn't. Even if someone manages to get such file, man2html will
40  * handle it like a manpage and will usually not produce any output (or crash).
41  *
42  * If you find any bugs when normal manpages are converted, please report
43  * them to me (rcb5@win.tue.nl) after you have checked that man(1) can handle
44  * the manpage correct.
45  *
46  * Known bugs and missing features:
47  *
48  *  * Equations are not converted at all.
49  *  * Tables are converted but some features are not possible in html.
50  *  * The tabbing environment is converted by counting characters and adding
51  *    spaces. This might go wrong (outside <PRE>)
52  *  * Some pages look better if man2html works in troff mode, especially pages
53  *    with tables. You can decide at compile time which made you want to use.
54  *
55  *    -DNROFF=0     troff mode
56  *    -DNROFF=1     nroff mode   (default)
57  *
58  *    if you install both modes, you should compile with the correct CGIBASE.
59  *  * Some manpages rely on the fact that troff/nroff is used to convert
60  *    them and use features which are not descripted in the man manpages.
61  *    (definitions, calculations, conditionals, requests). I can't guarantee
62  *    that all these features work on all manpages. (I didn't have the
63  *    time to look through all the available manpages.)
64  */
65 #ifdef HAVE_CONFIG_H
66 #include <config.h>
67 #endif
68 
69 #define NROFF 0
70 
71 #include <unistd.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <sys/stat.h>
76 #include <ctype.h>
77 #include <sys/types.h>
78 #include <time.h>
79 #include <sys/time.h>
80 #include <errno.h>
81 
82 #define NULL_TERMINATED(n) ((n) + 1)
83 
84 #define HUGE_STR_MAX  10000
85 #define LARGE_STR_MAX 2000
86 #define MED_STR_MAX   500
87 #define SMALL_STR_MAX 100
88 #define TINY_STR_MAX  10
89 
90 #define MAX_MAN_PATHS 100	/* Max number of directories */
91 #define MAX_ZCATS     10	/* Max number of zcat style programs */
92 #define MAX_WORDLIST  100
93 
94 #ifndef EXIT_SUCCESS
95 #define EXIT_SUCCESS 0
96 #endif
97 #ifndef EXIT_FAILURE
98 #define EXIT_FAILURE 1
99 #endif
100 #ifndef EXIT_USAGE
101 #define EXIT_USAGE 2
102 #endif
103 
104 static char location_base[NULL_TERMINATED(MED_STR_MAX)] = "";
105 
106 static char th_page_and_sec[128] = { '\0' };
107 static char th_datestr[128] = { '\0' };
108 static char th_version[128] = { '\0' };
109 
110 char   *signature = "<HR>\nThis document was created by man2html from %s.<BR>\nTime: %s\n";
111 
112 /* timeformat for signature */
113 #define TIMEFORMAT "%d %B %Y %T %Z"
114 
115 char *manpage;
116 
117 /* BSD mandoc Bl/El lists to HTML list types */
118 #define BL_DESC_LIST   1
119 #define BL_BULLET_LIST 2
120 #define BL_ENUM_LIST   4
121 
122 /* BSD mandoc Bd/Ed example(?) blocks */
123 #define BD_LITERAL  1
124 #define BD_INDENT   2
125 
126 #ifndef HAVE_STRERROR
127 static char *
strerror(int e)128 strerror(int e)
129 {
130 	static char emsg[40];
131 
132 #if defined (HAVE_SYS_ERRLIST)
133 	extern int sys_nerr;
134 	extern char *sys_errlist[];
135 
136 	if (e > 0 && e < sys_nerr)
137 		return (sys_errlist[e]);
138 	else
139 #endif				/* HAVE_SYS_ERRLIST */
140 	{
141 		sprintf(emsg, "Unknown system error %d", e);
142 		return (&emsg[0]);
143 	}
144 }
145 #endif				/* !HAVE_STRERROR */
146 
147 static char *
strgrow(char * old,int len)148 strgrow(char *old, int len)
149 {
150 	char   *new = realloc(old, (strlen(old) + len + 1) * sizeof(char));
151 
152 	if (!new) {
153 		fprintf(stderr, "man2html: out of memory");
154 		exit(EXIT_FAILURE);
155 	}
156 	return new;
157 }
158 
159 static char *
stralloc(int len)160 stralloc(int len)
161 {
162 	/* allocate enough for len + NULL */
163 	char   *new = malloc((len + 1) * sizeof(char));
164 
165 	if (!new) {
166 		fprintf(stderr, "man2html: out of memory");
167 		exit(EXIT_FAILURE);
168 	}
169 	return new;
170 }
171 
172 void *
xmalloc(size_t size)173 xmalloc (size_t size)
174 {
175 	void *ret;
176 
177 	ret = malloc (size);
178 	if (ret == 0) {
179 		fprintf(stderr, "man2html: out of memory");
180 		exit(EXIT_FAILURE);
181 	}
182 	return ret;
183 }
184 
185 /*
186  * Some systems don't have strdup so lets use our own - which can also
187  * check for out of memory.
188  */
189 static char *
strduplicate(char * from)190 strduplicate(char *from)
191 {
192 	char   *new = stralloc(strlen(from));
193 
194 	strcpy(new, from);
195 	return new;
196 }
197 
198 /* Assumes space for n plus a null */
199 static char *
strmaxcpy(char * to,char * from,int n)200 strmaxcpy(char *to, char *from, int n)
201 {
202 	int     len = strlen(from);
203 
204 	strncpy(to, from, n);
205 	to[(len <= n) ? len : n] = '\0';
206 	return to;
207 }
208 
209 static char *
strmaxcat(char * to,char * from,int n)210 strmaxcat(char *to, char *from, int n)
211 {
212 	int     to_len = strlen(to);
213 
214 	if (to_len < n) {
215 		int     from_len = strlen(from);
216 		int     cp = (to_len + from_len <= n) ? from_len : n - to_len;
217 
218 		strncpy(to + to_len, from, cp);
219 		to[to_len + cp] = '\0';
220 	}
221 	return to;
222 }
223 
224 /* Assumes space for limit plus a null */
225 static char *
strlimitcpy(char * to,char * from,int n,int limit)226 strlimitcpy(char *to, char *from, int n, int limit)
227 {
228 	int     len = n > limit ? limit : n;
229 
230 	strmaxcpy(to, from, len);
231 	to[len] = '\0';
232 	return to;
233 }
234 
235 /*
236  * takes string and escapes all metacharacters.  should be used before
237  * including string in system() or similar call.
238  */
239 static char *
escape_input(char * str)240 escape_input(char *str)
241 {
242 	int     i, j = 0;
243 	static char new[NULL_TERMINATED(MED_STR_MAX)];
244 
245 	if (strlen(str) * 2 + 1 > MED_STR_MAX) {
246 		fprintf(stderr,
247 			"man2html: escape_input - str too long:\n%-80s...\n",
248 			str);
249 		exit(EXIT_FAILURE);
250 	}
251 	for (i = 0; i < strlen(str); i++) {
252 		if (!(((str[i] >= 'A') && (str[i] <= 'Z')) ||
253 		      ((str[i] >= 'a') && (str[i] <= 'z')) ||
254 		      ((str[i] >= '0') && (str[i] <= '9')))) {
255 			new[j] = '\\';
256 			j++;
257 		}
258 		new[j] = str[i];
259 		j++;
260 	}
261 	new[j] = '\0';
262 	return new;
263 }
264 
265 static void
usage(void)266 usage(void)
267 {
268 	fprintf(stderr, "man2html: usage: man2html filename\n");
269 }
270 
271 
272 
273 /*
274  * below this you should not change anything unless you know a lot
275  * about this program or about troff.
276  */
277 
278 typedef struct STRDEF STRDEF;
279 struct STRDEF {
280 	int     nr, slen;
281 	char   *st;
282 	STRDEF *next;
283 };
284 
285 typedef struct INTDEF INTDEF;
286 struct INTDEF {
287 	int     nr;
288 	int     val;
289 	int     incr;
290 	INTDEF *next;
291 };
292 
293 static char NEWLINE[2] = "\n";
294 static char idxlabel[6] = "ixAAA";
295 
296 #define INDEXFILE "/tmp/manindex.list"
297 
298 static char *fname;
299 static FILE *idxfile;
300 
301 static STRDEF *chardef, *strdef, *defdef;
302 static INTDEF *intdef;
303 
304 #define V(A,B) ((A)*256+(B))
305 
306 static INTDEF standardint[] = {
307 	{V('n', ' '), NROFF, 0, NULL},
308 	{V('t', ' '), 1 - NROFF, 0, NULL},
309 	{V('o', ' '), 1, 0, NULL},
310 	{V('e', ' '), 0, 0, NULL},
311 	{V('.', 'l'), 70, 0, NULL},
312 	{V('.', '$'), 0, 0, NULL},
313 	{V('.', 'A'), NROFF, 0, NULL},
314 	{V('.', 'T'), 1 - NROFF, 0, NULL},
315 	{V('.', 'V'), 1, 0, NULL},	/* the me package tests for this */
316 {0, 0, 0, NULL}};
317 
318 static STRDEF standardstring[] = {
319 	{V('R', ' '), 1, "&#174;", NULL},
320 	{V('l', 'q'), 2, "``", NULL},
321 	{V('r', 'q'), 2, "''", NULL},
322 	{0, 0, NULL, NULL}
323 };
324 
325 
326 static STRDEF standardchar[] = {
327 	{V('*', '*'), 1, "*", NULL},
328 	{V('*', 'A'), 1, "A", NULL},
329 	{V('*', 'B'), 1, "B", NULL},
330 	{V('*', 'C'), 2, "Xi", NULL},
331 	{V('*', 'D'), 5, "Delta", NULL},
332 	{V('*', 'E'), 1, "E", NULL},
333 	{V('*', 'F'), 3, "Phi", NULL},
334 	{V('*', 'G'), 5, "Gamma", NULL},
335 	{V('*', 'H'), 5, "Theta", NULL},
336 	{V('*', 'I'), 1, "I", NULL},
337 	{V('*', 'K'), 1, "K", NULL},
338 	{V('*', 'L'), 6, "Lambda", NULL},
339 	{V('*', 'M'), 1, "M", NULL},
340 	{V('*', 'N'), 1, "N", NULL},
341 	{V('*', 'O'), 1, "O", NULL},
342 	{V('*', 'P'), 2, "Pi", NULL},
343 	{V('*', 'Q'), 3, "Psi", NULL},
344 	{V('*', 'R'), 1, "P", NULL},
345 	{V('*', 'S'), 5, "Sigma", NULL},
346 	{V('*', 'T'), 1, "T", NULL},
347 	{V('*', 'U'), 1, "Y", NULL},
348 	{V('*', 'W'), 5, "Omega", NULL},
349 	{V('*', 'X'), 1, "X", NULL},
350 	{V('*', 'Y'), 1, "H", NULL},
351 	{V('*', 'Z'), 1, "Z", NULL},
352 	{V('*', 'a'), 5, "alpha", NULL},
353 	{V('*', 'b'), 4, "beta", NULL},
354 	{V('*', 'c'), 2, "xi", NULL},
355 	{V('*', 'd'), 5, "delta", NULL},
356 	{V('*', 'e'), 7, "epsilon", NULL},
357 	{V('*', 'f'), 3, "phi", NULL},
358 	{V('*', 'g'), 5, "gamma", NULL},
359 	{V('*', 'h'), 5, "theta", NULL},
360 	{V('*', 'i'), 4, "iota", NULL},
361 	{V('*', 'k'), 5, "kappa", NULL},
362 	{V('*', 'l'), 6, "lambda", NULL},
363 	{V('*', 'm'), 1, "&#181;", NULL},
364 	{V('*', 'n'), 2, "nu", NULL},
365 	{V('*', 'o'), 1, "o", NULL},
366 	{V('*', 'p'), 2, "pi", NULL},
367 	{V('*', 'q'), 3, "psi", NULL},
368 	{V('*', 'r'), 3, "rho", NULL},
369 	{V('*', 's'), 5, "sigma", NULL},
370 	{V('*', 't'), 3, "tau", NULL},
371 	{V('*', 'u'), 7, "upsilon", NULL},
372 	{V('*', 'w'), 5, "omega", NULL},
373 	{V('*', 'x'), 3, "chi", NULL},
374 	{V('*', 'y'), 3, "eta", NULL},
375 	{V('*', 'z'), 4, "zeta", NULL},
376 	{V('t', 's'), 5, "sigma", NULL},
377 	{V('+', '-'), 1, "&#177;", NULL},
378 	{V('1', '2'), 1, "&#189;", NULL},
379 	{V('1', '4'), 1, "&#188;", NULL},
380 	{V('3', '4'), 1, "&#190;", NULL},
381 	{V('F', 'i'), 3, "ffi", NULL},
382 	{V('F', 'l'), 3, "ffl", NULL},
383 	{V('a', 'a'), 1, "&#180;", NULL},
384 	{V('a', 'p'), 1, "~", NULL},
385 	{V('b', 'r'), 1, "|", NULL},
386 	{V('b', 'u'), 1, "*", NULL},
387 	{V('b', 'v'), 1, "|", NULL},
388 	{V('c', 'i'), 1, "o", NULL},
389 	{V('c', 'o'), 1, "&#169;", NULL},
390 	{V('c', 't'), 1, "&#162;", NULL},
391 	{V('d', 'e'), 1, "&#176;", NULL},
392 	{V('d', 'g'), 1, "+", NULL},
393 	{V('d', 'i'), 1, "&#247;", NULL},
394 	{V('e', 'm'), 1, "-", NULL},
395 	{V('e', 'm'), 3, "---", NULL},
396 	{V('e', 'q'), 1, "=", NULL},
397 	{V('e', 's'), 1, "&#216;", NULL},
398 	{V('f', 'f'), 2, "ff", NULL},
399 	{V('f', 'i'), 2, "fi", NULL},
400 	{V('f', 'l'), 2, "fl", NULL},
401 	{V('f', 'm'), 1, "&#180;", NULL},
402 	{V('g', 'a'), 1, "`", NULL},
403 	{V('h', 'y'), 1, "-", NULL},
404 	{V('l', 'c'), 2, "|&#175;", NULL},
405 	{V('l', 'f'), 2, "|_", NULL},
406 	{V('l', 'k'), 1, "<FONT SIZE=+2>{</FONT>", NULL},
407 	{V('m', 'i'), 1, "-", NULL},
408 	{V('m', 'u'), 1, "&#215;", NULL},
409 	{V('n', 'o'), 1, "&#172;", NULL},
410 	{V('o', 'r'), 1, "|", NULL},
411 	{V('p', 'l'), 1, "+", NULL},
412 	{V('r', 'c'), 2, "&#175;|", NULL},
413 	{V('r', 'f'), 2, "_|", NULL},
414 	{V('r', 'g'), 1, "&#174;", NULL},
415 	{V('r', 'k'), 1, "<FONT SIZE=+2>}</FONT>", NULL},
416 	{V('r', 'n'), 1, "&#175;", NULL},
417 	{V('r', 'u'), 1, "_", NULL},
418 	{V('s', 'c'), 1, "&#167;", NULL},
419 	{V('s', 'l'), 1, "/", NULL},
420 	{V('s', 'q'), 2, "[]", NULL},
421 	{V('u', 'l'), 1, "_", NULL},
422 	{0, 0, NULL, NULL}
423 };
424 
425 /* default: print code */
426 
427 
428 static char eqndelimopen = 0, eqndelimclose = 0;
429 static char escapesym = '\\', nobreaksym = '\'', controlsym = '.', fieldsym = 0, padsym = 0;
430 
431 static char *buffer = NULL;
432 static int buffpos = 0, buffmax = 0;
433 static int scaninbuff = 0;
434 static int itemdepth = 0;
435 static int dl_set[20] = {0};
436 static int still_dd = 0;
437 static int tabstops[20] = {8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96};
438 static int maxtstop = 12;
439 static int curpos = 0;
440 
441 static char *scan_troff(char *c, int san, char **result);
442 static char *scan_troff_mandoc(char *c, int san, char **result);
443 
444 static char **argument = NULL;
445 
446 static char charb[TINY_STR_MAX];
447 
448 static void
print_sig(void)449 print_sig(void)
450 {
451 	char    datbuf[NULL_TERMINATED(MED_STR_MAX)];
452 	struct tm *timetm;
453 	time_t  clock;
454 
455 	datbuf[0] = '\0';
456 	clock = time(NULL);
457 	timetm = localtime(&clock);
458 	strftime(datbuf, MED_STR_MAX, TIMEFORMAT, timetm);
459 	printf(signature, manpage, datbuf);
460 }
461 
462 static char *
expand_char(int nr)463 expand_char(int nr)
464 {
465 	STRDEF *h;
466 
467 	h = chardef;
468 	if (!nr)
469 		return NULL;
470 	while (h)
471 		if (h->nr == nr) {
472 			curpos += h->slen;
473 			return h->st;
474 		} else
475 			h = h->next;
476 	charb[0] = nr / 256;
477 	charb[1] = nr % 256;
478 	charb[2] = '\0';
479 	if (charb[0] == '<') {	/* Fix up <= */
480 		charb[4] = charb[1];
481 		strncpy(charb, "&lt;", 4);
482 		charb[5] = '\0';
483 	}
484 	curpos += 2;
485 	return charb;
486 }
487 
488 static char *
expand_string(int nr)489 expand_string(int nr)
490 {
491 	STRDEF *h = strdef;
492 
493 	if (!nr)
494 		return NULL;
495 	while (h)
496 		if (h->nr == nr) {
497 			curpos += h->slen;
498 			return h->st;
499 		} else
500 			h = h->next;
501 	return NULL;
502 }
503 
504 static char *
read_man_page(char * filename)505 read_man_page(char *filename)
506 {
507 	char   *man_buf = NULL;
508 	int     i;
509 	FILE   *man_stream = NULL;
510 	struct stat stbuf;
511 	int     buf_size;
512 
513 	if (stat(filename, &stbuf) == -1)
514 		return NULL;
515 
516 	buf_size = stbuf.st_size;
517 	man_buf = stralloc(buf_size + 5);
518 	man_stream = fopen(filename, "r");
519 	if (man_stream) {
520 		man_buf[0] = '\n';
521 		if (fread(man_buf + 1, 1, buf_size, man_stream) == buf_size) {
522 			man_buf[buf_size] = '\n';
523 			man_buf[buf_size + 1] = man_buf[buf_size + 2] = '\0';
524 		} else {
525 			free(man_buf);
526 			man_buf = NULL;
527 		}
528 		fclose(man_stream);
529 	}
530 	return man_buf;
531 }
532 
533 
534 static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)];
535 static int obp = 0;
536 static int no_newline_output = 0;
537 static int newline_for_fun = 0;
538 static int output_possible = 0;
539 static int out_length = 0;
540 
541 /*
542  * Add the links to the output. At the moment the following are
543  * recognized:
544  *
545 #if 0
546  *	name(*)                 -> ../man?/name.*
547 #endif
548  *	method://string         -> method://string
549  *	www.host.name		-> http://www.host.name
550  *	ftp.host.name           -> ftp://ftp.host.name
551  *	name@host               -> mailto:name@host
552  *	<name.h>                -> file:/usr/include/name.h   (guess)
553  *
554  * Other possible links to add in the future:
555  *
556  * /dir/dir/file  -> file:/dir/dir/file
557  */
558 static void
add_links(char * c)559 add_links(char *c)
560 {
561 	int     i, j, nr;
562 	char   *f, *g, *h;
563 	char   *idtest[6];	/* url, mailto, www, ftp, manpage */
564 
565 	out_length += strlen(c);
566 	/* search for (section) */
567 	nr = 0;
568 	idtest[0] = strstr(c + 1, "://");
569 	idtest[1] = strchr(c + 1, '@');
570 	idtest[2] = strstr(c, "www.");
571 	idtest[3] = strstr(c, "ftp.");
572 #if 0
573 	idtest[4] = strchr(c + 1, '(');
574 #else
575 	idtest[4] = 0;
576 #endif
577 	idtest[5] = strstr(c + 1, ".h&gt;");
578 	for (i = 0; i < 6; i++)
579 		nr += (idtest[i] != NULL);
580 	while (nr) {
581 		j = -1;
582 		for (i = 0; i < 6; i++)
583 			if (idtest[i] && (j < 0 || idtest[i] < idtest[j]))
584 				j = i;
585 		switch (j) {
586 		case 5:	/* <name.h> */
587 			f = idtest[5];
588 			h = f + 2;
589 			g = f;
590 			while (g > c && g[-1] != ';')
591 				g--;
592 			if (g != c) {
593 				char    t;
594 
595 				t = *g;
596 				*g = '\0';
597 				fputs(c, stdout);
598 				*g = t;
599 				*h = '\0';
600 				printf("<A HREF=\"file:/usr/include/%s\">%s</A>&gt;", g, g);
601 				c = f + 6;
602 			} else {
603 				f[5] = '\0';
604 				fputs(c, stdout);
605 				f[5] = ';';
606 				c = f + 5;
607 			}
608 			break;
609 		case 4:	/* manpage */
610 #if 0
611 			f = idtest[j];
612 			/* check section */
613 			g = strchr(f, ')');
614 			if (g && f - g < 6 && (isalnum(f[-1]) || f[-1] == '>') &&
615 			    ((isdigit(f[1]) && f[1] != '0' &&
616 			      (f[2] == ')' || (isalpha(f[2]) && f[3] == ')') || f[2] == 'X')) ||
617 			   (f[2] == ')' && (f[1] == 'n' || f[1] == 'l')))) {
618 				/* this might be a link */
619 				h = f - 1;
620 				/* skip html makeup */
621 				while (h > c && *h == '>') {
622 					while (h != c && *h != '<')
623 						h--;
624 					if (h != c)
625 						h--;
626 				}
627 				if (isalnum(*h)) {
628 					char    t, sec, subsec, *e;
629 
630 					e = h + 1;
631 					sec = f[1];
632 					subsec = f[2];
633 					if ((subsec == 'X' && f[3] != ')') || subsec == ')')
634 						subsec = '\0';
635 					while (h > c && (isalnum(h[-1]) || h[-1] == '_' ||
636 					      h[-1] == '-' || h[-1] == '.'))
637 						h--;
638 					t = *h;
639 					*h = '\0';
640 					fputs(c, stdout);
641 					*h = t;
642 					t = *e;
643 					*e = '\0';
644 					if (subsec)
645 						printf("<A HREF=\""
646 						       CGIBASE
647 						  "?man%c/%s.%c%c\">%s</A>",
648 						       sec, h, sec, tolower(subsec), h);
649 					else
650 						printf("<A HREF=\""
651 						       CGIBASE
652 						    "?man%c/%s.%c\">%s</A>",
653 						       sec, h, sec, h);
654 					*e = t;
655 					c = e;
656 				}
657 			}
658 			*f = '\0';
659 			fputs(c, stdout);
660 			*f = '(';
661 			idtest[4] = f - 1;
662 			c = f;
663 #endif
664 			break;	/* manpage */
665 		case 3:	/* ftp */
666 		case 2:	/* www */
667 			g = f = idtest[j];
668 			while (*g && (isalnum(*g) || *g == '_' || *g == '-' || *g == '+' ||
669 				      *g == '.'))
670 				g++;
671 			if (g[-1] == '.')
672 				g--;
673 			if (g - f > 4) {
674 				char    t;
675 
676 				t = *f;
677 				*f = '\0';
678 				fputs(c, stdout);
679 				*f = t;
680 				t = *g;
681 				*g = '\0';
682 				printf("<A HREF=\"%s://%s\">%s</A>", (j == 3 ? "ftp" : "http"),
683 				       f, f);
684 				*g = t;
685 				c = g;
686 			} else {
687 				f[3] = '\0';
688 				fputs(c, stdout);
689 				c = f + 3;
690 				f[3] = '.';
691 			}
692 			break;
693 		case 1:	/* mailto */
694 			g = f = idtest[1];
695 			while (g > c && (isalnum(g[-1]) || g[-1] == '_' || g[-1] == '-' ||
696 			      g[-1] == '+' || g[-1] == '.' || g[-1] == '%'))
697 				g--;
698 			h = f + 1;
699 			while (*h && (isalnum(*h) || *h == '_' || *h == '-' || *h == '+' ||
700 				      *h == '.'))
701 				h++;
702 			if (*h == '.')
703 				h--;
704 			if (h - f > 4 && f - g > 1) {
705 				char    t;
706 
707 				t = *g;
708 				*g = '\0';
709 				fputs(c, stdout);
710 				*g = t;
711 				t = *h;
712 				*h = '\0';
713 				printf("<A HREF=\"mailto:%s\">%s</A>", g, g);
714 				*h = t;
715 				c = h;
716 			} else {
717 				*f = '\0';
718 				fputs(c, stdout);
719 				*f = '@';
720 				idtest[1] = c;
721 				c = f;
722 			}
723 			break;
724 		case 0:	/* url */
725 			g = f = idtest[0];
726 			while (g > c && isalpha(g[-1]) && islower(g[-1]))
727 				g--;
728 			h = f + 3;
729 			while (*h && !isspace(*h) && *h != '<' && *h != '>' && *h != '"' &&
730 			       *h != '&')
731 				h++;
732 			if (f - g > 2 && f - g < 7 && h - f > 3) {
733 				char    t;
734 
735 				t = *g;
736 				*g = '\0';
737 				fputs(c, stdout);
738 				*g = t;
739 				t = *h;
740 				*h = '\0';
741 				printf("<A HREF=\"%s\">%s</A>", g, g);
742 				*h = t;
743 				c = h;
744 			} else {
745 				f[1] = '\0';
746 				fputs(c, stdout);
747 				f[1] = '/';
748 				c = f + 1;
749 			}
750 			break;
751 		default:
752 			break;
753 		}
754 		nr = 0;
755 		if (idtest[0] && idtest[0] < c)
756 			idtest[0] = strstr(c + 1, "://");
757 		if (idtest[1] && idtest[1] < c)
758 			idtest[1] = strchr(c + 1, '@');
759 		if (idtest[2] && idtest[2] < c)
760 			idtest[2] = strstr(c, "www.");
761 		if (idtest[3] && idtest[3] < c)
762 			idtest[3] = strstr(c, "ftp.");
763 		if (idtest[4] && idtest[4] < c)
764 			idtest[4] = strchr(c + 1, '(');
765 		if (idtest[5] && idtest[5] < c)
766 			idtest[5] = strstr(c + 1, ".h&gt;");
767 		for (i = 0; i < 6; i++)
768 			nr += (idtest[i] != NULL);
769 	}
770 	fputs(c, stdout);
771 }
772 
773 static int current_font = 0;
774 static int current_size = 0;
775 static int fillout = 1;
776 
777 static void
out_html(char * c)778 out_html(char *c)
779 {
780 	if (!c)
781 		return;
782 	if (no_newline_output) {
783 		int     i = 0;
784 
785 		no_newline_output = 1;
786 		while (c[i]) {
787 			if (!no_newline_output)
788 				c[i - 1] = c[i];
789 			if (c[i] == '\n')
790 				no_newline_output = 1;
791 			i++;
792 		}
793 		if (!no_newline_output)
794 			c[i - 1] = 0;
795 	}
796 	if (scaninbuff) {
797 		while (*c) {
798 			if (buffpos >= buffmax) {
799 				char   *h;
800 
801 				h = realloc(buffer, buffmax * 2);
802 				if (!h)
803 					return;
804 				buffer = h;
805 				buffmax *= 2;
806 			}
807 			buffer[buffpos++] = *c++;
808 		}
809 	} else if (output_possible) {
810 		while (*c) {
811 			outbuffer[obp++] = *c;
812 			if (*c == '\n' || obp > HUGE_STR_MAX) {
813 				outbuffer[obp] = '\0';
814 				add_links(outbuffer);
815 				obp = 0;
816 			}
817 			c++;
818 		}
819 	}
820 }
821 
822 #define FO0 ""
823 #define FC0 ""
824 #define FO1 "<I>"
825 #define FC1 "</I>"
826 #define FO2 "<B>"
827 #define FC2 "</B>"
828 #define FO3 "<TT>"
829 #define FC3 "</TT>"
830 
831 static char *switchfont[16] = {
832 	"", FC0 FO1, FC0 FO2, FC0 FO3,
833 	FC1 FO0, "", FC1 FO2, FC1 FO3,
834 	FC2 FO0, FC2 FO1, "", FC2 FO3,
835 	FC3 FO0, FC3 FO1, FC3 FO2, ""
836 };
837 
838 static char *
change_to_font(int nr)839 change_to_font(int nr)
840 {
841 	int     i;
842 
843 	switch (nr) {
844 	case '0':
845 		nr++;
846 	case '1':
847 	case '2':
848 	case '3':
849 	case '4':
850 		nr = nr - '1';
851 		break;
852 	case V('C', 'W'):
853 		nr = 3;
854 		break;
855 	case 'L':
856 		nr = 3;
857 		break;
858 	case 'B':
859 		nr = 2;
860 		break;
861 	case 'I':
862 		nr = 1;
863 		break;
864 	case 'P':
865 	case 'R':
866 		nr = 0;
867 		break;
868 	case 0:
869 	case 1:
870 	case 2:
871 	case 3:
872 		break;
873 	default:
874 		nr = 0;
875 		break;
876 	}
877 	i = current_font * 4 + nr % 4;
878 	current_font = nr % 4;
879 	return switchfont[i];
880 }
881 
882 static char sizebuf[200];
883 
884 static char *
change_to_size(int nr)885 change_to_size(int nr)
886 {
887 	int     i;
888 
889 	switch (nr) {
890 	case '0':
891 	case '1':
892 	case '2':
893 	case '3':
894 	case '4':
895 	case '5':
896 	case '6':
897 	case '7':
898 	case '8':
899 	case '9':
900 		nr = nr - '0';
901 		break;
902 	case '\0':
903 		break;
904 	default:
905 		nr = current_size + nr;
906 		if (nr > 9)
907 			nr = 9;
908 		if (nr < -9)
909 			nr = -9;
910 		break;
911 	}
912 	if (nr == current_size)
913 		return "";
914 	i = current_font;
915 	sizebuf[0] = '\0';
916 	strcat(sizebuf, change_to_font(0));
917 	if (current_size)
918 		strcat(sizebuf, "</FONT>");
919 	current_size = nr;
920 	if (nr) {
921 		int     l;
922 
923 		strcat(sizebuf, "<FONT SIZE=");
924 		l = strlen(sizebuf);
925 		if (nr > 0)
926 			sizebuf[l++] = '+';
927 		else
928 			sizebuf[l++] = '-', nr = -nr;
929 		sizebuf[l++] = nr + '0';
930 		sizebuf[l++] = '>';
931 		sizebuf[l] = '\0';
932 	}
933 	strcat(sizebuf, change_to_font(i));
934 	return sizebuf;
935 }
936 
937 static int asint = 0;
938 static int intresult = 0;
939 
940 #define SKIPEOL while (*c && *c++!='\n')
941 
942 static int skip_escape = 0;
943 static int single_escape = 0;
944 
945 static char *
scan_escape(char * c)946 scan_escape(char *c)
947 {
948 	char   *h = NULL;
949 	char    b[5];
950 	INTDEF *intd;
951 	int     exoutputp, exskipescape;
952 	int     i, j;
953 
954 	intresult = 0;
955 	switch (*c) {
956 	case 'e':
957 		h = "\\";
958 		curpos++;
959 		break;
960 	case '0':
961 	case ' ':
962 		h = "&nbsp;";
963 		curpos++;
964 		break;
965 	case '|':
966 		h = "";
967 		break;
968 	case '"':
969 		SKIPEOL;
970 		c--;
971 		h = "";
972 		break;
973 	case '$':
974 		if (argument) {
975 			c++;
976 			i = (*c - '1');
977 			if (!(h = argument[i]))
978 				h = "";
979 		}
980 		break;
981 	case 'z':
982 		c++;
983 		if (*c == '\\') {
984 			c = scan_escape(c + 1);
985 			c--;
986 			h = "";
987 		} else {
988 			b[0] = *c;
989 			b[1] = '\0';
990 			h = "";
991 		}
992 		break;
993 	case 'k':
994 		c++;
995 		if (*c == '(')
996 			c += 2;
997 	case '^':
998 	case '!':
999 	case '%':
1000 	case 'a':
1001 	case 'd':
1002 	case 'r':
1003 	case 'u':
1004 	case '\n':
1005 	case '&':
1006 		h = "";
1007 		break;
1008 	case '(':
1009 		c++;
1010 		i = c[0] * 256 + c[1];
1011 		c++;
1012 		h = expand_char(i);
1013 		break;
1014 	case '*':
1015 		c++;
1016 		if (*c == '(') {
1017 			c++;
1018 			i = c[0] * 256 + c[1];
1019 			c++;
1020 		} else
1021 			i = *c * 256 + ' ';
1022 		h = expand_string(i);
1023 		break;
1024 	case 'f':
1025 		c++;
1026 		if (*c == '\\') {
1027 			c++;
1028 			c = scan_escape(c);
1029 			c--;
1030 			i = intresult;
1031 		} else if (*c != '(')
1032 			i = *c;
1033 		else {
1034 			c++;
1035 			i = c[0] * 256 + c[1];
1036 			c++;
1037 		}
1038 		if (!skip_escape)
1039 			h = change_to_font(i);
1040 		else
1041 			h = "";
1042 		break;
1043 	case 's':
1044 		c++;
1045 		j = 0;
1046 		i = 0;
1047 		if (*c == '-') {
1048 			j = -1;
1049 			c++;
1050 		} else if (*c == '+') {
1051 			j = 1;
1052 			c++;
1053 		}
1054 		if (*c == '0')
1055 			c++;
1056 		else if (*c == '\\') {
1057 			c++;
1058 			c = scan_escape(c);
1059 			i = intresult;
1060 			if (!j)
1061 				j = 1;
1062 		} else
1063 			while (isdigit(*c) && (!i || (!j && i < 4)))
1064 				i = i * 10 + (*c++) - '0';
1065 		if (!j) {
1066 			j = 1;
1067 			if (i)
1068 				i = i - 10;
1069 		}
1070 		if (!skip_escape)
1071 			h = change_to_size(i * j);
1072 		else
1073 			h = "";
1074 		c--;
1075 		break;
1076 	case 'n':
1077 		c++;
1078 		j = 0;
1079 		switch (*c) {
1080 		case '+':
1081 			j = 1;
1082 			c++;
1083 			break;
1084 		case '-':
1085 			j = -1;
1086 			c++;
1087 			break;
1088 		default:
1089 			break;
1090 		}
1091 		if (*c == '(') {
1092 			c++;
1093 			i = V(c[0], c[1]);
1094 			c = c + 1;
1095 		} else {
1096 			i = V(c[0], ' ');
1097 		}
1098 		intd = intdef;
1099 		while (intd && intd->nr != i)
1100 			intd = intd->next;
1101 		if (intd) {
1102 			intd->val = intd->val + j * intd->incr;
1103 			intresult = intd->val;
1104 		} else {
1105 			switch (i) {
1106 			case V('.', 's'):
1107 				intresult = current_size;
1108 				break;
1109 			case V('.', 'f'):
1110 				intresult = current_font;
1111 				break;
1112 			default:
1113 				intresult = 0;
1114 				break;
1115 			}
1116 		}
1117 		h = "";
1118 		break;
1119 	case 'w':
1120 		c++;
1121 		i = *c;
1122 		c++;
1123 		exoutputp = output_possible;
1124 		exskipescape = skip_escape;
1125 		output_possible = 0;
1126 		skip_escape = 1;
1127 		j = 0;
1128 		while (*c != i) {
1129 			j++;
1130 			if (*c == escapesym)
1131 				c = scan_escape(c + 1);
1132 			else
1133 				c++;
1134 		}
1135 		output_possible = exoutputp;
1136 		skip_escape = exskipescape;
1137 		intresult = j;
1138 		break;
1139 	case 'l':
1140 		h = "<HR>";
1141 		curpos = 0;
1142 	case 'b':
1143 	case 'v':
1144 	case 'x':
1145 	case 'o':
1146 	case 'L':
1147 	case 'h':
1148 		c++;
1149 		i = *c;
1150 		c++;
1151 		exoutputp = output_possible;
1152 		exskipescape = skip_escape;
1153 		output_possible = 0;
1154 		skip_escape = 1;
1155 		while (*c != i)
1156 			if (*c == escapesym)
1157 				c = scan_escape(c + 1);
1158 			else
1159 				c++;
1160 		output_possible = exoutputp;
1161 		skip_escape = exskipescape;
1162 		break;
1163 	case 'c':
1164 		no_newline_output = 1;
1165 		break;
1166 	case '{':
1167 		newline_for_fun++;
1168 		h = "";
1169 		break;
1170 	case '}':
1171 		if (newline_for_fun)
1172 			newline_for_fun--;
1173 		h = "";
1174 		break;
1175 	case 'p':
1176 		h = "<BR>\n";
1177 		curpos = 0;
1178 		break;
1179 	case 't':
1180 		h = "\t";
1181 		curpos = (curpos + 8) & 0xfff8;
1182 		break;
1183 	case '<':
1184 		h = "&lt;";
1185 		curpos++;
1186 		break;
1187 	case '>':
1188 		h = "&gt;";
1189 		curpos++;
1190 		break;
1191 	case '\\':
1192 		if (single_escape) {
1193 			c--;
1194 			break;
1195 		}
1196 	default:
1197 		b[0] = *c;
1198 		b[1] = 0;
1199 		h = b;
1200 		curpos++;
1201 		break;
1202 	}
1203 	c++;
1204 	if (!skip_escape)
1205 		out_html(h);
1206 	return c;
1207 }
1208 
1209 typedef struct TABLEITEM TABLEITEM;
1210 
1211 struct TABLEITEM {
1212 	char   *contents;
1213 	int     size, align, valign, colspan, rowspan, font, vleft, vright, space,
1214 	        width;
1215 	TABLEITEM *next;
1216 };
1217 
1218 static TABLEITEM emptyfield = {NULL, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, NULL};
1219 
1220 typedef struct TABLEROW TABLEROW;
1221 
1222 struct TABLEROW {
1223 	TABLEITEM *first;
1224 	TABLEROW *prev, *next;
1225 };
1226 
1227 static char *tableopt[] = {
1228 	"center", "expand", "box", "allbox", "doublebox",
1229 	"tab", "linesize", "delim", NULL
1230 };
1231 static int tableoptl[] = {6, 6, 3, 6, 9, 3, 8, 5, 0};
1232 
1233 static void
clear_table(TABLEROW * table)1234 clear_table(TABLEROW * table)
1235 {
1236 	TABLEROW *tr1, *tr2;
1237 	TABLEITEM *ti1, *ti2;
1238 
1239 	tr1 = table;
1240 	while (tr1->prev)
1241 		tr1 = tr1->prev;
1242 	while (tr1) {
1243 		ti1 = tr1->first;
1244 		while (ti1) {
1245 			ti2 = ti1->next;
1246 			if (ti1->contents)
1247 				free(ti1->contents);
1248 			free(ti1);
1249 			ti1 = ti2;
1250 		}
1251 		tr2 = tr1;
1252 		tr1 = tr1->next;
1253 		free(tr2);
1254 	}
1255 }
1256 
1257 static char *scan_expression(char *c, int *result);
1258 
1259 static char *
scan_format(char * c,TABLEROW ** result,int * maxcol)1260 scan_format(char *c, TABLEROW ** result, int *maxcol)
1261 {
1262 	TABLEROW *layout, *currow;
1263 	TABLEITEM *curfield;
1264 	int     i, j;
1265 
1266 	if (*result) {
1267 		clear_table(*result);
1268 	}
1269 	layout = currow = (TABLEROW *) xmalloc(sizeof(TABLEROW));
1270 	currow->next = currow->prev = NULL;
1271 	currow->first = curfield = (TABLEITEM *) xmalloc(sizeof(TABLEITEM));
1272 	*curfield = emptyfield;
1273 	while (*c && *c != '.') {
1274 		switch (*c) {
1275 		case 'C':
1276 		case 'c':
1277 		case 'N':
1278 		case 'n':
1279 		case 'R':
1280 		case 'r':
1281 		case 'A':
1282 		case 'a':
1283 		case 'L':
1284 		case 'l':
1285 		case 'S':
1286 		case 's':
1287 		case '^':
1288 		case '_':
1289 			if (curfield->align) {
1290 				curfield->next = (TABLEITEM *) xmalloc(sizeof(TABLEITEM));
1291 				curfield = curfield->next;
1292 				*curfield = emptyfield;
1293 			}
1294 			curfield->align = toupper(*c);
1295 			c++;
1296 			break;
1297 		case 'i':
1298 		case 'I':
1299 		case 'B':
1300 		case 'b':
1301 			curfield->font = toupper(*c);
1302 			c++;
1303 			break;
1304 		case 'f':
1305 		case 'F':
1306 			c++;
1307 			curfield->font = toupper(*c);
1308 			c++;
1309 			if (!isspace(*c))
1310 				c++;
1311 			break;
1312 		case 't':
1313 		case 'T':
1314 			curfield->valign = 't';
1315 			c++;
1316 			break;
1317 		case 'p':
1318 		case 'P':
1319 			c++;
1320 			i = j = 0;
1321 			if (*c == '+') {
1322 				j = 1;
1323 				c++;
1324 			}
1325 			if (*c == '-') {
1326 				j = -1;
1327 				c++;
1328 			}
1329 			while (isdigit(*c))
1330 				i = i * 10 + (*c++) - '0';
1331 			if (j)
1332 				curfield->size = i * j;
1333 			else
1334 				curfield->size = j - 10;
1335 			break;
1336 		case 'v':
1337 		case 'V':
1338 		case 'w':
1339 		case 'W':
1340 			c = scan_expression(c + 2, &curfield->width);
1341 			break;
1342 		case '|':
1343 			if (curfield->align)
1344 				curfield->vleft++;
1345 			else
1346 				curfield->vright++;
1347 			c++;
1348 			break;
1349 		case 'e':
1350 		case 'E':
1351 			c++;
1352 			break;
1353 		case '0':
1354 		case '1':
1355 		case '2':
1356 		case '3':
1357 		case '4':
1358 		case '5':
1359 		case '6':
1360 		case '7':
1361 		case '8':
1362 		case '9':
1363 			i = 0;
1364 			while (isdigit(*c))
1365 				i = i * 10 + (*c++) - '0';
1366 			curfield->space = i;
1367 			break;
1368 		case ',':
1369 		case '\n':
1370 			currow->next = (TABLEROW *) xmalloc(sizeof(TABLEROW));
1371 			currow->next->prev = currow;
1372 			currow = currow->next;
1373 			currow->next = NULL;
1374 			curfield = currow->first = (TABLEITEM *) xmalloc(sizeof(TABLEITEM));
1375 			*curfield = emptyfield;
1376 			c++;
1377 			break;
1378 		default:
1379 			c++;
1380 			break;
1381 		}
1382 	}
1383 	if (*c == '.')
1384 		while (*c++ != '\n');
1385 	*maxcol = 0;
1386 	currow = layout;
1387 	while (currow) {
1388 		curfield = layout->first;
1389 		i = 0;
1390 		while (curfield) {
1391 			i++;
1392 			curfield = curfield->next;
1393 		}
1394 		if (i > *maxcol)
1395 			*maxcol = i;
1396 		currow = currow->next;
1397 	}
1398 	*result = layout;
1399 	return c;
1400 }
1401 
1402 static TABLEROW *
next_row(TABLEROW * tr)1403 next_row(TABLEROW * tr)
1404 {
1405 	if (tr->next) {
1406 		tr = tr->next;
1407 		if (!tr->next)
1408 			next_row(tr);
1409 		return tr;
1410 	} else {
1411 		TABLEITEM *ti, *ti2;
1412 
1413 		tr->next = (TABLEROW *) xmalloc(sizeof(TABLEROW));
1414 		tr->next->prev = tr;
1415 		ti = tr->first;
1416 		tr = tr->next;
1417 		tr->next = NULL;
1418 		if (ti)
1419 			tr->first = ti2 = (TABLEITEM *) xmalloc(sizeof(TABLEITEM));
1420 		else
1421 			tr->first = ti2 = NULL;
1422 		while (ti != ti2) {
1423 			*ti2 = *ti;
1424 			ti2->contents = NULL;
1425 			if ((ti = ti->next)) {
1426 				ti2->next = (TABLEITEM *) xmalloc(sizeof(TABLEITEM));
1427 			}
1428 			ti2 = ti2->next;
1429 		}
1430 		return tr;
1431 	}
1432 }
1433 
1434 static char itemreset[20] = "\\fR\\s0";
1435 
1436 static char *
scan_table(char * c)1437 scan_table(char *c)
1438 {
1439 	char   *t, *h, *g;
1440 	int     center = 0, expand = 0, box = 0, border = 0, linesize = 1;
1441 	int     i, j, maxcol = 0, finished = 0;
1442 	int     oldfont, oldsize, oldfillout;
1443 	char    itemsep = '\t';
1444 	TABLEROW *layout = NULL, *currow, *ftable;
1445 	TABLEITEM *curfield;
1446 
1447 	while (*c++ != '\n');
1448 	h = c;
1449 	if (*h == '.')
1450 		return c - 1;
1451 	oldfont = current_font;
1452 	oldsize = current_size;
1453 	oldfillout = fillout;
1454 	out_html(change_to_font(0));
1455 	out_html(change_to_size(0));
1456 	if (!fillout) {
1457 		fillout = 1;
1458 		out_html("</PRE>");
1459 	}
1460 	while (*h && *h != '\n')
1461 		h++;
1462 	if (h[-1] == ';') {
1463 		/* scan table options */
1464 		while (c < h) {
1465 			while (isspace(*c))
1466 				c++;
1467 			for (i = 0; tableopt[i] && strncmp(tableopt[i], c, tableoptl[i]); i++);
1468 			c = c + tableoptl[i];
1469 			switch (i) {
1470 			case 0:
1471 				center = 1;
1472 				break;
1473 			case 1:
1474 				expand = 1;
1475 				break;
1476 			case 2:
1477 				box = 1;
1478 				break;
1479 			case 3:
1480 				border = 1;
1481 				break;
1482 			case 4:
1483 				box = 2;
1484 				break;
1485 			case 5:
1486 				while (*c++ != '(');
1487 				itemsep = *c++;
1488 				break;
1489 			case 6:
1490 				while (*c++ != '(');
1491 				linesize = 0;
1492 				while (isdigit(*c))
1493 					linesize = linesize * 10 + (*c++) - '0';
1494 				break;
1495 			case 7:
1496 				while (*c != ')')
1497 					c++;
1498 			default:
1499 				break;
1500 			}
1501 			c++;
1502 		}
1503 		c = h + 1;
1504 	}
1505 	/* scan layout */
1506 	c = scan_format(c, &layout, &maxcol);
1507 	currow = layout;
1508 	next_row(currow);
1509 	curfield = layout->first;
1510 	i = 0;
1511 	while (!finished) {
1512 		/* search item */
1513 		h = c;
1514 		if ((*c == '_' || *c == '=') && (c[1] == itemsep || c[1] == '\n')) {
1515 			if (c[-1] == '\n' && c[1] == '\n') {
1516 				if (currow->prev) {
1517 					currow->prev->next = (TABLEROW *) xmalloc(sizeof(TABLEROW));
1518 					currow->prev->next->next = currow;
1519 					currow->prev->next->prev = currow->prev;
1520 					currow->prev = currow->prev->next;
1521 				} else {
1522 					currow->prev = layout = (TABLEROW *) xmalloc(sizeof(TABLEROW));
1523 					currow->prev->prev = NULL;
1524 					currow->prev->next = currow;
1525 				}
1526 				curfield = currow->prev->first =
1527 					(TABLEITEM *) xmalloc(sizeof(TABLEITEM));
1528 				*curfield = emptyfield;
1529 				curfield->align = *c;
1530 				curfield->colspan = maxcol;
1531 				curfield = currow->first;
1532 				c = c + 2;
1533 			} else {
1534 				if (curfield) {
1535 					curfield->align = *c;
1536 					do {
1537 						curfield = curfield->next;
1538 					} while (curfield && curfield->align == 'S');
1539 				}
1540 				if (c[1] == '\n') {
1541 					currow = next_row(currow);
1542 					curfield = currow->first;
1543 				}
1544 				c = c + 2;
1545 			}
1546 		} else if (*c == 'T' && c[1] == '{') {
1547 			h = c + 2;
1548 			c = strstr(h, "\nT}");
1549 			c++;
1550 			*c = '\0';
1551 			g = NULL;
1552 			scan_troff(h, 0, &g);
1553 			scan_troff(itemreset, 0, &g);
1554 			*c = 'T';
1555 			c += 3;
1556 			if (curfield) {
1557 				curfield->contents = g;
1558 				do {
1559 					curfield = curfield->next;
1560 				} while (curfield && curfield->align == 'S');
1561 			} else if (g)
1562 				free(g);
1563 			if (c[-1] == '\n') {
1564 				currow = next_row(currow);
1565 				curfield = currow->first;
1566 			}
1567 		} else if (*c == '.' && c[1] == 'T' && c[2] == '&' && c[-1] == '\n') {
1568 			TABLEROW *hr;
1569 
1570 			while (*c++ != '\n');
1571 			hr = currow;
1572 			currow = currow->prev;
1573 			hr->prev = NULL;
1574 			c = scan_format(c, &hr, &i);
1575 			hr->prev = currow;
1576 			currow->next = hr;
1577 			currow = hr;
1578 			next_row(currow);
1579 			curfield = currow->first;
1580 		} else if (*c == '.' && c[1] == 'T' && c[2] == 'E' && c[-1] == '\n') {
1581 			finished = 1;
1582 			while (*c++ != '\n');
1583 			if (currow->prev)
1584 				currow->prev->next = NULL;
1585 			currow->prev = NULL;
1586 			clear_table(currow);
1587 		} else if (*c == '.' && c[-1] == '\n' && !isdigit(c[1])) {
1588 			/*
1589 			 * skip troff request inside table (usually only .sp
1590 			 * )
1591 			 */
1592 			while (*c++ != '\n');
1593 		} else {
1594 			h = c;
1595 			while (*c && (*c != itemsep || c[-1] == '\\') &&
1596 			       (*c != '\n' || c[-1] == '\\'))
1597 				c++;
1598 			i = 0;
1599 			if (*c == itemsep) {
1600 				i = 1;
1601 				*c = '\n';
1602 			}
1603 			if (h[0] == '\\' && h[2] == '\n' &&
1604 			    (h[1] == '_' || h[1] == '^')) {
1605 				if (curfield) {
1606 					curfield->align = h[1];
1607 					do {
1608 						curfield = curfield->next;
1609 					} while (curfield && curfield->align == 'S');
1610 				}
1611 				h = h + 3;
1612 			} else {
1613 				g = NULL;
1614 				h = scan_troff(h, 1, &g);
1615 				scan_troff(itemreset, 0, &g);
1616 				if (curfield) {
1617 					curfield->contents = g;
1618 					do {
1619 						curfield = curfield->next;
1620 					} while (curfield && curfield->align == 'S');
1621 				} else if (g)
1622 					free(g);
1623 			}
1624 			if (i)
1625 				*c = itemsep;
1626 			c = h;
1627 			if (c[-1] == '\n') {
1628 				currow = next_row(currow);
1629 				curfield = currow->first;
1630 			}
1631 		}
1632 	}
1633 	/* calculate colspan and rowspan */
1634 	currow = layout;
1635 	while (currow->next)
1636 		currow = currow->next;
1637 	while (currow) {
1638 		TABLEITEM *ti, *ti1 = NULL, *ti2 = NULL;
1639 
1640 		ti = currow->first;
1641 		if (currow->prev)
1642 			ti1 = currow->prev->first;
1643 		while (ti) {
1644 			switch (ti->align) {
1645 			case 'S':
1646 				if (ti2) {
1647 					ti2->colspan++;
1648 					if (ti2->rowspan < ti->rowspan)
1649 						ti2->rowspan = ti->rowspan;
1650 				}
1651 				break;
1652 			case '^':
1653 				if (ti1)
1654 					ti1->rowspan++;
1655 			default:
1656 				if (!ti2)
1657 					ti2 = ti;
1658 				else {
1659 					do {
1660 						ti2 = ti2->next;
1661 					} while (ti2 && curfield->align == 'S');
1662 				}
1663 				break;
1664 			}
1665 			ti = ti->next;
1666 			if (ti1)
1667 				ti1 = ti1->next;
1668 		}
1669 		currow = currow->prev;
1670 	}
1671 	/* produce html output */
1672 	if (center)
1673 		out_html("<CENTER>");
1674 	if (box == 2)
1675 		out_html("<TABLE BORDER><TR><TD>");
1676 	out_html("<TABLE");
1677 	if (box || border) {
1678 		out_html(" BORDER");
1679 		if (!border)
1680 			out_html("><TR><TD><TABLE");
1681 		if (expand)
1682 			out_html(" WIDTH=100%");
1683 	}
1684 	out_html(">\n");
1685 	currow = layout;
1686 	while (currow) {
1687 		j = 0;
1688 		out_html("<TR VALIGN=top>");
1689 		curfield = currow->first;
1690 		while (curfield) {
1691 			if (curfield->align != 'S' && curfield->align != '^') {
1692 				out_html("<TD");
1693 				switch (curfield->align) {
1694 				case 'N':
1695 					curfield->space += 4;
1696 				case 'R':
1697 					out_html(" ALIGN=right");
1698 					break;
1699 				case 'C':
1700 					out_html(" ALIGN=center");
1701 				default:
1702 					break;
1703 				}
1704 				if (!curfield->valign && curfield->rowspan > 1)
1705 					out_html(" VALIGN=center");
1706 				if (curfield->colspan > 1) {
1707 					char    buf[5];
1708 
1709 					out_html(" COLSPAN=");
1710 					sprintf(buf, "%i", curfield->colspan);
1711 					out_html(buf);
1712 				}
1713 				if (curfield->rowspan > 1) {
1714 					char    buf[5];
1715 
1716 					out_html(" ROWSPAN=");
1717 					sprintf(buf, "%i", curfield->rowspan);
1718 					out_html(buf);
1719 				}
1720 				j = j + curfield->colspan;
1721 				out_html(">");
1722 				if (curfield->size)
1723 					out_html(change_to_size(curfield->size));
1724 				if (curfield->font)
1725 					out_html(change_to_font(curfield->font));
1726 				switch (curfield->align) {
1727 				case '=':
1728 					out_html("<HR><HR>");
1729 					break;
1730 				case '_':
1731 					out_html("<HR>");
1732 					break;
1733 				default:
1734 					if (curfield->contents)
1735 						out_html(curfield->contents);
1736 					break;
1737 				}
1738 				if (curfield->space)
1739 					for (i = 0; i < curfield->space; i++)
1740 						out_html("&nbsp;");
1741 				if (curfield->font)
1742 					out_html(change_to_font(0));
1743 				if (curfield->size)
1744 					out_html(change_to_size(0));
1745 				if (j >= maxcol && curfield->align > '@' && curfield->align != '_')
1746 					out_html("<BR>");
1747 				out_html("</TD>");
1748 			}
1749 			curfield = curfield->next;
1750 		}
1751 		out_html("</TR>\n");
1752 		currow = currow->next;
1753 	}
1754 	if (box && !border)
1755 		out_html("</TABLE>");
1756 	out_html("</TABLE>");
1757 	if (box == 2)
1758 		out_html("</TABLE>");
1759 	if (center)
1760 		out_html("</CENTER>\n");
1761 	else
1762 		out_html("\n");
1763 	if (!oldfillout)
1764 		out_html("<PRE>");
1765 	fillout = oldfillout;
1766 	out_html(change_to_size(oldsize));
1767 	out_html(change_to_font(oldfont));
1768 	return c;
1769 }
1770 
1771 static char *
scan_expression(char * c,int * result)1772 scan_expression(char *c, int *result)
1773 {
1774 	int     value = 0, value2, j = 0, sign = 1, opex = 0;
1775 	char    oper = 'c';
1776 
1777 	if (*c == '!') {
1778 		c = scan_expression(c + 1, &value);
1779 		value = (!value);
1780 	} else if (*c == 'n') {
1781 		c++;
1782 		value = NROFF;
1783 	} else if (*c == 't') {
1784 		c++;
1785 		value = 1 - NROFF;
1786 	} else if (*c == '\'' || *c == '"' || *c < ' ' || (*c == '\\' && c[1] == '(')) {
1787 		/*
1788 		 * ?string1?string2? test if string1 equals string2.
1789 		 */
1790 		char   *st1 = NULL, *st2 = NULL, *h;
1791 		char   *tcmp = NULL;
1792 		char    sep;
1793 
1794 		sep = *c;
1795 		if (sep == '\\') {
1796 			tcmp = c;
1797 			c = c + 3;
1798 		}
1799 		c++;
1800 		h = c;
1801 		while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1802 			c++;
1803 		*c = '\n';
1804 		scan_troff(h, 1, &st1);
1805 		*c = sep;
1806 		if (tcmp)
1807 			c = c + 3;
1808 		c++;
1809 		h = c;
1810 		while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1811 			c++;
1812 		*c = '\n';
1813 		scan_troff(h, 1, &st2);
1814 		*c = sep;
1815 		if (!st1 && !st2)
1816 			value = 1;
1817 		else if (!st1 || !st2)
1818 			value = 0;
1819 		else
1820 			value = (!strcmp(st1, st2));
1821 		if (st1)
1822 			free(st1);
1823 		if (st2)
1824 			free(st2);
1825 		if (tcmp)
1826 			c = c + 3;
1827 		c++;
1828 	} else {
1829 		while (*c && !isspace(*c) && *c != ')') {
1830 			opex = 0;
1831 			switch (*c) {
1832 			case '(':
1833 				c = scan_expression(c + 1, &value2);
1834 				value2 = sign * value2;
1835 				opex = 1;
1836 				break;
1837 			case '.':
1838 			case '0':
1839 			case '1':
1840 			case '2':
1841 			case '3':
1842 			case '4':
1843 			case '5':
1844 			case '6':
1845 			case '7':
1846 			case '8':
1847 			case '9':{
1848 					int     num = 0, denum = 1;
1849 
1850 					value2 = 0;
1851 					while (isdigit(*c))
1852 						value2 = value2 * 10 + ((*c++) - '0');
1853 					if (*c == '.') {
1854 						c++;
1855 						while (isdigit(*c)) {
1856 							num = num * 10 + ((*c++) - '0');
1857 							denum = denum * 10;
1858 						}
1859 					}
1860 					if (isalpha(*c)) {
1861 						/* scale indicator */
1862 						switch (*c) {
1863 						case 'i':	/* inch -> 10pt */
1864 							value2 = value2 * 10 + (num * 10 + denum / 2) / denum;
1865 							num = 0;
1866 							break;
1867 						default:
1868 							break;
1869 						}
1870 						c++;
1871 					}
1872 					value2 = value2 + (num + denum / 2) / denum;
1873 					value2 = sign * value2;
1874 					opex = 1;
1875 					break;
1876 				}
1877 			case '\\':
1878 				c = scan_escape(c + 1);
1879 				value2 = intresult * sign;
1880 				if (isalpha(*c))
1881 					c++;	/* scale indicator */
1882 				opex = 1;
1883 				break;
1884 			case '-':
1885 				if (oper) {
1886 					sign = -1;
1887 					c++;
1888 					break;
1889 				}
1890 			case '>':
1891 			case '<':
1892 			case '+':
1893 			case '/':
1894 			case '*':
1895 			case '%':
1896 			case '&':
1897 			case '=':
1898 			case ':':
1899 				if (c[1] == '=')
1900 					oper = (*c++) + 16;
1901 				else
1902 					oper = *c;
1903 				c++;
1904 				break;
1905 			default:
1906 				c++;
1907 				break;
1908 			}
1909 			if (opex) {
1910 				sign = 1;
1911 				switch (oper) {
1912 				case 'c':
1913 					value = value2;
1914 					break;
1915 				case '-':
1916 					value = value - value2;
1917 					break;
1918 				case '+':
1919 					value = value + value2;
1920 					break;
1921 				case '*':
1922 					value = value * value2;
1923 					break;
1924 				case '/':
1925 					if (value2)
1926 						value = value / value2;
1927 					break;
1928 				case '%':
1929 					if (value2)
1930 						value = value % value2;
1931 					break;
1932 				case '<':
1933 					value = (value < value2);
1934 					break;
1935 				case '>':
1936 					value = (value > value2);
1937 					break;
1938 				case '>' + 16:
1939 					value = (value >= value2);
1940 					break;
1941 				case '<' + 16:
1942 					value = (value <= value2);
1943 					break;
1944 				case '=':
1945 				case '=' + 16:
1946 					value = (value == value2);
1947 					break;
1948 				case '&':
1949 					value = (value && value2);
1950 					break;
1951 				case ':':
1952 					value = (value || value2);
1953 					break;
1954 				default:
1955 					fprintf(stderr, "man2html: unknown operator %c.\n", oper);
1956 				}
1957 				oper = 0;
1958 			}
1959 		}
1960 		if (*c == ')')
1961 			c++;
1962 	}
1963 	*result = value;
1964 	return c;
1965 }
1966 
1967 static void
trans_char(char * c,char s,char t)1968 trans_char(char *c, char s, char t)
1969 {
1970 	char   *sl = c;
1971 	int     slash = 0;
1972 
1973 	while (*sl != '\n' || slash) {
1974 		if (!slash) {
1975 			if (*sl == escapesym)
1976 				slash = 1;
1977 			else if (*sl == s)
1978 				*sl = t;
1979 		} else
1980 			slash = 0;
1981 		sl++;
1982 	}
1983 }
1984 
1985 /* Remove \a from C in place.  Return modified C. */
1986 static char *
unescape(char * c)1987 unescape (char *c)
1988 {
1989 	int	i, l;
1990 
1991 	l = strlen (c);
1992 	i = 0;
1993 	while (i < l && c[i]) {
1994 		if (c[i] == '\a') {
1995 			if (c[i+1])
1996 				memmove (c + i, c + i + 1, l - i);
1997 			else {
1998 				c[i] = '\0';
1999 				break;
2000 			}
2001 		}
2002 		i++;
2003 	}
2004 	return c;
2005 }
2006 
2007 static char *
fill_words(char * c,char * words[],int * n)2008 fill_words(char *c, char *words[], int *n)
2009 {
2010 	char   *sl = c;
2011 	int     slash = 0;
2012 	int     skipspace = 0;
2013 
2014 	*n = 0;
2015 	words[*n] = sl;
2016 	while (*sl && (*sl != '\n' || slash)) {
2017 		if (!slash) {
2018 			if (*sl == '"') {
2019 				*sl = '\a';
2020 				skipspace = !skipspace;
2021 			} else if (*sl == '\a') {
2022 				/* handle already-translated " */
2023 				skipspace = !skipspace;
2024 			} else if (*sl == escapesym)
2025 				slash = 1;
2026 			else if ((*sl == ' ' || *sl == '\t') && !skipspace) {
2027 				*sl = '\n';
2028 				if (words[*n] != sl)
2029 					(*n)++;
2030 				words[*n] = sl + 1;
2031 			}
2032 		} else {
2033 			if (*sl == '"') {
2034 				sl--;
2035 				*sl = '\n';
2036 				if (words[*n] != sl)
2037 					(*n)++;
2038 				sl++;
2039 				while (*sl && *sl != '\n')
2040 					sl++;
2041 				words[*n] = sl;
2042 				sl--;
2043 			}
2044 			slash = 0;
2045 		}
2046 		sl++;
2047 	}
2048 	if (sl != words[*n])
2049 		(*n)++;
2050 	return sl;
2051 }
2052 
2053 static char *abbrev_list[] = {
2054 	"GSBG", "Getting Started ",
2055 	"SUBG", "Customizing SunOS",
2056 	"SHBG", "Basic Troubleshooting",
2057 	"SVBG", "SunView User's Guide",
2058 	"MMBG", "Mail and Messages",
2059 	"DMBG", "Doing More with SunOS",
2060 	"UNBG", "Using the Network",
2061 	"GDBG", "Games, Demos &amp; Other Pursuits",
2062 	"CHANGE", "SunOS 4.1 Release Manual",
2063 	"INSTALL", "Installing SunOS 4.1",
2064 	"ADMIN", "System and Network Administration",
2065 	"SECUR", "Security Features Guide",
2066 	"PROM", "PROM User's Manual",
2067 	"DIAG", "Sun System Diagnostics",
2068 	"SUNDIAG", "Sundiag User's Guide",
2069 	"MANPAGES", "SunOS Reference Manual",
2070 	"REFMAN", "SunOS Reference Manual",
2071 	"SSI", "Sun System Introduction",
2072 	"SSO", "System Services Overview",
2073 	"TEXT", "Editing Text Files",
2074 	"DOCS", "Formatting Documents",
2075 	"TROFF", "Using <B>nroff</B> and <B>troff</B>",
2076 	"INDEX", "Global Index",
2077 	"CPG", "C Programmer's Guide",
2078 	"CREF", "C Reference Manual",
2079 	"ASSY", "Assembly Language Reference",
2080 	"PUL", "Programming Utilities and Libraries",
2081 	"DEBUG", "Debugging Tools",
2082 	"NETP", "Network Programming",
2083 	"DRIVER", "Writing Device Drivers",
2084 	"STREAMS", "STREAMS Programming",
2085 	"SBDK", "SBus Developer's Kit",
2086 	"WDDS", "Writing Device Drivers for the SBus",
2087 	"FPOINT", "Floating-Point Programmer's Guide",
2088 	"SVPG", "SunView 1 Programmer's Guide",
2089 	"SVSPG", "SunView 1 System Programmer's Guide",
2090 	"PIXRCT", "Pixrect Reference Manual",
2091 	"CGI", "SunCGI Reference Manual",
2092 	"CORE", "SunCore Reference Manual",
2093 	"4ASSY", "Sun-4 Assembly Language Reference",
2094 	"SARCH", "<FONT SIZE=-1>SPARC</FONT> Architecture Manual",
2095 	"KR", "The C Programming Language",
2096 NULL, NULL};
2097 
2098 static char *
lookup_abbrev(char * c)2099 lookup_abbrev(char *c)
2100 {
2101 	int     i = 0;
2102 
2103 	if (!c)
2104 		return "";
2105 	while (abbrev_list[i] && strcmp(c, abbrev_list[i]))
2106 		i = i + 2;
2107 	if (abbrev_list[i])
2108 		return abbrev_list[i + 1];
2109 	else
2110 		return c;
2111 }
2112 
2113 static char manidx[NULL_TERMINATED(HUGE_STR_MAX)];
2114 static int subs = 0;
2115 static int mip = 0;
2116 static char label[5] = "lbAA";
2117 
2118 static void
add_to_index(int level,char * item)2119 add_to_index(int level, char *item)
2120 {
2121 	char   *c = NULL;
2122 
2123 	label[3]++;
2124 	if (label[3] > 'Z') {
2125 		label[3] = 'A';
2126 		label[2]++;
2127 	}
2128 	if (level != subs) {
2129 		if (subs) {
2130 			strmaxcpy(manidx + mip, "</DL>\n", HUGE_STR_MAX - mip);
2131 			mip += 6;
2132 		} else {
2133 			strmaxcpy(manidx + mip, "<DL>\n", HUGE_STR_MAX - mip);
2134 			mip += 5;
2135 		}
2136 	}
2137 	subs = level;
2138 	scan_troff(item, 1, &c);
2139 	sprintf(manidx + mip, "<DT><A HREF=\"#%s\">%s</A><DD>\n", label, c);
2140 	if (c)
2141 		free(c);
2142 	while (manidx[mip])
2143 		mip++;
2144 }
2145 
2146 static char *
skip_till_newline(char * c)2147 skip_till_newline(char *c)
2148 {
2149 	int     lvl = 0;
2150 
2151 	while (*c && *c != '\n' || lvl > 0) {
2152 		if (*c == '\\') {
2153 			c++;
2154 			if (*c == '}')
2155 				lvl--;
2156 			else if (*c == '{')
2157 				lvl++;
2158 		}
2159 		c++;
2160 	}
2161 	c++;
2162 	if (lvl < 0 && newline_for_fun) {
2163 		newline_for_fun = newline_for_fun + lvl;
2164 		if (newline_for_fun < 0)
2165 			newline_for_fun = 0;
2166 	}
2167 	return c;
2168 }
2169 
2170 static void
outputPageHeader(char * l,char * c,char * r)2171 outputPageHeader(char *l, char *c, char *r)
2172 {
2173 	out_html("<TABLE WIDTH=100%>\n<TR>\n");
2174 	out_html("<TH ALIGN=LEFT width=33%>");
2175 	out_html(l);
2176 	out_html("<TH ALIGN=CENTER width=33%>");
2177 	out_html(c);
2178 	out_html("<TH ALIGN=RIGHT width=33%>");
2179 	out_html(r);
2180 	out_html("\n</TR>\n</TABLE>\n");
2181 }
2182 
2183 static void
outputPageFooter(char * l,char * c,char * r)2184 outputPageFooter(char *l, char *c, char *r)
2185 {
2186 	out_html("<HR>\n");
2187 	outputPageHeader(l, c, r);
2188 }
2189 
2190 static int ifelseval = 0;
2191 
2192 static char *
scan_request(char * c)2193 scan_request(char *c)
2194 {
2195 	/* BSD Mandoc stuff */
2196 	static int mandoc_synopsis = 0;	/* True if we are in the synopsis
2197 					 * section */
2198 	static int mandoc_command = 0;	/* True if this is mandoc page */
2199 	static int mandoc_bd_options;	/* Only copes with non-nested Bd's */
2200 
2201 	int     i, j, mode = 0;
2202 	char   *h;
2203 	char   *wordlist[MAX_WORDLIST];
2204 	int     words;
2205 	char   *sl;
2206 	STRDEF *owndef;
2207 
2208 	while (*c == ' ' || *c == '\t')
2209 		c++;
2210 	if (c[0] == '\n')
2211 		return c + 1;
2212 	if (c[1] == '\n')
2213 		j = 1;
2214 	else
2215 		j = 2;
2216 	while (c[j] == ' ' || c[j] == '\t')
2217 		j++;
2218 	if (c[0] == escapesym) {
2219 		/* some pages use .\" .\$1 .\} */
2220 		/* .\$1 is too difficult/stupid */
2221 		if (c[1] == '$')
2222 			c = skip_till_newline(c);
2223 		else
2224 			c = scan_escape(c + 1);
2225 	} else {
2226 		i = V(c[0], c[1]);
2227 		switch (i) {
2228 		case V('a', 'b'):
2229 			h = c + j;
2230 			while (*h && *h != '\n')
2231 				h++;
2232 			*h = '\0';
2233 			if (scaninbuff && buffpos) {
2234 				buffer[buffpos] = '\0';
2235 				puts(buffer);
2236 			}
2237 			/* fprintf(stderr, "%s\n", c+2); */
2238 			exit(0);
2239 			break;
2240 		case V('d', 'i'):
2241 			{
2242 				STRDEF *de;
2243 				int     oldcurpos = curpos;
2244 
2245 				c = c + j;
2246 				i = V(c[0], c[1]);
2247 				if (*c == '\n') {
2248 					c++;
2249 					break;
2250 				}
2251 				while (*c && *c != '\n')
2252 					c++;
2253 				c++;
2254 				h = c;
2255 				while (*c && strncmp(c, ".di", 3))
2256 					while (*c && *c++ != '\n');
2257 				*c = '\0';
2258 				de = strdef;
2259 				while (de && de->nr != i)
2260 					de = de->next;
2261 				if (!de) {
2262 					de = (STRDEF *) xmalloc(sizeof(STRDEF));
2263 					de->nr = i;
2264 					de->slen = 0;
2265 					de->next = strdef;
2266 					de->st = NULL;
2267 					strdef = de;
2268 				} else {
2269 					if (de->st)
2270 						free(de->st);
2271 					de->slen = 0;
2272 					de->st = NULL;
2273 				}
2274 				scan_troff(h, 0, &de->st);
2275 				*c = '.';
2276 				while (*c && *c++ != '\n');
2277 				break;
2278 			}
2279 		case V('d', 's'):
2280 			mode = 1;
2281 		case V('a', 's'):
2282 			{
2283 				STRDEF *de;
2284 				int     oldcurpos = curpos;
2285 
2286 				c = c + j;
2287 				i = V(c[0], c[1]);
2288 				j = 0;
2289 				while (c[j] && c[j] != '\n')
2290 					j++;
2291 				if (j < 3) {
2292 					c = c + j;
2293 					break;
2294 				}
2295 				if (c[1] == ' ')
2296 					c = c + 1;
2297 				else
2298 					c = c + 2;
2299 				while (isspace(*c))
2300 					c++;
2301 				if (*c == '"')
2302 					c++;
2303 				de = strdef;
2304 				while (de && de->nr != i)
2305 					de = de->next;
2306 				single_escape = 1;
2307 				curpos = 0;
2308 				if (!de) {
2309 					char   *h;
2310 
2311 					de = (STRDEF *) xmalloc(sizeof(STRDEF));
2312 					de->nr = i;
2313 					de->slen = 0;
2314 					de->next = strdef;
2315 					de->st = NULL;
2316 					strdef = de;
2317 					h = NULL;
2318 					c = scan_troff(c, 1, &h);
2319 					de->st = h;
2320 					de->slen = curpos;
2321 				} else {
2322 					if (mode) {
2323 						char   *h = NULL;
2324 
2325 						c = scan_troff(c, 1, &h);
2326 						free(de->st);
2327 						de->slen = 0;
2328 						de->st = h;
2329 					} else
2330 						c = scan_troff(c, 1, &de->st);
2331 					de->slen += curpos;
2332 				}
2333 				single_escape = 0;
2334 				curpos = oldcurpos;
2335 			}
2336 			break;
2337 		case V('b', 'r'):
2338 			if (still_dd)
2339 				out_html("<DD>");
2340 			else
2341 				out_html("<BR>\n");
2342 			curpos = 0;
2343 			c = c + j;
2344 			if (c[0] == escapesym) {
2345 				c = scan_escape(c + 1);
2346 			}
2347 			c = skip_till_newline(c);
2348 			break;
2349 		case V('c', '2'):
2350 			c = c + j;
2351 			if (*c != '\n') {
2352 				nobreaksym = *c;
2353 			} else
2354 				nobreaksym = '\'';
2355 			c = skip_till_newline(c);
2356 			break;
2357 		case V('c', 'c'):
2358 			c = c + j;
2359 			if (*c != '\n') {
2360 				controlsym = *c;
2361 			} else
2362 				controlsym = '.';
2363 			c = skip_till_newline(c);
2364 			break;
2365 		case V('c', 'e'):
2366 			c = c + j;
2367 			if (*c == '\n') {
2368 				i = 1;
2369 			} else {
2370 				i = 0;
2371 				while ('0' <= *c && *c <= '9') {
2372 					i = i * 10 + *c - '0';
2373 					c++;
2374 				}
2375 			}
2376 			c = skip_till_newline(c);
2377 			/* center next i lines */
2378 			if (i > 0) {
2379 				out_html("<CENTER>\n");
2380 				while (i && *c) {
2381 					char   *line = NULL;
2382 
2383 					c = scan_troff(c, 1, &line);
2384 					if (line && strncmp(line, "<BR>", 4)) {
2385 						out_html(line);
2386 						out_html("<BR>\n");
2387 						i--;
2388 					}
2389 				}
2390 				out_html("</CENTER>\n");
2391 				curpos = 0;
2392 			}
2393 			break;
2394 		case V('e', 'c'):
2395 			c = c + j;
2396 			if (*c != '\n') {
2397 				escapesym = *c;
2398 			} else
2399 				escapesym = '\\';
2400 			break;
2401 			c = skip_till_newline(c);
2402 		case V('e', 'o'):
2403 			escapesym = '\0';
2404 			c = skip_till_newline(c);
2405 			break;
2406 		case V('e', 'x'):
2407 			exit(0);
2408 			break;
2409 		case V('f', 'c'):
2410 			c = c + j;
2411 			if (*c == '\n') {
2412 				fieldsym = padsym = '\0';
2413 			} else {
2414 				fieldsym = c[0];
2415 				padsym = c[1];
2416 			}
2417 			c = skip_till_newline(c);
2418 			break;
2419 		case V('f', 'i'):
2420 			if (!fillout) {
2421 				out_html(change_to_font(0));
2422 				out_html(change_to_size('0'));
2423 				out_html("</PRE>\n");
2424 			}
2425 			curpos = 0;
2426 			fillout = 1;
2427 			c = skip_till_newline(c);
2428 			break;
2429 		case V('f', 't'):
2430 			c = c + j;
2431 			if (*c == '\n') {
2432 				out_html(change_to_font(0));
2433 			} else {
2434 				if (*c == escapesym) {
2435 					int     fn;
2436 
2437 					c = scan_expression(c, &fn);
2438 					c--;
2439 					out_html(change_to_font(fn));
2440 				} else {
2441 					out_html(change_to_font(*c));
2442 					c++;
2443 				}
2444 			}
2445 			c = skip_till_newline(c);
2446 			break;
2447 		case V('e', 'l'):
2448 			/* .el anything : else part of if else */
2449 			if (ifelseval) {
2450 				c = c + j;
2451 				c[-1] = '\n';
2452 				c = scan_troff(c, 1, NULL);
2453 			} else
2454 				c = skip_till_newline(c + j);
2455 			break;
2456 		case V('i', 'e'):
2457 			/* .ie c anything : then part of if else */
2458 		case V('i', 'f'):
2459 			/*
2460 			 * .if c anything .if !c anything .if N anything .if
2461 			 * !N anything .if 'string1'string2' anything .if
2462 			 * !'string1'string2' anything
2463 			 */
2464 			c = c + j;
2465 			c = scan_expression(c, &i);
2466 			ifelseval = !i;
2467 			if (i) {
2468 				*c = '\n';
2469 				c++;
2470 				c = scan_troff(c, 1, NULL);
2471 			} else
2472 				c = skip_till_newline(c);
2473 			break;
2474 		case V('i', 'g'):
2475 			{
2476 				char   *endwith = "..\n";
2477 
2478 				i = 3;
2479 				c = c + j;
2480 				if (*c != '\n') {
2481 					endwith = c - 1;
2482 					i = 1;
2483 					c[-1] = '.';
2484 					while (*c && *c != '\n')
2485 						c++, i++;
2486 				}
2487 				c++;
2488 				while (*c && strncmp(c, endwith, i))
2489 					while (*c++ != '\n');
2490 				while (*c++ != '\n');
2491 				break;
2492 			}
2493 		case V('n', 'f'):
2494 			if (fillout) {
2495 				out_html(change_to_font(0));
2496 				out_html(change_to_size('0'));
2497 				out_html("<PRE>\n");
2498 			}
2499 			curpos = 0;
2500 			fillout = 0;
2501 			c = skip_till_newline(c);
2502 			break;
2503 		case V('p', 's'):
2504 			c = c + j;
2505 			if (*c == '\n') {
2506 				out_html(change_to_size('0'));
2507 			} else {
2508 				j = 0;
2509 				i = 0;
2510 				if (*c == '-') {
2511 					j = -1;
2512 					c++;
2513 				} else if (*c == '+') {
2514 					j = 1;
2515 					c++;
2516 				}
2517 				c = scan_expression(c, &i);
2518 				if (!j) {
2519 					j = 1;
2520 					if (i > 5)
2521 						i = i - 10;
2522 				}
2523 				out_html(change_to_size(i * j));
2524 			}
2525 			c = skip_till_newline(c);
2526 			break;
2527 		case V('s', 'p'):
2528 			c = c + j;
2529 			if (fillout)
2530 				out_html("<P>");
2531 			else {
2532 				out_html(NEWLINE);
2533 				NEWLINE[0] = '\n';
2534 			}
2535 			curpos = 0;
2536 			c = skip_till_newline(c);
2537 			break;
2538 		case V('s', 'o'):
2539 			{
2540 				FILE   *f;
2541 				struct stat stbuf;
2542 				int     l = 0;
2543 				char   *buf;
2544 				char   *name = NULL;
2545 
2546 				curpos = 0;
2547 				c = c + j;
2548 				if (*c == '/') {
2549 					h = c;
2550 				} else {
2551 					h = c - 3;
2552 					h[0] = '.';
2553 					h[1] = '.';
2554 					h[2] = '/';
2555 				}
2556 				while (*c != '\n')
2557 					c++;
2558 				*c = '\0';
2559 				scan_troff(h, 1, &name);
2560 				if (name[3] == '/')
2561 					h = name + 3;
2562 				else
2563 					h = name;
2564 				if (stat(h, &stbuf) != -1)
2565 					l = stbuf.st_size;
2566 #if NOCGI
2567 				if (!out_length) {
2568 					char   *t, *s;
2569 
2570 					t = strrchr(fname, '/');
2571 					if (!t)
2572 						t = fname;
2573 					fprintf(stderr, "ln -s %s.html %s.html\n", h, t);
2574 					s = strrchr(t, '.');
2575 					if (!s)
2576 						s = t;
2577 					printf("<HTML><HEAD><TITLE> Manpage of %s</TITLE>\n"
2578 					       "</HEAD><BODY>\n"
2579 					       "See the manpage for <A HREF=\"%s.html\">%s</A>.\n"
2580 					       "</BODY></HTML>\n",
2581 					       s, h, h);
2582 				} else
2583 #endif
2584 				{
2585 					/*
2586 					 * this works alright, except for
2587 					 * section 3
2588 					 */
2589 					buf = read_man_page(h);
2590 					if (!buf) {
2591 
2592 						fprintf(stderr, "man2html: unable to open or read file %s.\n",
2593 							h);
2594 						out_html("<BLOCKQUOTE>"
2595 							 "man2html: unable to open or read file.\n");
2596 						out_html(h);
2597 						out_html("</BLOCKQUOTE>\n");
2598 					} else {
2599 						buf[0] = buf[l] = '\n';
2600 						buf[l + 1] = buf[l + 2] = '\0';
2601 						scan_troff(buf + 1, 0, NULL);
2602 					}
2603 					if (buf)
2604 						free(buf);
2605 				}
2606 				*c++ = '\n';
2607 				break;
2608 			}
2609 		case V('t', 'a'):
2610 			c = c + j;
2611 			j = 0;
2612 			while (*c != '\n') {
2613 				sl = scan_expression(c, &tabstops[j]);
2614 				if (*c == '-' || *c == '+')
2615 					tabstops[j] += tabstops[j - 1];
2616 				c = sl;
2617 				while (*c == ' ' || *c == '\t')
2618 					c++;
2619 				j++;
2620 			}
2621 			maxtstop = j;
2622 			curpos = 0;
2623 			break;
2624 		case V('t', 'i'):
2625 			/*
2626 			 * while (itemdepth || dl_set[itemdepth]) {
2627 			 * out_html("</DL>\n"); if (dl_set[itemdepth])
2628 			 * dl_set[itemdepth]=0; else itemdepth--; }
2629 			 */
2630 			out_html("<BR>\n");
2631 			c = c + j;
2632 			c = scan_expression(c, &j);
2633 			for (i = 0; i < j; i++)
2634 				out_html("&nbsp;");
2635 			curpos = j;
2636 			c = skip_till_newline(c);
2637 			break;
2638 		case V('t', 'm'):
2639 			c = c + j;
2640 			h = c;
2641 			while (*c != '\n')
2642 				c++;
2643 			*c = '\0';
2644 			/* fprintf(stderr,"%s\n", h); */
2645 			*c = '\n';
2646 			break;
2647 		case V('B', ' '):
2648 		case V('B', '\n'):
2649 		case V('I', ' '):
2650 		case V('I', '\n'):
2651 			/* parse one line in a certain font */
2652 			out_html(change_to_font(*c));
2653 			trans_char(c, '"', '\a');
2654 			c = c + j;
2655 			if (*c == '\n')
2656 				c++;
2657 			c = scan_troff(c, 1, NULL);
2658 			out_html(change_to_font('R'));
2659 			out_html(NEWLINE);
2660 			if (fillout)
2661 				curpos++;
2662 			else
2663 				curpos = 0;
2664 			break;
2665 		case V('O', 'P'):	/* groff manpages use this
2666 					 * construction */
2667 			/* .OP a b : [ <B>a</B> <I>b</I> ] */
2668 			mode = 1;
2669 			c[0] = 'B';
2670 			c[1] = 'I';
2671 			out_html(change_to_font('R'));
2672 			out_html("[");
2673 			curpos++;
2674 		case V('B', 'R'):
2675 		case V('B', 'I'):
2676 		case V('I', 'B'):
2677 		case V('I', 'R'):
2678 		case V('R', 'B'):
2679 		case V('R', 'I'):
2680 			{
2681 				char    font[2];
2682 
2683 				font[0] = c[0];
2684 				font[1] = c[1];
2685 				c = c + j;
2686 				if (*c == '\n')
2687 					c++;
2688 				sl = fill_words(c, wordlist, &words);
2689 				c = sl + 1;
2690 				/*
2691 				 * .BR name (section) indicates a link. It
2692 				 * will be added in the output routine.
2693 				 */
2694 				for (i = 0; i < words; i++) {
2695 					if (mode) {
2696 						out_html(" ");
2697 						curpos++;
2698 					}
2699 					wordlist[i][-1] = ' ';
2700 					out_html(change_to_font(font[i & 1]));
2701 					scan_troff(wordlist[i], 1, NULL);
2702 				}
2703 				out_html(change_to_font('R'));
2704 				if (mode) {
2705 					out_html(" ]");
2706 					curpos++;
2707 				}
2708 				out_html(NEWLINE);
2709 				if (!fillout)
2710 					curpos = 0;
2711 				else
2712 					curpos++;
2713 			}
2714 			break;
2715 		case V('D', 'T'):
2716 			for (j = 0; j < 20; j++)
2717 				tabstops[j] = (j + 1) * 8;
2718 			maxtstop = 20;
2719 			c = skip_till_newline(c);
2720 			break;
2721 		case V('I', 'P'):
2722 			sl = fill_words(c + j, wordlist, &words);
2723 			c = sl + 1;
2724 			if (!dl_set[itemdepth]) {
2725 				out_html("<DL COMPACT>\n");
2726 				dl_set[itemdepth] = 1;
2727 			}
2728 			out_html("<DT>");
2729 			if (words) {
2730 				scan_troff(wordlist[0], 1, NULL);
2731 			}
2732 			out_html("<DD>");
2733 			curpos = 0;
2734 			break;
2735 		case V('T', 'P'):
2736 			if (!dl_set[itemdepth]) {
2737 				out_html("<DL COMPACT>\n");
2738 				dl_set[itemdepth] = 1;
2739 			}
2740 			out_html("<DT>");
2741 			c = skip_till_newline(c);
2742 			/* somewhere a definition ends with '.TP' */
2743 			if (!*c)
2744 				still_dd = 1;
2745 			else {
2746 				c = scan_troff(c, 1, NULL);
2747 				out_html("<DD>");
2748 			}
2749 			curpos = 0;
2750 			break;
2751 		case V('I', 'X'):
2752 			/* general index */
2753 			sl = fill_words(c + j, wordlist, &words);
2754 			c = sl + 1;
2755 			j = 4;
2756 			while (idxlabel[j] == 'Z')
2757 				idxlabel[j--] = 'A';
2758 			idxlabel[j]++;
2759 #ifdef MAKEINDEX
2760 			fprintf(idxfile, "%s@%s@", fname, idxlabel);
2761 			for (j = 0; j < words; j++) {
2762 				h = NULL;
2763 				scan_troff(wordlist[j], 1, &h);
2764 				fprintf(idxfile, "_\b@%s", h);
2765 				free(h);
2766 			}
2767 			fprintf(idxfile, "\n");
2768 #endif
2769 			out_html("<A NAME=\"");
2770 			out_html(idxlabel);
2771 			/*
2772 			 * this will not work in mosaic (due to a bug).
2773 			 * Adding '&nbsp;' between '>' and '<' solves it, but
2774 			 * creates some space. A normal space does not work.
2775 			 */
2776 			out_html("\"></A>");
2777 			break;
2778 		case V('L', 'P'):
2779 		case V('P', 'P'):
2780 			if (dl_set[itemdepth]) {
2781 				out_html("</DL>\n");
2782 				dl_set[itemdepth] = 0;
2783 			}
2784 			if (fillout)
2785 				out_html("<P>\n");
2786 			else {
2787 				out_html(NEWLINE);
2788 				NEWLINE[0] = '\n';
2789 			}
2790 			curpos = 0;
2791 			c = skip_till_newline(c);
2792 			break;
2793 		case V('H', 'P'):
2794 			if (!dl_set[itemdepth]) {
2795 				out_html("<DL COMPACT>");
2796 				dl_set[itemdepth] = 1;
2797 			}
2798 			out_html("<DT>\n");
2799 			still_dd = 1;
2800 			c = skip_till_newline(c);
2801 			curpos = 0;
2802 			break;
2803 		case V('P', 'D'):
2804 			c = skip_till_newline(c);
2805 			break;
2806 		case V('R', 's'):	/* BSD mandoc */
2807 		case V('R', 'S'):
2808 			sl = fill_words(c + j, wordlist, &words);
2809 			j = 1;
2810 			if (words > 0)
2811 				scan_expression(wordlist[0], &j);
2812 			if (j >= 0) {
2813 				itemdepth++;
2814 				dl_set[itemdepth] = 0;
2815 				out_html("<DL COMPACT><DT><DD>");
2816 				c = skip_till_newline(c);
2817 				curpos = 0;
2818 				break;
2819 			}
2820 		case V('R', 'e'):	/* BSD mandoc */
2821 		case V('R', 'E'):
2822 			if (itemdepth > 0) {
2823 				if (dl_set[itemdepth])
2824 					out_html("</DL>");
2825 				out_html("</DL>\n");
2826 				itemdepth--;
2827 			}
2828 			c = skip_till_newline(c);
2829 			curpos = 0;
2830 			break;
2831 		case V('S', 'B'):
2832 			out_html(change_to_size(-1));
2833 			out_html(change_to_font('B'));
2834 			c = scan_troff(c + j, 1, NULL);
2835 			out_html(change_to_font('R'));
2836 			out_html(change_to_size('0'));
2837 			break;
2838 		case V('S', 'M'):
2839 			c = c + j;
2840 			if (*c == '\n')
2841 				c++;
2842 			out_html(change_to_size(-1));
2843 			trans_char(c, '"', '\a');
2844 			c = scan_troff(c, 1, NULL);
2845 			out_html(change_to_size('0'));
2846 			break;
2847 		case V('S', 's'):	/* BSD mandoc */
2848 			mandoc_command = 1;
2849 		case V('S', 'S'):
2850 			mode = 1;
2851 		case V('S', 'h'):	/* BSD mandoc */
2852 			/* hack for fallthru from above */
2853 			mandoc_command = !mode || mandoc_command;
2854 		case V('S', 'H'):
2855 			c = c + j;
2856 			if (*c == '\n')
2857 				c++;
2858 			while (itemdepth || dl_set[itemdepth]) {
2859 				out_html("</DL>\n");
2860 				if (dl_set[itemdepth])
2861 					dl_set[itemdepth] = 0;
2862 				else if (itemdepth > 0)
2863 					itemdepth--;
2864 			}
2865 			out_html(change_to_font(0));
2866 			out_html(change_to_size(0));
2867 			if (!fillout) {
2868 				fillout = 1;
2869 				out_html("</PRE>");
2870 			}
2871 			trans_char(c, '"', '\a');
2872 			add_to_index(mode, c);
2873 			out_html("<A NAME=\"");
2874 			out_html(label);
2875 			/* &nbsp; for mosaic users */
2876 			if (mode)
2877 				out_html("\">&nbsp;</A>\n<H4>");
2878 			else
2879 				out_html("\">&nbsp;</A>\n<H3>");
2880 			mandoc_synopsis = strncmp(c, "SYNOPSIS", 8) == 0;
2881 			c = mandoc_command ? scan_troff_mandoc(c, 1, NULL) : scan_troff(c, 1, NULL);
2882 			if (mode)
2883 				out_html("</H4>\n");
2884 			else
2885 				out_html("</H3>\n");
2886 			curpos = 0;
2887 			break;
2888 		case V('T', 'S'):
2889 			c = scan_table(c);
2890 			break;
2891 		case V('D', 't'):	/* BSD mandoc */
2892 			mandoc_command = 1;
2893 		case V('T', 'H'):
2894 			if (!output_possible) {
2895 				sl = fill_words(c + j, wordlist, &words);
2896 				if (words > 1) {
2897 					char	*t;
2898 					for (i = 1; i < words; i++)
2899 						wordlist[i][-1] = '\0';
2900 					*sl = '\0';
2901 					output_possible = 1;
2902 					sprintf(th_page_and_sec, "%s(%s)", wordlist[0], wordlist[1]);
2903 					if (words > 2) {
2904 						t = unescape(wordlist[2]);
2905 						strncpy(th_datestr, t, sizeof(th_datestr));
2906 						th_datestr[sizeof(th_datestr) - 1] = '\0';
2907 					} else
2908 						th_datestr[0] = '\0';
2909 					if (words > 3) {
2910 						t = unescape(wordlist[3]);
2911 						strncpy(th_version, t, sizeof(th_version));
2912 						th_version[sizeof(th_version) - 1] = '\0';
2913 					} else
2914 						th_version[0] = '\0';
2915 					out_html("<HTML><HEAD>\n<TITLE>");
2916 					out_html(th_page_and_sec);
2917 					out_html(" Manual Page");
2918 					out_html("</TITLE>\n</HEAD>\n<BODY>");
2919 
2920 					outputPageHeader(th_page_and_sec, th_datestr, th_page_and_sec);
2921 
2922 					out_html("<BR><A HREF=\"#index\">Index</A>\n");
2923 					*sl = '\n';
2924 					out_html("<HR>\n");
2925 					if (mandoc_command)
2926 						out_html("<BR>BSD mandoc<BR>");
2927 				}
2928 				c = sl + 1;
2929 			} else
2930 				c = skip_till_newline(c);
2931 			curpos = 0;
2932 			break;
2933 		case V('T', 'X'):
2934 			sl = fill_words(c + j, wordlist, &words);
2935 			*sl = '\0';
2936 			out_html(change_to_font('I'));
2937 			if (words > 1)
2938 				wordlist[1][-1] = '\0';
2939 			c = lookup_abbrev(wordlist[0]);
2940 			curpos += strlen(c);
2941 			out_html(c);
2942 			out_html(change_to_font('R'));
2943 			if (words > 1)
2944 				out_html(wordlist[1]);
2945 			*sl = '\n';
2946 			c = sl + 1;
2947 			break;
2948 		case V('r', 'm'):
2949 			/* .rm xx : Remove request, macro or string */
2950 		case V('r', 'n'):
2951 			/*
2952 			 * .rn xx yy : Rename request, macro or string xx to
2953 			 * yy
2954 			 */
2955 			{
2956 				STRDEF *de;
2957 
2958 				c = c + j;
2959 				i = V(c[0], c[1]);
2960 				c = c + 2;
2961 				while (isspace(*c) && *c != '\n')
2962 					c++;
2963 				j = V(c[0], c[1]);
2964 				while (*c && *c != '\n')
2965 					c++;
2966 				c++;
2967 				de = strdef;
2968 				while (de && de->nr != j)
2969 					de = de->next;
2970 				if (de) {
2971 					if (de->st)
2972 						free(de->st);
2973 					de->nr = 0;
2974 				}
2975 				de = strdef;
2976 				while (de && de->nr != i)
2977 					de = de->next;
2978 				if (de)
2979 					de->nr = j;
2980 				break;
2981 			}
2982 		case V('n', 'x'):
2983 			/* .nx filename : next file. */
2984 		case V('i', 'n'):
2985 			/* .in +-N : Indent */
2986 			c = skip_till_newline(c);
2987 			break;
2988 		case V('n', 'r'):
2989 			/*
2990 			 * .nr R +-N M: define and set number register R by
2991 			 * +-N; auto-increment by M
2992 			 */
2993 			{
2994 				INTDEF *intd;
2995 
2996 				c = c + j;
2997 				i = V(c[0], c[1]);
2998 				c = c + 2;
2999 				intd = intdef;
3000 				while (intd && intd->nr != i)
3001 					intd = intd->next;
3002 				if (!intd) {
3003 					intd = (INTDEF *) xmalloc(sizeof(INTDEF));
3004 					intd->nr = i;
3005 					intd->val = 0;
3006 					intd->incr = 0;
3007 					intd->next = intdef;
3008 					intdef = intd;
3009 				}
3010 				while (*c == ' ' || *c == '\t')
3011 					c++;
3012 				c = scan_expression(c, &intd->val);
3013 				if (*c != '\n') {
3014 					while (*c == ' ' || *c == '\t')
3015 						c++;
3016 					c = scan_expression(c, &intd->incr);
3017 				}
3018 				c = skip_till_newline(c);
3019 				break;
3020 			}
3021 		case V('a', 'm'):
3022 			/* .am xx yy : append to a macro. */
3023 			/* define or handle as .ig yy */
3024 			mode = 1;
3025 		case V('d', 'e'):
3026 			/*
3027 			 * .de xx yy : define or redefine macro xx; end at
3028 			 * .yy (..)
3029 			 */
3030 			/* define or handle as .ig yy */
3031 			{
3032 				STRDEF *de;
3033 				int     olen = 0;
3034 
3035 				c = c + j;
3036 				sl = fill_words(c, wordlist, &words);
3037 				i = V(c[0], c[1]);
3038 				j = 2;
3039 				if (words == 1)
3040 					wordlist[1] = "..";
3041 				else {
3042 					wordlist[1]--;
3043 					wordlist[1][0] = '.';
3044 					j = 3;
3045 				}
3046 				c = sl + 1;
3047 				sl = c;
3048 				while (*c && strncmp(c, wordlist[1], j))
3049 					c = skip_till_newline(c);
3050 				de = defdef;
3051 				while (de && de->nr != i)
3052 					de = de->next;
3053 				if (mode && de)
3054 					olen = strlen(de->st);
3055 				j = olen + c - sl;
3056 				h = stralloc(j * 2 + 4);
3057 				if (h) {
3058 					for (j = 0; j < olen; j++)
3059 						h[j] = de->st[j];
3060 					if (!j || h[j - 1] != '\n')
3061 						h[j++] = '\n';
3062 					while (sl != c) {
3063 						if (sl[0] == '\\' && sl[1] == '\\') {
3064 							h[j++] = '\\';
3065 							sl++;
3066 						} else
3067 							h[j++] = *sl;
3068 						sl++;
3069 					}
3070 					h[j] = '\0';
3071 					if (de) {
3072 						if (de->st)
3073 							free(de->st);
3074 						de->st = h;
3075 					} else {
3076 						de = (STRDEF *) xmalloc(sizeof(STRDEF));
3077 						de->nr = i;
3078 						de->next = defdef;
3079 						de->st = h;
3080 						defdef = de;
3081 					}
3082 				}
3083 			}
3084 			c = skip_till_newline(c);
3085 			break;
3086 		case V('B', 'l'):	/* BSD mandoc */
3087 			{
3088 				char    list_options[NULL_TERMINATED(MED_STR_MAX)];
3089 				char   *nl = strchr(c, '\n');
3090 
3091 				c = c + j;
3092 				if (dl_set[itemdepth]) {	/* These things can
3093 								 * nest. */
3094 					itemdepth++;
3095 				}
3096 				if (nl) {	/* Parse list options */
3097 					strlimitcpy(list_options, c, nl - c, MED_STR_MAX);
3098 				}
3099 				if (strstr(list_options, "-bullet")) {	/* HTML Unnumbered List */
3100 					dl_set[itemdepth] = BL_BULLET_LIST;
3101 					out_html("<UL>\n");
3102 				} else if (strstr(list_options, "-enum")) {	/* HTML Ordered List */
3103 					dl_set[itemdepth] = BL_ENUM_LIST;
3104 					out_html("<OL>\n");
3105 				} else {	/* HTML Descriptive List */
3106 					dl_set[itemdepth] = BL_DESC_LIST;
3107 					out_html("<DL COMPACT>\n");
3108 				}
3109 				if (fillout)
3110 					out_html("<P>\n");
3111 				else {
3112 					out_html(NEWLINE);
3113 					NEWLINE[0] = '\n';
3114 				}
3115 				curpos = 0;
3116 				c = skip_till_newline(c);
3117 				break;
3118 			}
3119 		case V('E', 'l'):	/* BSD mandoc */
3120 			c = c + j;
3121 			if (dl_set[itemdepth] & BL_DESC_LIST) {
3122 				out_html("</DL>\n");
3123 			} else if (dl_set[itemdepth] & BL_BULLET_LIST) {
3124 				out_html("</UL>\n");
3125 			} else if (dl_set[itemdepth] & BL_ENUM_LIST) {
3126 				out_html("</OL>\n");
3127 			}
3128 			dl_set[itemdepth] = 0;
3129 			if (itemdepth > 0)
3130 				itemdepth--;
3131 			if (fillout)
3132 				out_html("<P>\n");
3133 			else {
3134 				out_html(NEWLINE);
3135 				NEWLINE[0] = '\n';
3136 			}
3137 			curpos = 0;
3138 			c = skip_till_newline(c);
3139 			break;
3140 		case V('I', 't'):	/* BSD mandoc */
3141 			c = c + j;
3142 			if (strncmp(c, "Xo", 2) == 0 && isspace(*(c + 2))) {
3143 				c = skip_till_newline(c);
3144 			}
3145 			if (dl_set[itemdepth] & BL_DESC_LIST) {
3146 				out_html("<DT>");
3147 				out_html(change_to_font('B'));
3148 				if (*c == '\n') {	/* Don't allow embedded
3149 							 * comms after a newline */
3150 					c++;
3151 					c = scan_troff(c, 1, NULL);
3152 				} else {	/* Do allow embedded comms on
3153 						 * the same line. */
3154 					c = scan_troff_mandoc(c, 1, NULL);
3155 				}
3156 				out_html(change_to_font('R'));
3157 				out_html(NEWLINE);
3158 				out_html("<DD>");
3159 			} else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST)) {
3160 				out_html("<LI>");
3161 				c = scan_troff_mandoc(c, 1, NULL);
3162 				out_html(NEWLINE);
3163 			}
3164 			if (fillout)
3165 				curpos++;
3166 			else
3167 				curpos = 0;
3168 			break;
3169 		case V('B', 'k'):	/* BSD mandoc */
3170 		case V('E', 'k'):	/* BSD mandoc */
3171 		case V('D', 'd'):	/* BSD mandoc */
3172 		case V('O', 's'):	/* BSD mandoc */
3173 			trans_char(c, '"', '\a');
3174 			c = c + j;
3175 			if (*c == '\n')
3176 				c++;
3177 			c = scan_troff_mandoc(c, 1, NULL);
3178 			out_html(NEWLINE);
3179 			if (fillout)
3180 				curpos++;
3181 			else
3182 				curpos = 0;
3183 			break;
3184 		case V('B', 't'):	/* BSD mandoc */
3185 			trans_char(c, '"', '\a');
3186 			c = c + j;
3187 			out_html(" is currently in beta test.");
3188 			if (fillout)
3189 				curpos++;
3190 			else
3191 				curpos = 0;
3192 			break;
3193 		case V('B', 'x'):	/* BSD mandoc */
3194 			trans_char(c, '"', '\a');
3195 			c = c + j;
3196 			if (*c == '\n')
3197 				c++;
3198 			out_html("BSD ");
3199 			c = scan_troff_mandoc(c, 1, NULL);
3200 			if (fillout)
3201 				curpos++;
3202 			else
3203 				curpos = 0;
3204 			break;
3205 		case V('D', 'l'):	/* BSD mandoc */
3206 			c = c + j;
3207 			out_html(NEWLINE);
3208 			out_html("<BLOCKQUOTE>");
3209 			out_html(change_to_font('L'));
3210 			if (*c == '\n')
3211 				c++;
3212 			c = scan_troff_mandoc(c, 1, NULL);
3213 			out_html(change_to_font('R'));
3214 			out_html("</BLOCKQUOTE>");
3215 			if (fillout)
3216 				curpos++;
3217 			else
3218 				curpos = 0;
3219 			break;
3220 		case V('B', 'd'):	/* BSD mandoc */
3221 			{	/* Seems like a kind of example/literal mode */
3222 				char    bd_options[NULL_TERMINATED(MED_STR_MAX)];
3223 				char   *nl = strchr(c, '\n');
3224 
3225 				c = c + j;
3226 				if (nl) {
3227 					strlimitcpy(bd_options, c, nl - c, MED_STR_MAX);
3228 				}
3229 				out_html(NEWLINE);
3230 				mandoc_bd_options = 0;	/* Remember options for
3231 							 * terminating Bl */
3232 				if (strstr(bd_options, "-offset indent")) {
3233 					mandoc_bd_options |= BD_INDENT;
3234 					out_html("<BLOCKQUOTE>\n");
3235 				}
3236 				if (strstr(bd_options, "-literal")
3237 				    || strstr(bd_options, "-unfilled")) {
3238 					if (fillout) {
3239 						mandoc_bd_options |= BD_LITERAL;
3240 						out_html(change_to_font(0));
3241 						out_html(change_to_size('0'));
3242 						out_html("<PRE>\n");
3243 					}
3244 					curpos = 0;
3245 					fillout = 0;
3246 				}
3247 				c = skip_till_newline(c);
3248 				break;
3249 			}
3250 		case V('E', 'd'):	/* BSD mandoc */
3251 			if (mandoc_bd_options & BD_LITERAL) {
3252 				if (!fillout) {
3253 					out_html(change_to_font(0));
3254 					out_html(change_to_size('0'));
3255 					out_html("</PRE>\n");
3256 				}
3257 			}
3258 			if (mandoc_bd_options & BD_INDENT)
3259 				out_html("</BLOCKQUOTE>\n");
3260 			curpos = 0;
3261 			fillout = 1;
3262 			c = skip_till_newline(c);
3263 			break;
3264 		case V('B', 'e'):	/* BSD mandoc */
3265 			c = c + j;
3266 			if (fillout)
3267 				out_html("<P>");
3268 			else {
3269 				out_html(NEWLINE);
3270 				NEWLINE[0] = '\n';
3271 			}
3272 			curpos = 0;
3273 			c = skip_till_newline(c);
3274 			break;
3275 		case V('X', 'r'):	/* BSD mandoc */
3276 			{
3277 				/*
3278 				 * Translate xyz 1 to xyz(1) Allow for
3279 				 * multiple spaces.  Allow the section to be
3280 				 * missing.
3281 				 */
3282 				char    buff[NULL_TERMINATED(MED_STR_MAX)];
3283 				char   *bufptr;
3284 
3285 				trans_char(c, '"', '\a');
3286 				bufptr = buff;
3287 				c = c + j;
3288 				if (*c == '\n')
3289 					c++;	/* Skip spaces */
3290 				while (isspace(*c) && *c != '\n')
3291 					c++;
3292 				while (isalnum(*c)) {	/* Copy the xyz part */
3293 					*bufptr = *c;
3294 					bufptr++;
3295 					if (bufptr >= buff + MED_STR_MAX)
3296 						break;
3297 					c++;
3298 				}
3299 				while (isspace(*c) && *c != '\n')
3300 					c++;	/* Skip spaces */
3301 				if (isdigit(*c)) {	/* Convert the number if
3302 							 * there is one */
3303 					*bufptr = '(';
3304 					bufptr++;
3305 					if (bufptr < buff + MED_STR_MAX) {
3306 						while (isalnum(*c)) {
3307 							*bufptr = *c;
3308 							bufptr++;
3309 							if (bufptr >= buff + MED_STR_MAX)
3310 								break;
3311 							c++;
3312 						}
3313 						if (bufptr < buff + MED_STR_MAX) {
3314 							*bufptr = ')';
3315 							bufptr++;
3316 						}
3317 					}
3318 				}
3319 				while (*c != '\n') {	/* Copy the remainder */
3320 					if (!isspace(*c)) {
3321 						*bufptr = *c;
3322 						bufptr++;
3323 						if (bufptr >= buff + MED_STR_MAX)
3324 							break;
3325 					}
3326 					c++;
3327 				}
3328 				*bufptr = '\n';
3329 				scan_troff_mandoc(buff, 1, NULL);
3330 
3331 				out_html(NEWLINE);
3332 				if (fillout)
3333 					curpos++;
3334 				else
3335 					curpos = 0;
3336 			}
3337 			break;
3338 		case V('F', 'l'):	/* BSD mandoc */
3339 			trans_char(c, '"', '\a');
3340 			c = c + j;
3341 			out_html("-");
3342 			if (*c != '\n') {
3343 				out_html(change_to_font('B'));
3344 				c = scan_troff_mandoc(c, 1, NULL);
3345 				out_html(change_to_font('R'));
3346 			}
3347 			out_html(NEWLINE);
3348 			if (fillout)
3349 				curpos++;
3350 			else
3351 				curpos = 0;
3352 			break;
3353 		case V('P', 'a'):	/* BSD mandoc */
3354 		case V('P', 'f'):	/* BSD mandoc */
3355 			trans_char(c, '"', '\a');
3356 			c = c + j;
3357 			if (*c == '\n')
3358 				c++;
3359 			c = scan_troff_mandoc(c, 1, NULL);
3360 			out_html(NEWLINE);
3361 			if (fillout)
3362 				curpos++;
3363 			else
3364 				curpos = 0;
3365 			break;
3366 		case V('P', 'p'):	/* BSD mandoc */
3367 			if (fillout)
3368 				out_html("<P>\n");
3369 			else {
3370 				out_html(NEWLINE);
3371 				NEWLINE[0] = '\n';
3372 			}
3373 			curpos = 0;
3374 			c = skip_till_newline(c);
3375 			break;
3376 		case V('D', 'q'):	/* BSD mandoc */
3377 			trans_char(c, '"', '\a');
3378 			c = c + j;
3379 			if (*c == '\n')
3380 				c++;
3381 			out_html("``");
3382 			c = scan_troff_mandoc(c, 1, NULL);
3383 			out_html("''");
3384 			out_html(NEWLINE);
3385 			if (fillout)
3386 				curpos++;
3387 			else
3388 				curpos = 0;
3389 			break;
3390 		case V('O', 'p'):	/* BSD mandoc */
3391 			trans_char(c, '"', '\a');
3392 			c = c + j;
3393 			if (*c == '\n')
3394 				c++;
3395 			out_html(change_to_font('R'));
3396 			out_html("[");
3397 			c = scan_troff_mandoc(c, 1, NULL);
3398 			out_html(change_to_font('R'));
3399 			out_html("]");
3400 			out_html(NEWLINE);
3401 			if (fillout)
3402 				curpos++;
3403 			else
3404 				curpos = 0;
3405 			break;
3406 		case V('O', 'o'):	/* BSD mandoc */
3407 			trans_char(c, '"', '\a');
3408 			c = c + j;
3409 			if (*c == '\n')
3410 				c++;
3411 			out_html(change_to_font('R'));
3412 			out_html("[");
3413 			c = scan_troff_mandoc(c, 1, NULL);
3414 			if (fillout)
3415 				curpos++;
3416 			else
3417 				curpos = 0;
3418 			break;
3419 		case V('O', 'c'):	/* BSD mandoc */
3420 			trans_char(c, '"', '\a');
3421 			c = c + j;
3422 			c = scan_troff_mandoc(c, 1, NULL);
3423 			out_html(change_to_font('R'));
3424 			out_html("]");
3425 			if (fillout)
3426 				curpos++;
3427 			else
3428 				curpos = 0;
3429 			break;
3430 		case V('P', 'q'):	/* BSD mandoc */
3431 			trans_char(c, '"', '\a');
3432 			c = c + j;
3433 			if (*c == '\n')
3434 				c++;
3435 			out_html("(");
3436 			c = scan_troff_mandoc(c, 1, NULL);
3437 			out_html(")");
3438 			out_html(NEWLINE);
3439 			if (fillout)
3440 				curpos++;
3441 			else
3442 				curpos = 0;
3443 			break;
3444 		case V('Q', 'l'):	/* BSD mandoc */
3445 			{	/* Single quote first word in the line */
3446 				char   *sp;
3447 
3448 				trans_char(c, '"', '\a');
3449 				c = c + j;
3450 				if (*c == '\n')
3451 					c++;
3452 				sp = c;
3453 				do {	/* Find first whitespace after the
3454 					 * first word that isn't a mandoc
3455 					 * macro */
3456 					while (*sp && isspace(*sp))
3457 						sp++;
3458 					while (*sp && !isspace(*sp))
3459 						sp++;
3460 				} while (*sp && isupper(*(sp - 2)) && islower(*(sp - 1)));
3461 
3462 				/*
3463 				 * Use a newline to mark the end of text to
3464 				 * be quoted
3465 				 */
3466 				if (*sp)
3467 					*sp = '\n';
3468 				out_html("`");	/* Quote the text */
3469 				c = scan_troff_mandoc(c, 1, NULL);
3470 				out_html("'");
3471 				out_html(NEWLINE);
3472 				if (fillout)
3473 					curpos++;
3474 				else
3475 					curpos = 0;
3476 				break;
3477 			}
3478 		case V('S', 'q'):	/* BSD mandoc */
3479 			trans_char(c, '"', '\a');
3480 			c = c + j;
3481 			if (*c == '\n')
3482 				c++;
3483 			out_html("`");
3484 			c = scan_troff_mandoc(c, 1, NULL);
3485 			out_html("'");
3486 			out_html(NEWLINE);
3487 			if (fillout)
3488 				curpos++;
3489 			else
3490 				curpos = 0;
3491 			break;
3492 		case V('A', 'r'):	/* BSD mandoc */
3493 			/* parse one line in italics */
3494 			out_html(change_to_font('I'));
3495 			trans_char(c, '"', '\a');
3496 			c = c + j;
3497 			if (*c == '\n') {	/* An empty Ar means "file
3498 						 * ..." */
3499 				out_html("file ...");
3500 			} else {
3501 				c = scan_troff_mandoc(c, 1, NULL);
3502 			}
3503 			out_html(change_to_font('R'));
3504 			out_html(NEWLINE);
3505 			if (fillout)
3506 				curpos++;
3507 			else
3508 				curpos = 0;
3509 			break;
3510 		case V('A', 'd'):	/* BSD mandoc */
3511 		case V('E', 'm'):	/* BSD mandoc */
3512 		case V('V', 'a'):	/* BSD mandoc */
3513 		case V('X', 'c'):	/* BSD mandoc */
3514 			/* parse one line in italics */
3515 			out_html(change_to_font('I'));
3516 			trans_char(c, '"', '\a');
3517 			c = c + j;
3518 			if (*c == '\n')
3519 				c++;
3520 			c = scan_troff_mandoc(c, 1, NULL);
3521 			out_html(change_to_font('R'));
3522 			out_html(NEWLINE);
3523 			if (fillout)
3524 				curpos++;
3525 			else
3526 				curpos = 0;
3527 			break;
3528 		case V('N', 'd'):	/* BSD mandoc */
3529 			trans_char(c, '"', '\a');
3530 			c = c + j;
3531 			if (*c == '\n')
3532 				c++;
3533 			out_html(" - ");
3534 			c = scan_troff_mandoc(c, 1, NULL);
3535 			out_html(NEWLINE);
3536 			if (fillout)
3537 				curpos++;
3538 			else
3539 				curpos = 0;
3540 			break;
3541 		case V('N', 'm'):	/* BSD mandoc */
3542 			{
3543 				static char mandoc_name[NULL_TERMINATED(SMALL_STR_MAX)] = "";
3544 
3545 				trans_char(c, '"', '\a');
3546 				c = c + j;
3547 				if (mandoc_synopsis) {	/* Break lines only in
3548 							 * the Synopsis. The
3549 							 * Synopsis section
3550 							 * seems to be treated
3551 							 * as a special case -
3552 							 * Bummer! */
3553 					static int count = 0;	/* Don't break on the
3554 								 * first Nm */
3555 
3556 					if (count) {
3557 						out_html("<BR>");
3558 					} else {
3559 						char   *end = strchr(c, '\n');
3560 
3561 						if (end) {	/* Remember the name for
3562 								 * later. */
3563 							strlimitcpy(mandoc_name, c, end - c, SMALL_STR_MAX);
3564 						}
3565 					}
3566 					count++;
3567 				}
3568 				out_html(change_to_font('B'));
3569 				while (*c == ' ' || *c == '\t')
3570 					c++;
3571 				if (*c == '\n') {	/* If Nm has no
3572 							 * argument, use one
3573 							 * from an earlier Nm
3574 							 * command that did have
3575 							 * one.  Hope there
3576 							 * aren't too many
3577 							 * commands that do
3578 							 * this. */
3579 					out_html(mandoc_name);
3580 				} else {
3581 					c = scan_troff_mandoc(c, 1, NULL);
3582 				}
3583 				out_html(change_to_font('R'));
3584 				out_html(NEWLINE);
3585 				if (fillout)
3586 					curpos++;
3587 				else
3588 					curpos = 0;
3589 				break;
3590 			}
3591 		case V('C', 'd'):	/* BSD mandoc */
3592 		case V('C', 'm'):	/* BSD mandoc */
3593 		case V('I', 'c'):	/* BSD mandoc */
3594 		case V('M', 's'):	/* BSD mandoc */
3595 		case V('O', 'r'):	/* BSD mandoc */
3596 		case V('S', 'y'):	/* BSD mandoc */
3597 			/* parse one line in bold */
3598 			out_html(change_to_font('B'));
3599 			trans_char(c, '"', '\a');
3600 			c = c + j;
3601 			if (*c == '\n')
3602 				c++;
3603 			c = scan_troff_mandoc(c, 1, NULL);
3604 			out_html(change_to_font('R'));
3605 			out_html(NEWLINE);
3606 			if (fillout)
3607 				curpos++;
3608 			else
3609 				curpos = 0;
3610 			break;
3611 		case V('D', 'v'):	/* BSD mandoc */
3612 		case V('E', 'v'):	/* BSD mandoc */
3613 		case V('F', 'r'):	/* BSD mandoc */
3614 		case V('L', 'i'):	/* BSD mandoc */
3615 		case V('N', 'o'):	/* BSD mandoc */
3616 		case V('N', 's'):	/* BSD mandoc */
3617 		case V('T', 'n'):	/* BSD mandoc */
3618 		case V('n', 'N'):	/* BSD mandoc */
3619 			trans_char(c, '"', '\a');
3620 			c = c + j;
3621 			if (*c == '\n')
3622 				c++;
3623 			out_html(change_to_font('B'));
3624 			c = scan_troff_mandoc(c, 1, NULL);
3625 			out_html(change_to_font('R'));
3626 			out_html(NEWLINE);
3627 			if (fillout)
3628 				curpos++;
3629 			else
3630 				curpos = 0;
3631 			break;
3632 		case V('%', 'A'):	/* BSD mandoc biblio stuff */
3633 		case V('%', 'D'):
3634 		case V('%', 'N'):
3635 		case V('%', 'O'):
3636 		case V('%', 'P'):
3637 		case V('%', 'Q'):
3638 		case V('%', 'V'):
3639 			c = c + j;
3640 			if (*c == '\n')
3641 				c++;
3642 			c = scan_troff(c, 1, NULL);	/* Don't allow embedded
3643 							 * mandoc coms */
3644 			if (fillout)
3645 				curpos++;
3646 			else
3647 				curpos = 0;
3648 			break;
3649 		case V('%', 'B'):
3650 		case V('%', 'J'):
3651 		case V('%', 'R'):
3652 		case V('%', 'T'):
3653 			c = c + j;
3654 			out_html(change_to_font('I'));
3655 			if (*c == '\n')
3656 				c++;
3657 			c = scan_troff(c, 1, NULL);	/* Don't allow embedded
3658 							 * mandoc coms */
3659 			out_html(change_to_font('R'));
3660 			if (fillout)
3661 				curpos++;
3662 			else
3663 				curpos = 0;
3664 			break;
3665 		default:
3666 			/* search macro database of self-defined macros */
3667 			owndef = defdef;
3668 			while (owndef && owndef->nr != i)
3669 				owndef = owndef->next;
3670 			if (owndef) {
3671 				char  **oldargument;
3672 				int     deflen;
3673 				int     onff;
3674 
3675 				sl = fill_words(c + j, wordlist, &words);
3676 				c = sl + 1;
3677 				*sl = '\0';
3678 				for (i = 1; i < words; i++)
3679 					wordlist[i][-1] = '\0';
3680 				for (i = 0; i < words; i++) {
3681 					char   *h = NULL;
3682 
3683 					if (mandoc_command) {
3684 						scan_troff_mandoc(wordlist[i], 1, &h);
3685 					} else {
3686 						scan_troff(wordlist[i], 1, &h);
3687 					}
3688 					wordlist[i] = h;
3689 				}
3690 				for (i = words; i < 20; i++)
3691 					wordlist[i] = NULL;
3692 				deflen = strlen(owndef->st);
3693 				for (i = 0; owndef->st[deflen + 2 + i] = owndef->st[i]; i++);
3694 				oldargument = argument;
3695 				argument = wordlist;
3696 				onff = newline_for_fun;
3697 				if (mandoc_command) {
3698 					scan_troff_mandoc(owndef->st + deflen + 2, 0, NULL);
3699 				} else {
3700 					scan_troff(owndef->st + deflen + 2, 0, NULL);
3701 				}
3702 				newline_for_fun = onff;
3703 				argument = oldargument;
3704 				for (i = 0; i < words; i++)
3705 					if (wordlist[i])
3706 						free(wordlist[i]);
3707 				*sl = '\n';
3708 			} else if (mandoc_command &&
3709 				   ((isupper(*c) && islower(*(c + 1)))
3710 				    || (islower(*c) && isupper(*(c + 1))))
3711 				) {	/* Let through any BSD mandoc
3712 					 * commands that haven't been delt
3713 					 * with. I don't want to miss
3714 					 * anything out of the text. */
3715 				char    buf[4];
3716 
3717 				strncpy(buf, c, 2);
3718 				buf[2] = ' ';
3719 				buf[3] = '\0';
3720 				out_html(buf);	/* Print the command (it
3721 						 * might just be text). */
3722 				c = c + j;
3723 				trans_char(c, '"', '\a');
3724 				if (*c == '\n')
3725 					c++;
3726 				out_html(change_to_font('R'));
3727 				c = scan_troff(c, 1, NULL);
3728 				out_html(NEWLINE);
3729 				if (fillout)
3730 					curpos++;
3731 				else
3732 					curpos = 0;
3733 			} else {
3734 				c = skip_till_newline(c);
3735 			}
3736 			break;
3737 		}
3738 	}
3739 	if (fillout) {
3740 		out_html(NEWLINE);
3741 		curpos++;
3742 	}
3743 	NEWLINE[0] = '\n';
3744 	return c;
3745 }
3746 
3747 static void
flush(void)3748 flush(void)
3749 {
3750 }
3751 
3752 static int contained_tab = 0;
3753 static int mandoc_line = 0;	/* Signals whether to look for embedded
3754 				 * mandoc commands. */
3755 
3756 /* san : stop at newline */
3757 static char *
scan_troff(char * c,int san,char ** result)3758 scan_troff(char *c, int san, char **result)
3759 {
3760 	char   *h;
3761 	char    intbuff[NULL_TERMINATED(MED_STR_MAX)];
3762 	int     ibp = 0;
3763 	int     i;
3764 	char   *exbuffer;
3765 	int     exbuffpos, exbuffmax, exscaninbuff, exnewline_for_fun;
3766 	int     usenbsp = 0;
3767 
3768 #define FLUSHIBP  if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; }
3769 
3770 	exbuffer = buffer;
3771 	exbuffpos = buffpos;
3772 	exbuffmax = buffmax;
3773 	exnewline_for_fun = newline_for_fun;
3774 	exscaninbuff = scaninbuff;
3775 	newline_for_fun = 0;
3776 	if (result) {
3777 		if (*result) {
3778 			buffer = *result;
3779 			buffpos = strlen(buffer);
3780 			buffmax = buffpos;
3781 		} else {
3782 			buffer = stralloc(LARGE_STR_MAX);
3783 			buffpos = 0;
3784 			buffmax = LARGE_STR_MAX;
3785 		}
3786 		scaninbuff = 1;
3787 	}
3788 	h = c;
3789 	/* start scanning */
3790 
3791 	while (*h && (!san || newline_for_fun || *h != '\n')) {
3792 
3793 		if (*h == escapesym) {
3794 			h++;
3795 			FLUSHIBP;
3796 			h = scan_escape(h);
3797 		} else if (*h == controlsym && h[-1] == '\n') {
3798 			h++;
3799 			FLUSHIBP;
3800 			h = scan_request(h);
3801 			if (san && h[-1] == '\n')
3802 				h--;
3803 		} else if (mandoc_line
3804 			   && *(h) && isupper(*(h))
3805 			   && *(h + 1) && islower(*(h + 1))
3806 			   && *(h + 2) && isspace(*(h + 2))) {
3807 			/*
3808 			 * BSD embedded command eg ".It Fl Ar arg1 Fl Ar
3809 			 * arg2"
3810 			 */
3811 			FLUSHIBP;
3812 			h = scan_request(h);
3813 			if (san && h[-1] == '\n')
3814 				h--;
3815 		} else if (*h == nobreaksym && h[-1] == '\n') {
3816 			h++;
3817 			FLUSHIBP;
3818 			h = scan_request(h);
3819 			if (san && h[-1] == '\n')
3820 				h--;
3821 		} else {
3822 			int     mx;
3823 
3824 			if (h[-1] == '\n' && still_dd && isalnum(*h)) {
3825 				/*
3826 				 * sometimes a .HP request is not followed by
3827 				 * a .br request
3828 				 */
3829 				FLUSHIBP;
3830 				out_html("<DD>");
3831 				curpos = 0;
3832 				still_dd = 0;
3833 			}
3834 			switch (*h) {
3835 			case '&':
3836 				intbuff[ibp++] = '&';
3837 				intbuff[ibp++] = 'a';
3838 				intbuff[ibp++] = 'm';
3839 				intbuff[ibp++] = 'p';
3840 				intbuff[ibp++] = ';';
3841 				curpos++;
3842 				break;
3843 			case '<':
3844 				intbuff[ibp++] = '&';
3845 				intbuff[ibp++] = 'l';
3846 				intbuff[ibp++] = 't';
3847 				intbuff[ibp++] = ';';
3848 				curpos++;
3849 				break;
3850 			case '>':
3851 				intbuff[ibp++] = '&';
3852 				intbuff[ibp++] = 'g';
3853 				intbuff[ibp++] = 't';
3854 				intbuff[ibp++] = ';';
3855 				curpos++;
3856 				break;
3857 			case '"':
3858 				intbuff[ibp++] = '&';
3859 				intbuff[ibp++] = 'q';
3860 				intbuff[ibp++] = 'u';
3861 				intbuff[ibp++] = 'o';
3862 				intbuff[ibp++] = 't';
3863 				intbuff[ibp++] = ';';
3864 				curpos++;
3865 				break;
3866 			case '\n':
3867 				if (h[-1] == '\n' && fillout) {
3868 					intbuff[ibp++] = '<';
3869 					intbuff[ibp++] = 'P';
3870 					intbuff[ibp++] = '>';
3871 				}
3872 				if (contained_tab && fillout) {
3873 					intbuff[ibp++] = '<';
3874 					intbuff[ibp++] = 'B';
3875 					intbuff[ibp++] = 'R';
3876 					intbuff[ibp++] = '>';
3877 				}
3878 				contained_tab = 0;
3879 				curpos = 0;
3880 				usenbsp = 0;
3881 				intbuff[ibp++] = '\n';
3882 				break;
3883 			case '\t':
3884 				{
3885 					int     curtab = 0;
3886 
3887 					contained_tab = 1;
3888 					FLUSHIBP;
3889 					/* like a typewriter, not like TeX */
3890 					tabstops[19] = curpos + 1;
3891 					while (curtab < maxtstop && tabstops[curtab] <= curpos)
3892 						curtab++;
3893 					if (curtab < maxtstop) {
3894 						if (!fillout) {
3895 							while (curpos < tabstops[curtab]) {
3896 								intbuff[ibp++] = ' ';
3897 								if (ibp > 480) {
3898 									FLUSHIBP;
3899 								}
3900 								curpos++;
3901 							}
3902 						} else {
3903 							out_html("<TT>");
3904 							while (curpos < tabstops[curtab]) {
3905 								out_html("&nbsp;");
3906 								curpos++;
3907 							}
3908 							out_html("</TT>");
3909 						}
3910 					}
3911 				}
3912 				break;
3913 			default:
3914 				if (*h == ' ' && (h[-1] == '\n' || usenbsp)) {
3915 					FLUSHIBP;
3916 					if (!usenbsp && fillout) {
3917 						out_html("<BR>");
3918 						curpos = 0;
3919 					}
3920 					usenbsp = fillout;
3921 					if (usenbsp)
3922 						out_html("&nbsp;");
3923 					else
3924 						intbuff[ibp++] = ' ';
3925 				} else if (*h > 31 && *h < 127)
3926 					intbuff[ibp++] = *h;
3927 				else if (((unsigned char) (*h)) > 127) {
3928 					intbuff[ibp++] = '&';
3929 					intbuff[ibp++] = '#';
3930 					intbuff[ibp++] = '0' + ((unsigned char) (*h)) / 100;
3931 					intbuff[ibp++] = '0' + (((unsigned char) (*h)) % 100) / 10;
3932 					intbuff[ibp++] = '0' + ((unsigned char) (*h)) % 10;
3933 					intbuff[ibp++] = ';';
3934 				}
3935 				curpos++;
3936 				break;
3937 			}
3938 			if (ibp > (MED_STR_MAX - 20))
3939 				FLUSHIBP;
3940 			h++;
3941 		}
3942 	}
3943 	FLUSHIBP;
3944 	if (buffer)
3945 		buffer[buffpos] = '\0';
3946 	if (san && *h)
3947 		h++;
3948 	newline_for_fun = exnewline_for_fun;
3949 	if (result) {
3950 		*result = buffer;
3951 		buffer = exbuffer;
3952 		buffpos = exbuffpos;
3953 		buffmax = exbuffmax;
3954 		scaninbuff = exscaninbuff;
3955 	}
3956 	return h;
3957 }
3958 
3959 
3960 static char *
scan_troff_mandoc(char * c,int san,char ** result)3961 scan_troff_mandoc(char *c, int san, char **result)
3962 {
3963 	char   *ret, *end = c;
3964 	int     oldval = mandoc_line;
3965 
3966 	mandoc_line = 1;
3967 	while (*end && *end != '\n') {
3968 		end++;
3969 	}
3970 
3971 	if (end > c + 2
3972 	    && ispunct(*(end - 1))
3973 	    && isspace(*(end - 2)) && *(end - 2) != '\n') {
3974 		/*
3975 		 * Don't format lonely punctuation E.g. in "xyz ," format the
3976 		 * xyz and then append the comma removing the space.
3977 		 */
3978 		*(end - 2) = '\n';
3979 		ret = scan_troff(c, san, result);
3980 		*(end - 2) = *(end - 1);
3981 		*(end - 1) = ' ';
3982 	} else {
3983 		ret = scan_troff(c, san, result);
3984 	}
3985 	mandoc_line = oldval;
3986 	return ret;
3987 }
3988 
3989 int
main(int argc,char ** argv)3990 main(int argc, char **argv)
3991 {
3992 	FILE   *f;
3993 	char   *t;
3994 	int     l, i;
3995 	char   *buf;
3996 	char   *h, *fullname;
3997 	STRDEF *stdf;
3998 
3999 	t = NULL;
4000 	while ((i = getopt(argc, argv, "")) != EOF) {
4001 		switch (i) {
4002 		default:
4003 			usage();
4004 			exit(EXIT_USAGE);
4005 		}
4006 	}
4007 
4008 	if (argc != 2) {
4009 		usage();
4010 		exit(EXIT_USAGE);
4011 	}
4012 	manpage = h = t = argv[1];
4013 	i = 0;
4014 
4015 	buf = read_man_page(h);
4016 	if (!buf) {
4017 		fprintf(stderr, "man2html: cannot read %s: %s\n", h, strerror(errno));
4018 		exit(1);
4019 	}
4020 #ifdef MAKEINDEX
4021 	idxfile = fopen(INDEXFILE, "a");
4022 #endif
4023 	stdf = &standardchar[0];
4024 	i = 0;
4025 	while (stdf->nr) {
4026 		stdf->next = &standardchar[i];
4027 		stdf = stdf->next;
4028 		i++;
4029 	}
4030 	chardef = &standardchar[0];
4031 
4032 	stdf = &standardstring[0];
4033 	i = 0;
4034 	while (stdf->nr) {
4035 		stdf->next = &standardstring[i];
4036 		stdf = stdf->next;
4037 		i++;
4038 	}
4039 	strdef = &standardstring[0];
4040 
4041 	intdef = &standardint[0];
4042 	i = 0;
4043 	while (intdef->nr) {
4044 		intdef->next = &standardint[i];
4045 		intdef = intdef->next;
4046 		i++;
4047 	}
4048 	intdef = &standardint[0];
4049 
4050 	defdef = NULL;
4051 
4052 	scan_troff(buf + 1, 0, NULL);
4053 
4054 	while (itemdepth || dl_set[itemdepth]) {
4055 		out_html("</DL>\n");
4056 		if (dl_set[itemdepth])
4057 			dl_set[itemdepth] = 0;
4058 		else if (itemdepth > 0)
4059 			itemdepth--;
4060 	}
4061 
4062 	out_html(change_to_font(0));
4063 	out_html(change_to_size(0));
4064 	if (!fillout) {
4065 		fillout = 1;
4066 		out_html("</PRE>");
4067 	}
4068 	out_html(NEWLINE);
4069 
4070 	if (output_possible) {
4071 		outputPageFooter(th_version, th_datestr, th_page_and_sec);
4072 		/* &nbsp; for mosaic users */
4073 		fputs("<HR>\n<A NAME=\"index\">&nbsp;</A><H2>Index</H2>\n<DL>\n", stdout);
4074 		manidx[mip] = 0;
4075 		fputs(manidx, stdout);
4076 		if (subs)
4077 			fputs("</DL>\n", stdout);
4078 		fputs("</DL>\n", stdout);
4079 		print_sig();
4080 		fputs("</BODY>\n</HTML>\n", stdout);
4081 	} else
4082 		fprintf(stderr, "man2html: no output produced\n");
4083 #ifdef MAKEINDEX
4084 	if (idxfile)
4085 		fclose(idxfile);
4086 #endif
4087 	exit(EXIT_SUCCESS);
4088 }
4089