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>\nThis document was created by man2html from %s.<BR>\nTime: %s\n";
107 /* timeformat for signature */
108 #define TIMEFORMAT "%d %B %Y %T %Z"
112 /* BSD mandoc Bl/El lists to HTML list types */
113 #define BL_DESC_LIST 1
114 #define BL_BULLET_LIST 2
115 #define BL_ENUM_LIST 4
117 /* BSD mandoc Bd/Ed example(?) blocks */
121 #ifndef HAVE_STRERROR
125 static char emsg[40];
127 #if defined (HAVE_SYS_ERRLIST)
129 extern char *sys_errlist[];
131 if (e > 0 && e < sys_nerr)
132 return (sys_errlist[e]);
134 #endif /* HAVE_SYS_ERRLIST */
136 sprintf(emsg, "Unknown system error %d", e);
140 #endif /* !HAVE_STRERROR */
143 strgrow(char *old, int len)
145 char *new = realloc(old, (strlen(old) + len + 1) * sizeof(char));
148 fprintf(stderr, "man2html: out of memory");
157 /* allocate enough for len + NULL */
158 char *new = malloc((len + 1) * sizeof(char));
161 fprintf(stderr, "man2html: out of memory");
168 * Some systems don't have strdup so lets use our own - which can also
169 * check for out of memory.
172 strduplicate(char *from)
174 char *new = stralloc(strlen(from));
180 /* Assumes space for n plus a null */
182 strmaxcpy(char *to, char *from, int n)
184 int len = strlen(from);
186 strncpy(to, from, n);
187 to[(len <= n) ? len : n] = '\0';
192 strmaxcat(char *to, char *from, int n)
194 int to_len = strlen(to);
197 int from_len = strlen(from);
198 int cp = (to_len + from_len <= n) ? from_len : n - to_len;
200 strncpy(to + to_len, from, cp);
201 to[to_len + cp] = '\0';
206 /* Assumes space for limit plus a null */
208 strlimitcpy(char *to, char *from, int n, int limit)
210 int len = n > limit ? limit : n;
212 strmaxcpy(to, from, len);
218 * takes string and escapes all metacharacters. should be used before
219 * including string in system() or similar call.
222 escape_input(char *str)
225 static char new[NULL_TERMINATED(MED_STR_MAX)];
227 if (strlen(str) * 2 + 1 > MED_STR_MAX) {
229 "man2html: escape_input - str too long:\n%-80s...\n",
233 for (i = 0; i < strlen(str); i++) {
234 if (!(((str[i] >= 'A') && (str[i] <= 'Z')) ||
235 ((str[i] >= 'a') && (str[i] <= 'z')) ||
236 ((str[i] >= '0') && (str[i] <= '9')))) {
250 fprintf(stderr, "man2html: usage: man2html filename\n");
256 * below this you should not change anything unless you know a lot
257 * about this program or about troff.
260 typedef struct STRDEF STRDEF;
267 typedef struct INTDEF INTDEF;
275 static char NEWLINE[2] = "\n";
276 static char idxlabel[6] = "ixAAA";
278 #define INDEXFILE "/tmp/manindex.list"
281 static FILE *idxfile;
283 static STRDEF *chardef, *strdef, *defdef;
284 static INTDEF *intdef;
286 #define V(A,B) ((A)*256+(B))
288 static INTDEF standardint[] = {
289 {V('n', ' '), NROFF, 0, NULL},
290 {V('t', ' '), 1 - NROFF, 0, NULL},
291 {V('o', ' '), 1, 0, NULL},
292 {V('e', ' '), 0, 0, NULL},
293 {V('.', 'l'), 70, 0, NULL},
294 {V('.', '$'), 0, 0, NULL},
295 {V('.', 'A'), NROFF, 0, NULL},
296 {V('.', 'T'), 1 - NROFF, 0, NULL},
297 {V('.', 'V'), 1, 0, NULL}, /* the me package tests for this */
300 static STRDEF standardstring[] = {
301 {V('R', ' '), 1, "®", NULL},
302 {V('l', 'q'), 2, "``", NULL},
303 {V('r', 'q'), 2, "''", NULL},
308 static STRDEF standardchar[] = {
309 {V('*', '*'), 1, "*", NULL},
310 {V('*', 'A'), 1, "A", NULL},
311 {V('*', 'B'), 1, "B", NULL},
312 {V('*', 'C'), 2, "Xi", NULL},
313 {V('*', 'D'), 5, "Delta", NULL},
314 {V('*', 'E'), 1, "E", NULL},
315 {V('*', 'F'), 3, "Phi", NULL},
316 {V('*', 'G'), 5, "Gamma", NULL},
317 {V('*', 'H'), 5, "Theta", NULL},
318 {V('*', 'I'), 1, "I", NULL},
319 {V('*', 'K'), 1, "K", NULL},
320 {V('*', 'L'), 6, "Lambda", NULL},
321 {V('*', 'M'), 1, "M", NULL},
322 {V('*', 'N'), 1, "N", NULL},
323 {V('*', 'O'), 1, "O", NULL},
324 {V('*', 'P'), 2, "Pi", NULL},
325 {V('*', 'Q'), 3, "Psi", NULL},
326 {V('*', 'R'), 1, "P", NULL},
327 {V('*', 'S'), 5, "Sigma", NULL},
328 {V('*', 'T'), 1, "T", NULL},
329 {V('*', 'U'), 1, "Y", NULL},
330 {V('*', 'W'), 5, "Omega", NULL},
331 {V('*', 'X'), 1, "X", NULL},
332 {V('*', 'Y'), 1, "H", NULL},
333 {V('*', 'Z'), 1, "Z", NULL},
334 {V('*', 'a'), 5, "alpha", NULL},
335 {V('*', 'b'), 4, "beta", NULL},
336 {V('*', 'c'), 2, "xi", NULL},
337 {V('*', 'd'), 5, "delta", NULL},
338 {V('*', 'e'), 7, "epsilon", NULL},
339 {V('*', 'f'), 3, "phi", NULL},
340 {V('*', 'g'), 5, "gamma", NULL},
341 {V('*', 'h'), 5, "theta", NULL},
342 {V('*', 'i'), 4, "iota", NULL},
343 {V('*', 'k'), 5, "kappa", NULL},
344 {V('*', 'l'), 6, "lambda", NULL},
345 {V('*', 'm'), 1, "µ", NULL},
346 {V('*', 'n'), 2, "nu", NULL},
347 {V('*', 'o'), 1, "o", NULL},
348 {V('*', 'p'), 2, "pi", NULL},
349 {V('*', 'q'), 3, "psi", NULL},
350 {V('*', 'r'), 3, "rho", NULL},
351 {V('*', 's'), 5, "sigma", NULL},
352 {V('*', 't'), 3, "tau", NULL},
353 {V('*', 'u'), 7, "upsilon", NULL},
354 {V('*', 'w'), 5, "omega", NULL},
355 {V('*', 'x'), 3, "chi", NULL},
356 {V('*', 'y'), 3, "eta", NULL},
357 {V('*', 'z'), 4, "zeta", NULL},
358 {V('t', 's'), 5, "sigma", NULL},
359 {V('+', '-'), 1, "±", NULL},
360 {V('1', '2'), 1, "½", NULL},
361 {V('1', '4'), 1, "¼", NULL},
362 {V('3', '4'), 1, "¾", NULL},
363 {V('F', 'i'), 3, "ffi", NULL},
364 {V('F', 'l'), 3, "ffl", NULL},
365 {V('a', 'a'), 1, "´", NULL},
366 {V('a', 'p'), 1, "~", NULL},
367 {V('b', 'r'), 1, "|", NULL},
368 {V('b', 'u'), 1, "*", NULL},
369 {V('b', 'v'), 1, "|", NULL},
370 {V('c', 'i'), 1, "o", NULL},
371 {V('c', 'o'), 1, "©", NULL},
372 {V('c', 't'), 1, "¢", NULL},
373 {V('d', 'e'), 1, "°", NULL},
374 {V('d', 'g'), 1, "+", NULL},
375 {V('d', 'i'), 1, "÷", NULL},
376 {V('e', 'm'), 1, "-", NULL},
377 {V('e', 'm'), 3, "---", NULL},
378 {V('e', 'q'), 1, "=", NULL},
379 {V('e', 's'), 1, "Ø", NULL},
380 {V('f', 'f'), 2, "ff", NULL},
381 {V('f', 'i'), 2, "fi", NULL},
382 {V('f', 'l'), 2, "fl", NULL},
383 {V('f', 'm'), 1, "´", NULL},
384 {V('g', 'a'), 1, "`", NULL},
385 {V('h', 'y'), 1, "-", NULL},
386 {V('l', 'c'), 2, "|¯", NULL},
387 {V('l', 'f'), 2, "|_", NULL},
388 {V('l', 'k'), 1, "<FONT SIZE=+2>{</FONT>", NULL},
389 {V('m', 'i'), 1, "-", NULL},
390 {V('m', 'u'), 1, "×", NULL},
391 {V('n', 'o'), 1, "¬", NULL},
392 {V('o', 'r'), 1, "|", NULL},
393 {V('p', 'l'), 1, "+", NULL},
394 {V('r', 'c'), 2, "¯|", NULL},
395 {V('r', 'f'), 2, "_|", NULL},
396 {V('r', 'g'), 1, "®", NULL},
397 {V('r', 'k'), 1, "<FONT SIZE=+2>}</FONT>", NULL},
398 {V('r', 'n'), 1, "¯", NULL},
399 {V('r', 'u'), 1, "_", NULL},
400 {V('s', 'c'), 1, "§", NULL},
401 {V('s', 'l'), 1, "/", NULL},
402 {V('s', 'q'), 2, "[]", NULL},
403 {V('u', 'l'), 1, "_", NULL},
407 /* default: print code */
410 static char eqndelimopen = 0, eqndelimclose = 0;
411 static char escapesym = '\\', nobreaksym = '\'', controlsym = '.', fieldsym = 0, padsym = 0;
413 static char *buffer = NULL;
414 static int buffpos = 0, buffmax = 0;
415 static int scaninbuff = 0;
416 static int itemdepth = 0;
417 static int dl_set[20] = {0};
418 static int still_dd = 0;
419 static int tabstops[20] = {8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96};
420 static int maxtstop = 12;
421 static int curpos = 0;
423 static char *scan_troff(char *c, int san, char **result);
424 static char *scan_troff_mandoc(char *c, int san, char **result);
426 static char **argument = NULL;
428 static char charb[TINY_STR_MAX];
433 char datbuf[NULL_TERMINATED(MED_STR_MAX)];
439 timetm = localtime(&clock);
440 strftime(datbuf, MED_STR_MAX, TIMEFORMAT, timetm);
441 printf(signature, manpage, datbuf);
461 if (charb[0] == '<') { /* Fix up <= */
463 strncpy(charb, "<", 4);
471 expand_string(int nr)
487 read_man_page(char *filename)
489 char *man_buf = NULL;
491 FILE *man_stream = NULL;
495 if (stat(filename, &stbuf) == -1)
498 buf_size = stbuf.st_size;
499 man_buf = stralloc(buf_size + 5);
500 man_stream = fopen(filename, "r");
503 if (fread(man_buf + 1, 1, buf_size, man_stream) == buf_size) {
504 man_buf[buf_size] = '\n';
505 man_buf[buf_size + 1] = man_buf[buf_size + 2] = '\0';
515 static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)];
517 static int no_newline_output = 0;
518 static int newline_for_fun = 0;
519 static int output_possible = 0;
520 static int out_length = 0;
523 * Add the links to the output. At the moment the following are
527 * name(*) -> ../man?/name.*
529 * method://string -> method://string
530 * www.host.name -> http://www.host.name
531 * ftp.host.name -> ftp://ftp.host.name
532 * name@host -> mailto:name@host
533 * <name.h> -> file:/usr/include/name.h (guess)
535 * Other possible links to add in the future:
537 * /dir/dir/file -> file:/dir/dir/file
544 char *idtest[6]; /* url, mailto, www, ftp, manpage */
546 out_length += strlen(c);
547 /* search for (section) */
549 idtest[0] = strstr(c + 1, "://");
550 idtest[1] = strchr(c + 1, '@');
551 idtest[2] = strstr(c, "www.");
552 idtest[3] = strstr(c, "ftp.");
554 idtest[4] = strchr(c + 1, '(');
558 idtest[5] = strstr(c + 1, ".h>");
559 for (i = 0; i < 6; i++)
560 nr += (idtest[i] != NULL);
563 for (i = 0; i < 6; i++)
564 if (idtest[i] && (j < 0 || idtest[i] < idtest[j]))
567 case 5: /* <name.h> */
571 while (g > c && g[-1] != ';')
581 printf("<A HREF=\"file:/usr/include/%s\">%s</A>>", g, g);
590 case 4: /* manpage */
595 if (g && f - g < 6 && (isalnum(f[-1]) || f[-1] == '>') &&
596 ((isdigit(f[1]) && f[1] != '0' &&
597 (f[2] == ')' || (isalpha(f[2]) && f[3] == ')') || f[2] == 'X')) ||
598 (f[2] == ')' && (f[1] == 'n' || f[1] == 'l')))) {
599 /* this might be a link */
601 /* skip html makeup */
602 while (h > c && *h == '>') {
603 while (h != c && *h != '<')
609 char t, sec, subsec, *e;
614 if ((subsec == 'X' && f[3] != ')') || subsec == ')')
616 while (h > c && (isalnum(h[-1]) || h[-1] == '_' ||
617 h[-1] == '-' || h[-1] == '.'))
628 "?man%c/%s.%c%c\">%s</A>",
629 sec, h, sec, tolower(subsec), h);
633 "?man%c/%s.%c\">%s</A>",
649 while (*g && (isalnum(*g) || *g == '_' || *g == '-' || *g == '+' ||
663 printf("<A HREF=\"%s://%s\">%s</A>", (j == 3 ? "ftp" : "http"),
676 while (g > c && (isalnum(g[-1]) || g[-1] == '_' || g[-1] == '-' ||
677 g[-1] == '+' || g[-1] == '.' || g[-1] == '%'))
680 while (*h && (isalnum(*h) || *h == '_' || *h == '-' || *h == '+' ||
685 if (h - f > 4 && f - g > 1) {
694 printf("<A HREF=\"mailto:%s\">%s</A>", g, g);
707 while (g > c && isalpha(g[-1]) && islower(g[-1]))
710 while (*h && !isspace(*h) && *h != '<' && *h != '>' && *h != '"' &&
713 if (f - g > 2 && f - g < 7 && h - f > 3) {
722 printf("<A HREF=\"%s\">%s</A>", g, g);
736 if (idtest[0] && idtest[0] < c)
737 idtest[0] = strstr(c + 1, "://");
738 if (idtest[1] && idtest[1] < c)
739 idtest[1] = strchr(c + 1, '@');
740 if (idtest[2] && idtest[2] < c)
741 idtest[2] = strstr(c, "www.");
742 if (idtest[3] && idtest[3] < c)
743 idtest[3] = strstr(c, "ftp.");
744 if (idtest[4] && idtest[4] < c)
745 idtest[4] = strchr(c + 1, '(');
746 if (idtest[5] && idtest[5] < c)
747 idtest[5] = strstr(c + 1, ".h>");
748 for (i = 0; i < 6; i++)
749 nr += (idtest[i] != NULL);
754 static int current_font = 0;
755 static int current_size = 0;
756 static int fillout = 1;
763 if (no_newline_output) {
766 no_newline_output = 1;
768 if (!no_newline_output)
771 no_newline_output = 1;
774 if (!no_newline_output)
779 if (buffpos >= buffmax) {
782 h = realloc(buffer, buffmax * 2);
788 buffer[buffpos++] = *c++;
790 } else if (output_possible) {
792 outbuffer[obp++] = *c;
793 if (*c == '\n' || obp > HUGE_STR_MAX) {
794 outbuffer[obp] = '\0';
795 add_links(outbuffer);
812 static char *switchfont[16] = {
813 "", FC0 FO1, FC0 FO2, FC0 FO3,
814 FC1 FO0, "", FC1 FO2, FC1 FO3,
815 FC2 FO0, FC2 FO1, "", FC2 FO3,
816 FC3 FO0, FC3 FO1, FC3 FO2, ""
820 change_to_font(int nr)
858 i = current_font * 4 + nr % 4;
859 current_font = nr % 4;
860 return switchfont[i];
863 static char sizebuf[200];
866 change_to_size(int nr)
886 nr = current_size + nr;
893 if (nr == current_size)
897 strcat(sizebuf, change_to_font(0));
899 strcat(sizebuf, "</FONT>");
904 strcat(sizebuf, "<FONT SIZE=");
909 sizebuf[l++] = '-', nr = -nr;
910 sizebuf[l++] = nr + '0';
914 strcat(sizebuf, change_to_font(i));
918 static int asint = 0;
919 static int intresult = 0;
921 #define SKIPEOL while (*c && *c++!='\n')
923 static int skip_escape = 0;
924 static int single_escape = 0;
932 int exoutputp, exskipescape;
958 if (!(h = argument[i]))
965 c = scan_escape(c + 1);
991 i = c[0] * 256 + c[1];
999 i = c[0] * 256 + c[1];
1003 h = expand_string(i);
1012 } else if (*c != '(')
1016 i = c[0] * 256 + c[1];
1020 h = change_to_font(i);
1031 } else if (*c == '+') {
1037 else if (*c == '\\') {
1044 while (isdigit(*c) && (!i || (!j && i < 4)))
1045 i = i * 10 + (*c++) - '0';
1052 h = change_to_size(i * j);
1080 while (intd && intd->nr != i)
1083 intd->val = intd->val + j * intd->incr;
1084 intresult = intd->val;
1088 intresult = current_size;
1091 intresult = current_font;
1104 exoutputp = output_possible;
1105 exskipescape = skip_escape;
1106 output_possible = 0;
1111 if (*c == escapesym)
1112 c = scan_escape(c + 1);
1116 output_possible = exoutputp;
1117 skip_escape = exskipescape;
1132 exoutputp = output_possible;
1133 exskipescape = skip_escape;
1134 output_possible = 0;
1137 if (*c == escapesym)
1138 c = scan_escape(c + 1);
1141 output_possible = exoutputp;
1142 skip_escape = exskipescape;
1145 no_newline_output = 1;
1152 if (newline_for_fun)
1162 curpos = (curpos + 8) & 0xfff8;
1173 if (single_escape) {
1190 typedef struct TABLEITEM TABLEITEM;
1194 int size, align, valign, colspan, rowspan, font, vleft, vright, space,
1199 static TABLEITEM emptyfield = {NULL, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, NULL};
1201 typedef struct TABLEROW TABLEROW;
1205 TABLEROW *prev, *next;
1208 static char *tableopt[] = {
1209 "center", "expand", "box", "allbox", "doublebox",
1210 "tab", "linesize", "delim", NULL
1212 static int tableoptl[] = {6, 6, 3, 6, 9, 3, 8, 5, 0};
1215 clear_table(TABLEROW * table)
1217 TABLEROW *tr1, *tr2;
1218 TABLEITEM *ti1, *ti2;
1228 free(ti1->contents);
1238 static char *scan_expression(char *c, int *result);
1241 scan_format(char *c, TABLEROW ** result, int *maxcol)
1243 TABLEROW *layout, *currow;
1244 TABLEITEM *curfield;
1248 clear_table(*result);
1250 layout = currow = (TABLEROW *) malloc(sizeof(TABLEROW));
1251 currow->next = currow->prev = NULL;
1252 currow->first = curfield = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1253 *curfield = emptyfield;
1254 while (*c && *c != '.') {
1270 if (curfield->align) {
1271 curfield->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1272 curfield = curfield->next;
1273 *curfield = emptyfield;
1275 curfield->align = toupper(*c);
1282 curfield->font = toupper(*c);
1288 curfield->font = toupper(*c);
1295 curfield->valign = 't';
1311 i = i * 10 + (*c++) - '0';
1313 curfield->size = i * j;
1315 curfield->size = j - 10;
1321 c = scan_expression(c + 2, &curfield->width);
1324 if (curfield->align)
1346 i = i * 10 + (*c++) - '0';
1347 curfield->space = i;
1351 currow->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1352 currow->next->prev = currow;
1353 currow = currow->next;
1354 currow->next = NULL;
1355 curfield = currow->first = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1356 *curfield = emptyfield;
1365 while (*c++ != '\n');
1369 curfield = layout->first;
1373 curfield = curfield->next;
1377 currow = currow->next;
1384 next_row(TABLEROW * tr)
1392 TABLEITEM *ti, *ti2;
1394 tr->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1395 tr->next->prev = tr;
1400 tr->first = ti2 = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1402 tr->first = ti2 = NULL;
1405 ti2->contents = NULL;
1406 if ((ti = ti->next)) {
1407 ti2->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1415 static char itemreset[20] = "\\fR\\s0";
1421 int center = 0, expand = 0, box = 0, border = 0, linesize = 1;
1422 int i, j, maxcol = 0, finished = 0;
1423 int oldfont, oldsize, oldfillout;
1424 char itemsep = '\t';
1425 TABLEROW *layout = NULL, *currow, *ftable;
1426 TABLEITEM *curfield;
1428 while (*c++ != '\n');
1432 oldfont = current_font;
1433 oldsize = current_size;
1434 oldfillout = fillout;
1435 out_html(change_to_font(0));
1436 out_html(change_to_size(0));
1441 while (*h && *h != '\n')
1444 /* scan table options */
1448 for (i = 0; tableopt[i] && strncmp(tableopt[i], c, tableoptl[i]); i++);
1449 c = c + tableoptl[i];
1467 while (*c++ != '(');
1471 while (*c++ != '(');
1474 linesize = linesize * 10 + (*c++) - '0';
1487 c = scan_format(c, &layout, &maxcol);
1490 curfield = layout->first;
1495 if ((*c == '_' || *c == '=') && (c[1] == itemsep || c[1] == '\n')) {
1496 if (c[-1] == '\n' && c[1] == '\n') {
1498 currow->prev->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1499 currow->prev->next->next = currow;
1500 currow->prev->next->prev = currow->prev;
1501 currow->prev = currow->prev->next;
1503 currow->prev = layout = (TABLEROW *) malloc(sizeof(TABLEROW));
1504 currow->prev->prev = NULL;
1505 currow->prev->next = currow;
1507 curfield = currow->prev->first =
1508 (TABLEITEM *) malloc(sizeof(TABLEITEM));
1509 *curfield = emptyfield;
1510 curfield->align = *c;
1511 curfield->colspan = maxcol;
1512 curfield = currow->first;
1516 curfield->align = *c;
1518 curfield = curfield->next;
1519 } while (curfield && curfield->align == 'S');
1522 currow = next_row(currow);
1523 curfield = currow->first;
1527 } else if (*c == 'T' && c[1] == '{') {
1529 c = strstr(h, "\nT}");
1533 scan_troff(h, 0, &g);
1534 scan_troff(itemreset, 0, &g);
1538 curfield->contents = g;
1540 curfield = curfield->next;
1541 } while (curfield && curfield->align == 'S');
1544 if (c[-1] == '\n') {
1545 currow = next_row(currow);
1546 curfield = currow->first;
1548 } else if (*c == '.' && c[1] == 'T' && c[2] == '&' && c[-1] == '\n') {
1551 while (*c++ != '\n');
1553 currow = currow->prev;
1555 c = scan_format(c, &hr, &i);
1560 curfield = currow->first;
1561 } else if (*c == '.' && c[1] == 'T' && c[2] == 'E' && c[-1] == '\n') {
1563 while (*c++ != '\n');
1565 currow->prev->next = NULL;
1566 currow->prev = NULL;
1567 clear_table(currow);
1568 } else if (*c == '.' && c[-1] == '\n' && !isdigit(c[1])) {
1570 * skip troff request inside table (usually only .sp
1573 while (*c++ != '\n');
1576 while (*c && (*c != itemsep || c[-1] == '\\') &&
1577 (*c != '\n' || c[-1] == '\\'))
1580 if (*c == itemsep) {
1584 if (h[0] == '\\' && h[2] == '\n' &&
1585 (h[1] == '_' || h[1] == '^')) {
1587 curfield->align = h[1];
1589 curfield = curfield->next;
1590 } while (curfield && curfield->align == 'S');
1595 h = scan_troff(h, 1, &g);
1596 scan_troff(itemreset, 0, &g);
1598 curfield->contents = g;
1600 curfield = curfield->next;
1601 } while (curfield && curfield->align == 'S');
1608 if (c[-1] == '\n') {
1609 currow = next_row(currow);
1610 curfield = currow->first;
1614 /* calculate colspan and rowspan */
1616 while (currow->next)
1617 currow = currow->next;
1619 TABLEITEM *ti, *ti1 = NULL, *ti2 = NULL;
1623 ti1 = currow->prev->first;
1625 switch (ti->align) {
1629 if (ti2->rowspan < ti->rowspan)
1630 ti2->rowspan = ti->rowspan;
1642 } while (ti2 && curfield->align == 'S');
1650 currow = currow->prev;
1652 /* produce html output */
1654 out_html("<CENTER>");
1656 out_html("<TABLE BORDER><TR><TD>");
1658 if (box || border) {
1659 out_html(" BORDER");
1661 out_html("><TR><TD><TABLE");
1663 out_html(" WIDTH=100%");
1669 out_html("<TR VALIGN=top>");
1670 curfield = currow->first;
1672 if (curfield->align != 'S' && curfield->align != '^') {
1674 switch (curfield->align) {
1676 curfield->space += 4;
1678 out_html(" ALIGN=right");
1681 out_html(" ALIGN=center");
1685 if (!curfield->valign && curfield->rowspan > 1)
1686 out_html(" VALIGN=center");
1687 if (curfield->colspan > 1) {
1690 out_html(" COLSPAN=");
1691 sprintf(buf, "%i", curfield->colspan);
1694 if (curfield->rowspan > 1) {
1697 out_html(" ROWSPAN=");
1698 sprintf(buf, "%i", curfield->rowspan);
1701 j = j + curfield->colspan;
1704 out_html(change_to_size(curfield->size));
1706 out_html(change_to_font(curfield->font));
1707 switch (curfield->align) {
1709 out_html("<HR><HR>");
1715 if (curfield->contents)
1716 out_html(curfield->contents);
1719 if (curfield->space)
1720 for (i = 0; i < curfield->space; i++)
1723 out_html(change_to_font(0));
1725 out_html(change_to_size(0));
1726 if (j >= maxcol && curfield->align > '@' && curfield->align != '_')
1730 curfield = curfield->next;
1732 out_html("</TR>\n");
1733 currow = currow->next;
1736 out_html("</TABLE>");
1737 out_html("</TABLE>");
1739 out_html("</TABLE>");
1741 out_html("</CENTER>\n");
1746 fillout = oldfillout;
1747 out_html(change_to_size(oldsize));
1748 out_html(change_to_font(oldfont));
1753 scan_expression(char *c, int *result)
1755 int value = 0, value2, j = 0, sign = 1, opex = 0;
1759 c = scan_expression(c + 1, &value);
1761 } else if (*c == 'n') {
1764 } else if (*c == 't') {
1767 } else if (*c == '\'' || *c == '"' || *c < ' ' || (*c == '\\' && c[1] == '(')) {
1769 * ?string1?string2? test if string1 equals string2.
1771 char *st1 = NULL, *st2 = NULL, *h;
1782 while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1785 scan_troff(h, 1, &st1);
1791 while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1794 scan_troff(h, 1, &st2);
1798 else if (!st1 || !st2)
1801 value = (!strcmp(st1, st2));
1810 while (*c && !isspace(*c) && *c != ')') {
1814 c = scan_expression(c + 1, &value2);
1815 value2 = sign * value2;
1829 int num = 0, denum = 1;
1833 value2 = value2 * 10 + ((*c++) - '0');
1836 while (isdigit(*c)) {
1837 num = num * 10 + ((*c++) - '0');
1842 /* scale indicator */
1844 case 'i': /* inch -> 10pt */
1845 value2 = value2 * 10 + (num * 10 + denum / 2) / denum;
1853 value2 = value2 + (num + denum / 2) / denum;
1854 value2 = sign * value2;
1859 c = scan_escape(c + 1);
1860 value2 = intresult * sign;
1862 c++; /* scale indicator */
1897 value = value - value2;
1900 value = value + value2;
1903 value = value * value2;
1907 value = value / value2;
1911 value = value % value2;
1914 value = (value < value2);
1917 value = (value > value2);
1920 value = (value >= value2);
1923 value = (value <= value2);
1927 value = (value == value2);
1930 value = (value && value2);
1933 value = (value || value2);
1936 fprintf(stderr, "man2html: unknown operator %c.\n", oper);
1949 trans_char(char *c, char s, char t)
1954 while (*sl != '\n' || slash) {
1956 if (*sl == escapesym)
1966 /* Remove \a from C in place. Return modified C. */
1974 while (i < l && c[i]) {
1977 strcpy(c + i, c + i + 1); /* should be memmove */
1989 fill_words(char *c, char *words[], int *n)
1997 while (*sl && (*sl != '\n' || slash)) {
2001 skipspace = !skipspace;
2002 } else if (*sl == '\a') {
2003 /* handle already-translated " */
2004 skipspace = !skipspace;
2005 } else if (*sl == escapesym)
2007 else if ((*sl == ' ' || *sl == '\t') && !skipspace) {
2009 if (words[*n] != sl)
2017 if (words[*n] != sl)
2020 while (*sl && *sl != '\n')
2029 if (sl != words[*n])
2034 static char *abbrev_list[] = {
2035 "GSBG", "Getting Started ",
2036 "SUBG", "Customizing SunOS",
2037 "SHBG", "Basic Troubleshooting",
2038 "SVBG", "SunView User's Guide",
2039 "MMBG", "Mail and Messages",
2040 "DMBG", "Doing More with SunOS",
2041 "UNBG", "Using the Network",
2042 "GDBG", "Games, Demos & Other Pursuits",
2043 "CHANGE", "SunOS 4.1 Release Manual",
2044 "INSTALL", "Installing SunOS 4.1",
2045 "ADMIN", "System and Network Administration",
2046 "SECUR", "Security Features Guide",
2047 "PROM", "PROM User's Manual",
2048 "DIAG", "Sun System Diagnostics",
2049 "SUNDIAG", "Sundiag User's Guide",
2050 "MANPAGES", "SunOS Reference Manual",
2051 "REFMAN", "SunOS Reference Manual",
2052 "SSI", "Sun System Introduction",
2053 "SSO", "System Services Overview",
2054 "TEXT", "Editing Text Files",
2055 "DOCS", "Formatting Documents",
2056 "TROFF", "Using <B>nroff</B> and <B>troff</B>",
2057 "INDEX", "Global Index",
2058 "CPG", "C Programmer's Guide",
2059 "CREF", "C Reference Manual",
2060 "ASSY", "Assembly Language Reference",
2061 "PUL", "Programming Utilities and Libraries",
2062 "DEBUG", "Debugging Tools",
2063 "NETP", "Network Programming",
2064 "DRIVER", "Writing Device Drivers",
2065 "STREAMS", "STREAMS Programming",
2066 "SBDK", "SBus Developer's Kit",
2067 "WDDS", "Writing Device Drivers for the SBus",
2068 "FPOINT", "Floating-Point Programmer's Guide",
2069 "SVPG", "SunView 1 Programmer's Guide",
2070 "SVSPG", "SunView 1 System Programmer's Guide",
2071 "PIXRCT", "Pixrect Reference Manual",
2072 "CGI", "SunCGI Reference Manual",
2073 "CORE", "SunCore Reference Manual",
2074 "4ASSY", "Sun-4 Assembly Language Reference",
2075 "SARCH", "<FONT SIZE=-1>SPARC</FONT> Architecture Manual",
2076 "KR", "The C Programming Language",
2080 lookup_abbrev(char *c)
2086 while (abbrev_list[i] && strcmp(c, abbrev_list[i]))
2089 return abbrev_list[i + 1];
2094 static char manidx[NULL_TERMINATED(HUGE_STR_MAX)];
2095 static int subs = 0;
2097 static char label[5] = "lbAA";
2100 add_to_index(int level, char *item)
2105 if (label[3] > 'Z') {
2109 if (level != subs) {
2111 strmaxcpy(manidx + mip, "</DL>\n", HUGE_STR_MAX - mip);
2114 strmaxcpy(manidx + mip, "<DL>\n", HUGE_STR_MAX - mip);
2119 scan_troff(item, 1, &c);
2120 sprintf(manidx + mip, "<DT><A HREF=\"#%s\">%s</A><DD>\n", label, c);
2128 skip_till_newline(char *c)
2132 while (*c && *c != '\n' || lvl > 0) {
2143 if (lvl < 0 && newline_for_fun) {
2144 newline_for_fun = newline_for_fun + lvl;
2145 if (newline_for_fun < 0)
2146 newline_for_fun = 0;
2151 static int ifelseval = 0;
2154 scan_request(char *c)
2156 /* BSD Mandoc stuff */
2157 static int mandoc_synopsis = 0; /* True if we are in the synopsis
2159 static int mandoc_command = 0; /* True if this is mandoc page */
2160 static int mandoc_bd_options; /* Only copes with non-nested Bd's */
2164 char *wordlist[MAX_WORDLIST];
2169 while (*c == ' ' || *c == '\t')
2177 while (c[j] == ' ' || c[j] == '\t')
2179 if (c[0] == escapesym) {
2180 /* some pages use .\" .\$1 .\} */
2181 /* .\$1 is too difficult/stupid */
2183 c = skip_till_newline(c);
2185 c = scan_escape(c + 1);
2191 while (*h && *h != '\n')
2194 if (scaninbuff && buffpos) {
2195 buffer[buffpos] = '\0';
2198 /* fprintf(stderr, "%s\n", c+2); */
2204 int oldcurpos = curpos;
2212 while (*c && *c != '\n')
2216 while (*c && strncmp(c, ".di", 3))
2217 while (*c && *c++ != '\n');
2220 while (de && de->nr != i)
2223 de = (STRDEF *) malloc(sizeof(STRDEF));
2235 scan_troff(h, 0, &de->st);
2237 while (*c && *c++ != '\n');
2245 int oldcurpos = curpos;
2250 while (c[j] && c[j] != '\n')
2265 while (de && de->nr != i)
2272 de = (STRDEF *) malloc(sizeof(STRDEF));
2279 c = scan_troff(c, 1, &h);
2286 c = scan_troff(c, 1, &h);
2291 c = scan_troff(c, 1, &de->st);
2305 if (c[0] == escapesym) {
2306 c = scan_escape(c + 1);
2308 c = skip_till_newline(c);
2316 c = skip_till_newline(c);
2324 c = skip_till_newline(c);
2332 while ('0' <= *c && *c <= '9') {
2333 i = i * 10 + *c - '0';
2337 c = skip_till_newline(c);
2338 /* center next i lines */
2340 out_html("<CENTER>\n");
2344 c = scan_troff(c, 1, &line);
2345 if (line && strncmp(line, "<BR>", 4)) {
2351 out_html("</CENTER>\n");
2362 c = skip_till_newline(c);
2365 c = skip_till_newline(c);
2373 fieldsym = padsym = '\0';
2378 c = skip_till_newline(c);
2382 out_html(change_to_font(0));
2383 out_html(change_to_size('0'));
2384 out_html("</PRE>\n");
2388 c = skip_till_newline(c);
2393 out_html(change_to_font(0));
2395 if (*c == escapesym) {
2398 c = scan_expression(c, &fn);
2400 out_html(change_to_font(fn));
2402 out_html(change_to_font(*c));
2406 c = skip_till_newline(c);
2409 /* .el anything : else part of if else */
2413 c = scan_troff(c, 1, NULL);
2415 c = skip_till_newline(c + j);
2418 /* .ie c anything : then part of if else */
2421 * .if c anything .if !c anything .if N anything .if
2422 * !N anything .if 'string1'string2' anything .if
2423 * !'string1'string2' anything
2426 c = scan_expression(c, &i);
2431 c = scan_troff(c, 1, NULL);
2433 c = skip_till_newline(c);
2437 char *endwith = "..\n";
2445 while (*c && *c != '\n')
2449 while (*c && strncmp(c, endwith, i))
2450 while (*c++ != '\n');
2451 while (*c++ != '\n');
2456 out_html(change_to_font(0));
2457 out_html(change_to_size('0'));
2458 out_html("<PRE>\n");
2462 c = skip_till_newline(c);
2467 out_html(change_to_size('0'));
2474 } else if (*c == '+') {
2478 c = scan_expression(c, &i);
2484 out_html(change_to_size(i * j));
2486 c = skip_till_newline(c);
2497 c = skip_till_newline(c);
2520 scan_troff(h, 1, &name);
2525 if (stat(h, &stbuf) != -1)
2527 buf = stralloc(l + 4);
2532 t = strrchr(fname, '/');
2535 fprintf(stderr, "ln -s %s.html %s.html\n", h, t);
2536 s = strrchr(t, '.');
2539 printf("<HTML><HEAD><TITLE> Manpage of %s</TITLE>\n"
2541 "See the manpage for <A HREF=\"%s.html\">%s</A>.\n"
2548 * this works alright, except for
2551 buf = read_man_page(h);
2554 fprintf(stderr, "man2html: unable to open or read file %s.\n",
2556 out_html("<BLOCKQUOTE>"
2557 "man2html: unable to open or read file.\n");
2559 out_html("</BLOCKQUOTE>\n");
2561 buf[0] = buf[l] = '\n';
2562 buf[l + 1] = buf[l + 2] = '\0';
2563 scan_troff(buf + 1, 0, NULL);
2574 while (*c != '\n') {
2575 sl = scan_expression(c, &tabstops[j]);
2576 if (*c == '-' || *c == '+')
2577 tabstops[j] += tabstops[j - 1];
2579 while (*c == ' ' || *c == '\t')
2588 * while (itemdepth || dl_set[itemdepth]) {
2589 * out_html("</DL>\n"); if (dl_set[itemdepth])
2590 * dl_set[itemdepth]=0; else itemdepth--; }
2594 c = scan_expression(c, &j);
2595 for (i = 0; i < j; i++)
2598 c = skip_till_newline(c);
2606 /* fprintf(stderr,"%s\n", h); */
2613 /* parse one line in a certain font */
2614 out_html(change_to_font(*c));
2615 trans_char(c, '"', '\a');
2619 c = scan_troff(c, 1, NULL);
2620 out_html(change_to_font('R'));
2627 case V('O', 'P'): /* groff manpages use this
2629 /* .OP a b : [ <B>a</B> <I>b</I> ] */
2633 out_html(change_to_font('R'));
2650 sl = fill_words(c, wordlist, &words);
2653 * .BR name (section) indicates a link. It
2654 * will be added in the output routine.
2656 for (i = 0; i < words; i++) {
2661 wordlist[i][-1] = ' ';
2662 out_html(change_to_font(font[i & 1]));
2663 scan_troff(wordlist[i], 1, NULL);
2665 out_html(change_to_font('R'));
2678 for (j = 0; j < 20; j++)
2679 tabstops[j] = (j + 1) * 8;
2681 c = skip_till_newline(c);
2684 sl = fill_words(c + j, wordlist, &words);
2686 if (!dl_set[itemdepth]) {
2687 out_html("<DL COMPACT>\n");
2688 dl_set[itemdepth] = 1;
2692 scan_troff(wordlist[0], 1, NULL);
2698 if (!dl_set[itemdepth]) {
2699 out_html("<DL COMPACT>\n");
2700 dl_set[itemdepth] = 1;
2703 c = skip_till_newline(c);
2704 /* somewhere a definition ends with '.TP' */
2708 c = scan_troff(c, 1, NULL);
2715 sl = fill_words(c + j, wordlist, &words);
2718 while (idxlabel[j] == 'Z')
2719 idxlabel[j--] = 'A';
2722 fprintf(idxfile, "%s@%s@", fname, idxlabel);
2723 for (j = 0; j < words; j++) {
2725 scan_troff(wordlist[j], 1, &h);
2726 fprintf(idxfile, "_\b@%s", h);
2729 fprintf(idxfile, "\n");
2731 out_html("<A NAME=\"");
2734 * this will not work in mosaic (due to a bug).
2735 * Adding ' ' between '>' and '<' solves it, but
2736 * creates some space. A normal space does not work.
2738 out_html("\"></A>");
2742 if (dl_set[itemdepth]) {
2743 out_html("</DL>\n");
2744 dl_set[itemdepth] = 0;
2753 c = skip_till_newline(c);
2756 if (!dl_set[itemdepth]) {
2757 out_html("<DL COMPACT>");
2758 dl_set[itemdepth] = 1;
2762 c = skip_till_newline(c);
2766 c = skip_till_newline(c);
2768 case V('R', 's'): /* BSD mandoc */
2770 sl = fill_words(c + j, wordlist, &words);
2773 scan_expression(wordlist[0], &j);
2776 dl_set[itemdepth] = 0;
2777 out_html("<DL COMPACT><DT><DD>");
2778 c = skip_till_newline(c);
2782 case V('R', 'e'): /* BSD mandoc */
2784 if (itemdepth > 0) {
2785 if (dl_set[itemdepth])
2787 out_html("</DL>\n");
2790 c = skip_till_newline(c);
2794 out_html(change_to_size(-1));
2795 out_html(change_to_font('B'));
2796 c = scan_troff(c + j, 1, NULL);
2797 out_html(change_to_font('R'));
2798 out_html(change_to_size('0'));
2804 out_html(change_to_size(-1));
2805 trans_char(c, '"', '\a');
2806 c = scan_troff(c, 1, NULL);
2807 out_html(change_to_size('0'));
2809 case V('S', 's'): /* BSD mandoc */
2813 case V('S', 'h'): /* BSD mandoc */
2814 /* hack for fallthru from above */
2815 mandoc_command = !mode || mandoc_command;
2820 while (itemdepth || dl_set[itemdepth]) {
2821 out_html("</DL>\n");
2822 if (dl_set[itemdepth])
2823 dl_set[itemdepth] = 0;
2824 else if (itemdepth > 0)
2827 out_html(change_to_font(0));
2828 out_html(change_to_size(0));
2833 trans_char(c, '"', '\a');
2834 add_to_index(mode, c);
2835 out_html("<A NAME=\"");
2837 /* for mosaic users */
2839 out_html("\"> </A>\n<H3>");
2841 out_html("\"> </A>\n<H2>");
2842 mandoc_synopsis = strncmp(c, "SYNOPSIS", 8) == 0;
2843 c = mandoc_command ? scan_troff_mandoc(c, 1, NULL) : scan_troff(c, 1, NULL);
2845 out_html("</H3>\n");
2847 out_html("</H2>\n");
2853 case V('D', 't'): /* BSD mandoc */
2856 if (!output_possible) {
2857 sl = fill_words(c + j, wordlist, &words);
2859 char page_and_sec[128];
2861 for (i = 1; i < words; i++)
2862 wordlist[i][-1] = '\0';
2864 output_possible = 1;
2865 sprintf(page_and_sec, "%s(%s)", wordlist[0], wordlist[1]);
2866 out_html("<HTML><HEAD>\n<TITLE>");
2867 out_html(page_and_sec);
2868 out_html(" Manual Page");
2869 out_html("</TITLE>\n</HEAD>\n<BODY>");
2870 out_html("<TABLE WIDTH=100%>\n");
2871 out_html("<TH ALIGN=LEFT>");
2872 out_html(page_and_sec);
2873 out_html("<TH ALIGN=CENTER>");
2874 out_html(unescape(wordlist[2]));
2875 out_html("<TH ALIGN=RIGHT>");
2876 out_html(page_and_sec);
2877 out_html("\n</TABLE>\n");
2878 out_html("<BR><A HREF=\"#index\">Index</A>\n");
2882 out_html("<BR>BSD mandoc<BR>");
2886 c = skip_till_newline(c);
2890 sl = fill_words(c + j, wordlist, &words);
2892 out_html(change_to_font('I'));
2894 wordlist[1][-1] = '\0';
2895 c = lookup_abbrev(wordlist[0]);
2896 curpos += strlen(c);
2898 out_html(change_to_font('R'));
2900 out_html(wordlist[1]);
2905 /* .rm xx : Remove request, macro or string */
2908 * .rn xx yy : Rename request, macro or string xx to
2917 while (isspace(*c) && *c != '\n')
2920 while (*c && *c != '\n')
2924 while (de && de->nr != j)
2932 while (de && de->nr != i)
2939 /* .nx filename : next file. */
2941 /* .in +-N : Indent */
2942 c = skip_till_newline(c);
2946 * .nr R +-N M: define and set number register R by
2947 * +-N; auto-increment by M
2956 while (intd && intd->nr != i)
2959 intd = (INTDEF *) malloc(sizeof(INTDEF));
2963 intd->next = intdef;
2966 while (*c == ' ' || *c == '\t')
2968 c = scan_expression(c, &intd->val);
2970 while (*c == ' ' || *c == '\t')
2972 c = scan_expression(c, &intd->incr);
2974 c = skip_till_newline(c);
2978 /* .am xx yy : append to a macro. */
2979 /* define or handle as .ig yy */
2983 * .de xx yy : define or redefine macro xx; end at
2986 /* define or handle as .ig yy */
2992 sl = fill_words(c, wordlist, &words);
2999 wordlist[1][0] = '.';
3004 while (*c && strncmp(c, wordlist[1], j))
3005 c = skip_till_newline(c);
3007 while (de && de->nr != i)
3010 olen = strlen(de->st);
3012 h = stralloc(j * 2 + 4);
3014 for (j = 0; j < olen; j++)
3016 if (!j || h[j - 1] != '\n')
3019 if (sl[0] == '\\' && sl[1] == '\\') {
3032 de = (STRDEF *) malloc(sizeof(STRDEF));
3040 c = skip_till_newline(c);
3042 case V('B', 'l'): /* BSD mandoc */
3044 char list_options[NULL_TERMINATED(MED_STR_MAX)];
3045 char *nl = strchr(c, '\n');
3048 if (dl_set[itemdepth]) { /* These things can
3052 if (nl) { /* Parse list options */
3053 strlimitcpy(list_options, c, nl - c, MED_STR_MAX);
3055 if (strstr(list_options, "-bullet")) { /* HTML Unnumbered List */
3056 dl_set[itemdepth] = BL_BULLET_LIST;
3058 } else if (strstr(list_options, "-enum")) { /* HTML Ordered List */
3059 dl_set[itemdepth] = BL_ENUM_LIST;
3061 } else { /* HTML Descriptive List */
3062 dl_set[itemdepth] = BL_DESC_LIST;
3063 out_html("<DL COMPACT>\n");
3072 c = skip_till_newline(c);
3075 case V('E', 'l'): /* BSD mandoc */
3077 if (dl_set[itemdepth] & BL_DESC_LIST) {
3078 out_html("</DL>\n");
3079 } else if (dl_set[itemdepth] & BL_BULLET_LIST) {
3080 out_html("</UL>\n");
3081 } else if (dl_set[itemdepth] & BL_ENUM_LIST) {
3082 out_html("</OL>\n");
3084 dl_set[itemdepth] = 0;
3094 c = skip_till_newline(c);
3096 case V('I', 't'): /* BSD mandoc */
3098 if (strncmp(c, "Xo", 2) == 0 && isspace(*(c + 2))) {
3099 c = skip_till_newline(c);
3101 if (dl_set[itemdepth] & BL_DESC_LIST) {
3103 out_html(change_to_font('B'));
3104 if (*c == '\n') { /* Don't allow embedded
3105 * comms after a newline */
3107 c = scan_troff(c, 1, NULL);
3108 } else { /* Do allow embedded comms on
3110 c = scan_troff_mandoc(c, 1, NULL);
3112 out_html(change_to_font('R'));
3115 } else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST)) {
3117 c = scan_troff_mandoc(c, 1, NULL);
3125 case V('B', 'k'): /* BSD mandoc */
3126 case V('E', 'k'): /* BSD mandoc */
3127 case V('D', 'd'): /* BSD mandoc */
3128 case V('O', 's'): /* BSD mandoc */
3129 trans_char(c, '"', '\a');
3133 c = scan_troff_mandoc(c, 1, NULL);
3140 case V('B', 't'): /* BSD mandoc */
3141 trans_char(c, '"', '\a');
3143 out_html(" is currently in beta test.");
3149 case V('B', 'x'): /* BSD mandoc */
3150 trans_char(c, '"', '\a');
3155 c = scan_troff_mandoc(c, 1, NULL);
3161 case V('D', 'l'): /* BSD mandoc */
3164 out_html("<BLOCKQUOTE>");
3165 out_html(change_to_font('L'));
3168 c = scan_troff_mandoc(c, 1, NULL);
3169 out_html(change_to_font('R'));
3170 out_html("</BLOCKQUOTE>");
3176 case V('B', 'd'): /* BSD mandoc */
3177 { /* Seems like a kind of example/literal mode */
3178 char bd_options[NULL_TERMINATED(MED_STR_MAX)];
3179 char *nl = strchr(c, '\n');
3183 strlimitcpy(bd_options, c, nl - c, MED_STR_MAX);
3186 mandoc_bd_options = 0; /* Remember options for
3188 if (strstr(bd_options, "-offset indent")) {
3189 mandoc_bd_options |= BD_INDENT;
3190 out_html("<BLOCKQUOTE>\n");
3192 if (strstr(bd_options, "-literal")
3193 || strstr(bd_options, "-unfilled")) {
3195 mandoc_bd_options |= BD_LITERAL;
3196 out_html(change_to_font(0));
3197 out_html(change_to_size('0'));
3198 out_html("<PRE>\n");
3203 c = skip_till_newline(c);
3206 case V('E', 'd'): /* BSD mandoc */
3207 if (mandoc_bd_options & BD_LITERAL) {
3209 out_html(change_to_font(0));
3210 out_html(change_to_size('0'));
3211 out_html("</PRE>\n");
3214 if (mandoc_bd_options & BD_INDENT)
3215 out_html("</BLOCKQUOTE>\n");
3218 c = skip_till_newline(c);
3220 case V('B', 'e'): /* BSD mandoc */
3229 c = skip_till_newline(c);
3231 case V('X', 'r'): /* BSD mandoc */
3234 * Translate xyz 1 to xyz(1) Allow for
3235 * multiple spaces. Allow the section to be
3238 char buff[NULL_TERMINATED(MED_STR_MAX)];
3241 trans_char(c, '"', '\a');
3245 c++; /* Skip spaces */
3246 while (isspace(*c) && *c != '\n')
3248 while (isalnum(*c)) { /* Copy the xyz part */
3251 if (bufptr >= buff + MED_STR_MAX)
3255 while (isspace(*c) && *c != '\n')
3256 c++; /* Skip spaces */
3257 if (isdigit(*c)) { /* Convert the number if
3261 if (bufptr < buff + MED_STR_MAX) {
3262 while (isalnum(*c)) {
3265 if (bufptr >= buff + MED_STR_MAX)
3269 if (bufptr < buff + MED_STR_MAX) {
3275 while (*c != '\n') { /* Copy the remainder */
3279 if (bufptr >= buff + MED_STR_MAX)
3285 scan_troff_mandoc(buff, 1, NULL);
3294 case V('F', 'l'): /* BSD mandoc */
3295 trans_char(c, '"', '\a');
3299 out_html(change_to_font('B'));
3300 c = scan_troff_mandoc(c, 1, NULL);
3301 out_html(change_to_font('R'));
3309 case V('P', 'a'): /* BSD mandoc */
3310 case V('P', 'f'): /* BSD mandoc */
3311 trans_char(c, '"', '\a');
3315 c = scan_troff_mandoc(c, 1, NULL);
3322 case V('P', 'p'): /* BSD mandoc */
3330 c = skip_till_newline(c);
3332 case V('D', 'q'): /* BSD mandoc */
3333 trans_char(c, '"', '\a');
3338 c = scan_troff_mandoc(c, 1, NULL);
3346 case V('O', 'p'): /* BSD mandoc */
3347 trans_char(c, '"', '\a');
3351 out_html(change_to_font('R'));
3353 c = scan_troff_mandoc(c, 1, NULL);
3354 out_html(change_to_font('R'));
3362 case V('O', 'o'): /* BSD mandoc */
3363 trans_char(c, '"', '\a');
3367 out_html(change_to_font('R'));
3369 c = scan_troff_mandoc(c, 1, NULL);
3375 case V('O', 'c'): /* BSD mandoc */
3376 trans_char(c, '"', '\a');
3378 c = scan_troff_mandoc(c, 1, NULL);
3379 out_html(change_to_font('R'));
3386 case V('P', 'q'): /* BSD mandoc */
3387 trans_char(c, '"', '\a');
3392 c = scan_troff_mandoc(c, 1, NULL);
3400 case V('Q', 'l'): /* BSD mandoc */
3401 { /* Single quote first word in the line */
3404 trans_char(c, '"', '\a');
3409 do { /* Find first whitespace after the
3410 * first word that isn't a mandoc
3412 while (*sp && isspace(*sp))
3414 while (*sp && !isspace(*sp))
3416 } while (*sp && isupper(*(sp - 2)) && islower(*(sp - 1)));
3419 * Use a newline to mark the end of text to
3424 out_html("`"); /* Quote the text */
3425 c = scan_troff_mandoc(c, 1, NULL);
3434 case V('S', 'q'): /* BSD mandoc */
3435 trans_char(c, '"', '\a');
3440 c = scan_troff_mandoc(c, 1, NULL);
3448 case V('A', 'r'): /* BSD mandoc */
3449 /* parse one line in italics */
3450 out_html(change_to_font('I'));
3451 trans_char(c, '"', '\a');
3453 if (*c == '\n') { /* An empty Ar means "file
3455 out_html("file ...");
3457 c = scan_troff_mandoc(c, 1, NULL);
3459 out_html(change_to_font('R'));
3466 case V('A', 'd'): /* BSD mandoc */
3467 case V('E', 'm'): /* BSD mandoc */
3468 case V('V', 'a'): /* BSD mandoc */
3469 case V('X', 'c'): /* BSD mandoc */
3470 /* parse one line in italics */
3471 out_html(change_to_font('I'));
3472 trans_char(c, '"', '\a');
3476 c = scan_troff_mandoc(c, 1, NULL);
3477 out_html(change_to_font('R'));
3484 case V('N', 'd'): /* BSD mandoc */
3485 trans_char(c, '"', '\a');
3490 c = scan_troff_mandoc(c, 1, NULL);
3497 case V('N', 'm'): /* BSD mandoc */
3499 static char mandoc_name[NULL_TERMINATED(SMALL_STR_MAX)] = "";
3501 trans_char(c, '"', '\a');
3503 if (mandoc_synopsis) { /* Break lines only in
3506 * seems to be treated
3507 * as a special case -
3509 static int count = 0; /* Don't break on the
3515 char *end = strchr(c, '\n');
3517 if (end) { /* Remember the name for
3519 strlimitcpy(mandoc_name, c, end - c, SMALL_STR_MAX);
3524 out_html(change_to_font('B'));
3525 while (*c == ' ' || *c == '\t')
3527 if (*c == '\n') { /* If Nm has no
3529 * from an earlier Nm
3530 * command that did have
3535 out_html(mandoc_name);
3537 c = scan_troff_mandoc(c, 1, NULL);
3539 out_html(change_to_font('R'));
3547 case V('C', 'd'): /* BSD mandoc */
3548 case V('C', 'm'): /* BSD mandoc */
3549 case V('I', 'c'): /* BSD mandoc */
3550 case V('M', 's'): /* BSD mandoc */
3551 case V('O', 'r'): /* BSD mandoc */
3552 case V('S', 'y'): /* BSD mandoc */
3553 /* parse one line in bold */
3554 out_html(change_to_font('B'));
3555 trans_char(c, '"', '\a');
3559 c = scan_troff_mandoc(c, 1, NULL);
3560 out_html(change_to_font('R'));
3567 case V('D', 'v'): /* BSD mandoc */
3568 case V('E', 'v'): /* BSD mandoc */
3569 case V('F', 'r'): /* BSD mandoc */
3570 case V('L', 'i'): /* BSD mandoc */
3571 case V('N', 'o'): /* BSD mandoc */
3572 case V('N', 's'): /* BSD mandoc */
3573 case V('T', 'n'): /* BSD mandoc */
3574 case V('n', 'N'): /* BSD mandoc */
3575 trans_char(c, '"', '\a');
3579 out_html(change_to_font('B'));
3580 c = scan_troff_mandoc(c, 1, NULL);
3581 out_html(change_to_font('R'));
3588 case V('%', 'A'): /* BSD mandoc biblio stuff */
3598 c = scan_troff(c, 1, NULL); /* Don't allow embedded
3610 out_html(change_to_font('I'));
3613 c = scan_troff(c, 1, NULL); /* Don't allow embedded
3615 out_html(change_to_font('R'));
3622 /* search macro database of self-defined macros */
3624 while (owndef && owndef->nr != i)
3625 owndef = owndef->next;
3631 sl = fill_words(c + j, wordlist, &words);
3634 for (i = 1; i < words; i++)
3635 wordlist[i][-1] = '\0';
3636 for (i = 0; i < words; i++) {
3639 if (mandoc_command) {
3640 scan_troff_mandoc(wordlist[i], 1, &h);
3642 scan_troff(wordlist[i], 1, &h);
3646 for (i = words; i < 20; i++)
3648 deflen = strlen(owndef->st);
3649 for (i = 0; owndef->st[deflen + 2 + i] = owndef->st[i]; i++);
3650 oldargument = argument;
3651 argument = wordlist;
3652 onff = newline_for_fun;
3653 if (mandoc_command) {
3654 scan_troff_mandoc(owndef->st + deflen + 2, 0, NULL);
3656 scan_troff(owndef->st + deflen + 2, 0, NULL);
3658 newline_for_fun = onff;
3659 argument = oldargument;
3660 for (i = 0; i < words; i++)
3664 } else if (mandoc_command &&
3665 ((isupper(*c) && islower(*(c + 1)))
3666 || (islower(*c) && isupper(*(c + 1))))
3667 ) { /* Let through any BSD mandoc
3668 * commands that haven't been delt
3669 * with. I don't want to miss
3670 * anything out of the text. */
3676 out_html(buf); /* Print the command (it
3677 * might just be text). */
3679 trans_char(c, '"', '\a');
3682 out_html(change_to_font('R'));
3683 c = scan_troff(c, 1, NULL);
3690 c = skip_till_newline(c);
3708 static int contained_tab = 0;
3709 static int mandoc_line = 0; /* Signals whether to look for embedded
3710 * mandoc commands. */
3712 /* san : stop at newline */
3714 scan_troff(char *c, int san, char **result)
3717 char intbuff[NULL_TERMINATED(MED_STR_MAX)];
3721 int exbuffpos, exbuffmax, exscaninbuff, exnewline_for_fun;
3724 #define FLUSHIBP if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; }
3727 exbuffpos = buffpos;
3728 exbuffmax = buffmax;
3729 exnewline_for_fun = newline_for_fun;
3730 exscaninbuff = scaninbuff;
3731 newline_for_fun = 0;
3735 buffpos = strlen(buffer);
3738 buffer = stralloc(LARGE_STR_MAX);
3740 buffmax = LARGE_STR_MAX;
3745 /* start scanning */
3747 while (*h && (!san || newline_for_fun || *h != '\n')) {
3749 if (*h == escapesym) {
3753 } else if (*h == controlsym && h[-1] == '\n') {
3756 h = scan_request(h);
3757 if (san && h[-1] == '\n')
3759 } else if (mandoc_line
3760 && *(h) && isupper(*(h))
3761 && *(h + 1) && islower(*(h + 1))
3762 && *(h + 2) && isspace(*(h + 2))) {
3764 * BSD imbedded command eg ".It Fl Ar arg1 Fl Ar
3768 h = scan_request(h);
3769 if (san && h[-1] == '\n')
3771 } else if (*h == nobreaksym && h[-1] == '\n') {
3774 h = scan_request(h);
3775 if (san && h[-1] == '\n')
3780 if (h[-1] == '\n' && still_dd && isalnum(*h)) {
3782 * sometimes a .HP request is not followed by
3792 intbuff[ibp++] = '&';
3793 intbuff[ibp++] = 'a';
3794 intbuff[ibp++] = 'm';
3795 intbuff[ibp++] = 'p';
3796 intbuff[ibp++] = ';';
3800 intbuff[ibp++] = '&';
3801 intbuff[ibp++] = 'l';
3802 intbuff[ibp++] = 't';
3803 intbuff[ibp++] = ';';
3807 intbuff[ibp++] = '&';
3808 intbuff[ibp++] = 'g';
3809 intbuff[ibp++] = 't';
3810 intbuff[ibp++] = ';';
3814 intbuff[ibp++] = '&';
3815 intbuff[ibp++] = 'q';
3816 intbuff[ibp++] = 'u';
3817 intbuff[ibp++] = 'o';
3818 intbuff[ibp++] = 't';
3819 intbuff[ibp++] = ';';
3823 if (h[-1] == '\n' && fillout) {
3824 intbuff[ibp++] = '<';
3825 intbuff[ibp++] = 'P';
3826 intbuff[ibp++] = '>';
3828 if (contained_tab && fillout) {
3829 intbuff[ibp++] = '<';
3830 intbuff[ibp++] = 'B';
3831 intbuff[ibp++] = 'R';
3832 intbuff[ibp++] = '>';
3837 intbuff[ibp++] = '\n';
3845 /* like a typewriter, not like TeX */
3846 tabstops[19] = curpos + 1;
3847 while (curtab < maxtstop && tabstops[curtab] <= curpos)
3849 if (curtab < maxtstop) {
3851 while (curpos < tabstops[curtab]) {
3852 intbuff[ibp++] = ' ';
3860 while (curpos < tabstops[curtab]) {
3870 if (*h == ' ' && (h[-1] == '\n' || usenbsp)) {
3872 if (!usenbsp && fillout) {
3880 intbuff[ibp++] = ' ';
3881 } else if (*h > 31 && *h < 127)
3882 intbuff[ibp++] = *h;
3883 else if (((unsigned char) (*h)) > 127) {
3884 intbuff[ibp++] = '&';
3885 intbuff[ibp++] = '#';
3886 intbuff[ibp++] = '0' + ((unsigned char) (*h)) / 100;
3887 intbuff[ibp++] = '0' + (((unsigned char) (*h)) % 100) / 10;
3888 intbuff[ibp++] = '0' + ((unsigned char) (*h)) % 10;
3889 intbuff[ibp++] = ';';
3894 if (ibp > (MED_STR_MAX - 20))
3901 buffer[buffpos] = '\0';
3904 newline_for_fun = exnewline_for_fun;
3908 buffpos = exbuffpos;
3909 buffmax = exbuffmax;
3910 scaninbuff = exscaninbuff;
3917 scan_troff_mandoc(char *c, int san, char **result)
3919 char *ret, *end = c;
3920 int oldval = mandoc_line;
3923 while (*end && *end != '\n') {
3928 && ispunct(*(end - 1))
3929 && isspace(*(end - 2)) && *(end - 2) != '\n') {
3931 * Don't format lonely punctuation E.g. in "xyz ," format the
3932 * xyz and then append the comma removing the space.
3935 ret = scan_troff(c, san, result);
3936 *(end - 2) = *(end - 1);
3939 ret = scan_troff(c, san, result);
3941 mandoc_line = oldval;
3945 main(int argc, char **argv)
3955 while ((i = getopt(argc, argv, "")) != EOF) {
3967 manpage = h = t = argv[1];
3970 buf = read_man_page(h);
3972 fprintf(stderr, "man2html: cannot read %s: %s\n", h, strerror(errno));
3976 idxfile = fopen(INDEXFILE, "a");
3978 stdf = &standardchar[0];
3981 stdf->next = &standardchar[i];
3985 chardef = &standardchar[0];
3987 stdf = &standardstring[0];
3990 stdf->next = &standardstring[i];
3994 strdef = &standardstring[0];
3996 intdef = &standardint[0];
3998 while (intdef->nr) {
3999 intdef->next = &standardint[i];
4000 intdef = intdef->next;
4003 intdef = &standardint[0];
4007 scan_troff(buf + 1, 0, NULL);
4009 while (itemdepth || dl_set[itemdepth]) {
4010 out_html("</DL>\n");
4011 if (dl_set[itemdepth])
4012 dl_set[itemdepth] = 0;
4013 else if (itemdepth > 0)
4017 out_html(change_to_font(0));
4018 out_html(change_to_size(0));
4025 if (output_possible) {
4026 /* for mosaic users */
4027 fputs("<HR>\n<A NAME=\"index\"> </A><H2>Index</H2>\n<DL>\n", stdout);
4029 fputs(manidx, stdout);
4031 fputs("</DL>\n", stdout);
4032 fputs("</DL>\n", stdout);
4034 fputs("</BODY>\n</HTML>\n", stdout);
4036 fprintf(stderr, "man2html: no output produced\n");