| /* |
| * This program was written by Richard Verhoeven (NL:5482ZX35) |
| * at the Eindhoven University of Technology. Email: rcb5@win.tue.nl |
| * |
| * Permission is granted to distribute, modify and use this program as long |
| * as this comment is not removed or changed. |
| * |
| * THIS IS A MODIFIED VERSION. IT WAS MODIFIED BY chet@po.cwru.edu FOR |
| * USE BY BASH. |
| */ |
| |
| /* |
| * man2html will add links to the converted manpages. The function add_links |
| * is used for that. At the moment it will add links as follows, where |
| * indicates what should match to start with: |
| * ^^^ |
| * Recognition Item Link |
| * ---------------------------------------------------------- |
| * name(*) Manpage ../man?/name.* |
| * ^ |
| * name@hostname Email address mailto:name@hostname |
| * ^ |
| * method://string URL method://string |
| * ^^^ |
| * www.host.name WWW server http://www.host.name |
| * ^^^^ |
| * ftp.host.name FTP server ftp://ftp.host.name |
| * ^^^^ |
| * <file.h> Include file file:/usr/include/file.h |
| * ^^^ |
| * |
| * Since man2html does not check if manpages, hosts or email addresses exist, |
| * some links might not work. For manpages, some extra checks are performed |
| * to make sure not every () pair creates a link. Also out of date pages |
| * might point to incorrect places. |
| * |
| * The program will not allow users to get system specific files, such as |
| * /etc/passwd. It will check that "man" is part of the specified file and |
| * that "/../" isn't. Even if someone manages to get such file, man2html will |
| * handle it like a manpage and will usually not produce any output (or crash). |
| * |
| * If you find any bugs when normal manpages are converted, please report |
| * them to me (rcb5@win.tue.nl) after you have checked that man(1) can handle |
| * the manpage correct. |
| * |
| * Known bugs and missing features: |
| * |
| * * Equations are not converted at all. |
| * * Tables are converted but some features are not possible in html. |
| * * The tabbing environment is converted by counting characters and adding |
| * spaces. This might go wrong (outside <PRE>) |
| * * Some pages look beter if man2html works in troff mode, especially pages |
| * with tables. You can deside at compile time which made you want to use. |
| * |
| * -DNROFF=0 troff mode |
| * -DNROFF=1 nroff mode (default) |
| * |
| * if you install both modes, you should compile with the correct CGIBASE. |
| * * Some manpages rely on the fact that troff/nroff is used to convert |
| * them and use features which are not descripted in the man manpages. |
| * (definitions, calculations, conditionals, requests). I can't guarantee |
| * that all these features work on all manpages. (I didn't have the |
| * time to look through all the available manpages.) |
| */ |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #define NROFF 0 |
| |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <ctype.h> |
| #include <sys/types.h> |
| #include <time.h> |
| #include <sys/time.h> |
| #include <errno.h> |
| |
| #define NULL_TERMINATED(n) ((n) + 1) |
| |
| #define HUGE_STR_MAX 10000 |
| #define LARGE_STR_MAX 2000 |
| #define MED_STR_MAX 500 |
| #define SMALL_STR_MAX 100 |
| #define TINY_STR_MAX 10 |
| |
| #define MAX_MAN_PATHS 100 /* Max number of directories */ |
| #define MAX_ZCATS 10 /* Max number of zcat style programs */ |
| #define MAX_WORDLIST 100 |
| |
| #ifndef EXIT_SUCCESS |
| #define EXIT_SUCCESS 0 |
| #endif |
| #ifndef EXIT_FAILURE |
| #define EXIT_FAILURE 1 |
| #endif |
| #ifndef EXIT_USAGE |
| #define EXIT_USAGE 2 |
| #endif |
| |
| static char location_base[NULL_TERMINATED(MED_STR_MAX)] = ""; |
| |
| static char th_page_and_sec[128] = { '\0' }; |
| static char th_datestr[128] = { '\0' }; |
| static char th_version[128] = { '\0' }; |
| |
| char *signature = "<HR>\nThis document was created by man2html from %s.<BR>\nTime: %s\n"; |
| |
| /* timeformat for signature */ |
| #define TIMEFORMAT "%d %B %Y %T %Z" |
| |
| char *manpage; |
| |
| /* BSD mandoc Bl/El lists to HTML list types */ |
| #define BL_DESC_LIST 1 |
| #define BL_BULLET_LIST 2 |
| #define BL_ENUM_LIST 4 |
| |
| /* BSD mandoc Bd/Ed example(?) blocks */ |
| #define BD_LITERAL 1 |
| #define BD_INDENT 2 |
| |
| #ifndef HAVE_STRERROR |
| static char * |
| strerror(int e) |
| { |
| static char emsg[40]; |
| |
| #if defined (HAVE_SYS_ERRLIST) |
| extern int sys_nerr; |
| extern char *sys_errlist[]; |
| |
| if (e > 0 && e < sys_nerr) |
| return (sys_errlist[e]); |
| else |
| #endif /* HAVE_SYS_ERRLIST */ |
| { |
| sprintf(emsg, "Unknown system error %d", e); |
| return (&emsg[0]); |
| } |
| } |
| #endif /* !HAVE_STRERROR */ |
| |
| static char * |
| strgrow(char *old, int len) |
| { |
| char *new = realloc(old, (strlen(old) + len + 1) * sizeof(char)); |
| |
| if (!new) { |
| fprintf(stderr, "man2html: out of memory"); |
| exit(EXIT_FAILURE); |
| } |
| return new; |
| } |
| |
| static char * |
| stralloc(int len) |
| { |
| /* allocate enough for len + NULL */ |
| char *new = malloc((len + 1) * sizeof(char)); |
| |
| if (!new) { |
| fprintf(stderr, "man2html: out of memory"); |
| exit(EXIT_FAILURE); |
| } |
| return new; |
| } |
| |
| /* |
| * Some systems don't have strdup so lets use our own - which can also |
| * check for out of memory. |
| */ |
| static char * |
| strduplicate(char *from) |
| { |
| char *new = stralloc(strlen(from)); |
| |
| strcpy(new, from); |
| return new; |
| } |
| |
| /* Assumes space for n plus a null */ |
| static char * |
| strmaxcpy(char *to, char *from, int n) |
| { |
| int len = strlen(from); |
| |
| strncpy(to, from, n); |
| to[(len <= n) ? len : n] = '\0'; |
| return to; |
| } |
| |
| static char * |
| strmaxcat(char *to, char *from, int n) |
| { |
| int to_len = strlen(to); |
| |
| if (to_len < n) { |
| int from_len = strlen(from); |
| int cp = (to_len + from_len <= n) ? from_len : n - to_len; |
| |
| strncpy(to + to_len, from, cp); |
| to[to_len + cp] = '\0'; |
| } |
| return to; |
| } |
| |
| /* Assumes space for limit plus a null */ |
| static char * |
| strlimitcpy(char *to, char *from, int n, int limit) |
| { |
| int len = n > limit ? limit : n; |
| |
| strmaxcpy(to, from, len); |
| to[len] = '\0'; |
| return to; |
| } |
| |
| /* |
| * takes string and escapes all metacharacters. should be used before |
| * including string in system() or similar call. |
| */ |
| static char * |
| escape_input(char *str) |
| { |
| int i, j = 0; |
| static char new[NULL_TERMINATED(MED_STR_MAX)]; |
| |
| if (strlen(str) * 2 + 1 > MED_STR_MAX) { |
| fprintf(stderr, |
| "man2html: escape_input - str too long:\n%-80s...\n", |
| str); |
| exit(EXIT_FAILURE); |
| } |
| for (i = 0; i < strlen(str); i++) { |
| if (!(((str[i] >= 'A') && (str[i] <= 'Z')) || |
| ((str[i] >= 'a') && (str[i] <= 'z')) || |
| ((str[i] >= '0') && (str[i] <= '9')))) { |
| new[j] = '\\'; |
| j++; |
| } |
| new[j] = str[i]; |
| j++; |
| } |
| new[j] = '\0'; |
| return new; |
| } |
| |
| static void |
| usage(void) |
| { |
| fprintf(stderr, "man2html: usage: man2html filename\n"); |
| } |
| |
| |
| |
| /* |
| * below this you should not change anything unless you know a lot |
| * about this program or about troff. |
| */ |
| |
| typedef struct STRDEF STRDEF; |
| struct STRDEF { |
| int nr, slen; |
| char *st; |
| STRDEF *next; |
| }; |
| |
| typedef struct INTDEF INTDEF; |
| struct INTDEF { |
| int nr; |
| int val; |
| int incr; |
| INTDEF *next; |
| }; |
| |
| static char NEWLINE[2] = "\n"; |
| static char idxlabel[6] = "ixAAA"; |
| |
| #define INDEXFILE "/tmp/manindex.list" |
| |
| static char *fname; |
| static FILE *idxfile; |
| |
| static STRDEF *chardef, *strdef, *defdef; |
| static INTDEF *intdef; |
| |
| #define V(A,B) ((A)*256+(B)) |
| |
| static INTDEF standardint[] = { |
| {V('n', ' '), NROFF, 0, NULL}, |
| {V('t', ' '), 1 - NROFF, 0, NULL}, |
| {V('o', ' '), 1, 0, NULL}, |
| {V('e', ' '), 0, 0, NULL}, |
| {V('.', 'l'), 70, 0, NULL}, |
| {V('.', '$'), 0, 0, NULL}, |
| {V('.', 'A'), NROFF, 0, NULL}, |
| {V('.', 'T'), 1 - NROFF, 0, NULL}, |
| {V('.', 'V'), 1, 0, NULL}, /* the me package tests for this */ |
| {0, 0, 0, NULL}}; |
| |
| static STRDEF standardstring[] = { |
| {V('R', ' '), 1, "®", NULL}, |
| {V('l', 'q'), 2, "``", NULL}, |
| {V('r', 'q'), 2, "''", NULL}, |
| {0, 0, NULL, NULL} |
| }; |
| |
| |
| static STRDEF standardchar[] = { |
| {V('*', '*'), 1, "*", NULL}, |
| {V('*', 'A'), 1, "A", NULL}, |
| {V('*', 'B'), 1, "B", NULL}, |
| {V('*', 'C'), 2, "Xi", NULL}, |
| {V('*', 'D'), 5, "Delta", NULL}, |
| {V('*', 'E'), 1, "E", NULL}, |
| {V('*', 'F'), 3, "Phi", NULL}, |
| {V('*', 'G'), 5, "Gamma", NULL}, |
| {V('*', 'H'), 5, "Theta", NULL}, |
| {V('*', 'I'), 1, "I", NULL}, |
| {V('*', 'K'), 1, "K", NULL}, |
| {V('*', 'L'), 6, "Lambda", NULL}, |
| {V('*', 'M'), 1, "M", NULL}, |
| {V('*', 'N'), 1, "N", NULL}, |
| {V('*', 'O'), 1, "O", NULL}, |
| {V('*', 'P'), 2, "Pi", NULL}, |
| {V('*', 'Q'), 3, "Psi", NULL}, |
| {V('*', 'R'), 1, "P", NULL}, |
| {V('*', 'S'), 5, "Sigma", NULL}, |
| {V('*', 'T'), 1, "T", NULL}, |
| {V('*', 'U'), 1, "Y", NULL}, |
| {V('*', 'W'), 5, "Omega", NULL}, |
| {V('*', 'X'), 1, "X", NULL}, |
| {V('*', 'Y'), 1, "H", NULL}, |
| {V('*', 'Z'), 1, "Z", NULL}, |
| {V('*', 'a'), 5, "alpha", NULL}, |
| {V('*', 'b'), 4, "beta", NULL}, |
| {V('*', 'c'), 2, "xi", NULL}, |
| {V('*', 'd'), 5, "delta", NULL}, |
| {V('*', 'e'), 7, "epsilon", NULL}, |
| {V('*', 'f'), 3, "phi", NULL}, |
| {V('*', 'g'), 5, "gamma", NULL}, |
| {V('*', 'h'), 5, "theta", NULL}, |
| {V('*', 'i'), 4, "iota", NULL}, |
| {V('*', 'k'), 5, "kappa", NULL}, |
| {V('*', 'l'), 6, "lambda", NULL}, |
| {V('*', 'm'), 1, "µ", NULL}, |
| {V('*', 'n'), 2, "nu", NULL}, |
| {V('*', 'o'), 1, "o", NULL}, |
| {V('*', 'p'), 2, "pi", NULL}, |
| {V('*', 'q'), 3, "psi", NULL}, |
| {V('*', 'r'), 3, "rho", NULL}, |
| {V('*', 's'), 5, "sigma", NULL}, |
| {V('*', 't'), 3, "tau", NULL}, |
| {V('*', 'u'), 7, "upsilon", NULL}, |
| {V('*', 'w'), 5, "omega", NULL}, |
| {V('*', 'x'), 3, "chi", NULL}, |
| {V('*', 'y'), 3, "eta", NULL}, |
| {V('*', 'z'), 4, "zeta", NULL}, |
| {V('t', 's'), 5, "sigma", NULL}, |
| {V('+', '-'), 1, "±", NULL}, |
| {V('1', '2'), 1, "½", NULL}, |
| {V('1', '4'), 1, "¼", NULL}, |
| {V('3', '4'), 1, "¾", NULL}, |
| {V('F', 'i'), 3, "ffi", NULL}, |
| {V('F', 'l'), 3, "ffl", NULL}, |
| {V('a', 'a'), 1, "´", NULL}, |
| {V('a', 'p'), 1, "~", NULL}, |
| {V('b', 'r'), 1, "|", NULL}, |
| {V('b', 'u'), 1, "*", NULL}, |
| {V('b', 'v'), 1, "|", NULL}, |
| {V('c', 'i'), 1, "o", NULL}, |
| {V('c', 'o'), 1, "©", NULL}, |
| {V('c', 't'), 1, "¢", NULL}, |
| {V('d', 'e'), 1, "°", NULL}, |
| {V('d', 'g'), 1, "+", NULL}, |
| {V('d', 'i'), 1, "÷", NULL}, |
| {V('e', 'm'), 1, "-", NULL}, |
| {V('e', 'm'), 3, "---", NULL}, |
| {V('e', 'q'), 1, "=", NULL}, |
| {V('e', 's'), 1, "Ø", NULL}, |
| {V('f', 'f'), 2, "ff", NULL}, |
| {V('f', 'i'), 2, "fi", NULL}, |
| {V('f', 'l'), 2, "fl", NULL}, |
| {V('f', 'm'), 1, "´", NULL}, |
| {V('g', 'a'), 1, "`", NULL}, |
| {V('h', 'y'), 1, "-", NULL}, |
| {V('l', 'c'), 2, "|¯", NULL}, |
| {V('l', 'f'), 2, "|_", NULL}, |
| {V('l', 'k'), 1, "<FONT SIZE=+2>{</FONT>", NULL}, |
| {V('m', 'i'), 1, "-", NULL}, |
| {V('m', 'u'), 1, "×", NULL}, |
| {V('n', 'o'), 1, "¬", NULL}, |
| {V('o', 'r'), 1, "|", NULL}, |
| {V('p', 'l'), 1, "+", NULL}, |
| {V('r', 'c'), 2, "¯|", NULL}, |
| {V('r', 'f'), 2, "_|", NULL}, |
| {V('r', 'g'), 1, "®", NULL}, |
| {V('r', 'k'), 1, "<FONT SIZE=+2>}</FONT>", NULL}, |
| {V('r', 'n'), 1, "¯", NULL}, |
| {V('r', 'u'), 1, "_", NULL}, |
| {V('s', 'c'), 1, "§", NULL}, |
| {V('s', 'l'), 1, "/", NULL}, |
| {V('s', 'q'), 2, "[]", NULL}, |
| {V('u', 'l'), 1, "_", NULL}, |
| {0, 0, NULL, NULL} |
| }; |
| |
| /* default: print code */ |
| |
| |
| static char eqndelimopen = 0, eqndelimclose = 0; |
| static char escapesym = '\\', nobreaksym = '\'', controlsym = '.', fieldsym = 0, padsym = 0; |
| |
| static char *buffer = NULL; |
| static int buffpos = 0, buffmax = 0; |
| static int scaninbuff = 0; |
| static int itemdepth = 0; |
| static int dl_set[20] = {0}; |
| static int still_dd = 0; |
| static int tabstops[20] = {8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96}; |
| static int maxtstop = 12; |
| static int curpos = 0; |
| |
| static char *scan_troff(char *c, int san, char **result); |
| static char *scan_troff_mandoc(char *c, int san, char **result); |
| |
| static char **argument = NULL; |
| |
| static char charb[TINY_STR_MAX]; |
| |
| static void |
| print_sig(void) |
| { |
| char datbuf[NULL_TERMINATED(MED_STR_MAX)]; |
| struct tm *timetm; |
| time_t clock; |
| |
| datbuf[0] = '\0'; |
| clock = time(NULL); |
| timetm = localtime(&clock); |
| strftime(datbuf, MED_STR_MAX, TIMEFORMAT, timetm); |
| printf(signature, manpage, datbuf); |
| } |
| |
| static char * |
| expand_char(int nr) |
| { |
| STRDEF *h; |
| |
| h = chardef; |
| if (!nr) |
| return NULL; |
| while (h) |
| if (h->nr == nr) { |
| curpos += h->slen; |
| return h->st; |
| } else |
| h = h->next; |
| charb[0] = nr / 256; |
| charb[1] = nr % 256; |
| charb[2] = '\0'; |
| if (charb[0] == '<') { /* Fix up <= */ |
| charb[4] = charb[1]; |
| strncpy(charb, "<", 4); |
| charb[5] = '\0'; |
| } |
| curpos += 2; |
| return charb; |
| } |
| |
| static char * |
| expand_string(int nr) |
| { |
| STRDEF *h = strdef; |
| |
| if (!nr) |
| return NULL; |
| while (h) |
| if (h->nr == nr) { |
| curpos += h->slen; |
| return h->st; |
| } else |
| h = h->next; |
| return NULL; |
| } |
| |
| static char * |
| read_man_page(char *filename) |
| { |
| char *man_buf = NULL; |
| int i; |
| FILE *man_stream = NULL; |
| struct stat stbuf; |
| int buf_size; |
| |
| if (stat(filename, &stbuf) == -1) |
| return NULL; |
| |
| buf_size = stbuf.st_size; |
| man_buf = stralloc(buf_size + 5); |
| man_stream = fopen(filename, "r"); |
| if (man_stream) { |
| man_buf[0] = '\n'; |
| if (fread(man_buf + 1, 1, buf_size, man_stream) == buf_size) { |
| man_buf[buf_size] = '\n'; |
| man_buf[buf_size + 1] = man_buf[buf_size + 2] = '\0'; |
| } else { |
| man_buf = NULL; |
| } |
| fclose(man_stream); |
| } |
| return man_buf; |
| } |
| |
| |
| static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)]; |
| static int obp = 0; |
| static int no_newline_output = 0; |
| static int newline_for_fun = 0; |
| static int output_possible = 0; |
| static int out_length = 0; |
| |
| /* |
| * Add the links to the output. At the moment the following are |
| * recognized: |
| * |
| #if 0 |
| * name(*) -> ../man?/name.* |
| #endif |
| * method://string -> method://string |
| * www.host.name -> http://www.host.name |
| * ftp.host.name -> ftp://ftp.host.name |
| * name@host -> mailto:name@host |
| * <name.h> -> file:/usr/include/name.h (guess) |
| * |
| * Other possible links to add in the future: |
| * |
| * /dir/dir/file -> file:/dir/dir/file |
| */ |
| static void |
| add_links(char *c) |
| { |
| int i, j, nr; |
| char *f, *g, *h; |
| char *idtest[6]; /* url, mailto, www, ftp, manpage */ |
| |
| out_length += strlen(c); |
| /* search for (section) */ |
| nr = 0; |
| idtest[0] = strstr(c + 1, "://"); |
| idtest[1] = strchr(c + 1, '@'); |
| idtest[2] = strstr(c, "www."); |
| idtest[3] = strstr(c, "ftp."); |
| #if 0 |
| idtest[4] = strchr(c + 1, '('); |
| #else |
| idtest[4] = 0; |
| #endif |
| idtest[5] = strstr(c + 1, ".h>"); |
| for (i = 0; i < 6; i++) |
| nr += (idtest[i] != NULL); |
| while (nr) { |
| j = -1; |
| for (i = 0; i < 6; i++) |
| if (idtest[i] && (j < 0 || idtest[i] < idtest[j])) |
| j = i; |
| switch (j) { |
| case 5: /* <name.h> */ |
| f = idtest[5]; |
| h = f + 2; |
| g = f; |
| while (g > c && g[-1] != ';') |
| g--; |
| if (g != c) { |
| char t; |
| |
| t = *g; |
| *g = '\0'; |
| fputs(c, stdout); |
| *g = t; |
| *h = '\0'; |
| printf("<A HREF=\"file:/usr/include/%s\">%s</A>>", g, g); |
| c = f + 6; |
| } else { |
| f[5] = '\0'; |
| fputs(c, stdout); |
| f[5] = ';'; |
| c = f + 5; |
| } |
| break; |
| case 4: /* manpage */ |
| #if 0 |
| f = idtest[j]; |
| /* check section */ |
| g = strchr(f, ')'); |
| if (g && f - g < 6 && (isalnum(f[-1]) || f[-1] == '>') && |
| ((isdigit(f[1]) && f[1] != '0' && |
| (f[2] == ')' || (isalpha(f[2]) && f[3] == ')') || f[2] == 'X')) || |
| (f[2] == ')' && (f[1] == 'n' || f[1] == 'l')))) { |
| /* this might be a link */ |
| h = f - 1; |
| /* skip html makeup */ |
| while (h > c && *h == '>') { |
| while (h != c && *h != '<') |
| h--; |
| if (h != c) |
| h--; |
| } |
| if (isalnum(*h)) { |
| char t, sec, subsec, *e; |
| |
| e = h + 1; |
| sec = f[1]; |
| subsec = f[2]; |
| if ((subsec == 'X' && f[3] != ')') || subsec == ')') |
| subsec = '\0'; |
| while (h > c && (isalnum(h[-1]) || h[-1] == '_' || |
| h[-1] == '-' || h[-1] == '.')) |
| h--; |
| t = *h; |
| *h = '\0'; |
| fputs(c, stdout); |
| *h = t; |
| t = *e; |
| *e = '\0'; |
| if (subsec) |
| printf("<A HREF=\"" |
| CGIBASE |
| "?man%c/%s.%c%c\">%s</A>", |
| sec, h, sec, tolower(subsec), h); |
| else |
| printf("<A HREF=\"" |
| CGIBASE |
| "?man%c/%s.%c\">%s</A>", |
| sec, h, sec, h); |
| *e = t; |
| c = e; |
| } |
| } |
| *f = '\0'; |
| fputs(c, stdout); |
| *f = '('; |
| idtest[4] = f - 1; |
| c = f; |
| #endif |
| break; /* manpage */ |
| case 3: /* ftp */ |
| case 2: /* www */ |
| g = f = idtest[j]; |
| while (*g && (isalnum(*g) || *g == '_' || *g == '-' || *g == '+' || |
| *g == '.')) |
| g++; |
| if (g[-1] == '.') |
| g--; |
| if (g - f > 4) { |
| char t; |
| |
| t = *f; |
| *f = '\0'; |
| fputs(c, stdout); |
| *f = t; |
| t = *g; |
| *g = '\0'; |
| printf("<A HREF=\"%s://%s\">%s</A>", (j == 3 ? "ftp" : "http"), |
| f, f); |
| *g = t; |
| c = g; |
| } else { |
| f[3] = '\0'; |
| fputs(c, stdout); |
| c = f + 3; |
| f[3] = '.'; |
| } |
| break; |
| case 1: /* mailto */ |
| g = f = idtest[1]; |
| while (g > c && (isalnum(g[-1]) || g[-1] == '_' || g[-1] == '-' || |
| g[-1] == '+' || g[-1] == '.' || g[-1] == '%')) |
| g--; |
| h = f + 1; |
| while (*h && (isalnum(*h) || *h == '_' || *h == '-' || *h == '+' || |
| *h == '.')) |
| h++; |
| if (*h == '.') |
| h--; |
| if (h - f > 4 && f - g > 1) { |
| char t; |
| |
| t = *g; |
| *g = '\0'; |
| fputs(c, stdout); |
| *g = t; |
| t = *h; |
| *h = '\0'; |
| printf("<A HREF=\"mailto:%s\">%s</A>", g, g); |
| *h = t; |
| c = h; |
| } else { |
| *f = '\0'; |
| fputs(c, stdout); |
| *f = '@'; |
| idtest[1] = c; |
| c = f; |
| } |
| break; |
| case 0: /* url */ |
| g = f = idtest[0]; |
| while (g > c && isalpha(g[-1]) && islower(g[-1])) |
| g--; |
| h = f + 3; |
| while (*h && !isspace(*h) && *h != '<' && *h != '>' && *h != '"' && |
| *h != '&') |
| h++; |
| if (f - g > 2 && f - g < 7 && h - f > 3) { |
| char t; |
| |
| t = *g; |
| *g = '\0'; |
| fputs(c, stdout); |
| *g = t; |
| t = *h; |
| *h = '\0'; |
| printf("<A HREF=\"%s\">%s</A>", g, g); |
| *h = t; |
| c = h; |
| } else { |
| f[1] = '\0'; |
| fputs(c, stdout); |
| f[1] = '/'; |
| c = f + 1; |
| } |
| break; |
| default: |
| break; |
| } |
| nr = 0; |
| if (idtest[0] && idtest[0] < c) |
| idtest[0] = strstr(c + 1, "://"); |
| if (idtest[1] && idtest[1] < c) |
| idtest[1] = strchr(c + 1, '@'); |
| if (idtest[2] && idtest[2] < c) |
| idtest[2] = strstr(c, "www."); |
| if (idtest[3] && idtest[3] < c) |
| idtest[3] = strstr(c, "ftp."); |
| if (idtest[4] && idtest[4] < c) |
| idtest[4] = strchr(c + 1, '('); |
| if (idtest[5] && idtest[5] < c) |
| idtest[5] = strstr(c + 1, ".h>"); |
| for (i = 0; i < 6; i++) |
| nr += (idtest[i] != NULL); |
| } |
| fputs(c, stdout); |
| } |
| |
| static int current_font = 0; |
| static int current_size = 0; |
| static int fillout = 1; |
| |
| static void |
| out_html(char *c) |
| { |
| if (!c) |
| return; |
| if (no_newline_output) { |
| int i = 0; |
| |
| no_newline_output = 1; |
| while (c[i]) { |
| if (!no_newline_output) |
| c[i - 1] = c[i]; |
| if (c[i] == '\n') |
| no_newline_output = 1; |
| i++; |
| } |
| if (!no_newline_output) |
| c[i - 1] = 0; |
| } |
| if (scaninbuff) { |
| while (*c) { |
| if (buffpos >= buffmax) { |
| char *h; |
| |
| h = realloc(buffer, buffmax * 2); |
| if (!h) |
| return; |
| buffer = h; |
| buffmax *= 2; |
| } |
| buffer[buffpos++] = *c++; |
| } |
| } else if (output_possible) { |
| while (*c) { |
| outbuffer[obp++] = *c; |
| if (*c == '\n' || obp > HUGE_STR_MAX) { |
| outbuffer[obp] = '\0'; |
| add_links(outbuffer); |
| obp = 0; |
| } |
| c++; |
| } |
| } |
| } |
| |
| #define FO0 "" |
| #define FC0 "" |
| #define FO1 "<I>" |
| #define FC1 "</I>" |
| #define FO2 "<B>" |
| #define FC2 "</B>" |
| #define FO3 "<TT>" |
| #define FC3 "</TT>" |
| |
| static char *switchfont[16] = { |
| "", FC0 FO1, FC0 FO2, FC0 FO3, |
| FC1 FO0, "", FC1 FO2, FC1 FO3, |
| FC2 FO0, FC2 FO1, "", FC2 FO3, |
| FC3 FO0, FC3 FO1, FC3 FO2, "" |
| }; |
| |
| static char * |
| change_to_font(int nr) |
| { |
| int i; |
| |
| switch (nr) { |
| case '0': |
| nr++; |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| nr = nr - '1'; |
| break; |
| case V('C', 'W'): |
| nr = 3; |
| break; |
| case 'L': |
| nr = 3; |
| break; |
| case 'B': |
| nr = 2; |
| break; |
| case 'I': |
| nr = 1; |
| break; |
| case 'P': |
| case 'R': |
| nr = 0; |
| break; |
| case 0: |
| case 1: |
| case 2: |
| case 3: |
| break; |
| default: |
| nr = 0; |
| break; |
| } |
| i = current_font * 4 + nr % 4; |
| current_font = nr % 4; |
| return switchfont[i]; |
| } |
| |
| static char sizebuf[200]; |
| |
| static char * |
| change_to_size(int nr) |
| { |
| int i; |
| |
| switch (nr) { |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| nr = nr - '0'; |
| break; |
| case '\0': |
| break; |
| default: |
| nr = current_size + nr; |
| if (nr > 9) |
| nr = 9; |
| if (nr < -9) |
| nr = -9; |
| break; |
| } |
| if (nr == current_size) |
| return ""; |
| i = current_font; |
| sizebuf[0] = '\0'; |
| strcat(sizebuf, change_to_font(0)); |
| if (current_size) |
| strcat(sizebuf, "</FONT>"); |
| current_size = nr; |
| if (nr) { |
| int l; |
| |
| strcat(sizebuf, "<FONT SIZE="); |
| l = strlen(sizebuf); |
| if (nr > 0) |
| sizebuf[l++] = '+'; |
| else |
| sizebuf[l++] = '-', nr = -nr; |
| sizebuf[l++] = nr + '0'; |
| sizebuf[l++] = '>'; |
| sizebuf[l] = '\0'; |
| } |
| strcat(sizebuf, change_to_font(i)); |
| return sizebuf; |
| } |
| |
| static int asint = 0; |
| static int intresult = 0; |
| |
| #define SKIPEOL while (*c && *c++!='\n') |
| |
| static int skip_escape = 0; |
| static int single_escape = 0; |
| |
| static char * |
| scan_escape(char *c) |
| { |
| char *h = NULL; |
| char b[5]; |
| INTDEF *intd; |
| int exoutputp, exskipescape; |
| int i, j; |
| |
| intresult = 0; |
| switch (*c) { |
| case 'e': |
| h = "\\"; |
| curpos++; |
| break; |
| case '0': |
| case ' ': |
| h = " "; |
| curpos++; |
| break; |
| case '|': |
| h = ""; |
| break; |
| case '"': |
| SKIPEOL; |
| c--; |
| h = ""; |
| break; |
| case '$': |
| if (argument) { |
| c++; |
| i = (*c - '1'); |
| if (!(h = argument[i])) |
| h = ""; |
| } |
| break; |
| case 'z': |
| c++; |
| if (*c == '\\') { |
| c = scan_escape(c + 1); |
| c--; |
| h = ""; |
| } else { |
| b[0] = *c; |
| b[1] = '\0'; |
| h = ""; |
| } |
| break; |
| case 'k': |
| c++; |
| if (*c == '(') |
| c += 2; |
| case '^': |
| case '!': |
| case '%': |
| case 'a': |
| case 'd': |
| case 'r': |
| case 'u': |
| case '\n': |
| case '&': |
| h = ""; |
| break; |
| case '(': |
| c++; |
| i = c[0] * 256 + c[1]; |
| c++; |
| h = expand_char(i); |
| break; |
| case '*': |
| c++; |
| if (*c == '(') { |
| c++; |
| i = c[0] * 256 + c[1]; |
| c++; |
| } else |
| i = *c * 256 + ' '; |
| h = expand_string(i); |
| break; |
| case 'f': |
| c++; |
| if (*c == '\\') { |
| c++; |
| c = scan_escape(c); |
| c--; |
| i = intresult; |
| } else if (*c != '(') |
| i = *c; |
| else { |
| c++; |
| i = c[0] * 256 + c[1]; |
| c++; |
| } |
| if (!skip_escape) |
| h = change_to_font(i); |
| else |
| h = ""; |
| break; |
| case 's': |
| c++; |
| j = 0; |
| i = 0; |
| if (*c == '-') { |
| j = -1; |
| c++; |
| } else if (*c == '+') { |
| j = 1; |
| c++; |
| } |
| if (*c == '0') |
| c++; |
| else if (*c == '\\') { |
| c++; |
| c = scan_escape(c); |
| i = intresult; |
| if (!j) |
| j = 1; |
| } else |
| while (isdigit(*c) && (!i || (!j && i < 4))) |
| i = i * 10 + (*c++) - '0'; |
| if (!j) { |
| j = 1; |
| if (i) |
| i = i - 10; |
| } |
| if (!skip_escape) |
| h = change_to_size(i * j); |
| else |
| h = ""; |
| c--; |
| break; |
| case 'n': |
| c++; |
| j = 0; |
| switch (*c) { |
| case '+': |
| j = 1; |
| c++; |
| break; |
| case '-': |
| j = -1; |
| c++; |
| break; |
| default: |
| break; |
| } |
| if (*c == '(') { |
| c++; |
| i = V(c[0], c[1]); |
| c = c + 1; |
| } else { |
| i = V(c[0], ' '); |
| } |
| intd = intdef; |
| while (intd && intd->nr != i) |
| intd = intd->next; |
| if (intd) { |
| intd->val = intd->val + j * intd->incr; |
| intresult = intd->val; |
| } else { |
| switch (i) { |
| case V('.', 's'): |
| intresult = current_size; |
| break; |
| case V('.', 'f'): |
| intresult = current_font; |
| break; |
| default: |
| intresult = 0; |
| break; |
| } |
| } |
| h = ""; |
| break; |
| case 'w': |
| c++; |
| i = *c; |
| c++; |
| exoutputp = output_possible; |
| exskipescape = skip_escape; |
| output_possible = 0; |
| skip_escape = 1; |
| j = 0; |
| while (*c != i) { |
| j++; |
| if (*c == escapesym) |
| c = scan_escape(c + 1); |
| else |
| c++; |
| } |
| output_possible = exoutputp; |
| skip_escape = exskipescape; |
| intresult = j; |
| break; |
| case 'l': |
| h = "<HR>"; |
| curpos = 0; |
| case 'b': |
| case 'v': |
| case 'x': |
| case 'o': |
| case 'L': |
| case 'h': |
| c++; |
| i = *c; |
| c++; |
| exoutputp = output_possible; |
| exskipescape = skip_escape; |
| output_possible = 0; |
| skip_escape = 1; |
| while (*c != i) |
| if (*c == escapesym) |
| c = scan_escape(c + 1); |
| else |
| c++; |
| output_possible = exoutputp; |
| skip_escape = exskipescape; |
| break; |
| case 'c': |
| no_newline_output = 1; |
| break; |
| case '{': |
| newline_for_fun++; |
| h = ""; |
| break; |
| case '}': |
| if (newline_for_fun) |
| newline_for_fun--; |
| h = ""; |
| break; |
| case 'p': |
| h = "<BR>\n"; |
| curpos = 0; |
| break; |
| case 't': |
| h = "\t"; |
| curpos = (curpos + 8) & 0xfff8; |
| break; |
| case '<': |
| h = "<"; |
| curpos++; |
| break; |
| case '>': |
| h = ">"; |
| curpos++; |
| break; |
| case '\\': |
| if (single_escape) { |
| c--; |
| break; |
| } |
| default: |
| b[0] = *c; |
| b[1] = 0; |
| h = b; |
| curpos++; |
| break; |
| } |
| c++; |
| if (!skip_escape) |
| out_html(h); |
| return c; |
| } |
| |
| typedef struct TABLEITEM TABLEITEM; |
| |
| struct TABLEITEM { |
| char *contents; |
| int size, align, valign, colspan, rowspan, font, vleft, vright, space, |
| width; |
| TABLEITEM *next; |
| }; |
| |
| static TABLEITEM emptyfield = {NULL, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, NULL}; |
| |
| typedef struct TABLEROW TABLEROW; |
| |
| struct TABLEROW { |
| TABLEITEM *first; |
| TABLEROW *prev, *next; |
| }; |
| |
| static char *tableopt[] = { |
| "center", "expand", "box", "allbox", "doublebox", |
| "tab", "linesize", "delim", NULL |
| }; |
| static int tableoptl[] = {6, 6, 3, 6, 9, 3, 8, 5, 0}; |
| |
| static void |
| clear_table(TABLEROW * table) |
| { |
| TABLEROW *tr1, *tr2; |
| TABLEITEM *ti1, *ti2; |
| |
| tr1 = table; |
| while (tr1->prev) |
| tr1 = tr1->prev; |
| while (tr1) { |
| ti1 = tr1->first; |
| while (ti1) { |
| ti2 = ti1->next; |
| if (ti1->contents) |
| free(ti1->contents); |
| free(ti1); |
| ti1 = ti2; |
| } |
| tr2 = tr1; |
| tr1 = tr1->next; |
| free(tr2); |
| } |
| } |
| |
| static char *scan_expression(char *c, int *result); |
| |
| static char * |
| scan_format(char *c, TABLEROW ** result, int *maxcol) |
| { |
| TABLEROW *layout, *currow; |
| TABLEITEM *curfield; |
| int i, j; |
| |
| if (*result) { |
| clear_table(*result); |
| } |
| layout = currow = (TABLEROW *) malloc(sizeof(TABLEROW)); |
| currow->next = currow->prev = NULL; |
| currow->first = curfield = (TABLEITEM *) malloc(sizeof(TABLEITEM)); |
| *curfield = emptyfield; |
| while (*c && *c != '.') { |
| switch (*c) { |
| case 'C': |
| case 'c': |
| case 'N': |
| case 'n': |
| case 'R': |
| case 'r': |
| case 'A': |
| case 'a': |
| case 'L': |
| case 'l': |
| case 'S': |
| case 's': |
| case '^': |
| case '_': |
| if (curfield->align) { |
| curfield->next = (TABLEITEM *) malloc(sizeof(TABLEITEM)); |
| curfield = curfield->next; |
| *curfield = emptyfield; |
| } |
| curfield->align = toupper(*c); |
| c++; |
| break; |
| case 'i': |
| case 'I': |
| case 'B': |
| case 'b': |
| curfield->font = toupper(*c); |
| c++; |
| break; |
| case 'f': |
| case 'F': |
| c++; |
| curfield->font = toupper(*c); |
| c++; |
| if (!isspace(*c)) |
| c++; |
| break; |
| case 't': |
| case 'T': |
| curfield->valign = 't'; |
| c++; |
| break; |
| case 'p': |
| case 'P': |
| c++; |
| i = j = 0; |
| if (*c == '+') { |
| j = 1; |
| c++; |
| } |
| if (*c == '-') { |
| j = -1; |
| c++; |
| } |
| while (isdigit(*c)) |
| i = i * 10 + (*c++) - '0'; |
| if (j) |
| curfield->size = i * j; |
| else |
| curfield->size = j - 10; |
| break; |
| case 'v': |
| case 'V': |
| case 'w': |
| case 'W': |
| c = scan_expression(c + 2, &curfield->width); |
| break; |
| case '|': |
| if (curfield->align) |
| curfield->vleft++; |
| else |
| curfield->vright++; |
| c++; |
| break; |
| case 'e': |
| case 'E': |
| c++; |
| break; |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| i = 0; |
| while (isdigit(*c)) |
| i = i * 10 + (*c++) - '0'; |
| curfield->space = i; |
| break; |
| case ',': |
| case '\n': |
| currow->next = (TABLEROW *) malloc(sizeof(TABLEROW)); |
| currow->next->prev = currow; |
| currow = currow->next; |
| currow->next = NULL; |
| curfield = currow->first = (TABLEITEM *) malloc(sizeof(TABLEITEM)); |
| *curfield = emptyfield; |
| c++; |
| break; |
| default: |
| c++; |
| break; |
| } |
| } |
| if (*c == '.') |
| while (*c++ != '\n'); |
| *maxcol = 0; |
| currow = layout; |
| while (currow) { |
| curfield = layout->first; |
| i = 0; |
| while (curfield) { |
| i++; |
| curfield = curfield->next; |
| } |
| if (i > *maxcol) |
| *maxcol = i; |
| currow = currow->next; |
| } |
| *result = layout; |
| return c; |
| } |
| |
| static TABLEROW * |
| next_row(TABLEROW * tr) |
| { |
| if (tr->next) { |
| tr = tr->next; |
| if (!tr->next) |
| next_row(tr); |
| return tr; |
| } else { |
| TABLEITEM *ti, *ti2; |
| |
| tr->next = (TABLEROW *) malloc(sizeof(TABLEROW)); |
| tr->next->prev = tr; |
| ti = tr->first; |
| tr = tr->next; |
| tr->next = NULL; |
| if (ti) |
| tr->first = ti2 = (TABLEITEM *) malloc(sizeof(TABLEITEM)); |
| else |
| tr->first = ti2 = NULL; |
| while (ti != ti2) { |
| *ti2 = *ti; |
| ti2->contents = NULL; |
| if ((ti = ti->next)) { |
| ti2->next = (TABLEITEM *) malloc(sizeof(TABLEITEM)); |
| } |
| ti2 = ti2->next; |
| } |
| return tr; |
| } |
| } |
| |
| static char itemreset[20] = "\\fR\\s0"; |
| |
| static char * |
| scan_table(char *c) |
| { |
| char *t, *h, *g; |
| int center = 0, expand = 0, box = 0, border = 0, linesize = 1; |
| int i, j, maxcol = 0, finished = 0; |
| int oldfont, oldsize, oldfillout; |
| char itemsep = '\t'; |
| TABLEROW *layout = NULL, *currow, *ftable; |
| TABLEITEM *curfield; |
| |
| while (*c++ != '\n'); |
| h = c; |
| if (*h == '.') |
| return c - 1; |
| oldfont = current_font; |
| oldsize = current_size; |
| oldfillout = fillout; |
| out_html(change_to_font(0)); |
| out_html(change_to_size(0)); |
| if (!fillout) { |
| fillout = 1; |
| out_html("</PRE>"); |
| } |
| while (*h && *h != '\n') |
| h++; |
| if (h[-1] == ';') { |
| /* scan table options */ |
| while (c < h) { |
| while (isspace(*c)) |
| c++; |
| for (i = 0; tableopt[i] && strncmp(tableopt[i], c, tableoptl[i]); i++); |
| c = c + tableoptl[i]; |
| switch (i) { |
| case 0: |
| center = 1; |
| break; |
| case 1: |
| expand = 1; |
| break; |
| case 2: |
| box = 1; |
| break; |
| case 3: |
| border = 1; |
| break; |
| case 4: |
| box = 2; |
| break; |
| case 5: |
| while (*c++ != '('); |
| itemsep = *c++; |
| break; |
| case 6: |
| while (*c++ != '('); |
| linesize = 0; |
| while (isdigit(*c)) |
| linesize = linesize * 10 + (*c++) - '0'; |
| break; |
| case 7: |
| while (*c != ')') |
| c++; |
| default: |
| break; |
| } |
| c++; |
| } |
| c = h + 1; |
| } |
| /* scan layout */ |
| c = scan_format(c, &layout, &maxcol); |
| currow = layout; |
| next_row(currow); |
| curfield = layout->first; |
| i = 0; |
| while (!finished) { |
| /* search item */ |
| h = c; |
| if ((*c == '_' || *c == '=') && (c[1] == itemsep || c[1] == '\n')) { |
| if (c[-1] == '\n' && c[1] == '\n') { |
| if (currow->prev) { |
| currow->prev->next = (TABLEROW *) malloc(sizeof(TABLEROW)); |
| currow->prev->next->next = currow; |
| currow->prev->next->prev = currow->prev; |
| currow->prev = currow->prev->next; |
| } else { |
| currow->prev = layout = (TABLEROW *) malloc(sizeof(TABLEROW)); |
| currow->prev->prev = NULL; |
| currow->prev->next = currow; |
| } |
| curfield = currow->prev->first = |
| (TABLEITEM *) malloc(sizeof(TABLEITEM)); |
| *curfield = emptyfield; |
| curfield->align = *c; |
| curfield->colspan = maxcol; |
| curfield = currow->first; |
| c = c + 2; |
| } else { |
| if (curfield) { |
| curfield->align = *c; |
| do { |
| curfield = curfield->next; |
| } while (curfield && curfield->align == 'S'); |
| } |
| if (c[1] == '\n') { |
| currow = next_row(currow); |
| curfield = currow->first; |
| } |
| c = c + 2; |
| } |
| } else if (*c == 'T' && c[1] == '{') { |
| h = c + 2; |
| c = strstr(h, "\nT}"); |
| c++; |
| *c = '\0'; |
| g = NULL; |
| scan_troff(h, 0, &g); |
| scan_troff(itemreset, 0, &g); |
| *c = 'T'; |
| c += 3; |
| if (curfield) { |
| curfield->contents = g; |
| do { |
| curfield = curfield->next; |
| } while (curfield && curfield->align == 'S'); |
| } else if (g) |
| free(g); |
| if (c[-1] == '\n') { |
| currow = next_row(currow); |
| curfield = currow->first; |
| } |
| } else if (*c == '.' && c[1] == 'T' && c[2] == '&' && c[-1] == '\n') { |
| TABLEROW *hr; |
| |
| while (*c++ != '\n'); |
| hr = currow; |
| currow = currow->prev; |
| hr->prev = NULL; |
| c = scan_format(c, &hr, &i); |
| hr->prev = currow; |
| currow->next = hr; |
| currow = hr; |
| next_row(currow); |
| curfield = currow->first; |
| } else if (*c == '.' && c[1] == 'T' && c[2] == 'E' && c[-1] == '\n') { |
| finished = 1; |
| while (*c++ != '\n'); |
| if (currow->prev) |
| currow->prev->next = NULL; |
| currow->prev = NULL; |
| clear_table(currow); |
| } else if (*c == '.' && c[-1] == '\n' && !isdigit(c[1])) { |
| /* |
| * skip troff request inside table (usually only .sp |
| * ) |
| */ |
| while (*c++ != '\n'); |
| } else { |
| h = c; |
| while (*c && (*c != itemsep || c[-1] == '\\') && |
| (*c != '\n' || c[-1] == '\\')) |
| c++; |
| i = 0; |
| if (*c == itemsep) { |
| i = 1; |
| *c = '\n'; |
| } |
| if (h[0] == '\\' && h[2] == '\n' && |
| (h[1] == '_' || h[1] == '^')) { |
| if (curfield) { |
| curfield->align = h[1]; |
| do { |
| curfield = curfield->next; |
| } while (curfield && curfield->align == 'S'); |
| } |
| h = h + 3; |
| } else { |
| g = NULL; |
| h = scan_troff(h, 1, &g); |
| scan_troff(itemreset, 0, &g); |
| if (curfield) { |
| curfield->contents = g; |
| do { |
| curfield = curfield->next; |
| } while (curfield && curfield->align == 'S'); |
| } else if (g) |
| free(g); |
| } |
| if (i) |
| *c = itemsep; |
| c = h; |
| if (c[-1] == '\n') { |
| currow = next_row(currow); |
| curfield = currow->first; |
| } |
| } |
| } |
| /* calculate colspan and rowspan */ |
| currow = layout; |
| while (currow->next) |
| currow = currow->next; |
| while (currow) { |
| TABLEITEM *ti, *ti1 = NULL, *ti2 = NULL; |
| |
| ti = currow->first; |
| if (currow->prev) |
| ti1 = currow->prev->first; |
| while (ti) { |
| switch (ti->align) { |
| case 'S': |
| if (ti2) { |
| ti2->colspan++; |
| if (ti2->rowspan < ti->rowspan) |
| ti2->rowspan = ti->rowspan; |
| } |
| break; |
| case '^': |
| if (ti1) |
| ti1->rowspan++; |
| default: |
| if (!ti2) |
| ti2 = ti; |
| else { |
| do { |
| ti2 = ti2->next; |
| } while (ti2 && curfield->align == 'S'); |
| } |
| break; |
| } |
| ti = ti->next; |
| if (ti1) |
| ti1 = ti1->next; |
| } |
| currow = currow->prev; |
| } |
| /* produce html output */ |
| if (center) |
| out_html("<CENTER>"); |
| if (box == 2) |
| out_html("<TABLE BORDER><TR><TD>"); |
| out_html("<TABLE"); |
| if (box || border) { |
| out_html(" BORDER"); |
| if (!border) |
| out_html("><TR><TD><TABLE"); |
| if (expand) |
| out_html(" WIDTH=100%"); |
| } |
| out_html(">\n"); |
| currow = layout; |
| while (currow) { |
| j = 0; |
| out_html("<TR VALIGN=top>"); |
| curfield = currow->first; |
| while (curfield) { |
| if (curfield->align != 'S' && curfield->align != '^') { |
| out_html("<TD"); |
| switch (curfield->align) { |
| case 'N': |
| curfield->space += 4; |
| case 'R': |
| out_html(" ALIGN=right"); |
| break; |
| case 'C': |
| out_html(" ALIGN=center"); |
| default: |
| break; |
| } |
| if (!curfield->valign && curfield->rowspan > 1) |
| out_html(" VALIGN=center"); |
| if (curfield->colspan > 1) { |
| char buf[5]; |
| |
| out_html(" COLSPAN="); |
| sprintf(buf, "%i", curfield->colspan); |
| out_html(buf); |
| } |
| if (curfield->rowspan > 1) { |
| char buf[5]; |
| |
| out_html(" ROWSPAN="); |
| sprintf(buf, "%i", curfield->rowspan); |
| out_html(buf); |
| } |
| j = j + curfield->colspan; |
| out_html(">"); |
| if (curfield->size) |
| out_html(change_to_size(curfield->size)); |
| if (curfield->font) |
| out_html(change_to_font(curfield->font)); |
| switch (curfield->align) { |
| case '=': |
| out_html("<HR><HR>"); |
| break; |
| case '_': |
| out_html("<HR>"); |
| break; |
| default: |
| if (curfield->contents) |
| out_html(curfield->contents); |
| break; |
| } |
| if (curfield->space) |
| for (i = 0; i < curfield->space; i++) |
| out_html(" "); |
| if (curfield->font) |
| out_html(change_to_font(0)); |
| if (curfield->size) |
| out_html(change_to_size(0)); |
| if (j >= maxcol && curfield->align > '@' && curfield->align != '_') |
| out_html("<BR>"); |
| out_html("</TD>"); |
| } |
| curfield = curfield->next; |
| } |
| out_html("</TR>\n"); |
| currow = currow->next; |
| } |
| if (box && !border) |
| out_html("</TABLE>"); |
| out_html("</TABLE>"); |
| if (box == 2) |
| out_html("</TABLE>"); |
| if (center) |
| out_html("</CENTER>\n"); |
| else |
| out_html("\n"); |
| if (!oldfillout) |
| out_html("<PRE>"); |
| fillout = oldfillout; |
| out_html(change_to_size(oldsize)); |
| out_html(change_to_font(oldfont)); |
| return c; |
| } |
| |
| static char * |
| scan_expression(char *c, int *result) |
| { |
| int value = 0, value2, j = 0, sign = 1, opex = 0; |
| char oper = 'c'; |
| |
| if (*c == '!') { |
| c = scan_expression(c + 1, &value); |
| value = (!value); |
| } else if (*c == 'n') { |
| c++; |
| value = NROFF; |
| } else if (*c == 't') { |
| c++; |
| value = 1 - NROFF; |
| } else if (*c == '\'' || *c == '"' || *c < ' ' || (*c == '\\' && c[1] == '(')) { |
| /* |
| * ?string1?string2? test if string1 equals string2. |
| */ |
| char *st1 = NULL, *st2 = NULL, *h; |
| char *tcmp = NULL; |
| char sep; |
| |
| sep = *c; |
| if (sep == '\\') { |
| tcmp = c; |
| c = c + 3; |
| } |
| c++; |
| h = c; |
| while (*c != sep && (!tcmp || strncmp(c, tcmp, 4))) |
| c++; |
| *c = '\n'; |
| scan_troff(h, 1, &st1); |
| *c = sep; |
| if (tcmp) |
| c = c + 3; |
| c++; |
| h = c; |
| while (*c != sep && (!tcmp || strncmp(c, tcmp, 4))) |
| c++; |
| *c = '\n'; |
| scan_troff(h, 1, &st2); |
| *c = sep; |
| if (!st1 && !st2) |
| value = 1; |
| else if (!st1 || !st2) |
| value = 0; |
| else |
| value = (!strcmp(st1, st2)); |
| if (st1) |
| free(st1); |
| if (st2) |
| free(st2); |
| if (tcmp) |
| c = c + 3; |
| c++; |
| } else { |
| while (*c && !isspace(*c) && *c != ')') { |
| opex = 0; |
| switch (*c) { |
| case '(': |
| c = scan_expression(c + 1, &value2); |
| value2 = sign * value2; |
| opex = 1; |
| break; |
| case '.': |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9':{ |
| int num = 0, denum = 1; |
| |
| value2 = 0; |
| while (isdigit(*c)) |
| value2 = value2 * 10 + ((*c++) - '0'); |
| if (*c == '.') { |
| c++; |
| while (isdigit(*c)) { |
| num = num * 10 + ((*c++) - '0'); |
| denum = denum * 10; |
| } |
| } |
| if (isalpha(*c)) { |
| /* scale indicator */ |
| switch (*c) { |
| case 'i': /* inch -> 10pt */ |
| value2 = value2 * 10 + (num * 10 + denum / 2) / denum; |
| num = 0; |
| break; |
| default: |
| break; |
| } |
| c++; |
| } |
| value2 = value2 + (num + denum / 2) / denum; |
| value2 = sign * value2; |
| opex = 1; |
| break; |
| } |
| case '\\': |
| c = scan_escape(c + 1); |
| value2 = intresult * sign; |
| if (isalpha(*c)) |
| c++; /* scale indicator */ |
| opex = 1; |
| break; |
| case '-': |
| if (oper) { |
| sign = -1; |
| c++; |
| break; |
| } |
| case '>': |
| case '<': |
| case '+': |
| case '/': |
| case '*': |
| case '%': |
| case '&': |
| case '=': |
| case ':': |
| if (c[1] == '=') |
| oper = (*c++) + 16; |
| else |
| oper = *c; |
| c++; |
| break; |
| default: |
| c++; |
| break; |
| } |
| if (opex) { |
| sign = 1; |
| switch (oper) { |
| case 'c': |
| value = value2; |
| break; |
| case '-': |
| value = value - value2; |
| break; |
| case '+': |
| value = value + value2; |
| break; |
| case '*': |
| value = value * value2; |
| break; |
| case '/': |
| if (value2) |
| value = value / value2; |
| break; |
| case '%': |
| if (value2) |
| value = value % value2; |
| break; |
| case '<': |
| value = (value < value2); |
| break; |
| case '>': |
| value = (value > value2); |
| break; |
| case '>' + 16: |
| value = (value >= value2); |
| break; |
| case '<' + 16: |
| value = (value <= value2); |
| break; |
| case '=': |
| case '=' + 16: |
| value = (value == value2); |
| break; |
| case '&': |
| value = (value && value2); |
| break; |
| case ':': |
| value = (value || value2); |
| break; |
| default: |
| fprintf(stderr, "man2html: unknown operator %c.\n", oper); |
| } |
| oper = 0; |
| } |
| } |
| if (*c == ')') |
| c++; |
| } |
| *result = value; |
| return c; |
| } |
| |
| static void |
| trans_char(char *c, char s, char t) |
| { |
| char *sl = c; |
| int slash = 0; |
| |
| while (*sl != '\n' || slash) { |
| if (!slash) { |
| if (*sl == escapesym) |
| slash = 1; |
| else if (*sl == s) |
| *sl = t; |
| } else |
| slash = 0; |
| sl++; |
| } |
| } |
| |
| /* Remove \a from C in place. Return modified C. */ |
| static char * |
| unescape (char *c) |
| { |
| int i, l; |
| |
| l = strlen (c); |
| i = 0; |
| while (i < l && c[i]) { |
| if (c[i] == '\a') { |
| if (c[i+1]) |
| strcpy(c + i, c + i + 1); /* should be memmove */ |
| else { |
| c[i] = '\0'; |
| break; |
| } |
| } |
| i++; |
| } |
| return c; |
| } |
| |
| static char * |
| fill_words(char *c, char *words[], int *n) |
| { |
| char *sl = c; |
| int slash = 0; |
| int skipspace = 0; |
| |
| *n = 0; |
| words[*n] = sl; |
| while (*sl && (*sl != '\n' || slash)) { |
| if (!slash) { |
| if (*sl == '"') { |
| *sl = '\a'; |
| skipspace = !skipspace; |
| } else if (*sl == '\a') { |
| /* handle already-translated " */ |
| skipspace = !skipspace; |
| } else if (*sl == escapesym) |
| slash = 1; |
| else if ((*sl == ' ' || *sl == '\t') && !skipspace) { |
| *sl = '\n'; |
| if (words[*n] != sl) |
| (*n)++; |
| words[*n] = sl + 1; |
| } |
| } else { |
| if (*sl == '"') { |
| sl--; |
| *sl = '\n'; |
| if (words[*n] != sl) |
| (*n)++; |
| sl++; |
| while (*sl && *sl != '\n') |
| sl++; |
| words[*n] = sl; |
| sl--; |
| } |
| slash = 0; |
| } |
| sl++; |
| } |
| if (sl != words[*n]) |
| (*n)++; |
| return sl; |
| } |
| |
| static char *abbrev_list[] = { |
| "GSBG", "Getting Started ", |
| "SUBG", "Customizing SunOS", |
| "SHBG", "Basic Troubleshooting", |
| "SVBG", "SunView User's Guide", |
| "MMBG", "Mail and Messages", |
| "DMBG", "Doing More with SunOS", |
| "UNBG", "Using the Network", |
| "GDBG", "Games, Demos & Other Pursuits", |
| "CHANGE", "SunOS 4.1 Release Manual", |
| "INSTALL", "Installing SunOS 4.1", |
| "ADMIN", "System and Network Administration", |
| "SECUR", "Security Features Guide", |
| "PROM", "PROM User's Manual", |
| "DIAG", "Sun System Diagnostics", |
| "SUNDIAG", "Sundiag User's Guide", |
| "MANPAGES", "SunOS Reference Manual", |
| "REFMAN", "SunOS Reference Manual", |
| "SSI", "Sun System Introduction", |
| "SSO", "System Services Overview", |
| "TEXT", "Editing Text Files", |
| "DOCS", "Formatting Documents", |
| "TROFF", "Using <B>nroff</B> and <B>troff</B>", |
| "INDEX", "Global Index", |
| "CPG", "C Programmer's Guide", |
| "CREF", "C Reference Manual", |
| "ASSY", "Assembly Language Reference", |
| "PUL", "Programming Utilities and Libraries", |
| "DEBUG", "Debugging Tools", |
| "NETP", "Network Programming", |
| "DRIVER", "Writing Device Drivers", |
| "STREAMS", "STREAMS Programming", |
| "SBDK", "SBus Developer's Kit", |
| "WDDS", "Writing Device Drivers for the SBus", |
| "FPOINT", "Floating-Point Programmer's Guide", |
| "SVPG", "SunView 1 Programmer's Guide", |
| "SVSPG", "SunView 1 System Programmer's Guide", |
| "PIXRCT", "Pixrect Reference Manual", |
| "CGI", "SunCGI Reference Manual", |
| "CORE", "SunCore Reference Manual", |
| "4ASSY", "Sun-4 Assembly Language Reference", |
| "SARCH", "<FONT SIZE=-1>SPARC</FONT> Architecture Manual", |
| "KR", "The C Programming Language", |
| NULL, NULL}; |
| |
| static char * |
| lookup_abbrev(char *c) |
| { |
| int i = 0; |
| |
| if (!c) |
| return ""; |
| while (abbrev_list[i] && strcmp(c, abbrev_list[i])) |
| i = i + 2; |
| if (abbrev_list[i]) |
| return abbrev_list[i + 1]; |
| else |
| return c; |
| } |
| |
| static char manidx[NULL_TERMINATED(HUGE_STR_MAX)]; |
| static int subs = 0; |
| static int mip = 0; |
| static char label[5] = "lbAA"; |
| |
| static void |
| add_to_index(int level, char *item) |
| { |
| char *c = NULL; |
| |
| label[3]++; |
| if (label[3] > 'Z') { |
| label[3] = 'A'; |
| label[2]++; |
| } |
| if (level != subs) { |
| if (subs) { |
| strmaxcpy(manidx + mip, "</DL>\n", HUGE_STR_MAX - mip); |
| mip += 6; |
| } else { |
| strmaxcpy(manidx + mip, "<DL>\n", HUGE_STR_MAX - mip); |
| mip += 5; |
| } |
| } |
| subs = level; |
| scan_troff(item, 1, &c); |
| sprintf(manidx + mip, "<DT><A HREF=\"#%s\">%s</A><DD>\n", label, c); |
| if (c) |
| free(c); |
| while (manidx[mip]) |
| mip++; |
| } |
| |
| static char * |
| skip_till_newline(char *c) |
| { |
| int lvl = 0; |
| |
| while (*c && *c != '\n' || lvl > 0) { |
| if (*c == '\\') { |
| c++; |
| if (*c == '}') |
| lvl--; |
| else if (*c == '{') |
| lvl++; |
| } |
| c++; |
| } |
| c++; |
| if (lvl < 0 && newline_for_fun) { |
| newline_for_fun = newline_for_fun + lvl; |
| if (newline_for_fun < 0) |
| newline_for_fun = 0; |
| } |
| return c; |
| } |
| |
| static void |
| outputPageHeader(char *l, char *c, char *r) |
| { |
| out_html("<TABLE WIDTH=100%>\n<TR>\n"); |
| out_html("<TH ALIGN=LEFT width=33%>"); |
| out_html(l); |
| out_html("<TH ALIGN=CENTER width=33%>"); |
| out_html(c); |
| out_html("<TH ALIGN=RIGHT width=33%>"); |
| out_html(r); |
| out_html("\n</TR>\n</TABLE>\n"); |
| } |
| |
| static void |
| outputPageFooter(char *l, char *c, char *r) |
| { |
| out_html("<HR>\n"); |
| outputPageHeader(l, c, r); |
| } |
| |
| static int ifelseval = 0; |
| |
| static char * |
| scan_request(char *c) |
| { |
| /* BSD Mandoc stuff */ |
| static int mandoc_synopsis = 0; /* True if we are in the synopsis |
| * section */ |
| static int mandoc_command = 0; /* True if this is mandoc page */ |
| static int mandoc_bd_options; /* Only copes with non-nested Bd's */ |
| |
| int i, j, mode = 0; |
| char *h; |
| char *wordlist[MAX_WORDLIST]; |
| int words; |
| char *sl; |
| STRDEF *owndef; |
| |
| while (*c == ' ' || *c == '\t') |
| c++; |
| if (c[0] == '\n') |
| return c + 1; |
| if (c[1] == '\n') |
| j = 1; |
| else |
| j = 2; |
| while (c[j] == ' ' || c[j] == '\t') |
| j++; |
| if (c[0] == escapesym) { |
| /* some pages use .\" .\$1 .\} */ |
| /* .\$1 is too difficult/stupid */ |
| if (c[1] == '$') |
| c = skip_till_newline(c); |
| else |
| c = scan_escape(c + 1); |
| } else { |
| i = V(c[0], c[1]); |
| switch (i) { |
| case V('a', 'b'): |
| h = c + j; |
| while (*h && *h != '\n') |
| h++; |
| *h = '\0'; |
| if (scaninbuff && buffpos) { |
| buffer[buffpos] = '\0'; |
| puts(buffer); |
| } |
| /* fprintf(stderr, "%s\n", c+2); */ |
| exit(0); |
| break; |
| case V('d', 'i'): |
| { |
| STRDEF *de; |
| int oldcurpos = curpos; |
| |
| c = c + j; |
| i = V(c[0], c[1]); |
| if (*c == '\n') { |
| c++; |
| break; |
| } |
| while (*c && *c != '\n') |
| c++; |
| c++; |
| h = c; |
| while (*c && strncmp(c, ".di", 3)) |
| while (*c && *c++ != '\n'); |
| *c = '\0'; |
| de = strdef; |
| while (de && de->nr != i) |
| de = de->next; |
| if (!de) { |
| de = (STRDEF *) malloc(sizeof(STRDEF)); |
| de->nr = i; |
| de->slen = 0; |
| de->next = strdef; |
| de->st = NULL; |
| strdef = de; |
| } else { |
| if (de->st) |
| free(de->st); |
| de->slen = 0; |
| de->st = NULL; |
| } |
| scan_troff(h, 0, &de->st); |
| *c = '.'; |
| while (*c && *c++ != '\n'); |
| break; |
| } |
| case V('d', 's'): |
| mode = 1; |
| case V('a', 's'): |
| { |
| STRDEF *de; |
| int oldcurpos = curpos; |
| |
| c = c + j; |
| i = V(c[0], c[1]); |
| j = 0; |
| while (c[j] && c[j] != '\n') |
| j++; |
| if (j < 3) { |
| c = c + j; |
| break; |
| } |
| if (c[1] == ' ') |
| c = c + 1; |
| else |
| c = c + 2; |
| while (isspace(*c)) |
| c++; |
| if (*c == '"') |
| c++; |
| de = strdef; |
| while (de && de->nr != i) |
| de = de->next; |
| single_escape = 1; |
| curpos = 0; |
| if (!de) { |
| char *h; |
| |
| de = (STRDEF *) malloc(sizeof(STRDEF)); |
| de->nr = i; |
| de->slen = 0; |
| de->next = strdef; |
| de->st = NULL; |
| strdef = de; |
| h = NULL; |
| c = scan_troff(c, 1, &h); |
| de->st = h; |
| de->slen = curpos; |
| } else { |
| if (mode) { |
| char *h = NULL; |
| |
| c = scan_troff(c, 1, &h); |
| free(de->st); |
| de->slen = 0; |
| de->st = h; |
| } else |
| c = scan_troff(c, 1, &de->st); |
| de->slen += curpos; |
| } |
| single_escape = 0; |
| curpos = oldcurpos; |
| } |
| break; |
| case V('b', 'r'): |
| if (still_dd) |
| out_html("<DD>"); |
| else |
| out_html("<BR>\n"); |
| curpos = 0; |
| c = c + j; |
| if (c[0] == escapesym) { |
| c = scan_escape(c + 1); |
| } |
| c = skip_till_newline(c); |
| break; |
| case V('c', '2'): |
| c = c + j; |
| if (*c != '\n') { |
| nobreaksym = *c; |
| } else |
| nobreaksym = '\''; |
| c = skip_till_newline(c); |
| break; |
| case V('c', 'c'): |
| c = c + j; |
| if (*c != '\n') { |
| controlsym = *c; |
| } else |
| controlsym = '.'; |
| c = skip_till_newline(c); |
| break; |
| case V('c', 'e'): |
| c = c + j; |
| if (*c == '\n') { |
| i = 1; |
| } else { |
| i = 0; |
| while ('0' <= *c && *c <= '9') { |
| i = i * 10 + *c - '0'; |
| c++; |
| } |
| } |
| c = skip_till_newline(c); |
| /* center next i lines */ |
| if (i > 0) { |
| out_html("<CENTER>\n"); |
| while (i && *c) { |
| char *line = NULL; |
| |
| c = scan_troff(c, 1, &line); |
| if (line && strncmp(line, "<BR>", 4)) { |
| out_html(line); |
| out_html("<BR>\n"); |
| i--; |
| } |
| } |
| out_html("</CENTER>\n"); |
| curpos = 0; |
| } |
| break; |
| case V('e', 'c'): |
| c = c + j; |
| if (*c != '\n') { |
| escapesym = *c; |
| } else |
| escapesym = '\\'; |
| break; |
| c = skip_till_newline(c); |
| case V('e', 'o'): |
| escapesym = '\0'; |
| c = skip_till_newline(c); |
| break; |
| case V('e', 'x'): |
| exit(0); |
| break; |
| case V('f', 'c'): |
| c = c + j; |
| if (*c == '\n') { |
| fieldsym = padsym = '\0'; |
| } else { |
| fieldsym = c[0]; |
| padsym = c[1]; |
| } |
| c = skip_till_newline(c); |
| break; |
| case V('f', 'i'): |
| if (!fillout) { |
| out_html(change_to_font(0)); |
| out_html(change_to_size('0')); |
| out_html("</PRE>\n"); |
| } |
| curpos = 0; |
| fillout = 1; |
| c = skip_till_newline(c); |
| break; |
| case V('f', 't'): |
| c = c + j; |
| if (*c == '\n') { |
| out_html(change_to_font(0)); |
| } else { |
| if (*c == escapesym) { |
| int fn; |
| |
| c = scan_expression(c, &fn); |
| c--; |
| out_html(change_to_font(fn)); |
| } else { |
| out_html(change_to_font(*c)); |
| c++; |
| } |
| } |
| c = skip_till_newline(c); |
| break; |
| case V('e', 'l'): |
| /* .el anything : else part of if else */ |
| if (ifelseval) { |
| c = c + j; |
| c[-1] = '\n'; |
| c = scan_troff(c, 1, NULL); |
| } else |
| c = skip_till_newline(c + j); |
| break; |
| case V('i', 'e'): |
| /* .ie c anything : then part of if else */ |
| case V('i', 'f'): |
| /* |
| * .if c anything .if !c anything .if N anything .if |
| * !N anything .if 'string1'string2' anything .if |
| * !'string1'string2' anything |
| */ |
| c = c + j; |
| c = scan_expression(c, &i); |
| ifelseval = !i; |
| if (i) { |
| *c = '\n'; |
| c++; |
| c = scan_troff(c, 1, NULL); |
| } else |
| c = skip_till_newline(c); |
| break; |
| case V('i', 'g'): |
| { |
| char *endwith = "..\n"; |
| |
| i = 3; |
| c = c + j; |
| if (*c != '\n') { |
| endwith = c - 1; |
| i = 1; |
| c[-1] = '.'; |
| while (*c && *c != '\n') |
| c++, i++; |
| } |
| c++; |
| while (*c && strncmp(c, endwith, i)) |
| while (*c++ != '\n'); |
| while (*c++ != '\n'); |
| break; |
| } |
| case V('n', 'f'): |
| if (fillout) { |
| out_html(change_to_font(0)); |
| out_html(change_to_size('0')); |
| out_html("<PRE>\n"); |
| } |
| curpos = 0; |
| fillout = 0; |
| c = skip_till_newline(c); |
| break; |
| case V('p', 's'): |
| c = c + j; |
| if (*c == '\n') { |
| out_html(change_to_size('0')); |
| } else { |
| j = 0; |
| i = 0; |
| if (*c == '-') { |
| j = -1; |
| c++; |
| } else if (*c == '+') { |
| j = 1; |
| c++; |
| } |
| c = scan_expression(c, &i); |
| if (!j) { |
| j = 1; |
| if (i > 5) |
| i = i - 10; |
| } |
| out_html(change_to_size(i * j)); |
| } |
| c = skip_till_newline(c); |
| break; |
| case V('s', 'p'): |
| c = c + j; |
| if (fillout) |
| out_html("<P>"); |
| else { |
| out_html(NEWLINE); |
| NEWLINE[0] = '\n'; |
| } |
| curpos = 0; |
| c = skip_till_newline(c); |
| break; |
| case V('s', 'o'): |
| { |
| FILE *f; |
| struct stat stbuf; |
| int l = 0; |
| char *buf; |
| char *name = NULL; |
| |
| curpos = 0; |
| c = c + j; |
| if (*c == '/') { |
| h = c; |
| } else { |
| h = c - 3; |
| h[0] = '.'; |
| h[1] = '.'; |
| h[2] = '/'; |
| } |
| while (*c != '\n') |
| c++; |
| *c = '\0'; |
| scan_troff(h, 1, &name); |
| if (name[3] == '/') |
| h = name + 3; |
| else |
| h = name; |
| if (stat(h, &stbuf) != -1) |
| l = stbuf.st_size; |
| buf = stralloc(l + 4); |
| #if NOCGI |
| if (!out_length) { |
| char *t, *s; |
| |
| t = strrchr(fname, '/'); |
| if (!t) |
| t = fname; |
| fprintf(stderr, "ln -s %s.html %s.html\n", h, t); |
| s = strrchr(t, '.'); |
| if (!s) |
| s = t; |
| printf("<HTML><HEAD><TITLE> Manpage of %s</TITLE>\n" |
| "</HEAD><BODY>\n" |
| "See the manpage for <A HREF=\"%s.html\">%s</A>.\n" |
| "</BODY></HTML>\n", |
| s, h, h); |
| } else |
| #endif |
| { |
| /* |
| * this works alright, except for |
| * section 3 |
| */ |
| buf = read_man_page(h); |
| if (!buf) { |
| |
| fprintf(stderr, "man2html: unable to open or read file %s.\n", |
| h); |
| out_html("<BLOCKQUOTE>" |
| "man2html: unable to open or read file.\n"); |
| out_html(h); |
| out_html("</BLOCKQUOTE>\n"); |
| } else { |
| buf[0] = buf[l] = '\n'; |
| buf[l + 1] = buf[l + 2] = '\0'; |
| scan_troff(buf + 1, 0, NULL); |
| } |
| if (buf) |
| free(buf); |
| } |
| *c++ = '\n'; |
| break; |
| } |
| case V('t', 'a'): |
| c = c + j; |
| j = 0; |
| while (*c != '\n') { |
| sl = scan_expression(c, &tabstops[j]); |
| if (*c == '-' || *c == '+') |
| tabstops[j] += tabstops[j - 1]; |
| c = sl; |
| while (*c == ' ' || *c == '\t') |
| c++; |
| j++; |
| } |
| maxtstop = j; |
| curpos = 0; |
| break; |
| case V('t', 'i'): |
| /* |
| * while (itemdepth || dl_set[itemdepth]) { |
| * out_html("</DL>\n"); if (dl_set[itemdepth]) |
| * dl_set[itemdepth]=0; else itemdepth--; } |
| */ |
| out_html("<BR>\n"); |
| c = c + j; |
| c = scan_expression(c, &j); |
| for (i = 0; i < j; i++) |
| out_html(" "); |
| curpos = j; |
| c = skip_till_newline(c); |
| break; |
| case V('t', 'm'): |
| c = c + j; |
| h = c; |
| while (*c != '\n') |
| c++; |
| *c = '\0'; |
| /* fprintf(stderr,"%s\n", h); */ |
| *c = '\n'; |
| break; |
| case V('B', ' '): |
| case V('B', '\n'): |
| case V('I', ' '): |
| case V('I', '\n'): |
| /* parse one line in a certain font */ |
| out_html(change_to_font(*c)); |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| c = scan_troff(c, 1, NULL); |
| out_html(change_to_font('R')); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('O', 'P'): /* groff manpages use this |
| * construction */ |
| /* .OP a b : [ <B>a</B> <I>b</I> ] */ |
| mode = 1; |
| c[0] = 'B'; |
| c[1] = 'I'; |
| out_html(change_to_font('R')); |
| out_html("["); |
| curpos++; |
| case V('B', 'R'): |
| case V('B', 'I'): |
| case V('I', 'B'): |
| case V('I', 'R'): |
| case V('R', 'B'): |
| case V('R', 'I'): |
| { |
| char font[2]; |
| |
| font[0] = c[0]; |
| font[1] = c[1]; |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| sl = fill_words(c, wordlist, &words); |
| c = sl + 1; |
| /* |
| * .BR name (section) indicates a link. It |
| * will be added in the output routine. |
| */ |
| for (i = 0; i < words; i++) { |
| if (mode) { |
| out_html(" "); |
| curpos++; |
| } |
| wordlist[i][-1] = ' '; |
| out_html(change_to_font(font[i & 1])); |
| scan_troff(wordlist[i], 1, NULL); |
| } |
| out_html(change_to_font('R')); |
| if (mode) { |
| out_html(" ]"); |
| curpos++; |
| } |
| out_html(NEWLINE); |
| if (!fillout) |
| curpos = 0; |
| else |
| curpos++; |
| } |
| break; |
| case V('D', 'T'): |
| for (j = 0; j < 20; j++) |
| tabstops[j] = (j + 1) * 8; |
| maxtstop = 20; |
| c = skip_till_newline(c); |
| break; |
| case V('I', 'P'): |
| sl = fill_words(c + j, wordlist, &words); |
| c = sl + 1; |
| if (!dl_set[itemdepth]) { |
| out_html("<DL COMPACT>\n"); |
| dl_set[itemdepth] = 1; |
| } |
| out_html("<DT>"); |
| if (words) { |
| scan_troff(wordlist[0], 1, NULL); |
| } |
| out_html("<DD>"); |
| curpos = 0; |
| break; |
| case V('T', 'P'): |
| if (!dl_set[itemdepth]) { |
| out_html("<DL COMPACT>\n"); |
| dl_set[itemdepth] = 1; |
| } |
| out_html("<DT>"); |
| c = skip_till_newline(c); |
| /* somewhere a definition ends with '.TP' */ |
| if (!*c) |
| still_dd = 1; |
| else { |
| c = scan_troff(c, 1, NULL); |
| out_html("<DD>"); |
| } |
| curpos = 0; |
| break; |
| case V('I', 'X'): |
| /* general index */ |
| sl = fill_words(c + j, wordlist, &words); |
| c = sl + 1; |
| j = 4; |
| while (idxlabel[j] == 'Z') |
| idxlabel[j--] = 'A'; |
| idxlabel[j]++; |
| #ifdef MAKEINDEX |
| fprintf(idxfile, "%s@%s@", fname, idxlabel); |
| for (j = 0; j < words; j++) { |
| h = NULL; |
| scan_troff(wordlist[j], 1, &h); |
| fprintf(idxfile, "_\b@%s", h); |
| free(h); |
| } |
| fprintf(idxfile, "\n"); |
| #endif |
| out_html("<A NAME=\""); |
| out_html(idxlabel); |
| /* |
| * this will not work in mosaic (due to a bug). |
| * Adding ' ' between '>' and '<' solves it, but |
| * creates some space. A normal space does not work. |
| */ |
| out_html("\"></A>"); |
| break; |
| case V('L', 'P'): |
| case V('P', 'P'): |
| if (dl_set[itemdepth]) { |
| out_html("</DL>\n"); |
| dl_set[itemdepth] = 0; |
| } |
| if (fillout) |
| out_html("<P>\n"); |
| else { |
| out_html(NEWLINE); |
| NEWLINE[0] = '\n'; |
| } |
| curpos = 0; |
| c = skip_till_newline(c); |
| break; |
| case V('H', 'P'): |
| if (!dl_set[itemdepth]) { |
| out_html("<DL COMPACT>"); |
| dl_set[itemdepth] = 1; |
| } |
| out_html("<DT>\n"); |
| still_dd = 1; |
| c = skip_till_newline(c); |
| curpos = 0; |
| break; |
| case V('P', 'D'): |
| c = skip_till_newline(c); |
| break; |
| case V('R', 's'): /* BSD mandoc */ |
| case V('R', 'S'): |
| sl = fill_words(c + j, wordlist, &words); |
| j = 1; |
| if (words > 0) |
| scan_expression(wordlist[0], &j); |
| if (j >= 0) { |
| itemdepth++; |
| dl_set[itemdepth] = 0; |
| out_html("<DL COMPACT><DT><DD>"); |
| c = skip_till_newline(c); |
| curpos = 0; |
| break; |
| } |
| case V('R', 'e'): /* BSD mandoc */ |
| case V('R', 'E'): |
| if (itemdepth > 0) { |
| if (dl_set[itemdepth]) |
| out_html("</DL>"); |
| out_html("</DL>\n"); |
| itemdepth--; |
| } |
| c = skip_till_newline(c); |
| curpos = 0; |
| break; |
| case V('S', 'B'): |
| out_html(change_to_size(-1)); |
| out_html(change_to_font('B')); |
| c = scan_troff(c + j, 1, NULL); |
| out_html(change_to_font('R')); |
| out_html(change_to_size('0')); |
| break; |
| case V('S', 'M'): |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| out_html(change_to_size(-1)); |
| trans_char(c, '"', '\a'); |
| c = scan_troff(c, 1, NULL); |
| out_html(change_to_size('0')); |
| break; |
| case V('S', 's'): /* BSD mandoc */ |
| mandoc_command = 1; |
| case V('S', 'S'): |
| mode = 1; |
| case V('S', 'h'): /* BSD mandoc */ |
| /* hack for fallthru from above */ |
| mandoc_command = !mode || mandoc_command; |
| case V('S', 'H'): |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| while (itemdepth || dl_set[itemdepth]) { |
| out_html("</DL>\n"); |
| if (dl_set[itemdepth]) |
| dl_set[itemdepth] = 0; |
| else if (itemdepth > 0) |
| itemdepth--; |
| } |
| out_html(change_to_font(0)); |
| out_html(change_to_size(0)); |
| if (!fillout) { |
| fillout = 1; |
| out_html("</PRE>"); |
| } |
| trans_char(c, '"', '\a'); |
| add_to_index(mode, c); |
| out_html("<A NAME=\""); |
| out_html(label); |
| /* for mosaic users */ |
| if (mode) |
| out_html("\"> </A>\n<H4>"); |
| else |
| out_html("\"> </A>\n<H3>"); |
| mandoc_synopsis = strncmp(c, "SYNOPSIS", 8) == 0; |
| c = mandoc_command ? scan_troff_mandoc(c, 1, NULL) : scan_troff(c, 1, NULL); |
| if (mode) |
| out_html("</H4>\n"); |
| else |
| out_html("</H3>\n"); |
| curpos = 0; |
| break; |
| case V('T', 'S'): |
| c = scan_table(c); |
| break; |
| case V('D', 't'): /* BSD mandoc */ |
| mandoc_command = 1; |
| case V('T', 'H'): |
| if (!output_possible) { |
| sl = fill_words(c + j, wordlist, &words); |
| if (words > 1) { |
| char *t; |
| for (i = 1; i < words; i++) |
| wordlist[i][-1] = '\0'; |
| *sl = '\0'; |
| output_possible = 1; |
| sprintf(th_page_and_sec, "%s(%s)", wordlist[0], wordlist[1]); |
| if (words > 2) { |
| t = unescape(wordlist[2]); |
| strncpy(th_datestr, t, sizeof(th_datestr)); |
| th_datestr[sizeof(th_datestr) - 1] = '\0'; |
| } else |
| th_datestr[0] = '\0'; |
| if (words > 3) { |
| t = unescape(wordlist[3]); |
| strncpy(th_version, t, sizeof(th_version)); |
| th_version[sizeof(th_version) - 1] = '\0'; |
| } else |
| th_version[0] = '\0'; |
| out_html("<HTML><HEAD>\n<TITLE>"); |
| out_html(th_page_and_sec); |
| out_html(" Manual Page"); |
| out_html("</TITLE>\n</HEAD>\n<BODY>"); |
| |
| outputPageHeader(th_page_and_sec, th_datestr, th_page_and_sec); |
| |
| out_html("<BR><A HREF=\"#index\">Index</A>\n"); |
| *sl = '\n'; |
| out_html("<HR>\n"); |
| if (mandoc_command) |
| out_html("<BR>BSD mandoc<BR>"); |
| } |
| c = sl + 1; |
| } else |
| c = skip_till_newline(c); |
| curpos = 0; |
| break; |
| case V('T', 'X'): |
| sl = fill_words(c + j, wordlist, &words); |
| *sl = '\0'; |
| out_html(change_to_font('I')); |
| if (words > 1) |
| wordlist[1][-1] = '\0'; |
| c = lookup_abbrev(wordlist[0]); |
| curpos += strlen(c); |
| out_html(c); |
| out_html(change_to_font('R')); |
| if (words > 1) |
| out_html(wordlist[1]); |
| *sl = '\n'; |
| c = sl + 1; |
| break; |
| case V('r', 'm'): |
| /* .rm xx : Remove request, macro or string */ |
| case V('r', 'n'): |
| /* |
| * .rn xx yy : Rename request, macro or string xx to |
| * yy |
| */ |
| { |
| STRDEF *de; |
| |
| c = c + j; |
| i = V(c[0], c[1]); |
| c = c + 2; |
| while (isspace(*c) && *c != '\n') |
| c++; |
| j = V(c[0], c[1]); |
| while (*c && *c != '\n') |
| c++; |
| c++; |
| de = strdef; |
| while (de && de->nr != j) |
| de = de->next; |
| if (de) { |
| if (de->st) |
| free(de->st); |
| de->nr = 0; |
| } |
| de = strdef; |
| while (de && de->nr != i) |
| de = de->next; |
| if (de) |
| de->nr = j; |
| break; |
| } |
| case V('n', 'x'): |
| /* .nx filename : next file. */ |
| case V('i', 'n'): |
| /* .in +-N : Indent */ |
| c = skip_till_newline(c); |
| break; |
| case V('n', 'r'): |
| /* |
| * .nr R +-N M: define and set number register R by |
| * +-N; auto-increment by M |
| */ |
| { |
| INTDEF *intd; |
| |
| c = c + j; |
| i = V(c[0], c[1]); |
| c = c + 2; |
| intd = intdef; |
| while (intd && intd->nr != i) |
| intd = intd->next; |
| if (!intd) { |
| intd = (INTDEF *) malloc(sizeof(INTDEF)); |
| intd->nr = i; |
| intd->val = 0; |
| intd->incr = 0; |
| intd->next = intdef; |
| intdef = intd; |
| } |
| while (*c == ' ' || *c == '\t') |
| c++; |
| c = scan_expression(c, &intd->val); |
| if (*c != '\n') { |
| while (*c == ' ' || *c == '\t') |
| c++; |
| c = scan_expression(c, &intd->incr); |
| } |
| c = skip_till_newline(c); |
| break; |
| } |
| case V('a', 'm'): |
| /* .am xx yy : append to a macro. */ |
| /* define or handle as .ig yy */ |
| mode = 1; |
| case V('d', 'e'): |
| /* |
| * .de xx yy : define or redefine macro xx; end at |
| * .yy (..) |
| */ |
| /* define or handle as .ig yy */ |
| { |
| STRDEF *de; |
| int olen = 0; |
| |
| c = c + j; |
| sl = fill_words(c, wordlist, &words); |
| i = V(c[0], c[1]); |
| j = 2; |
| if (words == 1) |
| wordlist[1] = ".."; |
| else { |
| wordlist[1]--; |
| wordlist[1][0] = '.'; |
| j = 3; |
| } |
| c = sl + 1; |
| sl = c; |
| while (*c && strncmp(c, wordlist[1], j)) |
| c = skip_till_newline(c); |
| de = defdef; |
| while (de && de->nr != i) |
| de = de->next; |
| if (mode && de) |
| olen = strlen(de->st); |
| j = olen + c - sl; |
| h = stralloc(j * 2 + 4); |
| if (h) { |
| for (j = 0; j < olen; j++) |
| h[j] = de->st[j]; |
| if (!j || h[j - 1] != '\n') |
| h[j++] = '\n'; |
| while (sl != c) { |
| if (sl[0] == '\\' && sl[1] == '\\') { |
| h[j++] = '\\'; |
| sl++; |
| } else |
| h[j++] = *sl; |
| sl++; |
| } |
| h[j] = '\0'; |
| if (de) { |
| if (de->st) |
| free(de->st); |
| de->st = h; |
| } else { |
| de = (STRDEF *) malloc(sizeof(STRDEF)); |
| de->nr = i; |
| de->next = defdef; |
| de->st = h; |
| defdef = de; |
| } |
| } |
| } |
| c = skip_till_newline(c); |
| break; |
| case V('B', 'l'): /* BSD mandoc */ |
| { |
| char list_options[NULL_TERMINATED(MED_STR_MAX)]; |
| char *nl = strchr(c, '\n'); |
| |
| c = c + j; |
| if (dl_set[itemdepth]) { /* These things can |
| * nest. */ |
| itemdepth++; |
| } |
| if (nl) { /* Parse list options */ |
| strlimitcpy(list_options, c, nl - c, MED_STR_MAX); |
| } |
| if (strstr(list_options, "-bullet")) { /* HTML Unnumbered List */ |
| dl_set[itemdepth] = BL_BULLET_LIST; |
| out_html("<UL>\n"); |
| } else if (strstr(list_options, "-enum")) { /* HTML Ordered List */ |
| dl_set[itemdepth] = BL_ENUM_LIST; |
| out_html("<OL>\n"); |
| } else { /* HTML Descriptive List */ |
| dl_set[itemdepth] = BL_DESC_LIST; |
| out_html("<DL COMPACT>\n"); |
| } |
| if (fillout) |
| out_html("<P>\n"); |
| else { |
| out_html(NEWLINE); |
| NEWLINE[0] = '\n'; |
| } |
| curpos = 0; |
| c = skip_till_newline(c); |
| break; |
| } |
| case V('E', 'l'): /* BSD mandoc */ |
| c = c + j; |
| if (dl_set[itemdepth] & BL_DESC_LIST) { |
| out_html("</DL>\n"); |
| } else if (dl_set[itemdepth] & BL_BULLET_LIST) { |
| out_html("</UL>\n"); |
| } else if (dl_set[itemdepth] & BL_ENUM_LIST) { |
| out_html("</OL>\n"); |
| } |
| dl_set[itemdepth] = 0; |
| if (itemdepth > 0) |
| itemdepth--; |
| if (fillout) |
| out_html("<P>\n"); |
| else { |
| out_html(NEWLINE); |
| NEWLINE[0] = '\n'; |
| } |
| curpos = 0; |
| c = skip_till_newline(c); |
| break; |
| case V('I', 't'): /* BSD mandoc */ |
| c = c + j; |
| if (strncmp(c, "Xo", 2) == 0 && isspace(*(c + 2))) { |
| c = skip_till_newline(c); |
| } |
| if (dl_set[itemdepth] & BL_DESC_LIST) { |
| out_html("<DT>"); |
| out_html(change_to_font('B')); |
| if (*c == '\n') { /* Don't allow embedded |
| * comms after a newline */ |
| c++; |
| c = scan_troff(c, 1, NULL); |
| } else { /* Do allow embedded comms on |
| * the same line. */ |
| c = scan_troff_mandoc(c, 1, NULL); |
| } |
| out_html(change_to_font('R')); |
| out_html(NEWLINE); |
| out_html("<DD>"); |
| } else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST)) { |
| out_html("<LI>"); |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(NEWLINE); |
| } |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('B', 'k'): /* BSD mandoc */ |
| case V('E', 'k'): /* BSD mandoc */ |
| case V('D', 'd'): /* BSD mandoc */ |
| case V('O', 's'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('B', 't'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| out_html(" is currently in beta test."); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('B', 'x'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| out_html("BSD "); |
| c = scan_troff_mandoc(c, 1, NULL); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('D', 'l'): /* BSD mandoc */ |
| c = c + j; |
| out_html(NEWLINE); |
| out_html("<BLOCKQUOTE>"); |
| out_html(change_to_font('L')); |
| if (*c == '\n') |
| c++; |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(change_to_font('R')); |
| out_html("</BLOCKQUOTE>"); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('B', 'd'): /* BSD mandoc */ |
| { /* Seems like a kind of example/literal mode */ |
| char bd_options[NULL_TERMINATED(MED_STR_MAX)]; |
| char *nl = strchr(c, '\n'); |
| |
| c = c + j; |
| if (nl) { |
| strlimitcpy(bd_options, c, nl - c, MED_STR_MAX); |
| } |
| out_html(NEWLINE); |
| mandoc_bd_options = 0; /* Remember options for |
| * terminating Bl */ |
| if (strstr(bd_options, "-offset indent")) { |
| mandoc_bd_options |= BD_INDENT; |
| out_html("<BLOCKQUOTE>\n"); |
| } |
| if (strstr(bd_options, "-literal") |
| || strstr(bd_options, "-unfilled")) { |
| if (fillout) { |
| mandoc_bd_options |= BD_LITERAL; |
| out_html(change_to_font(0)); |
| out_html(change_to_size('0')); |
| out_html("<PRE>\n"); |
| } |
| curpos = 0; |
| fillout = 0; |
| } |
| c = skip_till_newline(c); |
| break; |
| } |
| case V('E', 'd'): /* BSD mandoc */ |
| if (mandoc_bd_options & BD_LITERAL) { |
| if (!fillout) { |
| out_html(change_to_font(0)); |
| out_html(change_to_size('0')); |
| out_html("</PRE>\n"); |
| } |
| } |
| if (mandoc_bd_options & BD_INDENT) |
| out_html("</BLOCKQUOTE>\n"); |
| curpos = 0; |
| fillout = 1; |
| c = skip_till_newline(c); |
| break; |
| case V('B', 'e'): /* BSD mandoc */ |
| c = c + j; |
| if (fillout) |
| out_html("<P>"); |
| else { |
| out_html(NEWLINE); |
| NEWLINE[0] = '\n'; |
| } |
| curpos = 0; |
| c = skip_till_newline(c); |
| break; |
| case V('X', 'r'): /* BSD mandoc */ |
| { |
| /* |
| * Translate xyz 1 to xyz(1) Allow for |
| * multiple spaces. Allow the section to be |
| * missing. |
| */ |
| char buff[NULL_TERMINATED(MED_STR_MAX)]; |
| char *bufptr; |
| |
| trans_char(c, '"', '\a'); |
| bufptr = buff; |
| c = c + j; |
| if (*c == '\n') |
| c++; /* Skip spaces */ |
| while (isspace(*c) && *c != '\n') |
| c++; |
| while (isalnum(*c)) { /* Copy the xyz part */ |
| *bufptr = *c; |
| bufptr++; |
| if (bufptr >= buff + MED_STR_MAX) |
| break; |
| c++; |
| } |
| while (isspace(*c) && *c != '\n') |
| c++; /* Skip spaces */ |
| if (isdigit(*c)) { /* Convert the number if |
| * there is one */ |
| *bufptr = '('; |
| bufptr++; |
| if (bufptr < buff + MED_STR_MAX) { |
| while (isalnum(*c)) { |
| *bufptr = *c; |
| bufptr++; |
| if (bufptr >= buff + MED_STR_MAX) |
| break; |
| c++; |
| } |
| if (bufptr < buff + MED_STR_MAX) { |
| *bufptr = ')'; |
| bufptr++; |
| } |
| } |
| } |
| while (*c != '\n') { /* Copy the remainder */ |
| if (!isspace(*c)) { |
| *bufptr = *c; |
| bufptr++; |
| if (bufptr >= buff + MED_STR_MAX) |
| break; |
| } |
| c++; |
| } |
| *bufptr = '\n'; |
| scan_troff_mandoc(buff, 1, NULL); |
| |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| } |
| break; |
| case V('F', 'l'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| out_html("-"); |
| if (*c != '\n') { |
| out_html(change_to_font('B')); |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(change_to_font('R')); |
| } |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('P', 'a'): /* BSD mandoc */ |
| case V('P', 'f'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('P', 'p'): /* BSD mandoc */ |
| if (fillout) |
| out_html("<P>\n"); |
| else { |
| out_html(NEWLINE); |
| NEWLINE[0] = '\n'; |
| } |
| curpos = 0; |
| c = skip_till_newline(c); |
| break; |
| case V('D', 'q'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| out_html("``"); |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html("''"); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('O', 'p'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| out_html(change_to_font('R')); |
| out_html("["); |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(change_to_font('R')); |
| out_html("]"); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('O', 'o'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| out_html(change_to_font('R')); |
| out_html("["); |
| c = scan_troff_mandoc(c, 1, NULL); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('O', 'c'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(change_to_font('R')); |
| out_html("]"); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('P', 'q'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| out_html("("); |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(")"); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('Q', 'l'): /* BSD mandoc */ |
| { /* Single quote first word in the line */ |
| char *sp; |
| |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| sp = c; |
| do { /* Find first whitespace after the |
| * first word that isn't a mandoc |
| * macro */ |
| while (*sp && isspace(*sp)) |
| sp++; |
| while (*sp && !isspace(*sp)) |
| sp++; |
| } while (*sp && isupper(*(sp - 2)) && islower(*(sp - 1))); |
| |
| /* |
| * Use a newline to mark the end of text to |
| * be quoted |
| */ |
| if (*sp) |
| *sp = '\n'; |
| out_html("`"); /* Quote the text */ |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html("'"); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| } |
| case V('S', 'q'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| out_html("`"); |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html("'"); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('A', 'r'): /* BSD mandoc */ |
| /* parse one line in italics */ |
| out_html(change_to_font('I')); |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') { /* An empty Ar means "file |
| * ..." */ |
| out_html("file ..."); |
| } else { |
| c = scan_troff_mandoc(c, 1, NULL); |
| } |
| out_html(change_to_font('R')); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('A', 'd'): /* BSD mandoc */ |
| case V('E', 'm'): /* BSD mandoc */ |
| case V('V', 'a'): /* BSD mandoc */ |
| case V('X', 'c'): /* BSD mandoc */ |
| /* parse one line in italics */ |
| out_html(change_to_font('I')); |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(change_to_font('R')); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('N', 'd'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| out_html(" - "); |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('N', 'm'): /* BSD mandoc */ |
| { |
| static char mandoc_name[NULL_TERMINATED(SMALL_STR_MAX)] = ""; |
| |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (mandoc_synopsis) { /* Break lines only in |
| * the Synopsis. The |
| * Synopsis section |
| * seems to be treated |
| * as a special case - |
| * Bummer! */ |
| static int count = 0; /* Don't break on the |
| * first Nm */ |
| |
| if (count) { |
| out_html("<BR>"); |
| } else { |
| char *end = strchr(c, '\n'); |
| |
| if (end) { /* Remember the name for |
| * later. */ |
| strlimitcpy(mandoc_name, c, end - c, SMALL_STR_MAX); |
| } |
| } |
| count++; |
| } |
| out_html(change_to_font('B')); |
| while (*c == ' ' || *c == '\t') |
| c++; |
| if (*c == '\n') { /* If Nm has no |
| * argument, use one |
| * from an earlier Nm |
| * command that did have |
| * one. Hope there |
| * aren't too many |
| * commands that do |
| * this. */ |
| out_html(mandoc_name); |
| } else { |
| c = scan_troff_mandoc(c, 1, NULL); |
| } |
| out_html(change_to_font('R')); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| } |
| case V('C', 'd'): /* BSD mandoc */ |
| case V('C', 'm'): /* BSD mandoc */ |
| case V('I', 'c'): /* BSD mandoc */ |
| case V('M', 's'): /* BSD mandoc */ |
| case V('O', 'r'): /* BSD mandoc */ |
| case V('S', 'y'): /* BSD mandoc */ |
| /* parse one line in bold */ |
| out_html(change_to_font('B')); |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(change_to_font('R')); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('D', 'v'): /* BSD mandoc */ |
| case V('E', 'v'): /* BSD mandoc */ |
| case V('F', 'r'): /* BSD mandoc */ |
| case V('L', 'i'): /* BSD mandoc */ |
| case V('N', 'o'): /* BSD mandoc */ |
| case V('N', 's'): /* BSD mandoc */ |
| case V('T', 'n'): /* BSD mandoc */ |
| case V('n', 'N'): /* BSD mandoc */ |
| trans_char(c, '"', '\a'); |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| out_html(change_to_font('B')); |
| c = scan_troff_mandoc(c, 1, NULL); |
| out_html(change_to_font('R')); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('%', 'A'): /* BSD mandoc biblio stuff */ |
| case V('%', 'D'): |
| case V('%', 'N'): |
| case V('%', 'O'): |
| case V('%', 'P'): |
| case V('%', 'Q'): |
| case V('%', 'V'): |
| c = c + j; |
| if (*c == '\n') |
| c++; |
| c = scan_troff(c, 1, NULL); /* Don't allow embedded |
| * mandoc coms */ |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| case V('%', 'B'): |
| case V('%', 'J'): |
| case V('%', 'R'): |
| case V('%', 'T'): |
| c = c + j; |
| out_html(change_to_font('I')); |
| if (*c == '\n') |
| c++; |
| c = scan_troff(c, 1, NULL); /* Don't allow embedded |
| * mandoc coms */ |
| out_html(change_to_font('R')); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| break; |
| default: |
| /* search macro database of self-defined macros */ |
| owndef = defdef; |
| while (owndef && owndef->nr != i) |
| owndef = owndef->next; |
| if (owndef) { |
| char **oldargument; |
| int deflen; |
| int onff; |
| |
| sl = fill_words(c + j, wordlist, &words); |
| c = sl + 1; |
| *sl = '\0'; |
| for (i = 1; i < words; i++) |
| wordlist[i][-1] = '\0'; |
| for (i = 0; i < words; i++) { |
| char *h = NULL; |
| |
| if (mandoc_command) { |
| scan_troff_mandoc(wordlist[i], 1, &h); |
| } else { |
| scan_troff(wordlist[i], 1, &h); |
| } |
| wordlist[i] = h; |
| } |
| for (i = words; i < 20; i++) |
| wordlist[i] = NULL; |
| deflen = strlen(owndef->st); |
| for (i = 0; owndef->st[deflen + 2 + i] = owndef->st[i]; i++); |
| oldargument = argument; |
| argument = wordlist; |
| onff = newline_for_fun; |
| if (mandoc_command) { |
| scan_troff_mandoc(owndef->st + deflen + 2, 0, NULL); |
| } else { |
| scan_troff(owndef->st + deflen + 2, 0, NULL); |
| } |
| newline_for_fun = onff; |
| argument = oldargument; |
| for (i = 0; i < words; i++) |
| if (wordlist[i]) |
| free(wordlist[i]); |
| *sl = '\n'; |
| } else if (mandoc_command && |
| ((isupper(*c) && islower(*(c + 1))) |
| || (islower(*c) && isupper(*(c + 1)))) |
| ) { /* Let through any BSD mandoc |
| * commands that haven't been delt |
| * with. I don't want to miss |
| * anything out of the text. */ |
| char buf[4]; |
| |
| strncpy(buf, c, 2); |
| buf[2] = ' '; |
| buf[3] = '\0'; |
| out_html(buf); /* Print the command (it |
| * might just be text). */ |
| c = c + j; |
| trans_char(c, '"', '\a'); |
| if (*c == '\n') |
| c++; |
| out_html(change_to_font('R')); |
| c = scan_troff(c, 1, NULL); |
| out_html(NEWLINE); |
| if (fillout) |
| curpos++; |
| else |
| curpos = 0; |
| } else { |
| c = skip_till_newline(c); |
| } |
| break; |
| } |
| } |
| if (fillout) { |
| out_html(NEWLINE); |
| curpos++; |
| } |
| NEWLINE[0] = '\n'; |
| return c; |
| } |
| |
| static void |
| flush(void) |
| { |
| } |
| |
| static int contained_tab = 0; |
| static int mandoc_line = 0; /* Signals whether to look for embedded |
| * mandoc commands. */ |
| |
| /* san : stop at newline */ |
| static char * |
| scan_troff(char *c, int san, char **result) |
| { |
| char *h; |
| char intbuff[NULL_TERMINATED(MED_STR_MAX)]; |
| int ibp = 0; |
| int i; |
| char *exbuffer; |
| int exbuffpos, exbuffmax, exscaninbuff, exnewline_for_fun; |
| int usenbsp = 0; |
| |
| #define FLUSHIBP if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; } |
| |
| exbuffer = buffer; |
| exbuffpos = buffpos; |
| exbuffmax = buffmax; |
| exnewline_for_fun = newline_for_fun; |
| exscaninbuff = scaninbuff; |
| newline_for_fun = 0; |
| if (result) { |
| if (*result) { |
| buffer = *result; |
| buffpos = strlen(buffer); |
| buffmax = buffpos; |
| } else { |
| buffer = stralloc(LARGE_STR_MAX); |
| buffpos = 0; |
| buffmax = LARGE_STR_MAX; |
| } |
| scaninbuff = 1; |
| } |
| h = c; |
| /* start scanning */ |
| |
| while (*h && (!san || newline_for_fun || *h != '\n')) { |
| |
| if (*h == escapesym) { |
| h++; |
| FLUSHIBP; |
| h = scan_escape(h); |
| } else if (*h == controlsym && h[-1] == '\n') { |
| h++; |
| FLUSHIBP; |
| h = scan_request(h); |
| if (san && h[-1] == '\n') |
| h--; |
| } else if (mandoc_line |
| && *(h) && isupper(*(h)) |
| && *(h + 1) && islower(*(h + 1)) |
| && *(h + 2) && isspace(*(h + 2))) { |
| /* |
| * BSD imbedded command eg ".It Fl Ar arg1 Fl Ar |
| * arg2" |
| */ |
| FLUSHIBP; |
| h = scan_request(h); |
| if (san && h[-1] == '\n') |
| h--; |
| } else if (*h == nobreaksym && h[-1] == '\n') { |
| h++; |
| FLUSHIBP; |
| h = scan_request(h); |
| if (san && h[-1] == '\n') |
| h--; |
| } else { |
| int mx; |
| |
| if (h[-1] == '\n' && still_dd && isalnum(*h)) { |
| /* |
| * sometimes a .HP request is not followed by |
| * a .br request |
| */ |
| FLUSHIBP; |
| out_html("<DD>"); |
| curpos = 0; |
| still_dd = 0; |
| } |
| switch (*h) { |
| case '&': |
| intbuff[ibp++] = '&'; |
| intbuff[ibp++] = 'a'; |
| intbuff[ibp++] = 'm'; |
| intbuff[ibp++] = 'p'; |
| intbuff[ibp++] = ';'; |
| curpos++; |
| break; |
| case '<': |
| intbuff[ibp++] = '&'; |
| intbuff[ibp++] = 'l'; |
| intbuff[ibp++] = 't'; |
| intbuff[ibp++] = ';'; |
| curpos++; |
| break; |
| case '>': |
| intbuff[ibp++] = '&'; |
| intbuff[ibp++] = 'g'; |
| intbuff[ibp++] = 't'; |
| intbuff[ibp++] = ';'; |
| curpos++; |
| break; |
| case '"': |
| intbuff[ibp++] = '&'; |
| intbuff[ibp++] = 'q'; |
| intbuff[ibp++] = 'u'; |
| intbuff[ibp++] = 'o'; |
| intbuff[ibp++] = 't'; |
| intbuff[ibp++] = ';'; |
| curpos++; |
| break; |
| case '\n': |
| if (h[-1] == '\n' && fillout) { |
| intbuff[ibp++] = '<'; |
| intbuff[ibp++] = 'P'; |
| intbuff[ibp++] = '>'; |
| } |
| if (contained_tab && fillout) { |
| intbuff[ibp++] = '<'; |
| intbuff[ibp++] = 'B'; |
| intbuff[ibp++] = 'R'; |
| intbuff[ibp++] = '>'; |
| } |
| contained_tab = 0; |
| curpos = 0; |
| usenbsp = 0; |
| intbuff[ibp++] = '\n'; |
| break; |
| case '\t': |
| { |
| int curtab = 0; |
| |
| contained_tab = 1; |
| FLUSHIBP; |
| /* like a typewriter, not like TeX */ |
| tabstops[19] = curpos + 1; |
| while (curtab < maxtstop && tabstops[curtab] <= curpos) |
| curtab++; |
| if (curtab < maxtstop) { |
| if (!fillout) { |
| while (curpos < tabstops[curtab]) { |
| intbuff[ibp++] = ' '; |
| if (ibp > 480) { |
| FLUSHIBP; |
| } |
| curpos++; |
| } |
| } else { |
| out_html("<TT>"); |
| while (curpos < tabstops[curtab]) { |
| out_html(" "); |
| curpos++; |
| } |
| out_html("</TT>"); |
| } |
| } |
| } |
| break; |
| default: |
| if (*h == ' ' && (h[-1] == '\n' || usenbsp)) { |
| FLUSHIBP; |
| if (!usenbsp && fillout) { |
| out_html("<BR>"); |
| curpos = 0; |
| } |
| usenbsp = fillout; |
| if (usenbsp) |
| out_html(" "); |
| else |
| intbuff[ibp++] = ' '; |
| } else if (*h > 31 && *h < 127) |
| intbuff[ibp++] = *h; |
| else if (((unsigned char) (*h)) > 127) { |
| intbuff[ibp++] = '&'; |
| intbuff[ibp++] = '#'; |
| intbuff[ibp++] = '0' + ((unsigned char) (*h)) / 100; |
| intbuff[ibp++] = '0' + (((unsigned char) (*h)) % 100) / 10; |
| intbuff[ibp++] = '0' + ((unsigned char) (*h)) % 10; |
| intbuff[ibp++] = ';'; |
| } |
| curpos++; |
| break; |
| } |
| if (ibp > (MED_STR_MAX - 20)) |
| FLUSHIBP; |
| h++; |
| } |
| } |
| FLUSHIBP; |
| if (buffer) |
| buffer[buffpos] = '\0'; |
| if (san && *h) |
| h++; |
| newline_for_fun = exnewline_for_fun; |
| if (result) { |
| *result = buffer; |
| buffer = exbuffer; |
| buffpos = exbuffpos; |
| buffmax = exbuffmax; |
| scaninbuff = exscaninbuff; |
| } |
| return h; |
| } |
| |
| |
| static char * |
| scan_troff_mandoc(char *c, int san, char **result) |
| { |
| char *ret, *end = c; |
| int oldval = mandoc_line; |
| |
| mandoc_line = 1; |
| while (*end && *end != '\n') { |
| end++; |
| } |
| |
| if (end > c + 2 |
| && ispunct(*(end - 1)) |
| && isspace(*(end - 2)) && *(end - 2) != '\n') { |
| /* |
| * Don't format lonely punctuation E.g. in "xyz ," format the |
| * xyz and then append the comma removing the space. |
| */ |
| *(end - 2) = '\n'; |
| ret = scan_troff(c, san, result); |
| *(end - 2) = *(end - 1); |
| *(end - 1) = ' '; |
| } else { |
| ret = scan_troff(c, san, result); |
| } |
| mandoc_line = oldval; |
| return ret; |
| } |
| |
| main(int argc, char **argv) |
| { |
| FILE *f; |
| char *t; |
| int l, i; |
| char *buf; |
| char *h, *fullname; |
| STRDEF *stdf; |
| |
| t = NULL; |
| while ((i = getopt(argc, argv, "")) != EOF) { |
| switch (i) { |
| default: |
| usage(); |
| exit(EXIT_USAGE); |
| } |
| } |
| |
| if (argc != 2) { |
| usage(); |
| exit(EXIT_USAGE); |
| } |
| manpage = h = t = argv[1]; |
| i = 0; |
| |
| buf = read_man_page(h); |
| if (!buf) { |
| fprintf(stderr, "man2html: cannot read %s: %s\n", h, strerror(errno)); |
| exit(1); |
| } |
| #ifdef MAKEINDEX |
| idxfile = fopen(INDEXFILE, "a"); |
| #endif |
| stdf = &standardchar[0]; |
| i = 0; |
| while (stdf->nr) { |
| stdf->next = &standardchar[i]; |
| stdf = stdf->next; |
| i++; |
| } |
| chardef = &standardchar[0]; |
| |
| stdf = &standardstring[0]; |
| i = 0; |
| while (stdf->nr) { |
| stdf->next = &standardstring[i]; |
| stdf = stdf->next; |
| i++; |
| } |
| strdef = &standardstring[0]; |
| |
| intdef = &standardint[0]; |
| i = 0; |
| while (intdef->nr) { |
| intdef->next = &standardint[i]; |
| intdef = intdef->next; |
| i++; |
| } |
| intdef = &standardint[0]; |
| |
| defdef = NULL; |
| |
| scan_troff(buf + 1, 0, NULL); |
| |
| while (itemdepth || dl_set[itemdepth]) { |
| out_html("</DL>\n"); |
| if (dl_set[itemdepth]) |
| dl_set[itemdepth] = 0; |
| else if (itemdepth > 0) |
| itemdepth--; |
| } |
| |
| out_html(change_to_font(0)); |
| out_html(change_to_size(0)); |
| if (!fillout) { |
| fillout = 1; |
| out_html("</PRE>"); |
| } |
| out_html(NEWLINE); |
| |
| if (output_possible) { |
| outputPageFooter(th_version, th_datestr, th_page_and_sec); |
| /* for mosaic users */ |
| fputs("<HR>\n<A NAME=\"index\"> </A><H2>Index</H2>\n<DL>\n", stdout); |
| manidx[mip] = 0; |
| fputs(manidx, stdout); |
| if (subs) |
| fputs("</DL>\n", stdout); |
| fputs("</DL>\n", stdout); |
| print_sig(); |
| fputs("</BODY>\n</HTML>\n", stdout); |
| } else |
| fprintf(stderr, "man2html: no output produced\n"); |
| #ifdef MAKEINDEX |
| if (idxfile) |
| fclose(idxfile); |
| #endif |
| exit(EXIT_SUCCESS); |
| } |