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