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