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