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