4042 lines
		
	
	
	
		
			82 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			4042 lines
		
	
	
	
		
			82 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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 <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)] = "";
 | |
| 
 | |
| 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 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<H3>");
 | |
| 			else
 | |
| 				out_html("\"> </A>\n<H2>");
 | |
| 			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("</H3>\n");
 | |
| 			else
 | |
| 				out_html("</H2>\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    page_and_sec[128];
 | |
| 
 | |
| 					for (i = 1; i < words; i++)
 | |
| 						wordlist[i][-1] = '\0';
 | |
| 					*sl = '\0';
 | |
| 					output_possible = 1;
 | |
| 					sprintf(page_and_sec, "%s(%s)", wordlist[0], wordlist[1]);
 | |
| 					out_html("<HTML><HEAD>\n<TITLE>");
 | |
| 					out_html(page_and_sec);
 | |
| 					out_html(" Manual Page");
 | |
| 					out_html("</TITLE>\n</HEAD>\n<BODY>");
 | |
| 					out_html("<TABLE WIDTH=100%>\n");
 | |
| 					out_html("<TH ALIGN=LEFT>");
 | |
| 					out_html(page_and_sec);
 | |
| 					out_html("<TH ALIGN=CENTER>");
 | |
| 					out_html(unescape(wordlist[2]));
 | |
| 					out_html("<TH ALIGN=RIGHT>");
 | |
| 					out_html(page_and_sec);
 | |
| 					out_html("\n</TABLE>\n");
 | |
| 					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) {
 | |
| 		/*   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);
 | |
| }
 | 
