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