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 static char th_page_and_sec[128] = { '\0' };
106 static char th_datestr[128] = { '\0' };
107 static char th_version[128] = { '\0' };
109 char *signature = "<HR>\nThis document was created by man2html from %s.<BR>\nTime: %s\n";
111 /* timeformat for signature */
112 #define TIMEFORMAT "%d %B %Y %T %Z"
116 /* BSD mandoc Bl/El lists to HTML list types */
117 #define BL_DESC_LIST 1
118 #define BL_BULLET_LIST 2
119 #define BL_ENUM_LIST 4
121 /* BSD mandoc Bd/Ed example(?) blocks */
125 #ifndef HAVE_STRERROR
129 static char emsg[40];
131 #if defined (HAVE_SYS_ERRLIST)
133 extern char *sys_errlist[];
135 if (e > 0 && e < sys_nerr)
136 return (sys_errlist[e]);
138 #endif /* HAVE_SYS_ERRLIST */
140 sprintf(emsg, "Unknown system error %d", e);
144 #endif /* !HAVE_STRERROR */
147 strgrow(char *old, int len)
149 char *new = realloc(old, (strlen(old) + len + 1) * sizeof(char));
152 fprintf(stderr, "man2html: out of memory");
161 /* allocate enough for len + NULL */
162 char *new = malloc((len + 1) * sizeof(char));
165 fprintf(stderr, "man2html: out of memory");
172 * Some systems don't have strdup so lets use our own - which can also
173 * check for out of memory.
176 strduplicate(char *from)
178 char *new = stralloc(strlen(from));
184 /* Assumes space for n plus a null */
186 strmaxcpy(char *to, char *from, int n)
188 int len = strlen(from);
190 strncpy(to, from, n);
191 to[(len <= n) ? len : n] = '\0';
196 strmaxcat(char *to, char *from, int n)
198 int to_len = strlen(to);
201 int from_len = strlen(from);
202 int cp = (to_len + from_len <= n) ? from_len : n - to_len;
204 strncpy(to + to_len, from, cp);
205 to[to_len + cp] = '\0';
210 /* Assumes space for limit plus a null */
212 strlimitcpy(char *to, char *from, int n, int limit)
214 int len = n > limit ? limit : n;
216 strmaxcpy(to, from, len);
222 * takes string and escapes all metacharacters. should be used before
223 * including string in system() or similar call.
226 escape_input(char *str)
229 static char new[NULL_TERMINATED(MED_STR_MAX)];
231 if (strlen(str) * 2 + 1 > MED_STR_MAX) {
233 "man2html: escape_input - str too long:\n%-80s...\n",
237 for (i = 0; i < strlen(str); i++) {
238 if (!(((str[i] >= 'A') && (str[i] <= 'Z')) ||
239 ((str[i] >= 'a') && (str[i] <= 'z')) ||
240 ((str[i] >= '0') && (str[i] <= '9')))) {
254 fprintf(stderr, "man2html: usage: man2html filename\n");
260 * below this you should not change anything unless you know a lot
261 * about this program or about troff.
264 typedef struct STRDEF STRDEF;
271 typedef struct INTDEF INTDEF;
279 static char NEWLINE[2] = "\n";
280 static char idxlabel[6] = "ixAAA";
282 #define INDEXFILE "/tmp/manindex.list"
285 static FILE *idxfile;
287 static STRDEF *chardef, *strdef, *defdef;
288 static INTDEF *intdef;
290 #define V(A,B) ((A)*256+(B))
292 static INTDEF standardint[] = {
293 {V('n', ' '), NROFF, 0, NULL},
294 {V('t', ' '), 1 - NROFF, 0, NULL},
295 {V('o', ' '), 1, 0, NULL},
296 {V('e', ' '), 0, 0, NULL},
297 {V('.', 'l'), 70, 0, NULL},
298 {V('.', '$'), 0, 0, NULL},
299 {V('.', 'A'), NROFF, 0, NULL},
300 {V('.', 'T'), 1 - NROFF, 0, NULL},
301 {V('.', 'V'), 1, 0, NULL}, /* the me package tests for this */
304 static STRDEF standardstring[] = {
305 {V('R', ' '), 1, "®", NULL},
306 {V('l', 'q'), 2, "``", NULL},
307 {V('r', 'q'), 2, "''", NULL},
312 static STRDEF standardchar[] = {
313 {V('*', '*'), 1, "*", NULL},
314 {V('*', 'A'), 1, "A", NULL},
315 {V('*', 'B'), 1, "B", NULL},
316 {V('*', 'C'), 2, "Xi", NULL},
317 {V('*', 'D'), 5, "Delta", NULL},
318 {V('*', 'E'), 1, "E", NULL},
319 {V('*', 'F'), 3, "Phi", NULL},
320 {V('*', 'G'), 5, "Gamma", NULL},
321 {V('*', 'H'), 5, "Theta", NULL},
322 {V('*', 'I'), 1, "I", NULL},
323 {V('*', 'K'), 1, "K", NULL},
324 {V('*', 'L'), 6, "Lambda", NULL},
325 {V('*', 'M'), 1, "M", NULL},
326 {V('*', 'N'), 1, "N", NULL},
327 {V('*', 'O'), 1, "O", NULL},
328 {V('*', 'P'), 2, "Pi", NULL},
329 {V('*', 'Q'), 3, "Psi", NULL},
330 {V('*', 'R'), 1, "P", NULL},
331 {V('*', 'S'), 5, "Sigma", NULL},
332 {V('*', 'T'), 1, "T", NULL},
333 {V('*', 'U'), 1, "Y", NULL},
334 {V('*', 'W'), 5, "Omega", NULL},
335 {V('*', 'X'), 1, "X", NULL},
336 {V('*', 'Y'), 1, "H", NULL},
337 {V('*', 'Z'), 1, "Z", NULL},
338 {V('*', 'a'), 5, "alpha", NULL},
339 {V('*', 'b'), 4, "beta", NULL},
340 {V('*', 'c'), 2, "xi", NULL},
341 {V('*', 'd'), 5, "delta", NULL},
342 {V('*', 'e'), 7, "epsilon", NULL},
343 {V('*', 'f'), 3, "phi", NULL},
344 {V('*', 'g'), 5, "gamma", NULL},
345 {V('*', 'h'), 5, "theta", NULL},
346 {V('*', 'i'), 4, "iota", NULL},
347 {V('*', 'k'), 5, "kappa", NULL},
348 {V('*', 'l'), 6, "lambda", NULL},
349 {V('*', 'm'), 1, "µ", NULL},
350 {V('*', 'n'), 2, "nu", NULL},
351 {V('*', 'o'), 1, "o", NULL},
352 {V('*', 'p'), 2, "pi", NULL},
353 {V('*', 'q'), 3, "psi", NULL},
354 {V('*', 'r'), 3, "rho", NULL},
355 {V('*', 's'), 5, "sigma", NULL},
356 {V('*', 't'), 3, "tau", NULL},
357 {V('*', 'u'), 7, "upsilon", NULL},
358 {V('*', 'w'), 5, "omega", NULL},
359 {V('*', 'x'), 3, "chi", NULL},
360 {V('*', 'y'), 3, "eta", NULL},
361 {V('*', 'z'), 4, "zeta", NULL},
362 {V('t', 's'), 5, "sigma", NULL},
363 {V('+', '-'), 1, "±", NULL},
364 {V('1', '2'), 1, "½", NULL},
365 {V('1', '4'), 1, "¼", NULL},
366 {V('3', '4'), 1, "¾", NULL},
367 {V('F', 'i'), 3, "ffi", NULL},
368 {V('F', 'l'), 3, "ffl", NULL},
369 {V('a', 'a'), 1, "´", NULL},
370 {V('a', 'p'), 1, "~", NULL},
371 {V('b', 'r'), 1, "|", NULL},
372 {V('b', 'u'), 1, "*", NULL},
373 {V('b', 'v'), 1, "|", NULL},
374 {V('c', 'i'), 1, "o", NULL},
375 {V('c', 'o'), 1, "©", NULL},
376 {V('c', 't'), 1, "¢", NULL},
377 {V('d', 'e'), 1, "°", NULL},
378 {V('d', 'g'), 1, "+", NULL},
379 {V('d', 'i'), 1, "÷", NULL},
380 {V('e', 'm'), 1, "-", NULL},
381 {V('e', 'm'), 3, "---", NULL},
382 {V('e', 'q'), 1, "=", NULL},
383 {V('e', 's'), 1, "Ø", NULL},
384 {V('f', 'f'), 2, "ff", NULL},
385 {V('f', 'i'), 2, "fi", NULL},
386 {V('f', 'l'), 2, "fl", NULL},
387 {V('f', 'm'), 1, "´", NULL},
388 {V('g', 'a'), 1, "`", NULL},
389 {V('h', 'y'), 1, "-", NULL},
390 {V('l', 'c'), 2, "|¯", NULL},
391 {V('l', 'f'), 2, "|_", NULL},
392 {V('l', 'k'), 1, "<FONT SIZE=+2>{</FONT>", NULL},
393 {V('m', 'i'), 1, "-", NULL},
394 {V('m', 'u'), 1, "×", NULL},
395 {V('n', 'o'), 1, "¬", NULL},
396 {V('o', 'r'), 1, "|", NULL},
397 {V('p', 'l'), 1, "+", NULL},
398 {V('r', 'c'), 2, "¯|", NULL},
399 {V('r', 'f'), 2, "_|", NULL},
400 {V('r', 'g'), 1, "®", NULL},
401 {V('r', 'k'), 1, "<FONT SIZE=+2>}</FONT>", NULL},
402 {V('r', 'n'), 1, "¯", NULL},
403 {V('r', 'u'), 1, "_", NULL},
404 {V('s', 'c'), 1, "§", NULL},
405 {V('s', 'l'), 1, "/", NULL},
406 {V('s', 'q'), 2, "[]", NULL},
407 {V('u', 'l'), 1, "_", NULL},
411 /* default: print code */
414 static char eqndelimopen = 0, eqndelimclose = 0;
415 static char escapesym = '\\', nobreaksym = '\'', controlsym = '.', fieldsym = 0, padsym = 0;
417 static char *buffer = NULL;
418 static int buffpos = 0, buffmax = 0;
419 static int scaninbuff = 0;
420 static int itemdepth = 0;
421 static int dl_set[20] = {0};
422 static int still_dd = 0;
423 static int tabstops[20] = {8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96};
424 static int maxtstop = 12;
425 static int curpos = 0;
427 static char *scan_troff(char *c, int san, char **result);
428 static char *scan_troff_mandoc(char *c, int san, char **result);
430 static char **argument = NULL;
432 static char charb[TINY_STR_MAX];
437 char datbuf[NULL_TERMINATED(MED_STR_MAX)];
443 timetm = localtime(&clock);
444 strftime(datbuf, MED_STR_MAX, TIMEFORMAT, timetm);
445 printf(signature, manpage, datbuf);
465 if (charb[0] == '<') { /* Fix up <= */
467 strncpy(charb, "<", 4);
475 expand_string(int nr)
491 read_man_page(char *filename)
493 char *man_buf = NULL;
495 FILE *man_stream = NULL;
499 if (stat(filename, &stbuf) == -1)
502 buf_size = stbuf.st_size;
503 man_buf = stralloc(buf_size + 5);
504 man_stream = fopen(filename, "r");
507 if (fread(man_buf + 1, 1, buf_size, man_stream) == buf_size) {
508 man_buf[buf_size] = '\n';
509 man_buf[buf_size + 1] = man_buf[buf_size + 2] = '\0';
519 static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)];
521 static int no_newline_output = 0;
522 static int newline_for_fun = 0;
523 static int output_possible = 0;
524 static int out_length = 0;
527 * Add the links to the output. At the moment the following are
531 * name(*) -> ../man?/name.*
533 * method://string -> method://string
534 * www.host.name -> http://www.host.name
535 * ftp.host.name -> ftp://ftp.host.name
536 * name@host -> mailto:name@host
537 * <name.h> -> file:/usr/include/name.h (guess)
539 * Other possible links to add in the future:
541 * /dir/dir/file -> file:/dir/dir/file
548 char *idtest[6]; /* url, mailto, www, ftp, manpage */
550 out_length += strlen(c);
551 /* search for (section) */
553 idtest[0] = strstr(c + 1, "://");
554 idtest[1] = strchr(c + 1, '@');
555 idtest[2] = strstr(c, "www.");
556 idtest[3] = strstr(c, "ftp.");
558 idtest[4] = strchr(c + 1, '(');
562 idtest[5] = strstr(c + 1, ".h>");
563 for (i = 0; i < 6; i++)
564 nr += (idtest[i] != NULL);
567 for (i = 0; i < 6; i++)
568 if (idtest[i] && (j < 0 || idtest[i] < idtest[j]))
571 case 5: /* <name.h> */
575 while (g > c && g[-1] != ';')
585 printf("<A HREF=\"file:/usr/include/%s\">%s</A>>", g, g);
594 case 4: /* manpage */
599 if (g && f - g < 6 && (isalnum(f[-1]) || f[-1] == '>') &&
600 ((isdigit(f[1]) && f[1] != '0' &&
601 (f[2] == ')' || (isalpha(f[2]) && f[3] == ')') || f[2] == 'X')) ||
602 (f[2] == ')' && (f[1] == 'n' || f[1] == 'l')))) {
603 /* this might be a link */
605 /* skip html makeup */
606 while (h > c && *h == '>') {
607 while (h != c && *h != '<')
613 char t, sec, subsec, *e;
618 if ((subsec == 'X' && f[3] != ')') || subsec == ')')
620 while (h > c && (isalnum(h[-1]) || h[-1] == '_' ||
621 h[-1] == '-' || h[-1] == '.'))
632 "?man%c/%s.%c%c\">%s</A>",
633 sec, h, sec, tolower(subsec), h);
637 "?man%c/%s.%c\">%s</A>",
653 while (*g && (isalnum(*g) || *g == '_' || *g == '-' || *g == '+' ||
667 printf("<A HREF=\"%s://%s\">%s</A>", (j == 3 ? "ftp" : "http"),
680 while (g > c && (isalnum(g[-1]) || g[-1] == '_' || g[-1] == '-' ||
681 g[-1] == '+' || g[-1] == '.' || g[-1] == '%'))
684 while (*h && (isalnum(*h) || *h == '_' || *h == '-' || *h == '+' ||
689 if (h - f > 4 && f - g > 1) {
698 printf("<A HREF=\"mailto:%s\">%s</A>", g, g);
711 while (g > c && isalpha(g[-1]) && islower(g[-1]))
714 while (*h && !isspace(*h) && *h != '<' && *h != '>' && *h != '"' &&
717 if (f - g > 2 && f - g < 7 && h - f > 3) {
726 printf("<A HREF=\"%s\">%s</A>", g, g);
740 if (idtest[0] && idtest[0] < c)
741 idtest[0] = strstr(c + 1, "://");
742 if (idtest[1] && idtest[1] < c)
743 idtest[1] = strchr(c + 1, '@');
744 if (idtest[2] && idtest[2] < c)
745 idtest[2] = strstr(c, "www.");
746 if (idtest[3] && idtest[3] < c)
747 idtest[3] = strstr(c, "ftp.");
748 if (idtest[4] && idtest[4] < c)
749 idtest[4] = strchr(c + 1, '(');
750 if (idtest[5] && idtest[5] < c)
751 idtest[5] = strstr(c + 1, ".h>");
752 for (i = 0; i < 6; i++)
753 nr += (idtest[i] != NULL);
758 static int current_font = 0;
759 static int current_size = 0;
760 static int fillout = 1;
767 if (no_newline_output) {
770 no_newline_output = 1;
772 if (!no_newline_output)
775 no_newline_output = 1;
778 if (!no_newline_output)
783 if (buffpos >= buffmax) {
786 h = realloc(buffer, buffmax * 2);
792 buffer[buffpos++] = *c++;
794 } else if (output_possible) {
796 outbuffer[obp++] = *c;
797 if (*c == '\n' || obp > HUGE_STR_MAX) {
798 outbuffer[obp] = '\0';
799 add_links(outbuffer);
816 static char *switchfont[16] = {
817 "", FC0 FO1, FC0 FO2, FC0 FO3,
818 FC1 FO0, "", FC1 FO2, FC1 FO3,
819 FC2 FO0, FC2 FO1, "", FC2 FO3,
820 FC3 FO0, FC3 FO1, FC3 FO2, ""
824 change_to_font(int nr)
862 i = current_font * 4 + nr % 4;
863 current_font = nr % 4;
864 return switchfont[i];
867 static char sizebuf[200];
870 change_to_size(int nr)
890 nr = current_size + nr;
897 if (nr == current_size)
901 strcat(sizebuf, change_to_font(0));
903 strcat(sizebuf, "</FONT>");
908 strcat(sizebuf, "<FONT SIZE=");
913 sizebuf[l++] = '-', nr = -nr;
914 sizebuf[l++] = nr + '0';
918 strcat(sizebuf, change_to_font(i));
922 static int asint = 0;
923 static int intresult = 0;
925 #define SKIPEOL while (*c && *c++!='\n')
927 static int skip_escape = 0;
928 static int single_escape = 0;
936 int exoutputp, exskipescape;
962 if (!(h = argument[i]))
969 c = scan_escape(c + 1);
995 i = c[0] * 256 + c[1];
1003 i = c[0] * 256 + c[1];
1007 h = expand_string(i);
1016 } else if (*c != '(')
1020 i = c[0] * 256 + c[1];
1024 h = change_to_font(i);
1035 } else if (*c == '+') {
1041 else if (*c == '\\') {
1048 while (isdigit(*c) && (!i || (!j && i < 4)))
1049 i = i * 10 + (*c++) - '0';
1056 h = change_to_size(i * j);
1084 while (intd && intd->nr != i)
1087 intd->val = intd->val + j * intd->incr;
1088 intresult = intd->val;
1092 intresult = current_size;
1095 intresult = current_font;
1108 exoutputp = output_possible;
1109 exskipescape = skip_escape;
1110 output_possible = 0;
1115 if (*c == escapesym)
1116 c = scan_escape(c + 1);
1120 output_possible = exoutputp;
1121 skip_escape = exskipescape;
1136 exoutputp = output_possible;
1137 exskipescape = skip_escape;
1138 output_possible = 0;
1141 if (*c == escapesym)
1142 c = scan_escape(c + 1);
1145 output_possible = exoutputp;
1146 skip_escape = exskipescape;
1149 no_newline_output = 1;
1156 if (newline_for_fun)
1166 curpos = (curpos + 8) & 0xfff8;
1177 if (single_escape) {
1194 typedef struct TABLEITEM TABLEITEM;
1198 int size, align, valign, colspan, rowspan, font, vleft, vright, space,
1203 static TABLEITEM emptyfield = {NULL, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, NULL};
1205 typedef struct TABLEROW TABLEROW;
1209 TABLEROW *prev, *next;
1212 static char *tableopt[] = {
1213 "center", "expand", "box", "allbox", "doublebox",
1214 "tab", "linesize", "delim", NULL
1216 static int tableoptl[] = {6, 6, 3, 6, 9, 3, 8, 5, 0};
1219 clear_table(TABLEROW * table)
1221 TABLEROW *tr1, *tr2;
1222 TABLEITEM *ti1, *ti2;
1232 free(ti1->contents);
1242 static char *scan_expression(char *c, int *result);
1245 scan_format(char *c, TABLEROW ** result, int *maxcol)
1247 TABLEROW *layout, *currow;
1248 TABLEITEM *curfield;
1252 clear_table(*result);
1254 layout = currow = (TABLEROW *) malloc(sizeof(TABLEROW));
1255 currow->next = currow->prev = NULL;
1256 currow->first = curfield = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1257 *curfield = emptyfield;
1258 while (*c && *c != '.') {
1274 if (curfield->align) {
1275 curfield->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1276 curfield = curfield->next;
1277 *curfield = emptyfield;
1279 curfield->align = toupper(*c);
1286 curfield->font = toupper(*c);
1292 curfield->font = toupper(*c);
1299 curfield->valign = 't';
1315 i = i * 10 + (*c++) - '0';
1317 curfield->size = i * j;
1319 curfield->size = j - 10;
1325 c = scan_expression(c + 2, &curfield->width);
1328 if (curfield->align)
1350 i = i * 10 + (*c++) - '0';
1351 curfield->space = i;
1355 currow->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1356 currow->next->prev = currow;
1357 currow = currow->next;
1358 currow->next = NULL;
1359 curfield = currow->first = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1360 *curfield = emptyfield;
1369 while (*c++ != '\n');
1373 curfield = layout->first;
1377 curfield = curfield->next;
1381 currow = currow->next;
1388 next_row(TABLEROW * tr)
1396 TABLEITEM *ti, *ti2;
1398 tr->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1399 tr->next->prev = tr;
1404 tr->first = ti2 = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1406 tr->first = ti2 = NULL;
1409 ti2->contents = NULL;
1410 if ((ti = ti->next)) {
1411 ti2->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1419 static char itemreset[20] = "\\fR\\s0";
1425 int center = 0, expand = 0, box = 0, border = 0, linesize = 1;
1426 int i, j, maxcol = 0, finished = 0;
1427 int oldfont, oldsize, oldfillout;
1428 char itemsep = '\t';
1429 TABLEROW *layout = NULL, *currow, *ftable;
1430 TABLEITEM *curfield;
1432 while (*c++ != '\n');
1436 oldfont = current_font;
1437 oldsize = current_size;
1438 oldfillout = fillout;
1439 out_html(change_to_font(0));
1440 out_html(change_to_size(0));
1445 while (*h && *h != '\n')
1448 /* scan table options */
1452 for (i = 0; tableopt[i] && strncmp(tableopt[i], c, tableoptl[i]); i++);
1453 c = c + tableoptl[i];
1471 while (*c++ != '(');
1475 while (*c++ != '(');
1478 linesize = linesize * 10 + (*c++) - '0';
1491 c = scan_format(c, &layout, &maxcol);
1494 curfield = layout->first;
1499 if ((*c == '_' || *c == '=') && (c[1] == itemsep || c[1] == '\n')) {
1500 if (c[-1] == '\n' && c[1] == '\n') {
1502 currow->prev->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1503 currow->prev->next->next = currow;
1504 currow->prev->next->prev = currow->prev;
1505 currow->prev = currow->prev->next;
1507 currow->prev = layout = (TABLEROW *) malloc(sizeof(TABLEROW));
1508 currow->prev->prev = NULL;
1509 currow->prev->next = currow;
1511 curfield = currow->prev->first =
1512 (TABLEITEM *) malloc(sizeof(TABLEITEM));
1513 *curfield = emptyfield;
1514 curfield->align = *c;
1515 curfield->colspan = maxcol;
1516 curfield = currow->first;
1520 curfield->align = *c;
1522 curfield = curfield->next;
1523 } while (curfield && curfield->align == 'S');
1526 currow = next_row(currow);
1527 curfield = currow->first;
1531 } else if (*c == 'T' && c[1] == '{') {
1533 c = strstr(h, "\nT}");
1537 scan_troff(h, 0, &g);
1538 scan_troff(itemreset, 0, &g);
1542 curfield->contents = g;
1544 curfield = curfield->next;
1545 } while (curfield && curfield->align == 'S');
1548 if (c[-1] == '\n') {
1549 currow = next_row(currow);
1550 curfield = currow->first;
1552 } else if (*c == '.' && c[1] == 'T' && c[2] == '&' && c[-1] == '\n') {
1555 while (*c++ != '\n');
1557 currow = currow->prev;
1559 c = scan_format(c, &hr, &i);
1564 curfield = currow->first;
1565 } else if (*c == '.' && c[1] == 'T' && c[2] == 'E' && c[-1] == '\n') {
1567 while (*c++ != '\n');
1569 currow->prev->next = NULL;
1570 currow->prev = NULL;
1571 clear_table(currow);
1572 } else if (*c == '.' && c[-1] == '\n' && !isdigit(c[1])) {
1574 * skip troff request inside table (usually only .sp
1577 while (*c++ != '\n');
1580 while (*c && (*c != itemsep || c[-1] == '\\') &&
1581 (*c != '\n' || c[-1] == '\\'))
1584 if (*c == itemsep) {
1588 if (h[0] == '\\' && h[2] == '\n' &&
1589 (h[1] == '_' || h[1] == '^')) {
1591 curfield->align = h[1];
1593 curfield = curfield->next;
1594 } while (curfield && curfield->align == 'S');
1599 h = scan_troff(h, 1, &g);
1600 scan_troff(itemreset, 0, &g);
1602 curfield->contents = g;
1604 curfield = curfield->next;
1605 } while (curfield && curfield->align == 'S');
1612 if (c[-1] == '\n') {
1613 currow = next_row(currow);
1614 curfield = currow->first;
1618 /* calculate colspan and rowspan */
1620 while (currow->next)
1621 currow = currow->next;
1623 TABLEITEM *ti, *ti1 = NULL, *ti2 = NULL;
1627 ti1 = currow->prev->first;
1629 switch (ti->align) {
1633 if (ti2->rowspan < ti->rowspan)
1634 ti2->rowspan = ti->rowspan;
1646 } while (ti2 && curfield->align == 'S');
1654 currow = currow->prev;
1656 /* produce html output */
1658 out_html("<CENTER>");
1660 out_html("<TABLE BORDER><TR><TD>");
1662 if (box || border) {
1663 out_html(" BORDER");
1665 out_html("><TR><TD><TABLE");
1667 out_html(" WIDTH=100%");
1673 out_html("<TR VALIGN=top>");
1674 curfield = currow->first;
1676 if (curfield->align != 'S' && curfield->align != '^') {
1678 switch (curfield->align) {
1680 curfield->space += 4;
1682 out_html(" ALIGN=right");
1685 out_html(" ALIGN=center");
1689 if (!curfield->valign && curfield->rowspan > 1)
1690 out_html(" VALIGN=center");
1691 if (curfield->colspan > 1) {
1694 out_html(" COLSPAN=");
1695 sprintf(buf, "%i", curfield->colspan);
1698 if (curfield->rowspan > 1) {
1701 out_html(" ROWSPAN=");
1702 sprintf(buf, "%i", curfield->rowspan);
1705 j = j + curfield->colspan;
1708 out_html(change_to_size(curfield->size));
1710 out_html(change_to_font(curfield->font));
1711 switch (curfield->align) {
1713 out_html("<HR><HR>");
1719 if (curfield->contents)
1720 out_html(curfield->contents);
1723 if (curfield->space)
1724 for (i = 0; i < curfield->space; i++)
1727 out_html(change_to_font(0));
1729 out_html(change_to_size(0));
1730 if (j >= maxcol && curfield->align > '@' && curfield->align != '_')
1734 curfield = curfield->next;
1736 out_html("</TR>\n");
1737 currow = currow->next;
1740 out_html("</TABLE>");
1741 out_html("</TABLE>");
1743 out_html("</TABLE>");
1745 out_html("</CENTER>\n");
1750 fillout = oldfillout;
1751 out_html(change_to_size(oldsize));
1752 out_html(change_to_font(oldfont));
1757 scan_expression(char *c, int *result)
1759 int value = 0, value2, j = 0, sign = 1, opex = 0;
1763 c = scan_expression(c + 1, &value);
1765 } else if (*c == 'n') {
1768 } else if (*c == 't') {
1771 } else if (*c == '\'' || *c == '"' || *c < ' ' || (*c == '\\' && c[1] == '(')) {
1773 * ?string1?string2? test if string1 equals string2.
1775 char *st1 = NULL, *st2 = NULL, *h;
1786 while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1789 scan_troff(h, 1, &st1);
1795 while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1798 scan_troff(h, 1, &st2);
1802 else if (!st1 || !st2)
1805 value = (!strcmp(st1, st2));
1814 while (*c && !isspace(*c) && *c != ')') {
1818 c = scan_expression(c + 1, &value2);
1819 value2 = sign * value2;
1833 int num = 0, denum = 1;
1837 value2 = value2 * 10 + ((*c++) - '0');
1840 while (isdigit(*c)) {
1841 num = num * 10 + ((*c++) - '0');
1846 /* scale indicator */
1848 case 'i': /* inch -> 10pt */
1849 value2 = value2 * 10 + (num * 10 + denum / 2) / denum;
1857 value2 = value2 + (num + denum / 2) / denum;
1858 value2 = sign * value2;
1863 c = scan_escape(c + 1);
1864 value2 = intresult * sign;
1866 c++; /* scale indicator */
1901 value = value - value2;
1904 value = value + value2;
1907 value = value * value2;
1911 value = value / value2;
1915 value = value % value2;
1918 value = (value < value2);
1921 value = (value > value2);
1924 value = (value >= value2);
1927 value = (value <= value2);
1931 value = (value == value2);
1934 value = (value && value2);
1937 value = (value || value2);
1940 fprintf(stderr, "man2html: unknown operator %c.\n", oper);
1953 trans_char(char *c, char s, char t)
1958 while (*sl != '\n' || slash) {
1960 if (*sl == escapesym)
1970 /* Remove \a from C in place. Return modified C. */
1978 while (i < l && c[i]) {
1981 strcpy(c + i, c + i + 1); /* should be memmove */
1993 fill_words(char *c, char *words[], int *n)
2001 while (*sl && (*sl != '\n' || slash)) {
2005 skipspace = !skipspace;
2006 } else if (*sl == '\a') {
2007 /* handle already-translated " */
2008 skipspace = !skipspace;
2009 } else if (*sl == escapesym)
2011 else if ((*sl == ' ' || *sl == '\t') && !skipspace) {
2013 if (words[*n] != sl)
2021 if (words[*n] != sl)
2024 while (*sl && *sl != '\n')
2033 if (sl != words[*n])
2038 static char *abbrev_list[] = {
2039 "GSBG", "Getting Started ",
2040 "SUBG", "Customizing SunOS",
2041 "SHBG", "Basic Troubleshooting",
2042 "SVBG", "SunView User's Guide",
2043 "MMBG", "Mail and Messages",
2044 "DMBG", "Doing More with SunOS",
2045 "UNBG", "Using the Network",
2046 "GDBG", "Games, Demos & Other Pursuits",
2047 "CHANGE", "SunOS 4.1 Release Manual",
2048 "INSTALL", "Installing SunOS 4.1",
2049 "ADMIN", "System and Network Administration",
2050 "SECUR", "Security Features Guide",
2051 "PROM", "PROM User's Manual",
2052 "DIAG", "Sun System Diagnostics",
2053 "SUNDIAG", "Sundiag User's Guide",
2054 "MANPAGES", "SunOS Reference Manual",
2055 "REFMAN", "SunOS Reference Manual",
2056 "SSI", "Sun System Introduction",
2057 "SSO", "System Services Overview",
2058 "TEXT", "Editing Text Files",
2059 "DOCS", "Formatting Documents",
2060 "TROFF", "Using <B>nroff</B> and <B>troff</B>",
2061 "INDEX", "Global Index",
2062 "CPG", "C Programmer's Guide",
2063 "CREF", "C Reference Manual",
2064 "ASSY", "Assembly Language Reference",
2065 "PUL", "Programming Utilities and Libraries",
2066 "DEBUG", "Debugging Tools",
2067 "NETP", "Network Programming",
2068 "DRIVER", "Writing Device Drivers",
2069 "STREAMS", "STREAMS Programming",
2070 "SBDK", "SBus Developer's Kit",
2071 "WDDS", "Writing Device Drivers for the SBus",
2072 "FPOINT", "Floating-Point Programmer's Guide",
2073 "SVPG", "SunView 1 Programmer's Guide",
2074 "SVSPG", "SunView 1 System Programmer's Guide",
2075 "PIXRCT", "Pixrect Reference Manual",
2076 "CGI", "SunCGI Reference Manual",
2077 "CORE", "SunCore Reference Manual",
2078 "4ASSY", "Sun-4 Assembly Language Reference",
2079 "SARCH", "<FONT SIZE=-1>SPARC</FONT> Architecture Manual",
2080 "KR", "The C Programming Language",
2084 lookup_abbrev(char *c)
2090 while (abbrev_list[i] && strcmp(c, abbrev_list[i]))
2093 return abbrev_list[i + 1];
2098 static char manidx[NULL_TERMINATED(HUGE_STR_MAX)];
2099 static int subs = 0;
2101 static char label[5] = "lbAA";
2104 add_to_index(int level, char *item)
2109 if (label[3] > 'Z') {
2113 if (level != subs) {
2115 strmaxcpy(manidx + mip, "</DL>\n", HUGE_STR_MAX - mip);
2118 strmaxcpy(manidx + mip, "<DL>\n", HUGE_STR_MAX - mip);
2123 scan_troff(item, 1, &c);
2124 sprintf(manidx + mip, "<DT><A HREF=\"#%s\">%s</A><DD>\n", label, c);
2132 skip_till_newline(char *c)
2136 while (*c && *c != '\n' || lvl > 0) {
2147 if (lvl < 0 && newline_for_fun) {
2148 newline_for_fun = newline_for_fun + lvl;
2149 if (newline_for_fun < 0)
2150 newline_for_fun = 0;
2156 outputPageHeader(char *l, char *c, char *r)
2158 out_html("<TABLE WIDTH=100%>\n<TR>\n");
2159 out_html("<TH ALIGN=LEFT width=33%>");
2161 out_html("<TH ALIGN=CENTER width=33%>");
2163 out_html("<TH ALIGN=RIGHT width=33%>");
2165 out_html("\n</TR>\n</TABLE>\n");
2169 outputPageFooter(char *l, char *c, char *r)
2172 outputPageHeader(l, c, r);
2175 static int ifelseval = 0;
2178 scan_request(char *c)
2180 /* BSD Mandoc stuff */
2181 static int mandoc_synopsis = 0; /* True if we are in the synopsis
2183 static int mandoc_command = 0; /* True if this is mandoc page */
2184 static int mandoc_bd_options; /* Only copes with non-nested Bd's */
2188 char *wordlist[MAX_WORDLIST];
2193 while (*c == ' ' || *c == '\t')
2201 while (c[j] == ' ' || c[j] == '\t')
2203 if (c[0] == escapesym) {
2204 /* some pages use .\" .\$1 .\} */
2205 /* .\$1 is too difficult/stupid */
2207 c = skip_till_newline(c);
2209 c = scan_escape(c + 1);
2215 while (*h && *h != '\n')
2218 if (scaninbuff && buffpos) {
2219 buffer[buffpos] = '\0';
2222 /* fprintf(stderr, "%s\n", c+2); */
2228 int oldcurpos = curpos;
2236 while (*c && *c != '\n')
2240 while (*c && strncmp(c, ".di", 3))
2241 while (*c && *c++ != '\n');
2244 while (de && de->nr != i)
2247 de = (STRDEF *) malloc(sizeof(STRDEF));
2259 scan_troff(h, 0, &de->st);
2261 while (*c && *c++ != '\n');
2269 int oldcurpos = curpos;
2274 while (c[j] && c[j] != '\n')
2289 while (de && de->nr != i)
2296 de = (STRDEF *) malloc(sizeof(STRDEF));
2303 c = scan_troff(c, 1, &h);
2310 c = scan_troff(c, 1, &h);
2315 c = scan_troff(c, 1, &de->st);
2329 if (c[0] == escapesym) {
2330 c = scan_escape(c + 1);
2332 c = skip_till_newline(c);
2340 c = skip_till_newline(c);
2348 c = skip_till_newline(c);
2356 while ('0' <= *c && *c <= '9') {
2357 i = i * 10 + *c - '0';
2361 c = skip_till_newline(c);
2362 /* center next i lines */
2364 out_html("<CENTER>\n");
2368 c = scan_troff(c, 1, &line);
2369 if (line && strncmp(line, "<BR>", 4)) {
2375 out_html("</CENTER>\n");
2386 c = skip_till_newline(c);
2389 c = skip_till_newline(c);
2397 fieldsym = padsym = '\0';
2402 c = skip_till_newline(c);
2406 out_html(change_to_font(0));
2407 out_html(change_to_size('0'));
2408 out_html("</PRE>\n");
2412 c = skip_till_newline(c);
2417 out_html(change_to_font(0));
2419 if (*c == escapesym) {
2422 c = scan_expression(c, &fn);
2424 out_html(change_to_font(fn));
2426 out_html(change_to_font(*c));
2430 c = skip_till_newline(c);
2433 /* .el anything : else part of if else */
2437 c = scan_troff(c, 1, NULL);
2439 c = skip_till_newline(c + j);
2442 /* .ie c anything : then part of if else */
2445 * .if c anything .if !c anything .if N anything .if
2446 * !N anything .if 'string1'string2' anything .if
2447 * !'string1'string2' anything
2450 c = scan_expression(c, &i);
2455 c = scan_troff(c, 1, NULL);
2457 c = skip_till_newline(c);
2461 char *endwith = "..\n";
2469 while (*c && *c != '\n')
2473 while (*c && strncmp(c, endwith, i))
2474 while (*c++ != '\n');
2475 while (*c++ != '\n');
2480 out_html(change_to_font(0));
2481 out_html(change_to_size('0'));
2482 out_html("<PRE>\n");
2486 c = skip_till_newline(c);
2491 out_html(change_to_size('0'));
2498 } else if (*c == '+') {
2502 c = scan_expression(c, &i);
2508 out_html(change_to_size(i * j));
2510 c = skip_till_newline(c);
2521 c = skip_till_newline(c);
2544 scan_troff(h, 1, &name);
2549 if (stat(h, &stbuf) != -1)
2551 buf = stralloc(l + 4);
2556 t = strrchr(fname, '/');
2559 fprintf(stderr, "ln -s %s.html %s.html\n", h, t);
2560 s = strrchr(t, '.');
2563 printf("<HTML><HEAD><TITLE> Manpage of %s</TITLE>\n"
2565 "See the manpage for <A HREF=\"%s.html\">%s</A>.\n"
2572 * this works alright, except for
2575 buf = read_man_page(h);
2578 fprintf(stderr, "man2html: unable to open or read file %s.\n",
2580 out_html("<BLOCKQUOTE>"
2581 "man2html: unable to open or read file.\n");
2583 out_html("</BLOCKQUOTE>\n");
2585 buf[0] = buf[l] = '\n';
2586 buf[l + 1] = buf[l + 2] = '\0';
2587 scan_troff(buf + 1, 0, NULL);
2598 while (*c != '\n') {
2599 sl = scan_expression(c, &tabstops[j]);
2600 if (*c == '-' || *c == '+')
2601 tabstops[j] += tabstops[j - 1];
2603 while (*c == ' ' || *c == '\t')
2612 * while (itemdepth || dl_set[itemdepth]) {
2613 * out_html("</DL>\n"); if (dl_set[itemdepth])
2614 * dl_set[itemdepth]=0; else itemdepth--; }
2618 c = scan_expression(c, &j);
2619 for (i = 0; i < j; i++)
2622 c = skip_till_newline(c);
2630 /* fprintf(stderr,"%s\n", h); */
2637 /* parse one line in a certain font */
2638 out_html(change_to_font(*c));
2639 trans_char(c, '"', '\a');
2643 c = scan_troff(c, 1, NULL);
2644 out_html(change_to_font('R'));
2651 case V('O', 'P'): /* groff manpages use this
2653 /* .OP a b : [ <B>a</B> <I>b</I> ] */
2657 out_html(change_to_font('R'));
2674 sl = fill_words(c, wordlist, &words);
2677 * .BR name (section) indicates a link. It
2678 * will be added in the output routine.
2680 for (i = 0; i < words; i++) {
2685 wordlist[i][-1] = ' ';
2686 out_html(change_to_font(font[i & 1]));
2687 scan_troff(wordlist[i], 1, NULL);
2689 out_html(change_to_font('R'));
2702 for (j = 0; j < 20; j++)
2703 tabstops[j] = (j + 1) * 8;
2705 c = skip_till_newline(c);
2708 sl = fill_words(c + j, wordlist, &words);
2710 if (!dl_set[itemdepth]) {
2711 out_html("<DL COMPACT>\n");
2712 dl_set[itemdepth] = 1;
2716 scan_troff(wordlist[0], 1, NULL);
2722 if (!dl_set[itemdepth]) {
2723 out_html("<DL COMPACT>\n");
2724 dl_set[itemdepth] = 1;
2727 c = skip_till_newline(c);
2728 /* somewhere a definition ends with '.TP' */
2732 c = scan_troff(c, 1, NULL);
2739 sl = fill_words(c + j, wordlist, &words);
2742 while (idxlabel[j] == 'Z')
2743 idxlabel[j--] = 'A';
2746 fprintf(idxfile, "%s@%s@", fname, idxlabel);
2747 for (j = 0; j < words; j++) {
2749 scan_troff(wordlist[j], 1, &h);
2750 fprintf(idxfile, "_\b@%s", h);
2753 fprintf(idxfile, "\n");
2755 out_html("<A NAME=\"");
2758 * this will not work in mosaic (due to a bug).
2759 * Adding ' ' between '>' and '<' solves it, but
2760 * creates some space. A normal space does not work.
2762 out_html("\"></A>");
2766 if (dl_set[itemdepth]) {
2767 out_html("</DL>\n");
2768 dl_set[itemdepth] = 0;
2777 c = skip_till_newline(c);
2780 if (!dl_set[itemdepth]) {
2781 out_html("<DL COMPACT>");
2782 dl_set[itemdepth] = 1;
2786 c = skip_till_newline(c);
2790 c = skip_till_newline(c);
2792 case V('R', 's'): /* BSD mandoc */
2794 sl = fill_words(c + j, wordlist, &words);
2797 scan_expression(wordlist[0], &j);
2800 dl_set[itemdepth] = 0;
2801 out_html("<DL COMPACT><DT><DD>");
2802 c = skip_till_newline(c);
2806 case V('R', 'e'): /* BSD mandoc */
2808 if (itemdepth > 0) {
2809 if (dl_set[itemdepth])
2811 out_html("</DL>\n");
2814 c = skip_till_newline(c);
2818 out_html(change_to_size(-1));
2819 out_html(change_to_font('B'));
2820 c = scan_troff(c + j, 1, NULL);
2821 out_html(change_to_font('R'));
2822 out_html(change_to_size('0'));
2828 out_html(change_to_size(-1));
2829 trans_char(c, '"', '\a');
2830 c = scan_troff(c, 1, NULL);
2831 out_html(change_to_size('0'));
2833 case V('S', 's'): /* BSD mandoc */
2837 case V('S', 'h'): /* BSD mandoc */
2838 /* hack for fallthru from above */
2839 mandoc_command = !mode || mandoc_command;
2844 while (itemdepth || dl_set[itemdepth]) {
2845 out_html("</DL>\n");
2846 if (dl_set[itemdepth])
2847 dl_set[itemdepth] = 0;
2848 else if (itemdepth > 0)
2851 out_html(change_to_font(0));
2852 out_html(change_to_size(0));
2857 trans_char(c, '"', '\a');
2858 add_to_index(mode, c);
2859 out_html("<A NAME=\"");
2861 /* for mosaic users */
2863 out_html("\"> </A>\n<H4>");
2865 out_html("\"> </A>\n<H3>");
2866 mandoc_synopsis = strncmp(c, "SYNOPSIS", 8) == 0;
2867 c = mandoc_command ? scan_troff_mandoc(c, 1, NULL) : scan_troff(c, 1, NULL);
2869 out_html("</H4>\n");
2871 out_html("</H3>\n");
2877 case V('D', 't'): /* BSD mandoc */
2880 if (!output_possible) {
2881 sl = fill_words(c + j, wordlist, &words);
2884 for (i = 1; i < words; i++)
2885 wordlist[i][-1] = '\0';
2887 output_possible = 1;
2888 sprintf(th_page_and_sec, "%s(%s)", wordlist[0], wordlist[1]);
2890 t = unescape(wordlist[2]);
2891 strncpy(th_datestr, t, sizeof(th_datestr));
2892 th_datestr[sizeof(th_datestr) - 1] = '\0';
2894 th_datestr[0] = '\0';
2896 t = unescape(wordlist[3]);
2897 strncpy(th_version, t, sizeof(th_version));
2898 th_version[sizeof(th_version) - 1] = '\0';
2900 th_version[0] = '\0';
2901 out_html("<HTML><HEAD>\n<TITLE>");
2902 out_html(th_page_and_sec);
2903 out_html(" Manual Page");
2904 out_html("</TITLE>\n</HEAD>\n<BODY>");
2906 outputPageHeader(th_page_and_sec, th_datestr, th_page_and_sec);
2908 out_html("<BR><A HREF=\"#index\">Index</A>\n");
2912 out_html("<BR>BSD mandoc<BR>");
2916 c = skip_till_newline(c);
2920 sl = fill_words(c + j, wordlist, &words);
2922 out_html(change_to_font('I'));
2924 wordlist[1][-1] = '\0';
2925 c = lookup_abbrev(wordlist[0]);
2926 curpos += strlen(c);
2928 out_html(change_to_font('R'));
2930 out_html(wordlist[1]);
2935 /* .rm xx : Remove request, macro or string */
2938 * .rn xx yy : Rename request, macro or string xx to
2947 while (isspace(*c) && *c != '\n')
2950 while (*c && *c != '\n')
2954 while (de && de->nr != j)
2962 while (de && de->nr != i)
2969 /* .nx filename : next file. */
2971 /* .in +-N : Indent */
2972 c = skip_till_newline(c);
2976 * .nr R +-N M: define and set number register R by
2977 * +-N; auto-increment by M
2986 while (intd && intd->nr != i)
2989 intd = (INTDEF *) malloc(sizeof(INTDEF));
2993 intd->next = intdef;
2996 while (*c == ' ' || *c == '\t')
2998 c = scan_expression(c, &intd->val);
3000 while (*c == ' ' || *c == '\t')
3002 c = scan_expression(c, &intd->incr);
3004 c = skip_till_newline(c);
3008 /* .am xx yy : append to a macro. */
3009 /* define or handle as .ig yy */
3013 * .de xx yy : define or redefine macro xx; end at
3016 /* define or handle as .ig yy */
3022 sl = fill_words(c, wordlist, &words);
3029 wordlist[1][0] = '.';
3034 while (*c && strncmp(c, wordlist[1], j))
3035 c = skip_till_newline(c);
3037 while (de && de->nr != i)
3040 olen = strlen(de->st);
3042 h = stralloc(j * 2 + 4);
3044 for (j = 0; j < olen; j++)
3046 if (!j || h[j - 1] != '\n')
3049 if (sl[0] == '\\' && sl[1] == '\\') {
3062 de = (STRDEF *) malloc(sizeof(STRDEF));
3070 c = skip_till_newline(c);
3072 case V('B', 'l'): /* BSD mandoc */
3074 char list_options[NULL_TERMINATED(MED_STR_MAX)];
3075 char *nl = strchr(c, '\n');
3078 if (dl_set[itemdepth]) { /* These things can
3082 if (nl) { /* Parse list options */
3083 strlimitcpy(list_options, c, nl - c, MED_STR_MAX);
3085 if (strstr(list_options, "-bullet")) { /* HTML Unnumbered List */
3086 dl_set[itemdepth] = BL_BULLET_LIST;
3088 } else if (strstr(list_options, "-enum")) { /* HTML Ordered List */
3089 dl_set[itemdepth] = BL_ENUM_LIST;
3091 } else { /* HTML Descriptive List */
3092 dl_set[itemdepth] = BL_DESC_LIST;
3093 out_html("<DL COMPACT>\n");
3102 c = skip_till_newline(c);
3105 case V('E', 'l'): /* BSD mandoc */
3107 if (dl_set[itemdepth] & BL_DESC_LIST) {
3108 out_html("</DL>\n");
3109 } else if (dl_set[itemdepth] & BL_BULLET_LIST) {
3110 out_html("</UL>\n");
3111 } else if (dl_set[itemdepth] & BL_ENUM_LIST) {
3112 out_html("</OL>\n");
3114 dl_set[itemdepth] = 0;
3124 c = skip_till_newline(c);
3126 case V('I', 't'): /* BSD mandoc */
3128 if (strncmp(c, "Xo", 2) == 0 && isspace(*(c + 2))) {
3129 c = skip_till_newline(c);
3131 if (dl_set[itemdepth] & BL_DESC_LIST) {
3133 out_html(change_to_font('B'));
3134 if (*c == '\n') { /* Don't allow embedded
3135 * comms after a newline */
3137 c = scan_troff(c, 1, NULL);
3138 } else { /* Do allow embedded comms on
3140 c = scan_troff_mandoc(c, 1, NULL);
3142 out_html(change_to_font('R'));
3145 } else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST)) {
3147 c = scan_troff_mandoc(c, 1, NULL);
3155 case V('B', 'k'): /* BSD mandoc */
3156 case V('E', 'k'): /* BSD mandoc */
3157 case V('D', 'd'): /* BSD mandoc */
3158 case V('O', 's'): /* BSD mandoc */
3159 trans_char(c, '"', '\a');
3163 c = scan_troff_mandoc(c, 1, NULL);
3170 case V('B', 't'): /* BSD mandoc */
3171 trans_char(c, '"', '\a');
3173 out_html(" is currently in beta test.");
3179 case V('B', 'x'): /* BSD mandoc */
3180 trans_char(c, '"', '\a');
3185 c = scan_troff_mandoc(c, 1, NULL);
3191 case V('D', 'l'): /* BSD mandoc */
3194 out_html("<BLOCKQUOTE>");
3195 out_html(change_to_font('L'));
3198 c = scan_troff_mandoc(c, 1, NULL);
3199 out_html(change_to_font('R'));
3200 out_html("</BLOCKQUOTE>");
3206 case V('B', 'd'): /* BSD mandoc */
3207 { /* Seems like a kind of example/literal mode */
3208 char bd_options[NULL_TERMINATED(MED_STR_MAX)];
3209 char *nl = strchr(c, '\n');
3213 strlimitcpy(bd_options, c, nl - c, MED_STR_MAX);
3216 mandoc_bd_options = 0; /* Remember options for
3218 if (strstr(bd_options, "-offset indent")) {
3219 mandoc_bd_options |= BD_INDENT;
3220 out_html("<BLOCKQUOTE>\n");
3222 if (strstr(bd_options, "-literal")
3223 || strstr(bd_options, "-unfilled")) {
3225 mandoc_bd_options |= BD_LITERAL;
3226 out_html(change_to_font(0));
3227 out_html(change_to_size('0'));
3228 out_html("<PRE>\n");
3233 c = skip_till_newline(c);
3236 case V('E', 'd'): /* BSD mandoc */
3237 if (mandoc_bd_options & BD_LITERAL) {
3239 out_html(change_to_font(0));
3240 out_html(change_to_size('0'));
3241 out_html("</PRE>\n");
3244 if (mandoc_bd_options & BD_INDENT)
3245 out_html("</BLOCKQUOTE>\n");
3248 c = skip_till_newline(c);
3250 case V('B', 'e'): /* BSD mandoc */
3259 c = skip_till_newline(c);
3261 case V('X', 'r'): /* BSD mandoc */
3264 * Translate xyz 1 to xyz(1) Allow for
3265 * multiple spaces. Allow the section to be
3268 char buff[NULL_TERMINATED(MED_STR_MAX)];
3271 trans_char(c, '"', '\a');
3275 c++; /* Skip spaces */
3276 while (isspace(*c) && *c != '\n')
3278 while (isalnum(*c)) { /* Copy the xyz part */
3281 if (bufptr >= buff + MED_STR_MAX)
3285 while (isspace(*c) && *c != '\n')
3286 c++; /* Skip spaces */
3287 if (isdigit(*c)) { /* Convert the number if
3291 if (bufptr < buff + MED_STR_MAX) {
3292 while (isalnum(*c)) {
3295 if (bufptr >= buff + MED_STR_MAX)
3299 if (bufptr < buff + MED_STR_MAX) {
3305 while (*c != '\n') { /* Copy the remainder */
3309 if (bufptr >= buff + MED_STR_MAX)
3315 scan_troff_mandoc(buff, 1, NULL);
3324 case V('F', 'l'): /* BSD mandoc */
3325 trans_char(c, '"', '\a');
3329 out_html(change_to_font('B'));
3330 c = scan_troff_mandoc(c, 1, NULL);
3331 out_html(change_to_font('R'));
3339 case V('P', 'a'): /* BSD mandoc */
3340 case V('P', 'f'): /* BSD mandoc */
3341 trans_char(c, '"', '\a');
3345 c = scan_troff_mandoc(c, 1, NULL);
3352 case V('P', 'p'): /* BSD mandoc */
3360 c = skip_till_newline(c);
3362 case V('D', 'q'): /* BSD mandoc */
3363 trans_char(c, '"', '\a');
3368 c = scan_troff_mandoc(c, 1, NULL);
3376 case V('O', 'p'): /* BSD mandoc */
3377 trans_char(c, '"', '\a');
3381 out_html(change_to_font('R'));
3383 c = scan_troff_mandoc(c, 1, NULL);
3384 out_html(change_to_font('R'));
3392 case V('O', 'o'): /* BSD mandoc */
3393 trans_char(c, '"', '\a');
3397 out_html(change_to_font('R'));
3399 c = scan_troff_mandoc(c, 1, NULL);
3405 case V('O', 'c'): /* BSD mandoc */
3406 trans_char(c, '"', '\a');
3408 c = scan_troff_mandoc(c, 1, NULL);
3409 out_html(change_to_font('R'));
3416 case V('P', 'q'): /* BSD mandoc */
3417 trans_char(c, '"', '\a');
3422 c = scan_troff_mandoc(c, 1, NULL);
3430 case V('Q', 'l'): /* BSD mandoc */
3431 { /* Single quote first word in the line */
3434 trans_char(c, '"', '\a');
3439 do { /* Find first whitespace after the
3440 * first word that isn't a mandoc
3442 while (*sp && isspace(*sp))
3444 while (*sp && !isspace(*sp))
3446 } while (*sp && isupper(*(sp - 2)) && islower(*(sp - 1)));
3449 * Use a newline to mark the end of text to
3454 out_html("`"); /* Quote the text */
3455 c = scan_troff_mandoc(c, 1, NULL);
3464 case V('S', 'q'): /* BSD mandoc */
3465 trans_char(c, '"', '\a');
3470 c = scan_troff_mandoc(c, 1, NULL);
3478 case V('A', 'r'): /* BSD mandoc */
3479 /* parse one line in italics */
3480 out_html(change_to_font('I'));
3481 trans_char(c, '"', '\a');
3483 if (*c == '\n') { /* An empty Ar means "file
3485 out_html("file ...");
3487 c = scan_troff_mandoc(c, 1, NULL);
3489 out_html(change_to_font('R'));
3496 case V('A', 'd'): /* BSD mandoc */
3497 case V('E', 'm'): /* BSD mandoc */
3498 case V('V', 'a'): /* BSD mandoc */
3499 case V('X', 'c'): /* BSD mandoc */
3500 /* parse one line in italics */
3501 out_html(change_to_font('I'));
3502 trans_char(c, '"', '\a');
3506 c = scan_troff_mandoc(c, 1, NULL);
3507 out_html(change_to_font('R'));
3514 case V('N', 'd'): /* BSD mandoc */
3515 trans_char(c, '"', '\a');
3520 c = scan_troff_mandoc(c, 1, NULL);
3527 case V('N', 'm'): /* BSD mandoc */
3529 static char mandoc_name[NULL_TERMINATED(SMALL_STR_MAX)] = "";
3531 trans_char(c, '"', '\a');
3533 if (mandoc_synopsis) { /* Break lines only in
3536 * seems to be treated
3537 * as a special case -
3539 static int count = 0; /* Don't break on the
3545 char *end = strchr(c, '\n');
3547 if (end) { /* Remember the name for
3549 strlimitcpy(mandoc_name, c, end - c, SMALL_STR_MAX);
3554 out_html(change_to_font('B'));
3555 while (*c == ' ' || *c == '\t')
3557 if (*c == '\n') { /* If Nm has no
3559 * from an earlier Nm
3560 * command that did have
3565 out_html(mandoc_name);
3567 c = scan_troff_mandoc(c, 1, NULL);
3569 out_html(change_to_font('R'));
3577 case V('C', 'd'): /* BSD mandoc */
3578 case V('C', 'm'): /* BSD mandoc */
3579 case V('I', 'c'): /* BSD mandoc */
3580 case V('M', 's'): /* BSD mandoc */
3581 case V('O', 'r'): /* BSD mandoc */
3582 case V('S', 'y'): /* BSD mandoc */
3583 /* parse one line in bold */
3584 out_html(change_to_font('B'));
3585 trans_char(c, '"', '\a');
3589 c = scan_troff_mandoc(c, 1, NULL);
3590 out_html(change_to_font('R'));
3597 case V('D', 'v'): /* BSD mandoc */
3598 case V('E', 'v'): /* BSD mandoc */
3599 case V('F', 'r'): /* BSD mandoc */
3600 case V('L', 'i'): /* BSD mandoc */
3601 case V('N', 'o'): /* BSD mandoc */
3602 case V('N', 's'): /* BSD mandoc */
3603 case V('T', 'n'): /* BSD mandoc */
3604 case V('n', 'N'): /* BSD mandoc */
3605 trans_char(c, '"', '\a');
3609 out_html(change_to_font('B'));
3610 c = scan_troff_mandoc(c, 1, NULL);
3611 out_html(change_to_font('R'));
3618 case V('%', 'A'): /* BSD mandoc biblio stuff */
3628 c = scan_troff(c, 1, NULL); /* Don't allow embedded
3640 out_html(change_to_font('I'));
3643 c = scan_troff(c, 1, NULL); /* Don't allow embedded
3645 out_html(change_to_font('R'));
3652 /* search macro database of self-defined macros */
3654 while (owndef && owndef->nr != i)
3655 owndef = owndef->next;
3661 sl = fill_words(c + j, wordlist, &words);
3664 for (i = 1; i < words; i++)
3665 wordlist[i][-1] = '\0';
3666 for (i = 0; i < words; i++) {
3669 if (mandoc_command) {
3670 scan_troff_mandoc(wordlist[i], 1, &h);
3672 scan_troff(wordlist[i], 1, &h);
3676 for (i = words; i < 20; i++)
3678 deflen = strlen(owndef->st);
3679 for (i = 0; owndef->st[deflen + 2 + i] = owndef->st[i]; i++);
3680 oldargument = argument;
3681 argument = wordlist;
3682 onff = newline_for_fun;
3683 if (mandoc_command) {
3684 scan_troff_mandoc(owndef->st + deflen + 2, 0, NULL);
3686 scan_troff(owndef->st + deflen + 2, 0, NULL);
3688 newline_for_fun = onff;
3689 argument = oldargument;
3690 for (i = 0; i < words; i++)
3694 } else if (mandoc_command &&
3695 ((isupper(*c) && islower(*(c + 1)))
3696 || (islower(*c) && isupper(*(c + 1))))
3697 ) { /* Let through any BSD mandoc
3698 * commands that haven't been delt
3699 * with. I don't want to miss
3700 * anything out of the text. */
3706 out_html(buf); /* Print the command (it
3707 * might just be text). */
3709 trans_char(c, '"', '\a');
3712 out_html(change_to_font('R'));
3713 c = scan_troff(c, 1, NULL);
3720 c = skip_till_newline(c);
3738 static int contained_tab = 0;
3739 static int mandoc_line = 0; /* Signals whether to look for embedded
3740 * mandoc commands. */
3742 /* san : stop at newline */
3744 scan_troff(char *c, int san, char **result)
3747 char intbuff[NULL_TERMINATED(MED_STR_MAX)];
3751 int exbuffpos, exbuffmax, exscaninbuff, exnewline_for_fun;
3754 #define FLUSHIBP if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; }
3757 exbuffpos = buffpos;
3758 exbuffmax = buffmax;
3759 exnewline_for_fun = newline_for_fun;
3760 exscaninbuff = scaninbuff;
3761 newline_for_fun = 0;
3765 buffpos = strlen(buffer);
3768 buffer = stralloc(LARGE_STR_MAX);
3770 buffmax = LARGE_STR_MAX;
3775 /* start scanning */
3777 while (*h && (!san || newline_for_fun || *h != '\n')) {
3779 if (*h == escapesym) {
3783 } else if (*h == controlsym && h[-1] == '\n') {
3786 h = scan_request(h);
3787 if (san && h[-1] == '\n')
3789 } else if (mandoc_line
3790 && *(h) && isupper(*(h))
3791 && *(h + 1) && islower(*(h + 1))
3792 && *(h + 2) && isspace(*(h + 2))) {
3794 * BSD imbedded command eg ".It Fl Ar arg1 Fl Ar
3798 h = scan_request(h);
3799 if (san && h[-1] == '\n')
3801 } else if (*h == nobreaksym && h[-1] == '\n') {
3804 h = scan_request(h);
3805 if (san && h[-1] == '\n')
3810 if (h[-1] == '\n' && still_dd && isalnum(*h)) {
3812 * sometimes a .HP request is not followed by
3822 intbuff[ibp++] = '&';
3823 intbuff[ibp++] = 'a';
3824 intbuff[ibp++] = 'm';
3825 intbuff[ibp++] = 'p';
3826 intbuff[ibp++] = ';';
3830 intbuff[ibp++] = '&';
3831 intbuff[ibp++] = 'l';
3832 intbuff[ibp++] = 't';
3833 intbuff[ibp++] = ';';
3837 intbuff[ibp++] = '&';
3838 intbuff[ibp++] = 'g';
3839 intbuff[ibp++] = 't';
3840 intbuff[ibp++] = ';';
3844 intbuff[ibp++] = '&';
3845 intbuff[ibp++] = 'q';
3846 intbuff[ibp++] = 'u';
3847 intbuff[ibp++] = 'o';
3848 intbuff[ibp++] = 't';
3849 intbuff[ibp++] = ';';
3853 if (h[-1] == '\n' && fillout) {
3854 intbuff[ibp++] = '<';
3855 intbuff[ibp++] = 'P';
3856 intbuff[ibp++] = '>';
3858 if (contained_tab && fillout) {
3859 intbuff[ibp++] = '<';
3860 intbuff[ibp++] = 'B';
3861 intbuff[ibp++] = 'R';
3862 intbuff[ibp++] = '>';
3867 intbuff[ibp++] = '\n';
3875 /* like a typewriter, not like TeX */
3876 tabstops[19] = curpos + 1;
3877 while (curtab < maxtstop && tabstops[curtab] <= curpos)
3879 if (curtab < maxtstop) {
3881 while (curpos < tabstops[curtab]) {
3882 intbuff[ibp++] = ' ';
3890 while (curpos < tabstops[curtab]) {
3900 if (*h == ' ' && (h[-1] == '\n' || usenbsp)) {
3902 if (!usenbsp && fillout) {
3910 intbuff[ibp++] = ' ';
3911 } else if (*h > 31 && *h < 127)
3912 intbuff[ibp++] = *h;
3913 else if (((unsigned char) (*h)) > 127) {
3914 intbuff[ibp++] = '&';
3915 intbuff[ibp++] = '#';
3916 intbuff[ibp++] = '0' + ((unsigned char) (*h)) / 100;
3917 intbuff[ibp++] = '0' + (((unsigned char) (*h)) % 100) / 10;
3918 intbuff[ibp++] = '0' + ((unsigned char) (*h)) % 10;
3919 intbuff[ibp++] = ';';
3924 if (ibp > (MED_STR_MAX - 20))
3931 buffer[buffpos] = '\0';
3934 newline_for_fun = exnewline_for_fun;
3938 buffpos = exbuffpos;
3939 buffmax = exbuffmax;
3940 scaninbuff = exscaninbuff;
3947 scan_troff_mandoc(char *c, int san, char **result)
3949 char *ret, *end = c;
3950 int oldval = mandoc_line;
3953 while (*end && *end != '\n') {
3958 && ispunct(*(end - 1))
3959 && isspace(*(end - 2)) && *(end - 2) != '\n') {
3961 * Don't format lonely punctuation E.g. in "xyz ," format the
3962 * xyz and then append the comma removing the space.
3965 ret = scan_troff(c, san, result);
3966 *(end - 2) = *(end - 1);
3969 ret = scan_troff(c, san, result);
3971 mandoc_line = oldval;
3975 main(int argc, char **argv)
3985 while ((i = getopt(argc, argv, "")) != EOF) {
3997 manpage = h = t = argv[1];
4000 buf = read_man_page(h);
4002 fprintf(stderr, "man2html: cannot read %s: %s\n", h, strerror(errno));
4006 idxfile = fopen(INDEXFILE, "a");
4008 stdf = &standardchar[0];
4011 stdf->next = &standardchar[i];
4015 chardef = &standardchar[0];
4017 stdf = &standardstring[0];
4020 stdf->next = &standardstring[i];
4024 strdef = &standardstring[0];
4026 intdef = &standardint[0];
4028 while (intdef->nr) {
4029 intdef->next = &standardint[i];
4030 intdef = intdef->next;
4033 intdef = &standardint[0];
4037 scan_troff(buf + 1, 0, NULL);
4039 while (itemdepth || dl_set[itemdepth]) {
4040 out_html("</DL>\n");
4041 if (dl_set[itemdepth])
4042 dl_set[itemdepth] = 0;
4043 else if (itemdepth > 0)
4047 out_html(change_to_font(0));
4048 out_html(change_to_size(0));
4055 if (output_possible) {
4056 outputPageFooter(th_version, th_datestr, th_page_and_sec);
4057 /* for mosaic users */
4058 fputs("<HR>\n<A NAME=\"index\"> </A><H2>Index</H2>\n<DL>\n", stdout);
4060 fputs(manidx, stdout);
4062 fputs("</DL>\n", stdout);
4063 fputs("</DL>\n", stdout);
4065 fputs("</BODY>\n</HTML>\n", stdout);
4067 fprintf(stderr, "man2html: no output produced\n");