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.)
77 #include <sys/types.h>
82 #define NULL_TERMINATED(n) ((n) + 1)
84 #define HUGE_STR_MAX 10000
85 #define LARGE_STR_MAX 2000
86 #define MED_STR_MAX 500
87 #define SMALL_STR_MAX 100
88 #define TINY_STR_MAX 10
90 #define MAX_MAN_PATHS 100 /* Max number of directories */
91 #define MAX_ZCATS 10 /* Max number of zcat style programs */
92 #define MAX_WORDLIST 100
95 #define EXIT_SUCCESS 0
98 #define EXIT_FAILURE 1
104 static char location_base[NULL_TERMINATED(MED_STR_MAX)] = "";
106 static char th_page_and_sec[128] = { '\0' };
107 static char th_datestr[128] = { '\0' };
108 static char th_version[128] = { '\0' };
110 char *signature = "<HR>\nThis document was created by man2html from %s.<BR>\nTime: %s\n";
112 /* timeformat for signature */
113 #define TIMEFORMAT "%d %B %Y %T %Z"
117 /* BSD mandoc Bl/El lists to HTML list types */
118 #define BL_DESC_LIST 1
119 #define BL_BULLET_LIST 2
120 #define BL_ENUM_LIST 4
122 /* BSD mandoc Bd/Ed example(?) blocks */
126 #ifndef HAVE_STRERROR
130 static char emsg[40];
132 #if defined (HAVE_SYS_ERRLIST)
134 extern char *sys_errlist[];
136 if (e > 0 && e < sys_nerr)
137 return (sys_errlist[e]);
139 #endif /* HAVE_SYS_ERRLIST */
141 sprintf(emsg, "Unknown system error %d", e);
145 #endif /* !HAVE_STRERROR */
148 strgrow(char *old, int len)
150 char *new = realloc(old, (strlen(old) + len + 1) * sizeof(char));
153 fprintf(stderr, "man2html: out of memory");
162 /* allocate enough for len + NULL */
163 char *new = malloc((len + 1) * sizeof(char));
166 fprintf(stderr, "man2html: out of memory");
173 * Some systems don't have strdup so lets use our own - which can also
174 * check for out of memory.
177 strduplicate(char *from)
179 char *new = stralloc(strlen(from));
185 /* Assumes space for n plus a null */
187 strmaxcpy(char *to, char *from, int n)
189 int len = strlen(from);
191 strncpy(to, from, n);
192 to[(len <= n) ? len : n] = '\0';
197 strmaxcat(char *to, char *from, int n)
199 int to_len = strlen(to);
202 int from_len = strlen(from);
203 int cp = (to_len + from_len <= n) ? from_len : n - to_len;
205 strncpy(to + to_len, from, cp);
206 to[to_len + cp] = '\0';
211 /* Assumes space for limit plus a null */
213 strlimitcpy(char *to, char *from, int n, int limit)
215 int len = n > limit ? limit : n;
217 strmaxcpy(to, from, len);
223 * takes string and escapes all metacharacters. should be used before
224 * including string in system() or similar call.
227 escape_input(char *str)
230 static char new[NULL_TERMINATED(MED_STR_MAX)];
232 if (strlen(str) * 2 + 1 > MED_STR_MAX) {
234 "man2html: escape_input - str too long:\n%-80s...\n",
238 for (i = 0; i < strlen(str); i++) {
239 if (!(((str[i] >= 'A') && (str[i] <= 'Z')) ||
240 ((str[i] >= 'a') && (str[i] <= 'z')) ||
241 ((str[i] >= '0') && (str[i] <= '9')))) {
255 fprintf(stderr, "man2html: usage: man2html filename\n");
261 * below this you should not change anything unless you know a lot
262 * about this program or about troff.
265 typedef struct STRDEF STRDEF;
272 typedef struct INTDEF INTDEF;
280 static char NEWLINE[2] = "\n";
281 static char idxlabel[6] = "ixAAA";
283 #define INDEXFILE "/tmp/manindex.list"
286 static FILE *idxfile;
288 static STRDEF *chardef, *strdef, *defdef;
289 static INTDEF *intdef;
291 #define V(A,B) ((A)*256+(B))
293 static INTDEF standardint[] = {
294 {V('n', ' '), NROFF, 0, NULL},
295 {V('t', ' '), 1 - NROFF, 0, NULL},
296 {V('o', ' '), 1, 0, NULL},
297 {V('e', ' '), 0, 0, NULL},
298 {V('.', 'l'), 70, 0, NULL},
299 {V('.', '$'), 0, 0, NULL},
300 {V('.', 'A'), NROFF, 0, NULL},
301 {V('.', 'T'), 1 - NROFF, 0, NULL},
302 {V('.', 'V'), 1, 0, NULL}, /* the me package tests for this */
305 static STRDEF standardstring[] = {
306 {V('R', ' '), 1, "®", NULL},
307 {V('l', 'q'), 2, "``", NULL},
308 {V('r', 'q'), 2, "''", NULL},
313 static STRDEF standardchar[] = {
314 {V('*', '*'), 1, "*", NULL},
315 {V('*', 'A'), 1, "A", NULL},
316 {V('*', 'B'), 1, "B", NULL},
317 {V('*', 'C'), 2, "Xi", NULL},
318 {V('*', 'D'), 5, "Delta", NULL},
319 {V('*', 'E'), 1, "E", NULL},
320 {V('*', 'F'), 3, "Phi", NULL},
321 {V('*', 'G'), 5, "Gamma", NULL},
322 {V('*', 'H'), 5, "Theta", NULL},
323 {V('*', 'I'), 1, "I", NULL},
324 {V('*', 'K'), 1, "K", NULL},
325 {V('*', 'L'), 6, "Lambda", NULL},
326 {V('*', 'M'), 1, "M", NULL},
327 {V('*', 'N'), 1, "N", NULL},
328 {V('*', 'O'), 1, "O", NULL},
329 {V('*', 'P'), 2, "Pi", NULL},
330 {V('*', 'Q'), 3, "Psi", NULL},
331 {V('*', 'R'), 1, "P", NULL},
332 {V('*', 'S'), 5, "Sigma", NULL},
333 {V('*', 'T'), 1, "T", NULL},
334 {V('*', 'U'), 1, "Y", NULL},
335 {V('*', 'W'), 5, "Omega", NULL},
336 {V('*', 'X'), 1, "X", NULL},
337 {V('*', 'Y'), 1, "H", NULL},
338 {V('*', 'Z'), 1, "Z", NULL},
339 {V('*', 'a'), 5, "alpha", NULL},
340 {V('*', 'b'), 4, "beta", NULL},
341 {V('*', 'c'), 2, "xi", NULL},
342 {V('*', 'd'), 5, "delta", NULL},
343 {V('*', 'e'), 7, "epsilon", NULL},
344 {V('*', 'f'), 3, "phi", NULL},
345 {V('*', 'g'), 5, "gamma", NULL},
346 {V('*', 'h'), 5, "theta", NULL},
347 {V('*', 'i'), 4, "iota", NULL},
348 {V('*', 'k'), 5, "kappa", NULL},
349 {V('*', 'l'), 6, "lambda", NULL},
350 {V('*', 'm'), 1, "µ", NULL},
351 {V('*', 'n'), 2, "nu", NULL},
352 {V('*', 'o'), 1, "o", NULL},
353 {V('*', 'p'), 2, "pi", NULL},
354 {V('*', 'q'), 3, "psi", NULL},
355 {V('*', 'r'), 3, "rho", NULL},
356 {V('*', 's'), 5, "sigma", NULL},
357 {V('*', 't'), 3, "tau", NULL},
358 {V('*', 'u'), 7, "upsilon", NULL},
359 {V('*', 'w'), 5, "omega", NULL},
360 {V('*', 'x'), 3, "chi", NULL},
361 {V('*', 'y'), 3, "eta", NULL},
362 {V('*', 'z'), 4, "zeta", NULL},
363 {V('t', 's'), 5, "sigma", NULL},
364 {V('+', '-'), 1, "±", NULL},
365 {V('1', '2'), 1, "½", NULL},
366 {V('1', '4'), 1, "¼", NULL},
367 {V('3', '4'), 1, "¾", NULL},
368 {V('F', 'i'), 3, "ffi", NULL},
369 {V('F', 'l'), 3, "ffl", NULL},
370 {V('a', 'a'), 1, "´", NULL},
371 {V('a', 'p'), 1, "~", NULL},
372 {V('b', 'r'), 1, "|", NULL},
373 {V('b', 'u'), 1, "*", NULL},
374 {V('b', 'v'), 1, "|", NULL},
375 {V('c', 'i'), 1, "o", NULL},
376 {V('c', 'o'), 1, "©", NULL},
377 {V('c', 't'), 1, "¢", NULL},
378 {V('d', 'e'), 1, "°", NULL},
379 {V('d', 'g'), 1, "+", NULL},
380 {V('d', 'i'), 1, "÷", NULL},
381 {V('e', 'm'), 1, "-", NULL},
382 {V('e', 'm'), 3, "---", NULL},
383 {V('e', 'q'), 1, "=", NULL},
384 {V('e', 's'), 1, "Ø", NULL},
385 {V('f', 'f'), 2, "ff", NULL},
386 {V('f', 'i'), 2, "fi", NULL},
387 {V('f', 'l'), 2, "fl", NULL},
388 {V('f', 'm'), 1, "´", NULL},
389 {V('g', 'a'), 1, "`", NULL},
390 {V('h', 'y'), 1, "-", NULL},
391 {V('l', 'c'), 2, "|¯", NULL},
392 {V('l', 'f'), 2, "|_", NULL},
393 {V('l', 'k'), 1, "<FONT SIZE=+2>{</FONT>", NULL},
394 {V('m', 'i'), 1, "-", NULL},
395 {V('m', 'u'), 1, "×", NULL},
396 {V('n', 'o'), 1, "¬", NULL},
397 {V('o', 'r'), 1, "|", NULL},
398 {V('p', 'l'), 1, "+", NULL},
399 {V('r', 'c'), 2, "¯|", NULL},
400 {V('r', 'f'), 2, "_|", NULL},
401 {V('r', 'g'), 1, "®", NULL},
402 {V('r', 'k'), 1, "<FONT SIZE=+2>}</FONT>", NULL},
403 {V('r', 'n'), 1, "¯", NULL},
404 {V('r', 'u'), 1, "_", NULL},
405 {V('s', 'c'), 1, "§", NULL},
406 {V('s', 'l'), 1, "/", NULL},
407 {V('s', 'q'), 2, "[]", NULL},
408 {V('u', 'l'), 1, "_", NULL},
412 /* default: print code */
415 static char eqndelimopen = 0, eqndelimclose = 0;
416 static char escapesym = '\\', nobreaksym = '\'', controlsym = '.', fieldsym = 0, padsym = 0;
418 static char *buffer = NULL;
419 static int buffpos = 0, buffmax = 0;
420 static int scaninbuff = 0;
421 static int itemdepth = 0;
422 static int dl_set[20] = {0};
423 static int still_dd = 0;
424 static int tabstops[20] = {8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96};
425 static int maxtstop = 12;
426 static int curpos = 0;
428 static char *scan_troff(char *c, int san, char **result);
429 static char *scan_troff_mandoc(char *c, int san, char **result);
431 static char **argument = NULL;
433 static char charb[TINY_STR_MAX];
438 char datbuf[NULL_TERMINATED(MED_STR_MAX)];
444 timetm = localtime(&clock);
445 strftime(datbuf, MED_STR_MAX, TIMEFORMAT, timetm);
446 printf(signature, manpage, datbuf);
466 if (charb[0] == '<') { /* Fix up <= */
468 strncpy(charb, "<", 4);
476 expand_string(int nr)
492 read_man_page(char *filename)
494 char *man_buf = NULL;
496 FILE *man_stream = NULL;
500 if (stat(filename, &stbuf) == -1)
503 buf_size = stbuf.st_size;
504 man_buf = stralloc(buf_size + 5);
505 man_stream = fopen(filename, "r");
508 if (fread(man_buf + 1, 1, buf_size, man_stream) == buf_size) {
509 man_buf[buf_size] = '\n';
510 man_buf[buf_size + 1] = man_buf[buf_size + 2] = '\0';
520 static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)];
522 static int no_newline_output = 0;
523 static int newline_for_fun = 0;
524 static int output_possible = 0;
525 static int out_length = 0;
528 * Add the links to the output. At the moment the following are
532 * name(*) -> ../man?/name.*
534 * method://string -> method://string
535 * www.host.name -> http://www.host.name
536 * ftp.host.name -> ftp://ftp.host.name
537 * name@host -> mailto:name@host
538 * <name.h> -> file:/usr/include/name.h (guess)
540 * Other possible links to add in the future:
542 * /dir/dir/file -> file:/dir/dir/file
549 char *idtest[6]; /* url, mailto, www, ftp, manpage */
551 out_length += strlen(c);
552 /* search for (section) */
554 idtest[0] = strstr(c + 1, "://");
555 idtest[1] = strchr(c + 1, '@');
556 idtest[2] = strstr(c, "www.");
557 idtest[3] = strstr(c, "ftp.");
559 idtest[4] = strchr(c + 1, '(');
563 idtest[5] = strstr(c + 1, ".h>");
564 for (i = 0; i < 6; i++)
565 nr += (idtest[i] != NULL);
568 for (i = 0; i < 6; i++)
569 if (idtest[i] && (j < 0 || idtest[i] < idtest[j]))
572 case 5: /* <name.h> */
576 while (g > c && g[-1] != ';')
586 printf("<A HREF=\"file:/usr/include/%s\">%s</A>>", g, g);
595 case 4: /* manpage */
600 if (g && f - g < 6 && (isalnum(f[-1]) || f[-1] == '>') &&
601 ((isdigit(f[1]) && f[1] != '0' &&
602 (f[2] == ')' || (isalpha(f[2]) && f[3] == ')') || f[2] == 'X')) ||
603 (f[2] == ')' && (f[1] == 'n' || f[1] == 'l')))) {
604 /* this might be a link */
606 /* skip html makeup */
607 while (h > c && *h == '>') {
608 while (h != c && *h != '<')
614 char t, sec, subsec, *e;
619 if ((subsec == 'X' && f[3] != ')') || subsec == ')')
621 while (h > c && (isalnum(h[-1]) || h[-1] == '_' ||
622 h[-1] == '-' || h[-1] == '.'))
633 "?man%c/%s.%c%c\">%s</A>",
634 sec, h, sec, tolower(subsec), h);
638 "?man%c/%s.%c\">%s</A>",
654 while (*g && (isalnum(*g) || *g == '_' || *g == '-' || *g == '+' ||
668 printf("<A HREF=\"%s://%s\">%s</A>", (j == 3 ? "ftp" : "http"),
681 while (g > c && (isalnum(g[-1]) || g[-1] == '_' || g[-1] == '-' ||
682 g[-1] == '+' || g[-1] == '.' || g[-1] == '%'))
685 while (*h && (isalnum(*h) || *h == '_' || *h == '-' || *h == '+' ||
690 if (h - f > 4 && f - g > 1) {
699 printf("<A HREF=\"mailto:%s\">%s</A>", g, g);
712 while (g > c && isalpha(g[-1]) && islower(g[-1]))
715 while (*h && !isspace(*h) && *h != '<' && *h != '>' && *h != '"' &&
718 if (f - g > 2 && f - g < 7 && h - f > 3) {
727 printf("<A HREF=\"%s\">%s</A>", g, g);
741 if (idtest[0] && idtest[0] < c)
742 idtest[0] = strstr(c + 1, "://");
743 if (idtest[1] && idtest[1] < c)
744 idtest[1] = strchr(c + 1, '@');
745 if (idtest[2] && idtest[2] < c)
746 idtest[2] = strstr(c, "www.");
747 if (idtest[3] && idtest[3] < c)
748 idtest[3] = strstr(c, "ftp.");
749 if (idtest[4] && idtest[4] < c)
750 idtest[4] = strchr(c + 1, '(');
751 if (idtest[5] && idtest[5] < c)
752 idtest[5] = strstr(c + 1, ".h>");
753 for (i = 0; i < 6; i++)
754 nr += (idtest[i] != NULL);
759 static int current_font = 0;
760 static int current_size = 0;
761 static int fillout = 1;
768 if (no_newline_output) {
771 no_newline_output = 1;
773 if (!no_newline_output)
776 no_newline_output = 1;
779 if (!no_newline_output)
784 if (buffpos >= buffmax) {
787 h = realloc(buffer, buffmax * 2);
793 buffer[buffpos++] = *c++;
795 } else if (output_possible) {
797 outbuffer[obp++] = *c;
798 if (*c == '\n' || obp > HUGE_STR_MAX) {
799 outbuffer[obp] = '\0';
800 add_links(outbuffer);
817 static char *switchfont[16] = {
818 "", FC0 FO1, FC0 FO2, FC0 FO3,
819 FC1 FO0, "", FC1 FO2, FC1 FO3,
820 FC2 FO0, FC2 FO1, "", FC2 FO3,
821 FC3 FO0, FC3 FO1, FC3 FO2, ""
825 change_to_font(int nr)
863 i = current_font * 4 + nr % 4;
864 current_font = nr % 4;
865 return switchfont[i];
868 static char sizebuf[200];
871 change_to_size(int nr)
891 nr = current_size + nr;
898 if (nr == current_size)
902 strcat(sizebuf, change_to_font(0));
904 strcat(sizebuf, "</FONT>");
909 strcat(sizebuf, "<FONT SIZE=");
914 sizebuf[l++] = '-', nr = -nr;
915 sizebuf[l++] = nr + '0';
919 strcat(sizebuf, change_to_font(i));
923 static int asint = 0;
924 static int intresult = 0;
926 #define SKIPEOL while (*c && *c++!='\n')
928 static int skip_escape = 0;
929 static int single_escape = 0;
937 int exoutputp, exskipescape;
963 if (!(h = argument[i]))
970 c = scan_escape(c + 1);
996 i = c[0] * 256 + c[1];
1004 i = c[0] * 256 + c[1];
1008 h = expand_string(i);
1017 } else if (*c != '(')
1021 i = c[0] * 256 + c[1];
1025 h = change_to_font(i);
1036 } else if (*c == '+') {
1042 else if (*c == '\\') {
1049 while (isdigit(*c) && (!i || (!j && i < 4)))
1050 i = i * 10 + (*c++) - '0';
1057 h = change_to_size(i * j);
1085 while (intd && intd->nr != i)
1088 intd->val = intd->val + j * intd->incr;
1089 intresult = intd->val;
1093 intresult = current_size;
1096 intresult = current_font;
1109 exoutputp = output_possible;
1110 exskipescape = skip_escape;
1111 output_possible = 0;
1116 if (*c == escapesym)
1117 c = scan_escape(c + 1);
1121 output_possible = exoutputp;
1122 skip_escape = exskipescape;
1137 exoutputp = output_possible;
1138 exskipescape = skip_escape;
1139 output_possible = 0;
1142 if (*c == escapesym)
1143 c = scan_escape(c + 1);
1146 output_possible = exoutputp;
1147 skip_escape = exskipescape;
1150 no_newline_output = 1;
1157 if (newline_for_fun)
1167 curpos = (curpos + 8) & 0xfff8;
1178 if (single_escape) {
1195 typedef struct TABLEITEM TABLEITEM;
1199 int size, align, valign, colspan, rowspan, font, vleft, vright, space,
1204 static TABLEITEM emptyfield = {NULL, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, NULL};
1206 typedef struct TABLEROW TABLEROW;
1210 TABLEROW *prev, *next;
1213 static char *tableopt[] = {
1214 "center", "expand", "box", "allbox", "doublebox",
1215 "tab", "linesize", "delim", NULL
1217 static int tableoptl[] = {6, 6, 3, 6, 9, 3, 8, 5, 0};
1220 clear_table(TABLEROW * table)
1222 TABLEROW *tr1, *tr2;
1223 TABLEITEM *ti1, *ti2;
1233 free(ti1->contents);
1243 static char *scan_expression(char *c, int *result);
1246 scan_format(char *c, TABLEROW ** result, int *maxcol)
1248 TABLEROW *layout, *currow;
1249 TABLEITEM *curfield;
1253 clear_table(*result);
1255 layout = currow = (TABLEROW *) malloc(sizeof(TABLEROW));
1256 currow->next = currow->prev = NULL;
1257 currow->first = curfield = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1258 *curfield = emptyfield;
1259 while (*c && *c != '.') {
1275 if (curfield->align) {
1276 curfield->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1277 curfield = curfield->next;
1278 *curfield = emptyfield;
1280 curfield->align = toupper(*c);
1287 curfield->font = toupper(*c);
1293 curfield->font = toupper(*c);
1300 curfield->valign = 't';
1316 i = i * 10 + (*c++) - '0';
1318 curfield->size = i * j;
1320 curfield->size = j - 10;
1326 c = scan_expression(c + 2, &curfield->width);
1329 if (curfield->align)
1351 i = i * 10 + (*c++) - '0';
1352 curfield->space = i;
1356 currow->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1357 currow->next->prev = currow;
1358 currow = currow->next;
1359 currow->next = NULL;
1360 curfield = currow->first = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1361 *curfield = emptyfield;
1370 while (*c++ != '\n');
1374 curfield = layout->first;
1378 curfield = curfield->next;
1382 currow = currow->next;
1389 next_row(TABLEROW * tr)
1397 TABLEITEM *ti, *ti2;
1399 tr->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1400 tr->next->prev = tr;
1405 tr->first = ti2 = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1407 tr->first = ti2 = NULL;
1410 ti2->contents = NULL;
1411 if ((ti = ti->next)) {
1412 ti2->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
1420 static char itemreset[20] = "\\fR\\s0";
1426 int center = 0, expand = 0, box = 0, border = 0, linesize = 1;
1427 int i, j, maxcol = 0, finished = 0;
1428 int oldfont, oldsize, oldfillout;
1429 char itemsep = '\t';
1430 TABLEROW *layout = NULL, *currow, *ftable;
1431 TABLEITEM *curfield;
1433 while (*c++ != '\n');
1437 oldfont = current_font;
1438 oldsize = current_size;
1439 oldfillout = fillout;
1440 out_html(change_to_font(0));
1441 out_html(change_to_size(0));
1446 while (*h && *h != '\n')
1449 /* scan table options */
1453 for (i = 0; tableopt[i] && strncmp(tableopt[i], c, tableoptl[i]); i++);
1454 c = c + tableoptl[i];
1472 while (*c++ != '(');
1476 while (*c++ != '(');
1479 linesize = linesize * 10 + (*c++) - '0';
1492 c = scan_format(c, &layout, &maxcol);
1495 curfield = layout->first;
1500 if ((*c == '_' || *c == '=') && (c[1] == itemsep || c[1] == '\n')) {
1501 if (c[-1] == '\n' && c[1] == '\n') {
1503 currow->prev->next = (TABLEROW *) malloc(sizeof(TABLEROW));
1504 currow->prev->next->next = currow;
1505 currow->prev->next->prev = currow->prev;
1506 currow->prev = currow->prev->next;
1508 currow->prev = layout = (TABLEROW *) malloc(sizeof(TABLEROW));
1509 currow->prev->prev = NULL;
1510 currow->prev->next = currow;
1512 curfield = currow->prev->first =
1513 (TABLEITEM *) malloc(sizeof(TABLEITEM));
1514 *curfield = emptyfield;
1515 curfield->align = *c;
1516 curfield->colspan = maxcol;
1517 curfield = currow->first;
1521 curfield->align = *c;
1523 curfield = curfield->next;
1524 } while (curfield && curfield->align == 'S');
1527 currow = next_row(currow);
1528 curfield = currow->first;
1532 } else if (*c == 'T' && c[1] == '{') {
1534 c = strstr(h, "\nT}");
1538 scan_troff(h, 0, &g);
1539 scan_troff(itemreset, 0, &g);
1543 curfield->contents = g;
1545 curfield = curfield->next;
1546 } while (curfield && curfield->align == 'S');
1549 if (c[-1] == '\n') {
1550 currow = next_row(currow);
1551 curfield = currow->first;
1553 } else if (*c == '.' && c[1] == 'T' && c[2] == '&' && c[-1] == '\n') {
1556 while (*c++ != '\n');
1558 currow = currow->prev;
1560 c = scan_format(c, &hr, &i);
1565 curfield = currow->first;
1566 } else if (*c == '.' && c[1] == 'T' && c[2] == 'E' && c[-1] == '\n') {
1568 while (*c++ != '\n');
1570 currow->prev->next = NULL;
1571 currow->prev = NULL;
1572 clear_table(currow);
1573 } else if (*c == '.' && c[-1] == '\n' && !isdigit(c[1])) {
1575 * skip troff request inside table (usually only .sp
1578 while (*c++ != '\n');
1581 while (*c && (*c != itemsep || c[-1] == '\\') &&
1582 (*c != '\n' || c[-1] == '\\'))
1585 if (*c == itemsep) {
1589 if (h[0] == '\\' && h[2] == '\n' &&
1590 (h[1] == '_' || h[1] == '^')) {
1592 curfield->align = h[1];
1594 curfield = curfield->next;
1595 } while (curfield && curfield->align == 'S');
1600 h = scan_troff(h, 1, &g);
1601 scan_troff(itemreset, 0, &g);
1603 curfield->contents = g;
1605 curfield = curfield->next;
1606 } while (curfield && curfield->align == 'S');
1613 if (c[-1] == '\n') {
1614 currow = next_row(currow);
1615 curfield = currow->first;
1619 /* calculate colspan and rowspan */
1621 while (currow->next)
1622 currow = currow->next;
1624 TABLEITEM *ti, *ti1 = NULL, *ti2 = NULL;
1628 ti1 = currow->prev->first;
1630 switch (ti->align) {
1634 if (ti2->rowspan < ti->rowspan)
1635 ti2->rowspan = ti->rowspan;
1647 } while (ti2 && curfield->align == 'S');
1655 currow = currow->prev;
1657 /* produce html output */
1659 out_html("<CENTER>");
1661 out_html("<TABLE BORDER><TR><TD>");
1663 if (box || border) {
1664 out_html(" BORDER");
1666 out_html("><TR><TD><TABLE");
1668 out_html(" WIDTH=100%");
1674 out_html("<TR VALIGN=top>");
1675 curfield = currow->first;
1677 if (curfield->align != 'S' && curfield->align != '^') {
1679 switch (curfield->align) {
1681 curfield->space += 4;
1683 out_html(" ALIGN=right");
1686 out_html(" ALIGN=center");
1690 if (!curfield->valign && curfield->rowspan > 1)
1691 out_html(" VALIGN=center");
1692 if (curfield->colspan > 1) {
1695 out_html(" COLSPAN=");
1696 sprintf(buf, "%i", curfield->colspan);
1699 if (curfield->rowspan > 1) {
1702 out_html(" ROWSPAN=");
1703 sprintf(buf, "%i", curfield->rowspan);
1706 j = j + curfield->colspan;
1709 out_html(change_to_size(curfield->size));
1711 out_html(change_to_font(curfield->font));
1712 switch (curfield->align) {
1714 out_html("<HR><HR>");
1720 if (curfield->contents)
1721 out_html(curfield->contents);
1724 if (curfield->space)
1725 for (i = 0; i < curfield->space; i++)
1728 out_html(change_to_font(0));
1730 out_html(change_to_size(0));
1731 if (j >= maxcol && curfield->align > '@' && curfield->align != '_')
1735 curfield = curfield->next;
1737 out_html("</TR>\n");
1738 currow = currow->next;
1741 out_html("</TABLE>");
1742 out_html("</TABLE>");
1744 out_html("</TABLE>");
1746 out_html("</CENTER>\n");
1751 fillout = oldfillout;
1752 out_html(change_to_size(oldsize));
1753 out_html(change_to_font(oldfont));
1758 scan_expression(char *c, int *result)
1760 int value = 0, value2, j = 0, sign = 1, opex = 0;
1764 c = scan_expression(c + 1, &value);
1766 } else if (*c == 'n') {
1769 } else if (*c == 't') {
1772 } else if (*c == '\'' || *c == '"' || *c < ' ' || (*c == '\\' && c[1] == '(')) {
1774 * ?string1?string2? test if string1 equals string2.
1776 char *st1 = NULL, *st2 = NULL, *h;
1787 while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1790 scan_troff(h, 1, &st1);
1796 while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
1799 scan_troff(h, 1, &st2);
1803 else if (!st1 || !st2)
1806 value = (!strcmp(st1, st2));
1815 while (*c && !isspace(*c) && *c != ')') {
1819 c = scan_expression(c + 1, &value2);
1820 value2 = sign * value2;
1834 int num = 0, denum = 1;
1838 value2 = value2 * 10 + ((*c++) - '0');
1841 while (isdigit(*c)) {
1842 num = num * 10 + ((*c++) - '0');
1847 /* scale indicator */
1849 case 'i': /* inch -> 10pt */
1850 value2 = value2 * 10 + (num * 10 + denum / 2) / denum;
1858 value2 = value2 + (num + denum / 2) / denum;
1859 value2 = sign * value2;
1864 c = scan_escape(c + 1);
1865 value2 = intresult * sign;
1867 c++; /* scale indicator */
1902 value = value - value2;
1905 value = value + value2;
1908 value = value * value2;
1912 value = value / value2;
1916 value = value % value2;
1919 value = (value < value2);
1922 value = (value > value2);
1925 value = (value >= value2);
1928 value = (value <= value2);
1932 value = (value == value2);
1935 value = (value && value2);
1938 value = (value || value2);
1941 fprintf(stderr, "man2html: unknown operator %c.\n", oper);
1954 trans_char(char *c, char s, char t)
1959 while (*sl != '\n' || slash) {
1961 if (*sl == escapesym)
1971 /* Remove \a from C in place. Return modified C. */
1979 while (i < l && c[i]) {
1982 strcpy(c + i, c + i + 1); /* should be memmove */
1994 fill_words(char *c, char *words[], int *n)
2002 while (*sl && (*sl != '\n' || slash)) {
2006 skipspace = !skipspace;
2007 } else if (*sl == '\a') {
2008 /* handle already-translated " */
2009 skipspace = !skipspace;
2010 } else if (*sl == escapesym)
2012 else if ((*sl == ' ' || *sl == '\t') && !skipspace) {
2014 if (words[*n] != sl)
2022 if (words[*n] != sl)
2025 while (*sl && *sl != '\n')
2034 if (sl != words[*n])
2039 static char *abbrev_list[] = {
2040 "GSBG", "Getting Started ",
2041 "SUBG", "Customizing SunOS",
2042 "SHBG", "Basic Troubleshooting",
2043 "SVBG", "SunView User's Guide",
2044 "MMBG", "Mail and Messages",
2045 "DMBG", "Doing More with SunOS",
2046 "UNBG", "Using the Network",
2047 "GDBG", "Games, Demos & Other Pursuits",
2048 "CHANGE", "SunOS 4.1 Release Manual",
2049 "INSTALL", "Installing SunOS 4.1",
2050 "ADMIN", "System and Network Administration",
2051 "SECUR", "Security Features Guide",
2052 "PROM", "PROM User's Manual",
2053 "DIAG", "Sun System Diagnostics",
2054 "SUNDIAG", "Sundiag User's Guide",
2055 "MANPAGES", "SunOS Reference Manual",
2056 "REFMAN", "SunOS Reference Manual",
2057 "SSI", "Sun System Introduction",
2058 "SSO", "System Services Overview",
2059 "TEXT", "Editing Text Files",
2060 "DOCS", "Formatting Documents",
2061 "TROFF", "Using <B>nroff</B> and <B>troff</B>",
2062 "INDEX", "Global Index",
2063 "CPG", "C Programmer's Guide",
2064 "CREF", "C Reference Manual",
2065 "ASSY", "Assembly Language Reference",
2066 "PUL", "Programming Utilities and Libraries",
2067 "DEBUG", "Debugging Tools",
2068 "NETP", "Network Programming",
2069 "DRIVER", "Writing Device Drivers",
2070 "STREAMS", "STREAMS Programming",
2071 "SBDK", "SBus Developer's Kit",
2072 "WDDS", "Writing Device Drivers for the SBus",
2073 "FPOINT", "Floating-Point Programmer's Guide",
2074 "SVPG", "SunView 1 Programmer's Guide",
2075 "SVSPG", "SunView 1 System Programmer's Guide",
2076 "PIXRCT", "Pixrect Reference Manual",
2077 "CGI", "SunCGI Reference Manual",
2078 "CORE", "SunCore Reference Manual",
2079 "4ASSY", "Sun-4 Assembly Language Reference",
2080 "SARCH", "<FONT SIZE=-1>SPARC</FONT> Architecture Manual",
2081 "KR", "The C Programming Language",
2085 lookup_abbrev(char *c)
2091 while (abbrev_list[i] && strcmp(c, abbrev_list[i]))
2094 return abbrev_list[i + 1];
2099 static char manidx[NULL_TERMINATED(HUGE_STR_MAX)];
2100 static int subs = 0;
2102 static char label[5] = "lbAA";
2105 add_to_index(int level, char *item)
2110 if (label[3] > 'Z') {
2114 if (level != subs) {
2116 strmaxcpy(manidx + mip, "</DL>\n", HUGE_STR_MAX - mip);
2119 strmaxcpy(manidx + mip, "<DL>\n", HUGE_STR_MAX - mip);
2124 scan_troff(item, 1, &c);
2125 sprintf(manidx + mip, "<DT><A HREF=\"#%s\">%s</A><DD>\n", label, c);
2133 skip_till_newline(char *c)
2137 while (*c && *c != '\n' || lvl > 0) {
2148 if (lvl < 0 && newline_for_fun) {
2149 newline_for_fun = newline_for_fun + lvl;
2150 if (newline_for_fun < 0)
2151 newline_for_fun = 0;
2157 outputPageHeader(char *l, char *c, char *r)
2159 out_html("<TABLE WIDTH=100%>\n<TR>\n");
2160 out_html("<TH ALIGN=LEFT width=33%>");
2162 out_html("<TH ALIGN=CENTER width=33%>");
2164 out_html("<TH ALIGN=RIGHT width=33%>");
2166 out_html("\n</TR>\n</TABLE>\n");
2170 outputPageFooter(char *l, char *c, char *r)
2173 outputPageHeader(l, c, r);
2176 static int ifelseval = 0;
2179 scan_request(char *c)
2181 /* BSD Mandoc stuff */
2182 static int mandoc_synopsis = 0; /* True if we are in the synopsis
2184 static int mandoc_command = 0; /* True if this is mandoc page */
2185 static int mandoc_bd_options; /* Only copes with non-nested Bd's */
2189 char *wordlist[MAX_WORDLIST];
2194 while (*c == ' ' || *c == '\t')
2202 while (c[j] == ' ' || c[j] == '\t')
2204 if (c[0] == escapesym) {
2205 /* some pages use .\" .\$1 .\} */
2206 /* .\$1 is too difficult/stupid */
2208 c = skip_till_newline(c);
2210 c = scan_escape(c + 1);
2216 while (*h && *h != '\n')
2219 if (scaninbuff && buffpos) {
2220 buffer[buffpos] = '\0';
2223 /* fprintf(stderr, "%s\n", c+2); */
2229 int oldcurpos = curpos;
2237 while (*c && *c != '\n')
2241 while (*c && strncmp(c, ".di", 3))
2242 while (*c && *c++ != '\n');
2245 while (de && de->nr != i)
2248 de = (STRDEF *) malloc(sizeof(STRDEF));
2260 scan_troff(h, 0, &de->st);
2262 while (*c && *c++ != '\n');
2270 int oldcurpos = curpos;
2275 while (c[j] && c[j] != '\n')
2290 while (de && de->nr != i)
2297 de = (STRDEF *) malloc(sizeof(STRDEF));
2304 c = scan_troff(c, 1, &h);
2311 c = scan_troff(c, 1, &h);
2316 c = scan_troff(c, 1, &de->st);
2330 if (c[0] == escapesym) {
2331 c = scan_escape(c + 1);
2333 c = skip_till_newline(c);
2341 c = skip_till_newline(c);
2349 c = skip_till_newline(c);
2357 while ('0' <= *c && *c <= '9') {
2358 i = i * 10 + *c - '0';
2362 c = skip_till_newline(c);
2363 /* center next i lines */
2365 out_html("<CENTER>\n");
2369 c = scan_troff(c, 1, &line);
2370 if (line && strncmp(line, "<BR>", 4)) {
2376 out_html("</CENTER>\n");
2387 c = skip_till_newline(c);
2390 c = skip_till_newline(c);
2398 fieldsym = padsym = '\0';
2403 c = skip_till_newline(c);
2407 out_html(change_to_font(0));
2408 out_html(change_to_size('0'));
2409 out_html("</PRE>\n");
2413 c = skip_till_newline(c);
2418 out_html(change_to_font(0));
2420 if (*c == escapesym) {
2423 c = scan_expression(c, &fn);
2425 out_html(change_to_font(fn));
2427 out_html(change_to_font(*c));
2431 c = skip_till_newline(c);
2434 /* .el anything : else part of if else */
2438 c = scan_troff(c, 1, NULL);
2440 c = skip_till_newline(c + j);
2443 /* .ie c anything : then part of if else */
2446 * .if c anything .if !c anything .if N anything .if
2447 * !N anything .if 'string1'string2' anything .if
2448 * !'string1'string2' anything
2451 c = scan_expression(c, &i);
2456 c = scan_troff(c, 1, NULL);
2458 c = skip_till_newline(c);
2462 char *endwith = "..\n";
2470 while (*c && *c != '\n')
2474 while (*c && strncmp(c, endwith, i))
2475 while (*c++ != '\n');
2476 while (*c++ != '\n');
2481 out_html(change_to_font(0));
2482 out_html(change_to_size('0'));
2483 out_html("<PRE>\n");
2487 c = skip_till_newline(c);
2492 out_html(change_to_size('0'));
2499 } else if (*c == '+') {
2503 c = scan_expression(c, &i);
2509 out_html(change_to_size(i * j));
2511 c = skip_till_newline(c);
2522 c = skip_till_newline(c);
2545 scan_troff(h, 1, &name);
2550 if (stat(h, &stbuf) != -1)
2552 buf = stralloc(l + 4);
2557 t = strrchr(fname, '/');
2560 fprintf(stderr, "ln -s %s.html %s.html\n", h, t);
2561 s = strrchr(t, '.');
2564 printf("<HTML><HEAD><TITLE> Manpage of %s</TITLE>\n"
2566 "See the manpage for <A HREF=\"%s.html\">%s</A>.\n"
2573 * this works alright, except for
2576 buf = read_man_page(h);
2579 fprintf(stderr, "man2html: unable to open or read file %s.\n",
2581 out_html("<BLOCKQUOTE>"
2582 "man2html: unable to open or read file.\n");
2584 out_html("</BLOCKQUOTE>\n");
2586 buf[0] = buf[l] = '\n';
2587 buf[l + 1] = buf[l + 2] = '\0';
2588 scan_troff(buf + 1, 0, NULL);
2599 while (*c != '\n') {
2600 sl = scan_expression(c, &tabstops[j]);
2601 if (*c == '-' || *c == '+')
2602 tabstops[j] += tabstops[j - 1];
2604 while (*c == ' ' || *c == '\t')
2613 * while (itemdepth || dl_set[itemdepth]) {
2614 * out_html("</DL>\n"); if (dl_set[itemdepth])
2615 * dl_set[itemdepth]=0; else itemdepth--; }
2619 c = scan_expression(c, &j);
2620 for (i = 0; i < j; i++)
2623 c = skip_till_newline(c);
2631 /* fprintf(stderr,"%s\n", h); */
2638 /* parse one line in a certain font */
2639 out_html(change_to_font(*c));
2640 trans_char(c, '"', '\a');
2644 c = scan_troff(c, 1, NULL);
2645 out_html(change_to_font('R'));
2652 case V('O', 'P'): /* groff manpages use this
2654 /* .OP a b : [ <B>a</B> <I>b</I> ] */
2658 out_html(change_to_font('R'));
2675 sl = fill_words(c, wordlist, &words);
2678 * .BR name (section) indicates a link. It
2679 * will be added in the output routine.
2681 for (i = 0; i < words; i++) {
2686 wordlist[i][-1] = ' ';
2687 out_html(change_to_font(font[i & 1]));
2688 scan_troff(wordlist[i], 1, NULL);
2690 out_html(change_to_font('R'));
2703 for (j = 0; j < 20; j++)
2704 tabstops[j] = (j + 1) * 8;
2706 c = skip_till_newline(c);
2709 sl = fill_words(c + j, wordlist, &words);
2711 if (!dl_set[itemdepth]) {
2712 out_html("<DL COMPACT>\n");
2713 dl_set[itemdepth] = 1;
2717 scan_troff(wordlist[0], 1, NULL);
2723 if (!dl_set[itemdepth]) {
2724 out_html("<DL COMPACT>\n");
2725 dl_set[itemdepth] = 1;
2728 c = skip_till_newline(c);
2729 /* somewhere a definition ends with '.TP' */
2733 c = scan_troff(c, 1, NULL);
2740 sl = fill_words(c + j, wordlist, &words);
2743 while (idxlabel[j] == 'Z')
2744 idxlabel[j--] = 'A';
2747 fprintf(idxfile, "%s@%s@", fname, idxlabel);
2748 for (j = 0; j < words; j++) {
2750 scan_troff(wordlist[j], 1, &h);
2751 fprintf(idxfile, "_\b@%s", h);
2754 fprintf(idxfile, "\n");
2756 out_html("<A NAME=\"");
2759 * this will not work in mosaic (due to a bug).
2760 * Adding ' ' between '>' and '<' solves it, but
2761 * creates some space. A normal space does not work.
2763 out_html("\"></A>");
2767 if (dl_set[itemdepth]) {
2768 out_html("</DL>\n");
2769 dl_set[itemdepth] = 0;
2778 c = skip_till_newline(c);
2781 if (!dl_set[itemdepth]) {
2782 out_html("<DL COMPACT>");
2783 dl_set[itemdepth] = 1;
2787 c = skip_till_newline(c);
2791 c = skip_till_newline(c);
2793 case V('R', 's'): /* BSD mandoc */
2795 sl = fill_words(c + j, wordlist, &words);
2798 scan_expression(wordlist[0], &j);
2801 dl_set[itemdepth] = 0;
2802 out_html("<DL COMPACT><DT><DD>");
2803 c = skip_till_newline(c);
2807 case V('R', 'e'): /* BSD mandoc */
2809 if (itemdepth > 0) {
2810 if (dl_set[itemdepth])
2812 out_html("</DL>\n");
2815 c = skip_till_newline(c);
2819 out_html(change_to_size(-1));
2820 out_html(change_to_font('B'));
2821 c = scan_troff(c + j, 1, NULL);
2822 out_html(change_to_font('R'));
2823 out_html(change_to_size('0'));
2829 out_html(change_to_size(-1));
2830 trans_char(c, '"', '\a');
2831 c = scan_troff(c, 1, NULL);
2832 out_html(change_to_size('0'));
2834 case V('S', 's'): /* BSD mandoc */
2838 case V('S', 'h'): /* BSD mandoc */
2839 /* hack for fallthru from above */
2840 mandoc_command = !mode || mandoc_command;
2845 while (itemdepth || dl_set[itemdepth]) {
2846 out_html("</DL>\n");
2847 if (dl_set[itemdepth])
2848 dl_set[itemdepth] = 0;
2849 else if (itemdepth > 0)
2852 out_html(change_to_font(0));
2853 out_html(change_to_size(0));
2858 trans_char(c, '"', '\a');
2859 add_to_index(mode, c);
2860 out_html("<A NAME=\"");
2862 /* for mosaic users */
2864 out_html("\"> </A>\n<H4>");
2866 out_html("\"> </A>\n<H3>");
2867 mandoc_synopsis = strncmp(c, "SYNOPSIS", 8) == 0;
2868 c = mandoc_command ? scan_troff_mandoc(c, 1, NULL) : scan_troff(c, 1, NULL);
2870 out_html("</H4>\n");
2872 out_html("</H3>\n");
2878 case V('D', 't'): /* BSD mandoc */
2881 if (!output_possible) {
2882 sl = fill_words(c + j, wordlist, &words);
2885 for (i = 1; i < words; i++)
2886 wordlist[i][-1] = '\0';
2888 output_possible = 1;
2889 sprintf(th_page_and_sec, "%s(%s)", wordlist[0], wordlist[1]);
2891 t = unescape(wordlist[2]);
2892 strncpy(th_datestr, t, sizeof(th_datestr));
2893 th_datestr[sizeof(th_datestr) - 1] = '\0';
2895 th_datestr[0] = '\0';
2897 t = unescape(wordlist[3]);
2898 strncpy(th_version, t, sizeof(th_version));
2899 th_version[sizeof(th_version) - 1] = '\0';
2901 th_version[0] = '\0';
2902 out_html("<HTML><HEAD>\n<TITLE>");
2903 out_html(th_page_and_sec);
2904 out_html(" Manual Page");
2905 out_html("</TITLE>\n</HEAD>\n<BODY>");
2907 outputPageHeader(th_page_and_sec, th_datestr, th_page_and_sec);
2909 out_html("<BR><A HREF=\"#index\">Index</A>\n");
2913 out_html("<BR>BSD mandoc<BR>");
2917 c = skip_till_newline(c);
2921 sl = fill_words(c + j, wordlist, &words);
2923 out_html(change_to_font('I'));
2925 wordlist[1][-1] = '\0';
2926 c = lookup_abbrev(wordlist[0]);
2927 curpos += strlen(c);
2929 out_html(change_to_font('R'));
2931 out_html(wordlist[1]);
2936 /* .rm xx : Remove request, macro or string */
2939 * .rn xx yy : Rename request, macro or string xx to
2948 while (isspace(*c) && *c != '\n')
2951 while (*c && *c != '\n')
2955 while (de && de->nr != j)
2963 while (de && de->nr != i)
2970 /* .nx filename : next file. */
2972 /* .in +-N : Indent */
2973 c = skip_till_newline(c);
2977 * .nr R +-N M: define and set number register R by
2978 * +-N; auto-increment by M
2987 while (intd && intd->nr != i)
2990 intd = (INTDEF *) malloc(sizeof(INTDEF));
2994 intd->next = intdef;
2997 while (*c == ' ' || *c == '\t')
2999 c = scan_expression(c, &intd->val);
3001 while (*c == ' ' || *c == '\t')
3003 c = scan_expression(c, &intd->incr);
3005 c = skip_till_newline(c);
3009 /* .am xx yy : append to a macro. */
3010 /* define or handle as .ig yy */
3014 * .de xx yy : define or redefine macro xx; end at
3017 /* define or handle as .ig yy */
3023 sl = fill_words(c, wordlist, &words);
3030 wordlist[1][0] = '.';
3035 while (*c && strncmp(c, wordlist[1], j))
3036 c = skip_till_newline(c);
3038 while (de && de->nr != i)
3041 olen = strlen(de->st);
3043 h = stralloc(j * 2 + 4);
3045 for (j = 0; j < olen; j++)
3047 if (!j || h[j - 1] != '\n')
3050 if (sl[0] == '\\' && sl[1] == '\\') {
3063 de = (STRDEF *) malloc(sizeof(STRDEF));
3071 c = skip_till_newline(c);
3073 case V('B', 'l'): /* BSD mandoc */
3075 char list_options[NULL_TERMINATED(MED_STR_MAX)];
3076 char *nl = strchr(c, '\n');
3079 if (dl_set[itemdepth]) { /* These things can
3083 if (nl) { /* Parse list options */
3084 strlimitcpy(list_options, c, nl - c, MED_STR_MAX);
3086 if (strstr(list_options, "-bullet")) { /* HTML Unnumbered List */
3087 dl_set[itemdepth] = BL_BULLET_LIST;
3089 } else if (strstr(list_options, "-enum")) { /* HTML Ordered List */
3090 dl_set[itemdepth] = BL_ENUM_LIST;
3092 } else { /* HTML Descriptive List */
3093 dl_set[itemdepth] = BL_DESC_LIST;
3094 out_html("<DL COMPACT>\n");
3103 c = skip_till_newline(c);
3106 case V('E', 'l'): /* BSD mandoc */
3108 if (dl_set[itemdepth] & BL_DESC_LIST) {
3109 out_html("</DL>\n");
3110 } else if (dl_set[itemdepth] & BL_BULLET_LIST) {
3111 out_html("</UL>\n");
3112 } else if (dl_set[itemdepth] & BL_ENUM_LIST) {
3113 out_html("</OL>\n");
3115 dl_set[itemdepth] = 0;
3125 c = skip_till_newline(c);
3127 case V('I', 't'): /* BSD mandoc */
3129 if (strncmp(c, "Xo", 2) == 0 && isspace(*(c + 2))) {
3130 c = skip_till_newline(c);
3132 if (dl_set[itemdepth] & BL_DESC_LIST) {
3134 out_html(change_to_font('B'));
3135 if (*c == '\n') { /* Don't allow embedded
3136 * comms after a newline */
3138 c = scan_troff(c, 1, NULL);
3139 } else { /* Do allow embedded comms on
3141 c = scan_troff_mandoc(c, 1, NULL);
3143 out_html(change_to_font('R'));
3146 } else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST)) {
3148 c = scan_troff_mandoc(c, 1, NULL);
3156 case V('B', 'k'): /* BSD mandoc */
3157 case V('E', 'k'): /* BSD mandoc */
3158 case V('D', 'd'): /* BSD mandoc */
3159 case V('O', 's'): /* BSD mandoc */
3160 trans_char(c, '"', '\a');
3164 c = scan_troff_mandoc(c, 1, NULL);
3171 case V('B', 't'): /* BSD mandoc */
3172 trans_char(c, '"', '\a');
3174 out_html(" is currently in beta test.");
3180 case V('B', 'x'): /* BSD mandoc */
3181 trans_char(c, '"', '\a');
3186 c = scan_troff_mandoc(c, 1, NULL);
3192 case V('D', 'l'): /* BSD mandoc */
3195 out_html("<BLOCKQUOTE>");
3196 out_html(change_to_font('L'));
3199 c = scan_troff_mandoc(c, 1, NULL);
3200 out_html(change_to_font('R'));
3201 out_html("</BLOCKQUOTE>");
3207 case V('B', 'd'): /* BSD mandoc */
3208 { /* Seems like a kind of example/literal mode */
3209 char bd_options[NULL_TERMINATED(MED_STR_MAX)];
3210 char *nl = strchr(c, '\n');
3214 strlimitcpy(bd_options, c, nl - c, MED_STR_MAX);
3217 mandoc_bd_options = 0; /* Remember options for
3219 if (strstr(bd_options, "-offset indent")) {
3220 mandoc_bd_options |= BD_INDENT;
3221 out_html("<BLOCKQUOTE>\n");
3223 if (strstr(bd_options, "-literal")
3224 || strstr(bd_options, "-unfilled")) {
3226 mandoc_bd_options |= BD_LITERAL;
3227 out_html(change_to_font(0));
3228 out_html(change_to_size('0'));
3229 out_html("<PRE>\n");
3234 c = skip_till_newline(c);
3237 case V('E', 'd'): /* BSD mandoc */
3238 if (mandoc_bd_options & BD_LITERAL) {
3240 out_html(change_to_font(0));
3241 out_html(change_to_size('0'));
3242 out_html("</PRE>\n");
3245 if (mandoc_bd_options & BD_INDENT)
3246 out_html("</BLOCKQUOTE>\n");
3249 c = skip_till_newline(c);
3251 case V('B', 'e'): /* BSD mandoc */
3260 c = skip_till_newline(c);
3262 case V('X', 'r'): /* BSD mandoc */
3265 * Translate xyz 1 to xyz(1) Allow for
3266 * multiple spaces. Allow the section to be
3269 char buff[NULL_TERMINATED(MED_STR_MAX)];
3272 trans_char(c, '"', '\a');
3276 c++; /* Skip spaces */
3277 while (isspace(*c) && *c != '\n')
3279 while (isalnum(*c)) { /* Copy the xyz part */
3282 if (bufptr >= buff + MED_STR_MAX)
3286 while (isspace(*c) && *c != '\n')
3287 c++; /* Skip spaces */
3288 if (isdigit(*c)) { /* Convert the number if
3292 if (bufptr < buff + MED_STR_MAX) {
3293 while (isalnum(*c)) {
3296 if (bufptr >= buff + MED_STR_MAX)
3300 if (bufptr < buff + MED_STR_MAX) {
3306 while (*c != '\n') { /* Copy the remainder */
3310 if (bufptr >= buff + MED_STR_MAX)
3316 scan_troff_mandoc(buff, 1, NULL);
3325 case V('F', 'l'): /* BSD mandoc */
3326 trans_char(c, '"', '\a');
3330 out_html(change_to_font('B'));
3331 c = scan_troff_mandoc(c, 1, NULL);
3332 out_html(change_to_font('R'));
3340 case V('P', 'a'): /* BSD mandoc */
3341 case V('P', 'f'): /* BSD mandoc */
3342 trans_char(c, '"', '\a');
3346 c = scan_troff_mandoc(c, 1, NULL);
3353 case V('P', 'p'): /* BSD mandoc */
3361 c = skip_till_newline(c);
3363 case V('D', 'q'): /* BSD mandoc */
3364 trans_char(c, '"', '\a');
3369 c = scan_troff_mandoc(c, 1, NULL);
3377 case V('O', 'p'): /* BSD mandoc */
3378 trans_char(c, '"', '\a');
3382 out_html(change_to_font('R'));
3384 c = scan_troff_mandoc(c, 1, NULL);
3385 out_html(change_to_font('R'));
3393 case V('O', 'o'): /* BSD mandoc */
3394 trans_char(c, '"', '\a');
3398 out_html(change_to_font('R'));
3400 c = scan_troff_mandoc(c, 1, NULL);
3406 case V('O', 'c'): /* BSD mandoc */
3407 trans_char(c, '"', '\a');
3409 c = scan_troff_mandoc(c, 1, NULL);
3410 out_html(change_to_font('R'));
3417 case V('P', 'q'): /* BSD mandoc */
3418 trans_char(c, '"', '\a');
3423 c = scan_troff_mandoc(c, 1, NULL);
3431 case V('Q', 'l'): /* BSD mandoc */
3432 { /* Single quote first word in the line */
3435 trans_char(c, '"', '\a');
3440 do { /* Find first whitespace after the
3441 * first word that isn't a mandoc
3443 while (*sp && isspace(*sp))
3445 while (*sp && !isspace(*sp))
3447 } while (*sp && isupper(*(sp - 2)) && islower(*(sp - 1)));
3450 * Use a newline to mark the end of text to
3455 out_html("`"); /* Quote the text */
3456 c = scan_troff_mandoc(c, 1, NULL);
3465 case V('S', 'q'): /* BSD mandoc */
3466 trans_char(c, '"', '\a');
3471 c = scan_troff_mandoc(c, 1, NULL);
3479 case V('A', 'r'): /* BSD mandoc */
3480 /* parse one line in italics */
3481 out_html(change_to_font('I'));
3482 trans_char(c, '"', '\a');
3484 if (*c == '\n') { /* An empty Ar means "file
3486 out_html("file ...");
3488 c = scan_troff_mandoc(c, 1, NULL);
3490 out_html(change_to_font('R'));
3497 case V('A', 'd'): /* BSD mandoc */
3498 case V('E', 'm'): /* BSD mandoc */
3499 case V('V', 'a'): /* BSD mandoc */
3500 case V('X', 'c'): /* BSD mandoc */
3501 /* parse one line in italics */
3502 out_html(change_to_font('I'));
3503 trans_char(c, '"', '\a');
3507 c = scan_troff_mandoc(c, 1, NULL);
3508 out_html(change_to_font('R'));
3515 case V('N', 'd'): /* BSD mandoc */
3516 trans_char(c, '"', '\a');
3521 c = scan_troff_mandoc(c, 1, NULL);
3528 case V('N', 'm'): /* BSD mandoc */
3530 static char mandoc_name[NULL_TERMINATED(SMALL_STR_MAX)] = "";
3532 trans_char(c, '"', '\a');
3534 if (mandoc_synopsis) { /* Break lines only in
3537 * seems to be treated
3538 * as a special case -
3540 static int count = 0; /* Don't break on the
3546 char *end = strchr(c, '\n');
3548 if (end) { /* Remember the name for
3550 strlimitcpy(mandoc_name, c, end - c, SMALL_STR_MAX);
3555 out_html(change_to_font('B'));
3556 while (*c == ' ' || *c == '\t')
3558 if (*c == '\n') { /* If Nm has no
3560 * from an earlier Nm
3561 * command that did have
3566 out_html(mandoc_name);
3568 c = scan_troff_mandoc(c, 1, NULL);
3570 out_html(change_to_font('R'));
3578 case V('C', 'd'): /* BSD mandoc */
3579 case V('C', 'm'): /* BSD mandoc */
3580 case V('I', 'c'): /* BSD mandoc */
3581 case V('M', 's'): /* BSD mandoc */
3582 case V('O', 'r'): /* BSD mandoc */
3583 case V('S', 'y'): /* BSD mandoc */
3584 /* parse one line in bold */
3585 out_html(change_to_font('B'));
3586 trans_char(c, '"', '\a');
3590 c = scan_troff_mandoc(c, 1, NULL);
3591 out_html(change_to_font('R'));
3598 case V('D', 'v'): /* BSD mandoc */
3599 case V('E', 'v'): /* BSD mandoc */
3600 case V('F', 'r'): /* BSD mandoc */
3601 case V('L', 'i'): /* BSD mandoc */
3602 case V('N', 'o'): /* BSD mandoc */
3603 case V('N', 's'): /* BSD mandoc */
3604 case V('T', 'n'): /* BSD mandoc */
3605 case V('n', 'N'): /* BSD mandoc */
3606 trans_char(c, '"', '\a');
3610 out_html(change_to_font('B'));
3611 c = scan_troff_mandoc(c, 1, NULL);
3612 out_html(change_to_font('R'));
3619 case V('%', 'A'): /* BSD mandoc biblio stuff */
3629 c = scan_troff(c, 1, NULL); /* Don't allow embedded
3641 out_html(change_to_font('I'));
3644 c = scan_troff(c, 1, NULL); /* Don't allow embedded
3646 out_html(change_to_font('R'));
3653 /* search macro database of self-defined macros */
3655 while (owndef && owndef->nr != i)
3656 owndef = owndef->next;
3662 sl = fill_words(c + j, wordlist, &words);
3665 for (i = 1; i < words; i++)
3666 wordlist[i][-1] = '\0';
3667 for (i = 0; i < words; i++) {
3670 if (mandoc_command) {
3671 scan_troff_mandoc(wordlist[i], 1, &h);
3673 scan_troff(wordlist[i], 1, &h);
3677 for (i = words; i < 20; i++)
3679 deflen = strlen(owndef->st);
3680 for (i = 0; owndef->st[deflen + 2 + i] = owndef->st[i]; i++);
3681 oldargument = argument;
3682 argument = wordlist;
3683 onff = newline_for_fun;
3684 if (mandoc_command) {
3685 scan_troff_mandoc(owndef->st + deflen + 2, 0, NULL);
3687 scan_troff(owndef->st + deflen + 2, 0, NULL);
3689 newline_for_fun = onff;
3690 argument = oldargument;
3691 for (i = 0; i < words; i++)
3695 } else if (mandoc_command &&
3696 ((isupper(*c) && islower(*(c + 1)))
3697 || (islower(*c) && isupper(*(c + 1))))
3698 ) { /* Let through any BSD mandoc
3699 * commands that haven't been delt
3700 * with. I don't want to miss
3701 * anything out of the text. */
3707 out_html(buf); /* Print the command (it
3708 * might just be text). */
3710 trans_char(c, '"', '\a');
3713 out_html(change_to_font('R'));
3714 c = scan_troff(c, 1, NULL);
3721 c = skip_till_newline(c);
3739 static int contained_tab = 0;
3740 static int mandoc_line = 0; /* Signals whether to look for embedded
3741 * mandoc commands. */
3743 /* san : stop at newline */
3745 scan_troff(char *c, int san, char **result)
3748 char intbuff[NULL_TERMINATED(MED_STR_MAX)];
3752 int exbuffpos, exbuffmax, exscaninbuff, exnewline_for_fun;
3755 #define FLUSHIBP if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; }
3758 exbuffpos = buffpos;
3759 exbuffmax = buffmax;
3760 exnewline_for_fun = newline_for_fun;
3761 exscaninbuff = scaninbuff;
3762 newline_for_fun = 0;
3766 buffpos = strlen(buffer);
3769 buffer = stralloc(LARGE_STR_MAX);
3771 buffmax = LARGE_STR_MAX;
3776 /* start scanning */
3778 while (*h && (!san || newline_for_fun || *h != '\n')) {
3780 if (*h == escapesym) {
3784 } else if (*h == controlsym && h[-1] == '\n') {
3787 h = scan_request(h);
3788 if (san && h[-1] == '\n')
3790 } else if (mandoc_line
3791 && *(h) && isupper(*(h))
3792 && *(h + 1) && islower(*(h + 1))
3793 && *(h + 2) && isspace(*(h + 2))) {
3795 * BSD imbedded command eg ".It Fl Ar arg1 Fl Ar
3799 h = scan_request(h);
3800 if (san && h[-1] == '\n')
3802 } else if (*h == nobreaksym && h[-1] == '\n') {
3805 h = scan_request(h);
3806 if (san && h[-1] == '\n')
3811 if (h[-1] == '\n' && still_dd && isalnum(*h)) {
3813 * sometimes a .HP request is not followed by
3823 intbuff[ibp++] = '&';
3824 intbuff[ibp++] = 'a';
3825 intbuff[ibp++] = 'm';
3826 intbuff[ibp++] = 'p';
3827 intbuff[ibp++] = ';';
3831 intbuff[ibp++] = '&';
3832 intbuff[ibp++] = 'l';
3833 intbuff[ibp++] = 't';
3834 intbuff[ibp++] = ';';
3838 intbuff[ibp++] = '&';
3839 intbuff[ibp++] = 'g';
3840 intbuff[ibp++] = 't';
3841 intbuff[ibp++] = ';';
3845 intbuff[ibp++] = '&';
3846 intbuff[ibp++] = 'q';
3847 intbuff[ibp++] = 'u';
3848 intbuff[ibp++] = 'o';
3849 intbuff[ibp++] = 't';
3850 intbuff[ibp++] = ';';
3854 if (h[-1] == '\n' && fillout) {
3855 intbuff[ibp++] = '<';
3856 intbuff[ibp++] = 'P';
3857 intbuff[ibp++] = '>';
3859 if (contained_tab && fillout) {
3860 intbuff[ibp++] = '<';
3861 intbuff[ibp++] = 'B';
3862 intbuff[ibp++] = 'R';
3863 intbuff[ibp++] = '>';
3868 intbuff[ibp++] = '\n';
3876 /* like a typewriter, not like TeX */
3877 tabstops[19] = curpos + 1;
3878 while (curtab < maxtstop && tabstops[curtab] <= curpos)
3880 if (curtab < maxtstop) {
3882 while (curpos < tabstops[curtab]) {
3883 intbuff[ibp++] = ' ';
3891 while (curpos < tabstops[curtab]) {
3901 if (*h == ' ' && (h[-1] == '\n' || usenbsp)) {
3903 if (!usenbsp && fillout) {
3911 intbuff[ibp++] = ' ';
3912 } else if (*h > 31 && *h < 127)
3913 intbuff[ibp++] = *h;
3914 else if (((unsigned char) (*h)) > 127) {
3915 intbuff[ibp++] = '&';
3916 intbuff[ibp++] = '#';
3917 intbuff[ibp++] = '0' + ((unsigned char) (*h)) / 100;
3918 intbuff[ibp++] = '0' + (((unsigned char) (*h)) % 100) / 10;
3919 intbuff[ibp++] = '0' + ((unsigned char) (*h)) % 10;
3920 intbuff[ibp++] = ';';
3925 if (ibp > (MED_STR_MAX - 20))
3932 buffer[buffpos] = '\0';
3935 newline_for_fun = exnewline_for_fun;
3939 buffpos = exbuffpos;
3940 buffmax = exbuffmax;
3941 scaninbuff = exscaninbuff;
3948 scan_troff_mandoc(char *c, int san, char **result)
3950 char *ret, *end = c;
3951 int oldval = mandoc_line;
3954 while (*end && *end != '\n') {
3959 && ispunct(*(end - 1))
3960 && isspace(*(end - 2)) && *(end - 2) != '\n') {
3962 * Don't format lonely punctuation E.g. in "xyz ," format the
3963 * xyz and then append the comma removing the space.
3966 ret = scan_troff(c, san, result);
3967 *(end - 2) = *(end - 1);
3970 ret = scan_troff(c, san, result);
3972 mandoc_line = oldval;
3976 main(int argc, char **argv)
3986 while ((i = getopt(argc, argv, "")) != EOF) {
3998 manpage = h = t = argv[1];
4001 buf = read_man_page(h);
4003 fprintf(stderr, "man2html: cannot read %s: %s\n", h, strerror(errno));
4007 idxfile = fopen(INDEXFILE, "a");
4009 stdf = &standardchar[0];
4012 stdf->next = &standardchar[i];
4016 chardef = &standardchar[0];
4018 stdf = &standardstring[0];
4021 stdf->next = &standardstring[i];
4025 strdef = &standardstring[0];
4027 intdef = &standardint[0];
4029 while (intdef->nr) {
4030 intdef->next = &standardint[i];
4031 intdef = intdef->next;
4034 intdef = &standardint[0];
4038 scan_troff(buf + 1, 0, NULL);
4040 while (itemdepth || dl_set[itemdepth]) {
4041 out_html("</DL>\n");
4042 if (dl_set[itemdepth])
4043 dl_set[itemdepth] = 0;
4044 else if (itemdepth > 0)
4048 out_html(change_to_font(0));
4049 out_html(change_to_size(0));
4056 if (output_possible) {
4057 outputPageFooter(th_version, th_datestr, th_page_and_sec);
4058 /* for mosaic users */
4059 fputs("<HR>\n<A NAME=\"index\"> </A><H2>Index</H2>\n<DL>\n", stdout);
4061 fputs(manidx, stdout);
4063 fputs("</DL>\n", stdout);
4064 fputs("</DL>\n", stdout);
4066 fputs("</BODY>\n</HTML>\n", stdout);
4068 fprintf(stderr, "man2html: no output produced\n");