2 * This program was written by Richard Verhoeven (NL:5482ZX35)
3 * at the Eindhoven University of Technology. Email: rcb5@win.tue.nl
5 * Permission is granted to distribute, modify and use this program as long
6 * as this comment is not removed or changed.
8 * THIS IS A MODIFIED VERSION. IT WAS MODIFIED BY chet@po.cwru.edu FOR
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:
17 * Recognition Item Link
18 * ----------------------------------------------------------
19 * name(*) Manpage ../man?/name.*
21 * name@hostname Email address mailto:name@hostname
23 * method://string URL method://string
25 * www.host.name WWW server http://www.host.name
27 * ftp.host.name FTP server ftp://ftp.host.name
29 * <file.h> Include file file:/usr/include/file.h
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.
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).
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.
46 * Known bugs and missing features:
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.
55 * -DNROFF=0 troff mode
56 * -DNROFF=1 nroff mode (default)
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.)
76 #include <sys/types.h>
81 #define NULL_TERMINATED(n) ((n) + 1)
83 #define HUGE_STR_MAX 10000
84 #define LARGE_STR_MAX 2000
85 #define MED_STR_MAX 500
86 #define SMALL_STR_MAX 100
87 #define TINY_STR_MAX 10
89 #define MAX_MAN_PATHS 100 /* Max number of directories */
90 #define MAX_ZCATS 10 /* Max number of zcat style programs */
91 #define MAX_WORDLIST 100
94 #define EXIT_SUCCESS 0
97 #define EXIT_FAILURE 1
103 static char location_base[NULL_TERMINATED(MED_STR_MAX)] = "";
105 char *signature = "<HR>\n"
106 "This document was created by man2html\n"
107 "using the manual pages.<BR>\n"
110 /* timeformat for signature */
111 #define TIMEFORMAT "%T GMT, %B %d, %Y"
113 /* BSD mandoc Bl/El lists to HTML list types */
114 #define BL_DESC_LIST 1
115 #define BL_BULLET_LIST 2
116 #define BL_ENUM_LIST 4
118 /* BSD mandoc Bd/Ed example(?) blocks */
122 #ifndef HAVE_STRERROR
126 static char emsg[40];
128 #if defined (HAVE_SYS_ERRLIST)
130 extern char *sys_errlist[];
132 if (e > 0 && e < sys_nerr)
133 return (sys_errlist[e]);
135 #endif /* HAVE_SYS_ERRLIST */
137 sprintf(emsg, "Unknown system error %d", e);
141 #endif /* !HAVE_STRERROR */
144 strgrow(char *old, int len)
146 char *new = realloc(old, (strlen(old) + len + 1) * sizeof(char));
149 fprintf(stderr, "man2html: out of memory");
158 /* allocate enough for len + NULL */
159 char *new = malloc((len + 1) * sizeof(char));
162 fprintf(stderr, "man2html: out of memory");
169 * Some systems don't have strdup so lets use our own - which can also
170 * check for out of memory.
173 strduplicate(char *from)
175 char *new = stralloc(strlen(from));
181 /* Assumes space for n plus a null */
183 strmaxcpy(char *to, char *from, int n)
185 int len = strlen(from);
187 strncpy(to, from, n);
188 to[(len <= n) ? len : n] = '\0';
193 strmaxcat(char *to, char *from, int n)
195 int to_len = strlen(to);
198 int from_len = strlen(from);
199 int cp = (to_len + from_len <= n) ? from_len : n - to_len;
201 strncpy(to + to_len, from, cp);
202 to[to_len + cp] = '\0';
207 /* Assumes space for limit plus a null */
209 strlimitcpy(char *to, char *from, int n, int limit)
211 int len = n > limit ? limit : n;
213 strmaxcpy(to, from, len);
219 * takes string and escapes all metacharacters. should be used before
220 * including string in system() or similar call.
223 escape_input(char *str)
226 static char new[NULL_TERMINATED(MED_STR_MAX)];
228 if (strlen(str) * 2 + 1 > MED_STR_MAX) {
230 "man2html: escape_input - str too long:\n%-80s...\n",
234 for (i = 0; i < strlen(str); i++) {
235 if (!(((str[i] >= 'A') && (str[i] <= 'Z')) ||
236 ((str[i] >= 'a') && (str[i] <= 'z')) ||
237 ((str[i] >= '0') && (str[i] <= '9')))) {
251 fprintf(stderr, "man2html: usage: man2html filename\n");
257 * below this you should not change anything unless you know a lot
258 * about this program or about troff.
261 typedef struct STRDEF STRDEF;
268 typedef struct INTDEF INTDEF;
276 static char NEWLINE[2] = "\n";
277 static char idxlabel[6] = "ixAAA";
279 #define INDEXFILE "/tmp/manindex.list"
282 static FILE *idxfile;
284 static STRDEF *chardef, *strdef, *defdef;
285 static INTDEF *intdef;
287 #define V(A,B) ((A)*256+(B))
289 static INTDEF standardint[] = {
290 {V('n', ' '), NROFF, 0, NULL},
291 {V('t', ' '), 1 - NROFF, 0, NULL},
292 {V('o', ' '), 1, 0, NULL},
293 {V('e', ' '), 0, 0, NULL},
294 {V('.', 'l'), 70, 0, NULL},
295 {V('.', '$'), 0, 0, NULL},
296 {V('.', 'A'), NROFF, 0, NULL},
297 {V('.', 'T'), 1 - NROFF, 0, NULL},
298 {V('.', 'V'), 1, 0, NULL}, /* the me package tests for this */
301 static STRDEF standardstring[] = {
302 {V('R', ' '), 1, "®", NULL},
303 {V('l', 'q'), 2, "``", NULL},
304 {V('r', 'q'), 2, "''", NULL},
309 static STRDEF standardchar[] = {
310 {V('*', '*'), 1, "*", NULL},
311 {V('*', 'A'), 1, "A", NULL},
312 {V('*', 'B'), 1, "B", NULL},
313 {V('*', 'C'), 2, "Xi", NULL},
314 {V('*', 'D'), 5, "Delta", NULL},
315 {V('*', 'E'), 1, "E", NULL},
316 {V('*', 'F'), 3, "Phi", NULL},
317 {V('*', 'G'), 5, "Gamma", NULL},
318 {V('*', 'H'), 5, "Theta", NULL},
319 {V('*', 'I'), 1, "I", NULL},
320 {V('*', 'K'), 1, "K", NULL},
321 {V('*', 'L'), 6, "Lambda", NULL},
322 {V('*', 'M'), 1, "M", NULL},
323 {V('*', 'N'), 1, "N", NULL},
324 {V('*', 'O'), 1, "O", NULL},
325 {V('*', 'P'), 2, "Pi", NULL},
326 {V('*', 'Q'), 3, "Psi", NULL},
327 {V('*', 'R'), 1, "P", NULL},
328 {V('*', 'S'), 5, "Sigma", NULL},
329 {V('*', 'T'), 1, "T", NULL},
330 {V('*', 'U'), 1, "Y", NULL},
331 {V('*', 'W'), 5, "Omega", NULL},
332 {V('*', 'X'), 1, "X", NULL},
333 {V('*', 'Y'), 1, "H", NULL},
334 {V('*', 'Z'), 1, "Z", NULL},
335 {V('*', 'a'), 5, "alpha", NULL},
336 {V('*', 'b'), 4, "beta", NULL},
337 {V('*', 'c'), 2, "xi", NULL},
338 {V('*', 'd'), 5, "delta", NULL},
339 {V('*', 'e'), 7, "epsilon", NULL},
340 {V('*', 'f'), 3, "phi", NULL},
341 {V('*', 'g'), 5, "gamma", NULL},
342 {V('*', 'h'), 5, "theta", NULL},
343 {V('*', 'i'), 4, "iota", NULL},
344 {V('*', 'k'), 5, "kappa", NULL},
345 {V('*', 'l'), 6, "lambda", NULL},
346 {V('*', 'm'), 1, "µ", NULL},
347 {V('*', 'n'), 2, "nu", NULL},
348 {V('*', 'o'), 1, "o", NULL},
349 {V('*', 'p'), 2, "pi", NULL},
350 {V('*', 'q'), 3, "psi", NULL},
351 {V('*', 'r'), 3, "rho", NULL},
352 {V('*', 's'), 5, "sigma", NULL},
353 {V('*', 't'), 3, "tau", NULL},
354 {V('*', 'u'), 7, "upsilon", NULL},
355 {V('*', 'w'), 5, "omega", NULL},
356 {V('*', 'x'), 3, "chi", NULL},
357 {V('*', 'y'), 3, "eta", NULL},
358 {V('*', 'z'), 4, "zeta", NULL},
359 {V('t', 's'), 5, "sigma", NULL},
360 {V('+', '-'), 1, "±", NULL},
361 {V('1', '2'), 1, "½", NULL},
362 {V('1', '4'), 1, "¼", NULL},
363 {V('3', '4'), 1, "¾", NULL},
364 {V('F', 'i'), 3, "ffi", NULL},
365 {V('F', 'l'), 3, "ffl", NULL},
366 {V('a', 'a'), 1, "´", NULL},
367 {V('a', 'p'), 1, "~", NULL},
368 {V('b', 'r'), 1, "|", NULL},
369 {V('b', 'u'), 1, "*", NULL},
370 {V('b', 'v'), 1, "|", NULL},
371 {V('c', 'i'), 1, "o", NULL},
372 {V('c', 'o'), 1, "©", NULL},
373 {V('c', 't'), 1, "¢", NULL},
374 {V('d', 'e'), 1, "°", NULL},
375 {V('d', 'g'), 1, "+", NULL},
376 {V('d', 'i'), 1, "÷", NULL},
377 {V('e', 'm'), 1, "-", NULL},
378 {V('e', 'm'), 3, "---", NULL},
379 {V('e', 'q'), 1, "=", NULL},
380 {V('e', 's'), 1, "Ø", NULL},
381 {V('f', 'f'), 2, "ff", NULL},
382 {V('f', 'i'), 2, "fi", NULL},
383 {V('f', 'l'), 2, "fl", NULL},
384 {V('f', 'm'), 1, "´", NULL},
385 {V('g', 'a'), 1, "`", NULL},
386 {V('h', 'y'), 1, "-", NULL},
387 {V('l', 'c'), 2, "|¯", NULL},
388 {V('l', 'f'), 2, "|_", NULL},
389 {V('l', 'k'), 1, "<FONT SIZE=+2>{</FONT>", NULL},
390 {V('m', 'i'), 1, "-", NULL},
391 {V('m', 'u'), 1, "×", NULL},
392 {V('n', 'o'), 1, "¬", NULL},
393 {V('o', 'r'), 1, "|", NULL},
394 {V('p', 'l'), 1, "+", NULL},
395 {V('r', 'c'), 2, "¯|", NULL},
396 {V('r', 'f'), 2, "_|", NULL},
397 {V('r', 'g'), 1, "®", NULL},
398 {V('r', 'k'), 1, "<FONT SIZE=+2>}</FONT>", NULL},
399 {V('r', 'n'), 1, "¯", NULL},
400 {V('r', 'u'), 1, "_", NULL},
401 {V('s', 'c'), 1, "§", NULL},
402 {V('s', 'l'), 1, "/", NULL},
403 {V('s', 'q'), 2, "[]", NULL},
404 {V('u', 'l'), 1, "_", NULL},
408 /* default: print code */
411 static char eqndelimopen = 0, eqndelimclose = 0;
412 static char escapesym = '\\', nobreaksym = '\'', controlsym = '.', fieldsym = 0, padsym = 0;
414 static char *buffer = NULL;
415 static int buffpos = 0, buffmax = 0;
416 static int scaninbuff = 0;
417 static int itemdepth = 0;
418 static int dl_set[20] = {0};
419 static int still_dd = 0;
420 static int tabstops[20] = {8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96};
421 static int maxtstop = 12;
422 static int curpos = 0;
424 static char *scan_troff(char *c, int san, char **result);
425 static char *scan_troff_mandoc(char *c, int san, char **result);
427 static char **argument = NULL;
429 static char charb[TINY_STR_MAX];
434 char datbuf[NULL_TERMINATED(MED_STR_MAX)];
440 timetm = gmtime(&clock);
441 strftime(datbuf, MED_STR_MAX, TIMEFORMAT, timetm);
442 printf(signature, datbuf);
462 if (charb[0] == '<') { /* Fix up <= */
464 strncpy(charb, "<", 4);
472 expand_string(int nr)
488 read_man_page(char *filename)
490 char *man_buf = NULL;
492 FILE *man_stream = NULL;
496 if (stat(filename, &stbuf) == -1)
499 buf_size = stbuf.st_size;
500 man_buf = stralloc(buf_size + 5);
501 man_stream = fopen(filename, "r");
504 if (fread(man_buf + 1, 1, buf_size, man_stream) == buf_size) {
505 man_buf[buf_size] = '\n';
506 man_buf[buf_size + 1] = man_buf[buf_size + 2] = '\0';
516 static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)];
518 static int no_newline_output = 0;
519 static int newline_for_fun = 0;
520 static int output_possible = 0;
521 static int out_length = 0;
524 * Add the links to the output. At the moment the following are
528 * name(*) -> ../man?/name.*
530 * method://string -> method://string
531 * www.host.name -> http://www.host.name
532 * ftp.host.name -> ftp://ftp.host.name
533 * name@host -> mailto:name@host
534 * <name.h> -> file:/usr/include/name.h (guess)
536 * Other possible links to add in the future:
538 * /dir/dir/file -> file:/dir/dir/file
545 char *idtest[6]; /* url, mailto, www, ftp, manpage */
547 out_length += strlen(c);
548 /* search for (section) */
550 idtest[0] = strstr(c + 1, "://");
551 idtest[1] = strchr(c + 1, '@');
552 idtest[2] = strstr(c, "www.");
553 idtest[3] = strstr(c, "ftp.");
555 idtest[4] = strchr(c + 1, '(');
559 idtest[5] = strstr(c + 1, ".h>");
560 for (i = 0; i < 6; i++)
561 nr += (idtest[i] != NULL);
564 for (i = 0; i < 6; i++)
565 if (idtest[i] && (j < 0 || idtest[i] < idtest[j]))
568 case 5: /* <name.h> */
572 while (g > c && g[-1] != ';')
582 printf("<A HREF=\"file:/usr/include/%s\">%s</A>>", g, g);
591 case 4: /* manpage */
596 if (g && f - g < 6 && (isalnum(f[-1]) || f[-1] == '>') &&
597 ((isdigit(f[1]) && f[1] != '0' &&
598 (f[2] == ')' || (isalpha(f[2]) && f[3] == ')') || f[2] == 'X')) ||
599 (f[2] == ')' && (f[1] == 'n' || f[1] == 'l')))) {
600 /* this might be a link */
602 /* skip html makeup */
603 while (h > c && *h == '>') {
604 while (h != c && *h != '<')
610 char t, sec, subsec, *e;
615 if ((subsec == 'X' && f[3] != ')') || subsec == ')')
617 while (h > c && (isalnum(h[-1]) || h[-1] == '_' ||
618 h[-1] == '-' || h[-1] == '.'))
629 "?man%c/%s.%c%c\">%s</A>",
630 sec, h, sec, tolower(subsec), h);
634 "?man%c/%s.%c\">%s</A>",
650 while (*g && (isalnum(*g) || *g == '_' || *g == '-' || *g == '+' ||
664 printf("<A HREF=\"%s://%s\">%s</A>", (j == 3 ? "ftp" : "http"),
677 while (g > c && (isalnum(g[-1]) || g[-1] == '_' || g[-1] == '-' ||
678 g[-1] == '+' || g[-1] == '.' || g[-1] == '%'))
681 while (*h && (isalnum(*h) || *h == '_' || *h == '-' || *h == '+' ||
686 if (h - f > 4 && f - g > 1) {
695 printf("<A HREF=\"mailto:%s\">%s</A>", g, g);
708 while (g > c && isalpha(g[-1]) && islower(g[-1]))
711 while (*h && !isspace(*h) && *h != '<' && *h != '>' && *h != '"' &&
714 if (f - g > 2 && f - g < 7 && h - f > 3) {
723 printf("<A HREF=\"%s\">%s</A>", g, g);
737 if (idtest[0] && idtest[0] < c)
738 idtest[0] = strstr(c + 1, "://");
739 if (idtest[1] && idtest[1] < c)
740 idtest[1] = strchr(c + 1, '@');
741 if (idtest[2] && idtest[2] < c)
742 idtest[2] = strstr(c, "www.");
743 if (idtest[3] && idtest[3] < c)
744 idtest[3] = strstr(c, "ftp.");
745 if (idtest[4] && idtest[4] < c)
746 idtest[4] = strchr(c + 1, '(');
747 if (idtest[5] && idtest[5] < c)
748 idtest[5] = strstr(c + 1, ".h>");
749 for (i = 0; i < 6; i++)
750 nr += (idtest[i] != NULL);
755 static int current_font = 0;
756 static int current_size = 0;
757 static int fillout = 1;
764 if (no_newline_output) {
767 no_newline_output = 1;
769 if (!no_newline_output)
772 no_newline_output = 1;
775 if (!no_newline_output)
780 if (buffpos >= buffmax) {
783 h = realloc(buffer, buffmax * 2);
789 buffer[buffpos++] = *c++;
791 } else if (output_possible) {
793 outbuffer[obp++] = *c;
794 if (*c == '\n' || obp > HUGE_STR_MAX) {
795 outbuffer[obp] = '\0';
796 add_links(outbuffer);
813 static char *switchfont[16] = {
814 "", FC0 FO1, FC0 FO2, FC0 FO3,
815 FC1 FO0, "", FC1 FO2, FC1 FO3,
816 FC2 FO0, FC2 FO1, "", FC2 FO3,
817 FC3 FO0, FC3 FO1, FC3 FO2, ""
821 change_to_font(int nr)
859 i = current_font * 4 + nr % 4;
860 current_font = nr % 4;
861 return switchfont[i];
864 static char sizebuf[200];
867 change_to_size(int nr)
887 nr = current_size + nr;
894 if (nr == current_size)
898 strcat(sizebuf, change_to_font(0));
900 strcat(sizebuf, "</FONT>");
905 strcat(sizebuf, "<FONT SIZE=");
910 sizebuf[l++] = '-', nr = -nr;
911 sizebuf[l++] = nr + '0';
915 strcat(sizebuf, change_to_font(i));
919 static int asint = 0;
920 static int intresult = 0;
922 #define SKIPEOL while (*c && *c++!='\n')
924 static int skip_escape = 0;
925 static int single_escape = 0;
933 int exoutputp, exskipescape;
959 if (!(h = argument[i]))
966 c = scan_escape(c + 1);
992 i = c[0] * 256 + c[1];
1000 i = c[0] * 256 + c[1];
1004 h = expand_string(i);
1013 } else if (*c != '(')
1017 i = c[0] * 256 + c[1];
1021 h = change_to_font(i);
1032 } else if (*c == '+') {
1038 else if (*c == '\\') {
1045 while (isdigit(*c) && (!i || (!j && i < 4)))
1046 i = i * 10 + (*c++) - '0';
1053 h = change_to_size(i * j);
1081 while (intd && intd->nr != i)
1084 intd->val = intd->val + j * intd->incr;
1085 intresult = intd->val;
1089 intresult = current_size;
1092 intresult = current_font;
1105 exoutputp = output_possible;
1106 exskipescape = skip_escape;
1107 output_possible = 0;
1112 if (*c == escapesym)
1113 c = scan_escape(c + 1);
1117 output_possible = exoutputp;
1118 skip_escape = exskipescape;
1133 exoutputp = output_possible;
1134 exskipescape = skip_escape;
1135 output_possible = 0;
1138 if (*c == escapesym)
1139 c = scan_escape(c + 1);
1142 output_possible = exoutputp;
1143 skip_escape = exskipescape;
1146 no_newline_output = 1;
1153 if (newline_for_fun)
1163 curpos = (curpos + 8) & 0xfff8;
1174 if (single_escape) {
1191 typedef struct TABLEITEM TABLEITEM;
1195 int size, align, valign, colspan, rowspan, font, vleft, vright, space,
1200 static TABLEITEM emptyfield = {NULL, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, NULL};
1202 typedef struct TABLEROW TABLEROW;
1206 TABLEROW *prev, *next;
1209 static char *tableopt[] = {
1210 "center", "expand", "box", "allbox", "doublebox",
1211 "tab", "linesize", "delim", NULL
1213 static int tableoptl[] = {6, 6, 3, 6, 9, 3, 8, 5, 0};
1216 clear_table(TABLEROW * table)
1218 TABLEROW *tr1, *tr2;
1219 TABLEITEM *ti1, *ti2;
1229 free(ti1->contents);
1239 static char *scan_expression(char *c, int *result);
1242 scan_format(char *c, TABLEROW ** result, int *maxcol)
1244 TABLEROW *layout, *currow;
1245 TABLEITEM *curfield;
1249 clear_table(*result);
1251 layout = currow = (TABLEROW *) malloc(sizeof(TABLEROW));
1252 currow->next = currow->prev = NULL;
1253 currow->first = curfield = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1254 *curfield = emptyfield;
1255 while (*c && *c != '.') {
1271 if (curfield->align) {
1272 curfield->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1273 curfield = curfield->next;
1274 *curfield = emptyfield;
1276 curfield->align = toupper(*c);
1283 curfield->font = toupper(*c);
1289 curfield->font = toupper(*c);
1296 curfield->valign = 't';
1312 i = i * 10 + (*c++) - '0';
1314 curfield->size = i * j;
1316 curfield->size = j - 10;
1322 c = scan_expression(c + 2, &curfield->width);
1325 if (curfield->align)
1347 i = i * 10 + (*c++) - '0';
1348 curfield->space = i;
1352 currow->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1353 currow->next->prev = currow;
1354 currow = currow->next;
1355 currow->next = NULL;
1356 curfield = currow->first = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1357 *curfield = emptyfield;
1366 while (*c++ != '\n');
1370 curfield = layout->first;
1374 curfield = curfield->next;
1378 currow = currow->next;
1385 next_row(TABLEROW * tr)
1393 TABLEITEM *ti, *ti2;
1395 tr->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1396 tr->next->prev = tr;
1401 tr->first = ti2 = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1403 tr->first = ti2 = NULL;
1406 ti2->contents = NULL;
1407 if ((ti = ti->next)) {
1408 ti2->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1416 static char itemreset[20] = "\\fR\\s0";
1422 int center = 0, expand = 0, box = 0, border = 0, linesize = 1;
1423 int i, j, maxcol = 0, finished = 0;
1424 int oldfont, oldsize, oldfillout;
1425 char itemsep = '\t';
1426 TABLEROW *layout = NULL, *currow, *ftable;
1427 TABLEITEM *curfield;
1429 while (*c++ != '\n');
1433 oldfont = current_font;
1434 oldsize = current_size;
1435 oldfillout = fillout;
1436 out_html(change_to_font(0));
1437 out_html(change_to_size(0));
1442 while (*h && *h != '\n')
1445 /* scan table options */
1449 for (i = 0; tableopt[i] && strncmp(tableopt[i], c, tableoptl[i]); i++);
1450 c = c + tableoptl[i];
1468 while (*c++ != '(');
1472 while (*c++ != '(');
1475 linesize = linesize * 10 + (*c++) - '0';
1488 c = scan_format(c, &layout, &maxcol);
1491 curfield = layout->first;
1496 if ((*c == '_' || *c == '=') && (c[1] == itemsep || c[1] == '\n')) {
1497 if (c[-1] == '\n' && c[1] == '\n') {
1499 currow->prev->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1500 currow->prev->next->next = currow;
1501 currow->prev->next->prev = currow->prev;
1502 currow->prev = currow->prev->next;
1504 currow->prev = layout = (TABLEROW *) malloc(sizeof(TABLEROW));
1505 currow->prev->prev = NULL;
1506 currow->prev->next = currow;
1508 curfield = currow->prev->first =
1509 (TABLEITEM *) malloc(sizeof(TABLEITEM));
1510 *curfield = emptyfield;
1511 curfield->align = *c;
1512 curfield->colspan = maxcol;
1513 curfield = currow->first;
1517 curfield->align = *c;
1519 curfield = curfield->next;
1520 } while (curfield && curfield->align == 'S');
1523 currow = next_row(currow);
1524 curfield = currow->first;
1528 } else if (*c == 'T' && c[1] == '{') {
1530 c = strstr(h, "\nT}");
1534 scan_troff(h, 0, &g);
1535 scan_troff(itemreset, 0, &g);
1539 curfield->contents = g;
1541 curfield = curfield->next;
1542 } while (curfield && curfield->align == 'S');
1545 if (c[-1] == '\n') {
1546 currow = next_row(currow);
1547 curfield = currow->first;
1549 } else if (*c == '.' && c[1] == 'T' && c[2] == '&' && c[-1] == '\n') {
1552 while (*c++ != '\n');
1554 currow = currow->prev;
1556 c = scan_format(c, &hr, &i);
1561 curfield = currow->first;
1562 } else if (*c == '.' && c[1] == 'T' && c[2] == 'E' && c[-1] == '\n') {
1564 while (*c++ != '\n');
1566 currow->prev->next = NULL;
1567 currow->prev = NULL;
1568 clear_table(currow);
1569 } else if (*c == '.' && c[-1] == '\n' && !isdigit(c[1])) {
1571 * skip troff request inside table (usually only .sp
1574 while (*c++ != '\n');
1577 while (*c && (*c != itemsep || c[-1] == '\\') &&
1578 (*c != '\n' || c[-1] == '\\'))
1581 if (*c == itemsep) {
1585 if (h[0] == '\\' && h[2] == '\n' &&
1586 (h[1] == '_' || h[1] == '^')) {
1588 curfield->align = h[1];
1590 curfield = curfield->next;
1591 } while (curfield && curfield->align == 'S');
1596 h = scan_troff(h, 1, &g);
1597 scan_troff(itemreset, 0, &g);
1599 curfield->contents = g;
1601 curfield = curfield->next;
1602 } while (curfield && curfield->align == 'S');
1609 if (c[-1] == '\n') {
1610 currow = next_row(currow);
1611 curfield = currow->first;
1615 /* calculate colspan and rowspan */
1617 while (currow->next)
1618 currow = currow->next;
1620 TABLEITEM *ti, *ti1 = NULL, *ti2 = NULL;
1624 ti1 = currow->prev->first;
1626 switch (ti->align) {
1630 if (ti2->rowspan < ti->rowspan)
1631 ti2->rowspan = ti->rowspan;
1643 } while (ti2 && curfield->align == 'S');
1651 currow = currow->prev;
1653 /* produce html output */
1655 out_html("<CENTER>");
1657 out_html("<TABLE BORDER><TR><TD>");
1659 if (box || border) {
1660 out_html(" BORDER");
1662 out_html("><TR><TD><TABLE");
1664 out_html(" WIDTH=100%");
1670 out_html("<TR VALIGN=top>");
1671 curfield = currow->first;
1673 if (curfield->align != 'S' && curfield->align != '^') {
1675 switch (curfield->align) {
1677 curfield->space += 4;
1679 out_html(" ALIGN=right");
1682 out_html(" ALIGN=center");
1686 if (!curfield->valign && curfield->rowspan > 1)
1687 out_html(" VALIGN=center");
1688 if (curfield->colspan > 1) {
1691 out_html(" COLSPAN=");
1692 sprintf(buf, "%i", curfield->colspan);
1695 if (curfield->rowspan > 1) {
1698 out_html(" ROWSPAN=");
1699 sprintf(buf, "%i", curfield->rowspan);
1702 j = j + curfield->colspan;
1705 out_html(change_to_size(curfield->size));
1707 out_html(change_to_font(curfield->font));
1708 switch (curfield->align) {
1710 out_html("<HR><HR>");
1716 if (curfield->contents)
1717 out_html(curfield->contents);
1720 if (curfield->space)
1721 for (i = 0; i < curfield->space; i++)
1724 out_html(change_to_font(0));
1726 out_html(change_to_size(0));
1727 if (j >= maxcol && curfield->align > '@' && curfield->align != '_')
1731 curfield = curfield->next;
1733 out_html("</TR>\n");
1734 currow = currow->next;
1737 out_html("</TABLE>");
1738 out_html("</TABLE>");
1740 out_html("</TABLE>");
1742 out_html("</CENTER>\n");
1747 fillout = oldfillout;
1748 out_html(change_to_size(oldsize));
1749 out_html(change_to_font(oldfont));
1754 scan_expression(char *c, int *result)
1756 int value = 0, value2, j = 0, sign = 1, opex = 0;
1760 c = scan_expression(c + 1, &value);
1762 } else if (*c == 'n') {
1765 } else if (*c == 't') {
1768 } else if (*c == '\'' || *c == '"' || *c < ' ' || (*c == '\\' && c[1] == '(')) {
1770 * ?string1?string2? test if string1 equals string2.
1772 char *st1 = NULL, *st2 = NULL, *h;
1783 while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1786 scan_troff(h, 1, &st1);
1792 while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1795 scan_troff(h, 1, &st2);
1799 else if (!st1 || !st2)
1802 value = (!strcmp(st1, st2));
1811 while (*c && !isspace(*c) && *c != ')') {
1815 c = scan_expression(c + 1, &value2);
1816 value2 = sign * value2;
1830 int num = 0, denum = 1;
1834 value2 = value2 * 10 + ((*c++) - '0');
1837 while (isdigit(*c)) {
1838 num = num * 10 + ((*c++) - '0');
1843 /* scale indicator */
1845 case 'i': /* inch -> 10pt */
1846 value2 = value2 * 10 + (num * 10 + denum / 2) / denum;
1854 value2 = value2 + (num + denum / 2) / denum;
1855 value2 = sign * value2;
1860 c = scan_escape(c + 1);
1861 value2 = intresult * sign;
1863 c++; /* scale indicator */
1898 value = value - value2;
1901 value = value + value2;
1904 value = value * value2;
1908 value = value / value2;
1912 value = value % value2;
1915 value = (value < value2);
1918 value = (value > value2);
1921 value = (value >= value2);
1924 value = (value <= value2);
1928 value = (value == value2);
1931 value = (value && value2);
1934 value = (value || value2);
1937 fprintf(stderr, "man2html: unknown operator %c.\n", oper);
1950 trans_char(char *c, char s, char t)
1955 while (*sl != '\n' || slash) {
1957 if (*sl == escapesym)
1968 fill_words(char *c, char *words[], int *n)
1976 while (*sl && (*sl != '\n' || slash)) {
1980 skipspace = !skipspace;
1981 } else if (*sl == escapesym)
1983 else if ((*sl == ' ' || *sl == '\t') && !skipspace) {
1985 if (words[*n] != sl)
1993 if (words[*n] != sl)
1996 while (*sl && *sl != '\n')
2005 if (sl != words[*n])
2010 static char *abbrev_list[] = {
2011 "GSBG", "Getting Started ",
2012 "SUBG", "Customizing SunOS",
2013 "SHBG", "Basic Troubleshooting",
2014 "SVBG", "SunView User's Guide",
2015 "MMBG", "Mail and Messages",
2016 "DMBG", "Doing More with SunOS",
2017 "UNBG", "Using the Network",
2018 "GDBG", "Games, Demos & Other Pursuits",
2019 "CHANGE", "SunOS 4.1 Release Manual",
2020 "INSTALL", "Installing SunOS 4.1",
2021 "ADMIN", "System and Network Administration",
2022 "SECUR", "Security Features Guide",
2023 "PROM", "PROM User's Manual",
2024 "DIAG", "Sun System Diagnostics",
2025 "SUNDIAG", "Sundiag User's Guide",
2026 "MANPAGES", "SunOS Reference Manual",
2027 "REFMAN", "SunOS Reference Manual",
2028 "SSI", "Sun System Introduction",
2029 "SSO", "System Services Overview",
2030 "TEXT", "Editing Text Files",
2031 "DOCS", "Formatting Documents",
2032 "TROFF", "Using <B>nroff</B> and <B>troff</B>",
2033 "INDEX", "Global Index",
2034 "CPG", "C Programmer's Guide",
2035 "CREF", "C Reference Manual",
2036 "ASSY", "Assembly Language Reference",
2037 "PUL", "Programming Utilities and Libraries",
2038 "DEBUG", "Debugging Tools",
2039 "NETP", "Network Programming",
2040 "DRIVER", "Writing Device Drivers",
2041 "STREAMS", "STREAMS Programming",
2042 "SBDK", "SBus Developer's Kit",
2043 "WDDS", "Writing Device Drivers for the SBus",
2044 "FPOINT", "Floating-Point Programmer's Guide",
2045 "SVPG", "SunView 1 Programmer's Guide",
2046 "SVSPG", "SunView 1 System Programmer's Guide",
2047 "PIXRCT", "Pixrect Reference Manual",
2048 "CGI", "SunCGI Reference Manual",
2049 "CORE", "SunCore Reference Manual",
2050 "4ASSY", "Sun-4 Assembly Language Reference",
2051 "SARCH", "<FONT SIZE=-1>SPARC</FONT> Architecture Manual",
2052 "KR", "The C Programming Language",
2056 lookup_abbrev(char *c)
2062 while (abbrev_list[i] && strcmp(c, abbrev_list[i]))
2065 return abbrev_list[i + 1];
2070 static char manidx[NULL_TERMINATED(HUGE_STR_MAX)];
2071 static int subs = 0;
2073 static char label[5] = "lbAA";
2076 add_to_index(int level, char *item)
2081 if (label[3] > 'Z') {
2085 if (level != subs) {
2087 strmaxcpy(manidx + mip, "</DL>\n", HUGE_STR_MAX - mip);
2090 strmaxcpy(manidx + mip, "<DL>\n", HUGE_STR_MAX - mip);
2095 scan_troff(item, 1, &c);
2096 sprintf(manidx + mip, "<DT><A HREF=\"#%s\">%s</A><DD>\n", label, c);
2104 skip_till_newline(char *c)
2108 while (*c && *c != '\n' || lvl > 0) {
2119 if (lvl < 0 && newline_for_fun) {
2120 newline_for_fun = newline_for_fun + lvl;
2121 if (newline_for_fun < 0)
2122 newline_for_fun = 0;
2127 static int ifelseval = 0;
2130 scan_request(char *c)
2132 /* BSD Mandoc stuff */
2133 static int mandoc_synopsis = 0; /* True if we are in the synopsis
2135 static int mandoc_command = 0; /* True if this is mandoc page */
2136 static int mandoc_bd_options; /* Only copes with non-nested Bd's */
2140 char *wordlist[MAX_WORDLIST];
2145 while (*c == ' ' || *c == '\t')
2153 while (c[j] == ' ' || c[j] == '\t')
2155 if (c[0] == escapesym) {
2156 /* some pages use .\" .\$1 .\} */
2157 /* .\$1 is too difficult/stuppid */
2159 c = skip_till_newline(c);
2161 c = scan_escape(c + 1);
2167 while (*h && *h != '\n')
2170 if (scaninbuff && buffpos) {
2171 buffer[buffpos] = '\0';
2174 /* fprintf(stderr, "%s\n", c+2); */
2180 int oldcurpos = curpos;
2188 while (*c && *c != '\n')
2192 while (*c && strncmp(c, ".di", 3))
2193 while (*c && *c++ != '\n');
2196 while (de && de->nr != i)
2199 de = (STRDEF *) malloc(sizeof(STRDEF));
2211 scan_troff(h, 0, &de->st);
2213 while (*c && *c++ != '\n');
2221 int oldcurpos = curpos;
2226 while (c[j] && c[j] != '\n')
2241 while (de && de->nr != i)
2248 de = (STRDEF *) malloc(sizeof(STRDEF));
2255 c = scan_troff(c, 1, &h);
2262 c = scan_troff(c, 1, &h);
2267 c = scan_troff(c, 1, &de->st);
2281 if (c[0] == escapesym) {
2282 c = scan_escape(c + 1);
2284 c = skip_till_newline(c);
2292 c = skip_till_newline(c);
2300 c = skip_till_newline(c);
2308 while ('0' <= *c && *c <= '9') {
2309 i = i * 10 + *c - '0';
2313 c = skip_till_newline(c);
2314 /* center next i lines */
2316 out_html("<CENTER>\n");
2320 c = scan_troff(c, 1, &line);
2321 if (line && strncmp(line, "<BR>", 4)) {
2327 out_html("</CENTER>\n");
2338 c = skip_till_newline(c);
2341 c = skip_till_newline(c);
2349 fieldsym = padsym = '\0';
2354 c = skip_till_newline(c);
2358 out_html(change_to_font(0));
2359 out_html(change_to_size('0'));
2360 out_html("</PRE>\n");
2364 c = skip_till_newline(c);
2369 out_html(change_to_font(0));
2371 if (*c == escapesym) {
2374 c = scan_expression(c, &fn);
2376 out_html(change_to_font(fn));
2378 out_html(change_to_font(*c));
2382 c = skip_till_newline(c);
2385 /* .el anything : else part of if else */
2389 c = scan_troff(c, 1, NULL);
2391 c = skip_till_newline(c + j);
2394 /* .ie c anything : then part of if else */
2397 * .if c anything .if !c anything .if N anything .if
2398 * !N anything .if 'string1'string2' anything .if
2399 * !'string1'string2' anything
2402 c = scan_expression(c, &i);
2407 c = scan_troff(c, 1, NULL);
2409 c = skip_till_newline(c);
2413 char *endwith = "..\n";
2421 while (*c && *c != '\n')
2425 while (*c && strncmp(c, endwith, i))
2426 while (*c++ != '\n');
2427 while (*c++ != '\n');
2432 out_html(change_to_font(0));
2433 out_html(change_to_size('0'));
2434 out_html("<PRE>\n");
2438 c = skip_till_newline(c);
2443 out_html(change_to_size('0'));
2450 } else if (*c == '+') {
2454 c = scan_expression(c, &i);
2460 out_html(change_to_size(i * j));
2462 c = skip_till_newline(c);
2473 c = skip_till_newline(c);
2496 scan_troff(h, 1, &name);
2501 if (stat(h, &stbuf) != -1)
2503 buf = stralloc(l + 4);
2508 t = strrchr(fname, '/');
2511 fprintf(stderr, "ln -s %s.html %s.html\n", h, t);
2512 s = strrchr(t, '.');
2515 printf("<HTML><HEAD><TITLE> Manpage of %s</TITLE>\n"
2517 "See the manpage for <A HREF=\"%s.html\">%s</A>.\n"
2524 * this works alright, except for
2527 buf = read_man_page(h);
2530 fprintf(stderr, "man2html: unable to open or read file %s.\n",
2532 out_html("<BLOCKQUOTE>"
2533 "man2html: unable to open or read file.\n");
2535 out_html("</BLOCKQUOTE>\n");
2537 buf[0] = buf[l] = '\n';
2538 buf[l + 1] = buf[l + 2] = '\0';
2539 scan_troff(buf + 1, 0, NULL);
2550 while (*c != '\n') {
2551 sl = scan_expression(c, &tabstops[j]);
2552 if (*c == '-' || *c == '+')
2553 tabstops[j] += tabstops[j - 1];
2555 while (*c == ' ' || *c == '\t')
2564 * while (itemdepth || dl_set[itemdepth]) {
2565 * out_html("</DL>\n"); if (dl_set[itemdepth])
2566 * dl_set[itemdepth]=0; else itemdepth--; }
2570 c = scan_expression(c, &j);
2571 for (i = 0; i < j; i++)
2574 c = skip_till_newline(c);
2582 /* fprintf(stderr,"%s\n", h); */
2589 /* parse one line in a certain font */
2590 out_html(change_to_font(*c));
2591 trans_char(c, '"', '\a');
2595 c = scan_troff(c, 1, NULL);
2596 out_html(change_to_font('R'));
2603 case V('O', 'P'): /* groff manpages use this
2605 /* .OP a b : [ <B>a</B> <I>b</I> ] */
2609 out_html(change_to_font('R'));
2626 sl = fill_words(c, wordlist, &words);
2629 * .BR name (section) indicates a link. It
2630 * will be added in the output routine.
2632 for (i = 0; i < words; i++) {
2637 wordlist[i][-1] = ' ';
2638 out_html(change_to_font(font[i & 1]));
2639 scan_troff(wordlist[i], 1, NULL);
2641 out_html(change_to_font('R'));
2654 for (j = 0; j < 20; j++)
2655 tabstops[j] = (j + 1) * 8;
2657 c = skip_till_newline(c);
2660 sl = fill_words(c + j, wordlist, &words);
2662 if (!dl_set[itemdepth]) {
2663 out_html("<DL COMPACT>\n");
2664 dl_set[itemdepth] = 1;
2668 scan_troff(wordlist[0], 1, NULL);
2674 if (!dl_set[itemdepth]) {
2675 out_html("<DL COMPACT>\n");
2676 dl_set[itemdepth] = 1;
2679 c = skip_till_newline(c);
2680 /* somewhere a definition ends with '.TP' */
2684 c = scan_troff(c, 1, NULL);
2691 sl = fill_words(c + j, wordlist, &words);
2694 while (idxlabel[j] == 'Z')
2695 idxlabel[j--] = 'A';
2698 fprintf(idxfile, "%s@%s@", fname, idxlabel);
2699 for (j = 0; j < words; j++) {
2701 scan_troff(wordlist[j], 1, &h);
2702 fprintf(idxfile, "_\b@%s", h);
2705 fprintf(idxfile, "\n");
2707 out_html("<A NAME=\"");
2710 * this will not work in mosaic (due to a bug).
2711 * Adding ' ' between '>' and '<' solves it, but
2712 * creates some space. A normal space does not work.
2714 out_html("\"></A>");
2718 if (dl_set[itemdepth]) {
2719 out_html("</DL>\n");
2720 dl_set[itemdepth] = 0;
2729 c = skip_till_newline(c);
2732 if (!dl_set[itemdepth]) {
2733 out_html("<DL COMPACT>");
2734 dl_set[itemdepth] = 1;
2738 c = skip_till_newline(c);
2742 c = skip_till_newline(c);
2744 case V('R', 's'): /* BSD mandoc */
2746 sl = fill_words(c + j, wordlist, &words);
2749 scan_expression(wordlist[0], &j);
2752 dl_set[itemdepth] = 0;
2753 out_html("<DL COMPACT><DT><DD>");
2754 c = skip_till_newline(c);
2758 case V('R', 'e'): /* BSD mandoc */
2760 if (itemdepth > 0) {
2761 if (dl_set[itemdepth])
2763 out_html("</DL>\n");
2766 c = skip_till_newline(c);
2770 out_html(change_to_size(-1));
2771 out_html(change_to_font('B'));
2772 c = scan_troff(c + j, 1, NULL);
2773 out_html(change_to_font('R'));
2774 out_html(change_to_size('0'));
2780 out_html(change_to_size(-1));
2781 trans_char(c, '"', '\a');
2782 c = scan_troff(c, 1, NULL);
2783 out_html(change_to_size('0'));
2785 case V('S', 's'): /* BSD mandoc */
2789 case V('S', 'h'): /* BSD mandoc */
2790 /* hack for fallthru from above */
2791 mandoc_command = !mode || mandoc_command;
2796 while (itemdepth || dl_set[itemdepth]) {
2797 out_html("</DL>\n");
2798 if (dl_set[itemdepth])
2799 dl_set[itemdepth] = 0;
2800 else if (itemdepth > 0)
2803 out_html(change_to_font(0));
2804 out_html(change_to_size(0));
2809 trans_char(c, '"', '\a');
2810 add_to_index(mode, c);
2811 out_html("<A NAME=\"");
2813 /* for mosaic users */
2815 out_html("\"> </A>\n<H3>");
2817 out_html("\"> </A>\n<H2>");
2818 mandoc_synopsis = strncmp(c, "SYNOPSIS", 8) == 0;
2819 c = mandoc_command ? scan_troff_mandoc(c, 1, NULL) : scan_troff(c, 1, NULL);
2821 out_html("</H3>\n");
2823 out_html("</H2>\n");
2829 case V('D', 't'): /* BSD mandoc */
2832 if (!output_possible) {
2833 sl = fill_words(c + j, wordlist, &words);
2835 char page_and_sec[128];
2837 for (i = 1; i < words; i++)
2838 wordlist[i][-1] = '\0';
2840 output_possible = 1;
2841 sprintf(page_and_sec, "%s(%s)", wordlist[0], wordlist[1]);
2842 out_html("<HTML><HEAD>\n<TITLE>");
2843 out_html(page_and_sec);
2844 out_html(" Manual Page");
2845 out_html("</TITLE>\n</HEAD>\n<BODY>");
2846 out_html("<TABLE WIDTH=100%>\n");
2847 out_html("<TH ALIGN=LEFT>");
2848 out_html(page_and_sec);
2849 out_html("<TH ALIGN=CENTER>");
2850 out_html(wordlist[2]);
2851 out_html("<TH ALIGN=RIGHT>");
2852 out_html(page_and_sec);
2853 out_html("\n</TABLE>\n");
2854 out_html("<BR><A HREF=\"#index\">Index</A>\n");
2858 out_html("<BR>BSD mandoc<BR>");
2862 c = skip_till_newline(c);
2866 sl = fill_words(c + j, wordlist, &words);
2868 out_html(change_to_font('I'));
2870 wordlist[1][-1] = '\0';
2871 c = lookup_abbrev(wordlist[0]);
2872 curpos += strlen(c);
2874 out_html(change_to_font('R'));
2876 out_html(wordlist[1]);
2881 /* .rm xx : Remove request, macro or string */
2884 * .rn xx yy : Rename request, macro or string xx to
2893 while (isspace(*c) && *c != '\n')
2896 while (*c && *c != '\n')
2900 while (de && de->nr != j)
2908 while (de && de->nr != i)
2915 /* .nx filename : next file. */
2917 /* .in +-N : Indent */
2918 c = skip_till_newline(c);
2922 * .nr R +-N M: define and set number register R by
2923 * +-N; auto-increment by M
2932 while (intd && intd->nr != i)
2935 intd = (INTDEF *) malloc(sizeof(INTDEF));
2939 intd->next = intdef;
2942 while (*c == ' ' || *c == '\t')
2944 c = scan_expression(c, &intd->val);
2946 while (*c == ' ' || *c == '\t')
2948 c = scan_expression(c, &intd->incr);
2950 c = skip_till_newline(c);
2954 /* .am xx yy : append to a macro. */
2955 /* define or handle as .ig yy */
2959 * .de xx yy : define or redefine macro xx; end at
2962 /* define or handle as .ig yy */
2968 sl = fill_words(c, wordlist, &words);
2975 wordlist[1][0] = '.';
2980 while (*c && strncmp(c, wordlist[1], j))
2981 c = skip_till_newline(c);
2983 while (de && de->nr != i)
2986 olen = strlen(de->st);
2988 h = stralloc(j * 2 + 4);
2990 for (j = 0; j < olen; j++)
2992 if (!j || h[j - 1] != '\n')
2995 if (sl[0] == '\\' && sl[1] == '\\') {
3008 de = (STRDEF *) malloc(sizeof(STRDEF));
3016 c = skip_till_newline(c);
3018 case V('B', 'l'): /* BSD mandoc */
3020 char list_options[NULL_TERMINATED(MED_STR_MAX)];
3021 char *nl = strchr(c, '\n');
3024 if (dl_set[itemdepth]) { /* These things can
3028 if (nl) { /* Parse list options */
3029 strlimitcpy(list_options, c, nl - c, MED_STR_MAX);
3031 if (strstr(list_options, "-bullet")) { /* HTML Unnumbered List */
3032 dl_set[itemdepth] = BL_BULLET_LIST;
3034 } else if (strstr(list_options, "-enum")) { /* HTML Ordered List */
3035 dl_set[itemdepth] = BL_ENUM_LIST;
3037 } else { /* HTML Descriptive List */
3038 dl_set[itemdepth] = BL_DESC_LIST;
3039 out_html("<DL COMPACT>\n");
3048 c = skip_till_newline(c);
3051 case V('E', 'l'): /* BSD mandoc */
3053 if (dl_set[itemdepth] & BL_DESC_LIST) {
3054 out_html("</DL>\n");
3055 } else if (dl_set[itemdepth] & BL_BULLET_LIST) {
3056 out_html("</UL>\n");
3057 } else if (dl_set[itemdepth] & BL_ENUM_LIST) {
3058 out_html("</OL>\n");
3060 dl_set[itemdepth] = 0;
3070 c = skip_till_newline(c);
3072 case V('I', 't'): /* BSD mandoc */
3074 if (strncmp(c, "Xo", 2) == 0 && isspace(*(c + 2))) {
3075 c = skip_till_newline(c);
3077 if (dl_set[itemdepth] & BL_DESC_LIST) {
3079 out_html(change_to_font('B'));
3080 if (*c == '\n') { /* Don't allow embedded
3081 * comms after a newline */
3083 c = scan_troff(c, 1, NULL);
3084 } else { /* Do allow embedded comms on
3086 c = scan_troff_mandoc(c, 1, NULL);
3088 out_html(change_to_font('R'));
3091 } else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST)) {
3093 c = scan_troff_mandoc(c, 1, NULL);
3101 case V('B', 'k'): /* BSD mandoc */
3102 case V('E', 'k'): /* BSD mandoc */
3103 case V('D', 'd'): /* BSD mandoc */
3104 case V('O', 's'): /* BSD mandoc */
3105 trans_char(c, '"', '\a');
3109 c = scan_troff_mandoc(c, 1, NULL);
3116 case V('B', 't'): /* BSD mandoc */
3117 trans_char(c, '"', '\a');
3119 out_html(" is currently in beta test.");
3125 case V('B', 'x'): /* BSD mandoc */
3126 trans_char(c, '"', '\a');
3131 c = scan_troff_mandoc(c, 1, NULL);
3137 case V('D', 'l'): /* BSD mandoc */
3140 out_html("<BLOCKQUOTE>");
3141 out_html(change_to_font('L'));
3144 c = scan_troff_mandoc(c, 1, NULL);
3145 out_html(change_to_font('R'));
3146 out_html("</BLOCKQUOTE>");
3152 case V('B', 'd'): /* BSD mandoc */
3153 { /* Seems like a kind of example/literal mode */
3154 char bd_options[NULL_TERMINATED(MED_STR_MAX)];
3155 char *nl = strchr(c, '\n');
3159 strlimitcpy(bd_options, c, nl - c, MED_STR_MAX);
3162 mandoc_bd_options = 0; /* Remember options for
3164 if (strstr(bd_options, "-offset indent")) {
3165 mandoc_bd_options |= BD_INDENT;
3166 out_html("<BLOCKQUOTE>\n");
3168 if (strstr(bd_options, "-literal")
3169 || strstr(bd_options, "-unfilled")) {
3171 mandoc_bd_options |= BD_LITERAL;
3172 out_html(change_to_font(0));
3173 out_html(change_to_size('0'));
3174 out_html("<PRE>\n");
3179 c = skip_till_newline(c);
3182 case V('E', 'd'): /* BSD mandoc */
3183 if (mandoc_bd_options & BD_LITERAL) {
3185 out_html(change_to_font(0));
3186 out_html(change_to_size('0'));
3187 out_html("</PRE>\n");
3190 if (mandoc_bd_options & BD_INDENT)
3191 out_html("</BLOCKQUOTE>\n");
3194 c = skip_till_newline(c);
3196 case V('B', 'e'): /* BSD mandoc */
3205 c = skip_till_newline(c);
3207 case V('X', 'r'): /* BSD mandoc */
3210 * Translate xyz 1 to xyz(1) Allow for
3211 * multiple spaces. Allow the section to be
3214 char buff[NULL_TERMINATED(MED_STR_MAX)];
3217 trans_char(c, '"', '\a');
3221 c++; /* Skip spaces */
3222 while (isspace(*c) && *c != '\n')
3224 while (isalnum(*c)) { /* Copy the xyz part */
3227 if (bufptr >= buff + MED_STR_MAX)
3231 while (isspace(*c) && *c != '\n')
3232 c++; /* Skip spaces */
3233 if (isdigit(*c)) { /* Convert the number if
3237 if (bufptr < buff + MED_STR_MAX) {
3238 while (isalnum(*c)) {
3241 if (bufptr >= buff + MED_STR_MAX)
3245 if (bufptr < buff + MED_STR_MAX) {
3251 while (*c != '\n') { /* Copy the remainder */
3255 if (bufptr >= buff + MED_STR_MAX)
3261 scan_troff_mandoc(buff, 1, NULL);
3270 case V('F', 'l'): /* BSD mandoc */
3271 trans_char(c, '"', '\a');
3275 out_html(change_to_font('B'));
3276 c = scan_troff_mandoc(c, 1, NULL);
3277 out_html(change_to_font('R'));
3285 case V('P', 'a'): /* BSD mandoc */
3286 case V('P', 'f'): /* BSD mandoc */
3287 trans_char(c, '"', '\a');
3291 c = scan_troff_mandoc(c, 1, NULL);
3298 case V('P', 'p'): /* BSD mandoc */
3306 c = skip_till_newline(c);
3308 case V('D', 'q'): /* BSD mandoc */
3309 trans_char(c, '"', '\a');
3314 c = scan_troff_mandoc(c, 1, NULL);
3322 case V('O', 'p'): /* BSD mandoc */
3323 trans_char(c, '"', '\a');
3327 out_html(change_to_font('R'));
3329 c = scan_troff_mandoc(c, 1, NULL);
3330 out_html(change_to_font('R'));
3338 case V('O', 'o'): /* BSD mandoc */
3339 trans_char(c, '"', '\a');
3343 out_html(change_to_font('R'));
3345 c = scan_troff_mandoc(c, 1, NULL);
3351 case V('O', 'c'): /* BSD mandoc */
3352 trans_char(c, '"', '\a');
3354 c = scan_troff_mandoc(c, 1, NULL);
3355 out_html(change_to_font('R'));
3362 case V('P', 'q'): /* BSD mandoc */
3363 trans_char(c, '"', '\a');
3368 c = scan_troff_mandoc(c, 1, NULL);
3376 case V('Q', 'l'): /* BSD mandoc */
3377 { /* Single quote first word in the line */
3380 trans_char(c, '"', '\a');
3385 do { /* Find first whitespace after the
3386 * first word that isn't a mandoc
3388 while (*sp && isspace(*sp))
3390 while (*sp && !isspace(*sp))
3392 } while (*sp && isupper(*(sp - 2)) && islower(*(sp - 1)));
3395 * Use a newline to mark the end of text to
3400 out_html("`"); /* Quote the text */
3401 c = scan_troff_mandoc(c, 1, NULL);
3410 case V('S', 'q'): /* BSD mandoc */
3411 trans_char(c, '"', '\a');
3416 c = scan_troff_mandoc(c, 1, NULL);
3424 case V('A', 'r'): /* BSD mandoc */
3425 /* parse one line in italics */
3426 out_html(change_to_font('I'));
3427 trans_char(c, '"', '\a');
3429 if (*c == '\n') { /* An empty Ar means "file
3431 out_html("file ...");
3433 c = scan_troff_mandoc(c, 1, NULL);
3435 out_html(change_to_font('R'));
3442 case V('A', 'd'): /* BSD mandoc */
3443 case V('E', 'm'): /* BSD mandoc */
3444 case V('V', 'a'): /* BSD mandoc */
3445 case V('X', 'c'): /* BSD mandoc */
3446 /* parse one line in italics */
3447 out_html(change_to_font('I'));
3448 trans_char(c, '"', '\a');
3452 c = scan_troff_mandoc(c, 1, NULL);
3453 out_html(change_to_font('R'));
3460 case V('N', 'd'): /* BSD mandoc */
3461 trans_char(c, '"', '\a');
3466 c = scan_troff_mandoc(c, 1, NULL);
3473 case V('N', 'm'): /* BSD mandoc */
3475 static char mandoc_name[NULL_TERMINATED(SMALL_STR_MAX)] = "";
3477 trans_char(c, '"', '\a');
3479 if (mandoc_synopsis) { /* Break lines only in
3482 * seems to be treated
3483 * as a special case -
3485 static int count = 0; /* Don't break on the
3491 char *end = strchr(c, '\n');
3493 if (end) { /* Remember the name for
3495 strlimitcpy(mandoc_name, c, end - c, SMALL_STR_MAX);
3500 out_html(change_to_font('B'));
3501 while (*c == ' ' || *c == '\t')
3503 if (*c == '\n') { /* If Nm has no
3505 * from an earlier Nm
3506 * command that did have
3511 out_html(mandoc_name);
3513 c = scan_troff_mandoc(c, 1, NULL);
3515 out_html(change_to_font('R'));
3523 case V('C', 'd'): /* BSD mandoc */
3524 case V('C', 'm'): /* BSD mandoc */
3525 case V('I', 'c'): /* BSD mandoc */
3526 case V('M', 's'): /* BSD mandoc */
3527 case V('O', 'r'): /* BSD mandoc */
3528 case V('S', 'y'): /* BSD mandoc */
3529 /* parse one line in bold */
3530 out_html(change_to_font('B'));
3531 trans_char(c, '"', '\a');
3535 c = scan_troff_mandoc(c, 1, NULL);
3536 out_html(change_to_font('R'));
3543 case V('D', 'v'): /* BSD mandoc */
3544 case V('E', 'v'): /* BSD mandoc */
3545 case V('F', 'r'): /* BSD mandoc */
3546 case V('L', 'i'): /* BSD mandoc */
3547 case V('N', 'o'): /* BSD mandoc */
3548 case V('N', 's'): /* BSD mandoc */
3549 case V('T', 'n'): /* BSD mandoc */
3550 case V('n', 'N'): /* BSD mandoc */
3551 trans_char(c, '"', '\a');
3555 out_html(change_to_font('B'));
3556 c = scan_troff_mandoc(c, 1, NULL);
3557 out_html(change_to_font('R'));
3564 case V('%', 'A'): /* BSD mandoc biblio stuff */
3574 c = scan_troff(c, 1, NULL); /* Don't allow embedded
3586 out_html(change_to_font('I'));
3589 c = scan_troff(c, 1, NULL); /* Don't allow embedded
3591 out_html(change_to_font('R'));
3598 /* search macro database of self-defined macros */
3600 while (owndef && owndef->nr != i)
3601 owndef = owndef->next;
3607 sl = fill_words(c + j, wordlist, &words);
3610 for (i = 1; i < words; i++)
3611 wordlist[i][-1] = '\0';
3612 for (i = 0; i < words; i++) {
3615 if (mandoc_command) {
3616 scan_troff_mandoc(wordlist[i], 1, &h);
3618 scan_troff(wordlist[i], 1, &h);
3622 for (i = words; i < 20; i++)
3624 deflen = strlen(owndef->st);
3625 for (i = 0; owndef->st[deflen + 2 + i] = owndef->st[i]; i++);
3626 oldargument = argument;
3627 argument = wordlist;
3628 onff = newline_for_fun;
3629 if (mandoc_command) {
3630 scan_troff_mandoc(owndef->st + deflen + 2, 0, NULL);
3632 scan_troff(owndef->st + deflen + 2, 0, NULL);
3634 newline_for_fun = onff;
3635 argument = oldargument;
3636 for (i = 0; i < words; i++)
3640 } else if (mandoc_command &&
3641 ((isupper(*c) && islower(*(c + 1)))
3642 || (islower(*c) && isupper(*(c + 1))))
3643 ) { /* Let through any BSD mandoc
3644 * commands that haven't been delt
3645 * with. I don't want to miss
3646 * anything out of the text. */
3652 out_html(buf); /* Print the command (it
3653 * might just be text). */
3655 trans_char(c, '"', '\a');
3658 out_html(change_to_font('R'));
3659 c = scan_troff(c, 1, NULL);
3666 c = skip_till_newline(c);
3684 static int contained_tab = 0;
3685 static int mandoc_line = 0; /* Signals whether to look for embedded
3686 * mandoc commands. */
3688 /* san : stop at newline */
3690 scan_troff(char *c, int san, char **result)
3693 char intbuff[NULL_TERMINATED(MED_STR_MAX)];
3697 int exbuffpos, exbuffmax, exscaninbuff, exnewline_for_fun;
3700 #define FLUSHIBP if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; }
3703 exbuffpos = buffpos;
3704 exbuffmax = buffmax;
3705 exnewline_for_fun = newline_for_fun;
3706 exscaninbuff = scaninbuff;
3707 newline_for_fun = 0;
3711 buffpos = strlen(buffer);
3714 buffer = stralloc(LARGE_STR_MAX);
3716 buffmax = LARGE_STR_MAX;
3721 /* start scanning */
3723 while (*h && (!san || newline_for_fun || *h != '\n')) {
3725 if (*h == escapesym) {
3729 } else if (*h == controlsym && h[-1] == '\n') {
3732 h = scan_request(h);
3733 if (san && h[-1] == '\n')
3735 } else if (mandoc_line
3736 && *(h) && isupper(*(h))
3737 && *(h + 1) && islower(*(h + 1))
3738 && *(h + 2) && isspace(*(h + 2))) {
3740 * BSD imbedded command eg ".It Fl Ar arg1 Fl Ar
3744 h = scan_request(h);
3745 if (san && h[-1] == '\n')
3747 } else if (*h == nobreaksym && h[-1] == '\n') {
3750 h = scan_request(h);
3751 if (san && h[-1] == '\n')
3756 if (h[-1] == '\n' && still_dd && isalnum(*h)) {
3758 * sometimes a .HP request is not followed by
3768 intbuff[ibp++] = '&';
3769 intbuff[ibp++] = 'a';
3770 intbuff[ibp++] = 'm';
3771 intbuff[ibp++] = 'p';
3772 intbuff[ibp++] = ';';
3776 intbuff[ibp++] = '&';
3777 intbuff[ibp++] = 'l';
3778 intbuff[ibp++] = 't';
3779 intbuff[ibp++] = ';';
3783 intbuff[ibp++] = '&';
3784 intbuff[ibp++] = 'g';
3785 intbuff[ibp++] = 't';
3786 intbuff[ibp++] = ';';
3790 intbuff[ibp++] = '&';
3791 intbuff[ibp++] = 'q';
3792 intbuff[ibp++] = 'u';
3793 intbuff[ibp++] = 'o';
3794 intbuff[ibp++] = 't';
3795 intbuff[ibp++] = ';';
3799 if (h[-1] == '\n' && fillout) {
3800 intbuff[ibp++] = '<';
3801 intbuff[ibp++] = 'P';
3802 intbuff[ibp++] = '>';
3804 if (contained_tab && fillout) {
3805 intbuff[ibp++] = '<';
3806 intbuff[ibp++] = 'B';
3807 intbuff[ibp++] = 'R';
3808 intbuff[ibp++] = '>';
3813 intbuff[ibp++] = '\n';
3821 /* like a typewriter, not like TeX */
3822 tabstops[19] = curpos + 1;
3823 while (curtab < maxtstop && tabstops[curtab] <= curpos)
3825 if (curtab < maxtstop) {
3827 while (curpos < tabstops[curtab]) {
3828 intbuff[ibp++] = ' ';
3836 while (curpos < tabstops[curtab]) {
3846 if (*h == ' ' && (h[-1] == '\n' || usenbsp)) {
3848 if (!usenbsp && fillout) {
3856 intbuff[ibp++] = ' ';
3857 } else if (*h > 31 && *h < 127)
3858 intbuff[ibp++] = *h;
3859 else if (((unsigned char) (*h)) > 127) {
3860 intbuff[ibp++] = '&';
3861 intbuff[ibp++] = '#';
3862 intbuff[ibp++] = '0' + ((unsigned char) (*h)) / 100;
3863 intbuff[ibp++] = '0' + (((unsigned char) (*h)) % 100) / 10;
3864 intbuff[ibp++] = '0' + ((unsigned char) (*h)) % 10;
3865 intbuff[ibp++] = ';';
3870 if (ibp > (MED_STR_MAX - 20))
3877 buffer[buffpos] = '\0';
3880 newline_for_fun = exnewline_for_fun;
3884 buffpos = exbuffpos;
3885 buffmax = exbuffmax;
3886 scaninbuff = exscaninbuff;
3893 scan_troff_mandoc(char *c, int san, char **result)
3895 char *ret, *end = c;
3896 int oldval = mandoc_line;
3899 while (*end && *end != '\n') {
3904 && ispunct(*(end - 1))
3905 && isspace(*(end - 2)) && *(end - 2) != '\n') {
3907 * Don't format lonely punctuation E.g. in "xyz ," format the
3908 * xyz and then append the comma removing the space.
3911 ret = scan_troff(c, san, result);
3912 *(end - 2) = *(end - 1);
3915 ret = scan_troff(c, san, result);
3917 mandoc_line = oldval;
3921 main(int argc, char **argv)
3931 while ((i = getopt(argc, argv, "")) != EOF) {
3946 buf = read_man_page(h);
3948 fprintf(stderr, "man2html: cannot read %s: %s\n", h, strerror(errno));
3952 idxfile = fopen(INDEXFILE, "a");
3954 stdf = &standardchar[0];
3957 stdf->next = &standardchar[i];
3961 chardef = &standardchar[0];
3963 stdf = &standardstring[0];
3966 stdf->next = &standardstring[i];
3970 strdef = &standardstring[0];
3972 intdef = &standardint[0];
3974 while (intdef->nr) {
3975 intdef->next = &standardint[i];
3976 intdef = intdef->next;
3979 intdef = &standardint[0];
3983 scan_troff(buf + 1, 0, NULL);
3985 while (itemdepth || dl_set[itemdepth]) {
3986 out_html("</DL>\n");
3987 if (dl_set[itemdepth])
3988 dl_set[itemdepth] = 0;
3989 else if (itemdepth > 0)
3993 out_html(change_to_font(0));
3994 out_html(change_to_size(0));
4001 if (output_possible) {
4002 /* for mosaic users */
4003 fputs("<HR>\n<A NAME=\"index\"> </A><H2>Index</H2>\n<DL>\n", stdout);
4005 fputs(manidx, stdout);
4007 fputs("</DL>\n", stdout);
4008 fputs("</DL>\n", stdout);
4010 fputs("</BODY>\n</HTML>\n", stdout);
4012 fprintf(stderr, "man2html: no output produced\n");