|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <ctype.h> | 
|  |  | 
|  | enum{ | 
|  | Nfont = 11, | 
|  | Wid = 20	/* tmac.anhtml sets page width to 20" so we can recognize .nf text */ | 
|  | }; | 
|  |  | 
|  | typedef ulong Char; | 
|  | typedef struct Troffchar Troffchar; | 
|  | typedef struct Htmlchar Htmlchar; | 
|  | typedef struct Font Font; | 
|  | typedef struct HTMLfont HTMLfont; | 
|  |  | 
|  | /* a Char is 32 bits. low 16 bits are the rune. higher are attributes */ | 
|  | enum | 
|  | { | 
|  | Italic	=	16, | 
|  | Bold, | 
|  | CW, | 
|  | Indent1, | 
|  | Indent2, | 
|  | Indent3, | 
|  | Heading =	25, | 
|  | Anchor =	26	/* must be last */ | 
|  | }; | 
|  |  | 
|  | enum	/* magic emissions */ | 
|  | { | 
|  | Estring = 0, | 
|  | Epp = 1<<16 | 
|  | }; | 
|  |  | 
|  | int attrorder[] = { Indent1, Indent2, Indent3, Heading, Anchor, Italic, Bold, CW }; | 
|  |  | 
|  | int nest[10]; | 
|  | int nnest; | 
|  |  | 
|  | struct Troffchar | 
|  | { | 
|  | char *name; | 
|  | char *value; | 
|  | }; | 
|  |  | 
|  | struct Htmlchar | 
|  | { | 
|  | char *utf; | 
|  | char *name; | 
|  | int value; | 
|  | }; | 
|  |  | 
|  | #include "chars.h" | 
|  |  | 
|  | struct Font{ | 
|  | char		*name; | 
|  | HTMLfont	*htmlfont; | 
|  | }; | 
|  |  | 
|  | struct HTMLfont{ | 
|  | char	*name; | 
|  | char	*htmlname; | 
|  | int	bit; | 
|  | }; | 
|  |  | 
|  | /* R must be first; it's the default representation for fonts we don't recognize */ | 
|  | HTMLfont htmlfonts[] = | 
|  | { | 
|  | "R",			nil,		0, | 
|  | "LuxiSans",	nil,		0, | 
|  | "I",			"i",	Italic, | 
|  | "LuxiSans-Oblique",	"i",	Italic, | 
|  | "CW",		"tt",		CW, | 
|  | "LuxiMono",	"tt",		CW, | 
|  | nil,	nil | 
|  | }; | 
|  |  | 
|  | #define TABLE "<table border=0 cellpadding=0 cellspacing=0>" | 
|  |  | 
|  | char* | 
|  | onattr[8*sizeof(ulong)] = | 
|  | { | 
|  | 0, 0, 0, 0, 0, 0, 0, 0, | 
|  | 0, 0, 0, 0, 0, 0, 0, 0, | 
|  | "<i>",	/* italic */ | 
|  | "<b>",	/* bold */ | 
|  | "<tt><font size=+1>",	/* cw */ | 
|  | "<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",	/* indent1 */ | 
|  | "<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",	/* indent2 */ | 
|  | "<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",	/* indent3 */ | 
|  | 0, | 
|  | 0, | 
|  | 0, | 
|  | "<p><font size=+1><b>",	/* heading 25 */ | 
|  | "<unused>",	/* anchor 26 */ | 
|  | }; | 
|  |  | 
|  | char* | 
|  | offattr[8*sizeof(ulong)] = | 
|  | { | 
|  | 0, 0, 0, 0, 0, 0, 0, 0, | 
|  | 0, 0, 0, 0, 0, 0, 0, 0, | 
|  | "</i>",	/* italic */ | 
|  | "</b>",	/* bold */ | 
|  | "</font></tt>",	/* cw */ | 
|  | "<-/table>",	/* indent1 */ | 
|  | "<-/table>",	/* indent2 */ | 
|  | "<-/table>",	/* indent3 */ | 
|  | 0, | 
|  | 0, | 
|  | 0, | 
|  | "</b></font>",	/* heading 25 */ | 
|  | "</a>",	/* anchor 26 */ | 
|  | }; | 
|  |  | 
|  | Font *font[Nfont]; | 
|  |  | 
|  | Biobuf bout; | 
|  | int	debug = 0; | 
|  |  | 
|  | /* troff state */ | 
|  | int	page = 1; | 
|  | int	ft = 1; | 
|  | int	vp = 0; | 
|  | int	hp = 0; | 
|  | int	ps = 1; | 
|  | int	res = 720; | 
|  |  | 
|  | int		didP = 0; | 
|  | int		atnewline = 1; | 
|  | int		prevlineH = 0; | 
|  | ulong	attr = 0;	/* or'ed into each Char */ | 
|  |  | 
|  | Char		*chars; | 
|  | int		nchars; | 
|  | int		nalloc; | 
|  | char**	anchors;	/* allocated in order */ | 
|  | int		nanchors; | 
|  |  | 
|  | char *pagename; | 
|  | char *section; | 
|  |  | 
|  | char	*filename; | 
|  | int	cno; | 
|  | char	buf[8192]; | 
|  | char	*title = "Plan 9 man page"; | 
|  |  | 
|  | void	process(Biobuf*, char*); | 
|  | void	mountfont(int, char*); | 
|  | void	switchfont(int); | 
|  | void	header(char*); | 
|  | void	flush(void); | 
|  | void	trailer(void); | 
|  |  | 
|  | void* | 
|  | emalloc(ulong n) | 
|  | { | 
|  | void *p; | 
|  |  | 
|  | p = malloc(n); | 
|  | if(p == nil) | 
|  | sysfatal("malloc failed: %r"); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | void* | 
|  | erealloc(void *p, ulong n) | 
|  | { | 
|  |  | 
|  | p = realloc(p, n); | 
|  | if(p == nil) | 
|  | sysfatal("realloc failed: %r"); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | char* | 
|  | estrdup(char *s) | 
|  | { | 
|  | char *t; | 
|  |  | 
|  | t = strdup(s); | 
|  | if(t == nil) | 
|  | sysfatal("strdup failed: %r"); | 
|  | return t; | 
|  | } | 
|  |  | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | fprint(2, "usage: troff2html [-d] [-t title] [file ...]\n"); | 
|  | exits("usage"); | 
|  | } | 
|  |  | 
|  | int | 
|  | hccmp(const void *va, const void *vb) | 
|  | { | 
|  | Htmlchar *a, *b; | 
|  |  | 
|  | a = (Htmlchar*)va; | 
|  | b = (Htmlchar*)vb; | 
|  | return a->value - b->value; | 
|  | } | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | int i; | 
|  | Biobuf in, *inp; | 
|  | Rune r; | 
|  |  | 
|  | for(i=0; i<nelem(htmlchars); i++){ | 
|  | chartorune(&r, htmlchars[i].utf); | 
|  | htmlchars[i].value = r; | 
|  | } | 
|  | qsort(htmlchars, nelem(htmlchars), sizeof(htmlchars[0]), hccmp); | 
|  |  | 
|  | ARGBEGIN{ | 
|  | case 't': | 
|  | title = ARGF(); | 
|  | if(title == nil) | 
|  | usage(); | 
|  | break; | 
|  | case 'd': | 
|  | debug++; | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | }ARGEND | 
|  |  | 
|  | Binit(&bout, 1, OWRITE); | 
|  | if(argc == 0){ | 
|  | Binit(&in, 0, OREAD); | 
|  | process(&in, "<stdin>"); | 
|  | }else{ | 
|  | for(i=0; i<argc; i++){ | 
|  | inp = Bopen(argv[i], OREAD); | 
|  | if(inp == nil) | 
|  | sysfatal("can't open %s: %r", argv[i]); | 
|  | process(inp, argv[i]); | 
|  | Bterm(inp); | 
|  | } | 
|  | } | 
|  | header(title); | 
|  | flush(); | 
|  | trailer(); | 
|  | exits(nil); | 
|  | } | 
|  |  | 
|  | void | 
|  | emitul(ulong ul, int special) | 
|  | { | 
|  | ulong a, c; | 
|  |  | 
|  | if(nalloc == nchars){ | 
|  | nalloc += 10000; | 
|  | chars = realloc(chars, nalloc*sizeof(chars[0])); | 
|  | if(chars == nil) | 
|  | sysfatal("malloc failed: %r"); | 
|  | } | 
|  |  | 
|  | if(!special){ | 
|  | a = ul&~0xFFFF; | 
|  | c = ul&0xFFFF; | 
|  | /* | 
|  | * Attr-specific transformations. | 
|  | */ | 
|  | if((a&(1<<CW)) && c=='-') | 
|  | c = 0x2212; | 
|  | if(!(a&(1<<CW))){ | 
|  | if(c == '`') | 
|  | c = 0x2018; | 
|  | if(c == '\'') | 
|  | c = 0x2019; | 
|  | } | 
|  | ul = a|c; | 
|  |  | 
|  | /* | 
|  | * Turn single quotes into double quotes. | 
|  | */ | 
|  | if(nchars > 0){ | 
|  | if(c == 0x2018 && (chars[nchars-1]&0xFFFF) == 0x2018 | 
|  | && a==(chars[nchars-1]&~0xFFFF)){ | 
|  | chars[nchars-1] = (ul&~0xFFFF) | 0x201C; | 
|  | return; | 
|  | } | 
|  | if(c == 0x2019 && (chars[nchars-1]&0xFFFF) == 0x2019 | 
|  | && a==(chars[nchars-1]&~0xFFFF)){ | 
|  | chars[nchars-1] = (ul&~0xFFFF) | 0x201D; | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  | chars[nchars++] = ul; | 
|  | } | 
|  |  | 
|  | void | 
|  | emit(Rune r) | 
|  | { | 
|  | emitul(r | attr, 0); | 
|  | /* | 
|  | * Close man page references early, so that | 
|  | * .IR proof (1), | 
|  | * doesn't make the comma part of the link. | 
|  | */ | 
|  | if(r == ')') | 
|  | attr &= ~(1<<Anchor); | 
|  | } | 
|  |  | 
|  | void | 
|  | emitstr(char *s) | 
|  | { | 
|  | emitul(Estring | attr, 0); | 
|  | emitul((ulong)s, 1); | 
|  | } | 
|  |  | 
|  | int indentlevel; | 
|  | int linelen; | 
|  |  | 
|  | void | 
|  | iputrune(Biobuf *b, Rune r) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if(linelen++ > 60 && r == ' ') | 
|  | r = '\n'; | 
|  | if(r >= 0x80) | 
|  | Bprint(b, "&#%d;", r); | 
|  | else | 
|  | Bputrune(b, r); | 
|  | if(r == '\n'){ | 
|  | for(i=0; i<indentlevel; i++) | 
|  | Bprint(b, "    "); | 
|  | linelen = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | iputs(Biobuf *b, char *s) | 
|  | { | 
|  | if(s[0]=='<' && s[1]=='+'){ | 
|  | iputrune(b, '\n'); | 
|  | Bprint(b, "<%s", s+2); | 
|  | indentlevel++; | 
|  | iputrune(b, '\n'); | 
|  | }else if(s[0]=='<' && s[1]=='-'){ | 
|  | indentlevel--; | 
|  | iputrune(b, '\n'); | 
|  | Bprint(b, "<%s", s+2); | 
|  | iputrune(b, '\n'); | 
|  | }else | 
|  | Bprint(b, "%s", s); | 
|  | } | 
|  |  | 
|  | void | 
|  | setattr(ulong a) | 
|  | { | 
|  | int on, off, i, j; | 
|  |  | 
|  | on = a & ~attr; | 
|  | off = attr & ~a; | 
|  |  | 
|  | /* walk up the nest stack until we reach something we need to turn off. */ | 
|  | for(i=0; i<nnest; i++) | 
|  | if(off&(1<<nest[i])) | 
|  | break; | 
|  |  | 
|  | /* turn off everything above that */ | 
|  | for(j=nnest-1; j>=i; j--) | 
|  | iputs(&bout, offattr[nest[j]]); | 
|  |  | 
|  | /* turn on everything we just turned off but didn't want to */ | 
|  | for(j=i; j<nnest; j++) | 
|  | if(a&(1<<nest[j])) | 
|  | iputs(&bout, onattr[nest[j]]); | 
|  | else | 
|  | nest[j] = 0; | 
|  |  | 
|  | /* shift the zeros (turned off things) up */ | 
|  | for(i=j=0; i<nnest; i++) | 
|  | if(nest[i] != 0) | 
|  | nest[j++] = nest[i]; | 
|  | nnest = j; | 
|  |  | 
|  | /* now turn on the new attributes */ | 
|  | for(i=0; i<nelem(attrorder); i++){ | 
|  | j = attrorder[i]; | 
|  | if(on&(1<<j)){ | 
|  | if(j == Anchor) | 
|  | onattr[j] = anchors[nanchors++]; | 
|  | iputs(&bout, onattr[j]); | 
|  | nest[nnest++] = j; | 
|  | } | 
|  | } | 
|  | attr = a; | 
|  | } | 
|  |  | 
|  | void | 
|  | flush(void) | 
|  | { | 
|  | int i; | 
|  | ulong c, a; | 
|  |  | 
|  | nanchors = 0; | 
|  | for(i=0; i<nchars; i++){ | 
|  | c = chars[i]; | 
|  | if(c == Epp){ | 
|  | iputrune(&bout, '\n'); | 
|  | iputs(&bout, TABLE "<tr height=5><td></table>"); | 
|  | iputrune(&bout, '\n'); | 
|  | continue; | 
|  | } | 
|  | a = c & ~0xFFFF; | 
|  | c &= 0xFFFF; | 
|  | /* | 
|  | * If we're going to something off after a space, | 
|  | * let's just turn it off before. | 
|  | */ | 
|  | if(c==' ' && i<nchars-1 && (chars[i+1]&0xFFFF) >= 32) | 
|  | a ^= a & ~chars[i+1]; | 
|  | setattr(a); | 
|  | if(c == Estring){ | 
|  | /* next word is string to print */ | 
|  | iputs(&bout, (char*)chars[++i]); | 
|  | continue; | 
|  | } | 
|  | iputrune(&bout, c & 0xFFFF); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | header(char *s) | 
|  | { | 
|  | char *p; | 
|  |  | 
|  | Bprint(&bout, "<head>\n"); | 
|  | if(pagename && section){ | 
|  | char buf[512]; | 
|  | strecpy(buf, buf+sizeof buf, pagename); | 
|  | for(p=buf; *p; p++) | 
|  | *p = tolower((uchar)*p); | 
|  | Bprint(&bout, "<title>%s(%s) - %s</title>\n", buf, section, s); | 
|  | }else | 
|  | Bprint(&bout, "<title>%s</title>\n", s); | 
|  | Bprint(&bout, "<meta content=\"text/html; charset=utf-8\" http-equiv=Content-Type>\n"); | 
|  | Bprint(&bout, "</head>\n"); | 
|  | Bprint(&bout, "<body bgcolor=#ffffff>\n"); | 
|  | Bprint(&bout, "<table border=0 cellpadding=0 cellspacing=0 width=100%%>\n"); | 
|  | Bprint(&bout, "<tr height=10><td>\n"); | 
|  | Bprint(&bout, "<tr><td width=20><td>\n"); | 
|  | if(pagename && section){ | 
|  | Bprint(&bout, "<tr><td width=20><td><b>%s(%s)</b><td align=right><b>%s(%s)</b>\n", | 
|  | pagename, section, pagename, section); | 
|  | } | 
|  | Bprint(&bout, "<tr><td width=20><td colspan=2>\n"); | 
|  | } | 
|  |  | 
|  | void | 
|  | trailer(void) | 
|  | { | 
|  | Bprint(&bout, "<td width=20>\n"); | 
|  | Bprint(&bout, "<tr height=20><td>\n"); | 
|  | Bprint(&bout, "</table>\n"); | 
|  |  | 
|  | #ifdef LUCENT | 
|  | { | 
|  | Tm *t; | 
|  |  | 
|  | t = localtime(time(nil)); | 
|  | Bprint(&bout, TABLE "<tr height=20><td></table>\n"); | 
|  | Bprint(&bout, "<font size=-1><a href=\"http:/*www.lucent.com/copyright.html\">\n"); */ | 
|  | Bprint(&bout, "Portions Copyright</A> © %d Lucent Technologies.  All rights reserved.</font>\n", t->year+1900); | 
|  | } | 
|  | #endif | 
|  | Bprint(&bout, "<!-- TRAILER -->\n"); | 
|  | Bprint(&bout, "</body></html>\n"); | 
|  | } | 
|  |  | 
|  | int | 
|  | getc(Biobuf *b) | 
|  | { | 
|  | cno++; | 
|  | return Bgetrune(b); | 
|  | } | 
|  |  | 
|  | void | 
|  | ungetc(Biobuf *b) | 
|  | { | 
|  | cno--; | 
|  | Bungetrune(b); | 
|  | } | 
|  |  | 
|  | char* | 
|  | getline(Biobuf *b) | 
|  | { | 
|  | int i, c; | 
|  |  | 
|  | for(i=0; i<sizeof buf; i++){ | 
|  | c = getc(b); | 
|  | if(c == Beof) | 
|  | return nil; | 
|  | buf[i] = c; | 
|  | if(c == '\n'){ | 
|  | buf[i] = '\0'; | 
|  | break; | 
|  | } | 
|  | } | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | int | 
|  | getnum(Biobuf *b) | 
|  | { | 
|  | int i, c; | 
|  |  | 
|  | i = 0; | 
|  | for(;;){ | 
|  | c = getc(b); | 
|  | if(c<'0' || '9'<c){ | 
|  | ungetc(b); | 
|  | break; | 
|  | } | 
|  | i = i*10 + (c-'0'); | 
|  | } | 
|  | return i; | 
|  | } | 
|  |  | 
|  | char* | 
|  | getstr(Biobuf *b) | 
|  | { | 
|  | int i, c; | 
|  |  | 
|  | for(i=0; i<sizeof buf; i++){ | 
|  | /* must get bytes not runes */ | 
|  | cno++; | 
|  | c = Bgetc(b); | 
|  | if(c == Beof) | 
|  | return nil; | 
|  | buf[i] = c; | 
|  | if(c == '\n' || c==' ' || c=='\t'){ | 
|  | ungetc(b); | 
|  | buf[i] = '\0'; | 
|  | break; | 
|  | } | 
|  | } | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | int | 
|  | setnum(Biobuf *b, char *name, int min, int max) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | i = getnum(b); | 
|  | if(debug > 2) | 
|  | fprint(2, "set %s = %d\n", name, i); | 
|  | if(min<=i && i<max) | 
|  | return i; | 
|  | sysfatal("value of %s is %d; min %d max %d at %s:#%d", name, i, min, max, filename, cno); | 
|  | return i; | 
|  | } | 
|  |  | 
|  | void | 
|  | xcmd(Biobuf *b) | 
|  | { | 
|  | char *p, *fld[16], buf[1024]; | 
|  |  | 
|  | int i, nfld; | 
|  |  | 
|  | p = getline(b); | 
|  | if(p == nil) | 
|  | sysfatal("xcmd error: %r"); | 
|  | if(debug) | 
|  | fprint(2, "x command '%s'\n", p); | 
|  | nfld = tokenize(p, fld, nelem(fld)); | 
|  | if(nfld == 0) | 
|  | return; | 
|  | switch(fld[0][0]){ | 
|  | case 'f': | 
|  | /* mount font */ | 
|  | if(nfld != 3) | 
|  | break; | 
|  | i = atoi(fld[1]); | 
|  | if(i<0 || Nfont<=i) | 
|  | sysfatal("font %d out of range at %s:#%d", i, filename, cno); | 
|  | mountfont(i, fld[2]); | 
|  | return; | 
|  | case 'i': | 
|  | /* init */ | 
|  | return; | 
|  | case 'r': | 
|  | if(nfld<2 || atoi(fld[1])!=res) | 
|  | sysfatal("typesetter has unexpected resolution %s", fld[1]? fld[1] : "<unspecified>"); | 
|  | return; | 
|  | case 's': | 
|  | /* stop */ | 
|  | return; | 
|  | case 't': | 
|  | /* trailer */ | 
|  | return; | 
|  | case 'T': | 
|  | if(nfld!=2 || strcmp(fld[1], "utf")!=0) | 
|  | sysfatal("output for unknown typesetter type %s", fld[1]); | 
|  | return; | 
|  | case 'X': | 
|  | if(nfld<3 || strcmp(fld[1], "html")!=0) | 
|  | break; | 
|  | /* is it a man reference of the form cp(1)? */ | 
|  | /* X manref start/end cp (1) */ | 
|  | if(nfld==6 && strcmp(fld[2], "manref")==0){ | 
|  | /* was the right macro; is it the right form? */ | 
|  | if(strlen(fld[5])>=3 && | 
|  | fld[5][0]=='('/*)*/ && (fld[5][2]==/*(*/')' || (isalpha((uchar)fld[5][2]) && fld[5][3]==/*(*/')')) && | 
|  | '0'<=fld[5][1] && fld[5][1]<='9'){ | 
|  | if(strcmp(fld[3], "start") == 0){ | 
|  | /* set anchor attribute and remember string */ | 
|  | attr |= (1<<Anchor); | 
|  | #if 0 | 
|  | snprint(buf, sizeof buf, | 
|  | "<a href=\"/magic/man2html/man%c/%s\">", | 
|  | fld[5][1], fld[4]); | 
|  | #else | 
|  | snprint(buf, sizeof buf, | 
|  | "<a href=\"../man%c/%s.html\">", fld[5][1], fld[4]); | 
|  | for(p=buf; *p; p++) | 
|  | if('A' <= *p && *p <= 'Z') | 
|  | *p += 'a'-'A'; | 
|  | #endif | 
|  | nanchors++; | 
|  | anchors = erealloc(anchors, nanchors*sizeof(char*)); | 
|  | anchors[nanchors-1] = estrdup(buf); | 
|  | }else if(strcmp(fld[3], "end") == 0) | 
|  | attr &= ~(1<<Anchor); | 
|  | } | 
|  | }else if(nfld >= 4 && strcmp(fld[2], "href") == 0){ | 
|  | attr |= 1<<Anchor; | 
|  | nanchors++; | 
|  | anchors = erealloc(anchors, nanchors*sizeof(char*)); | 
|  | anchors[nanchors-1] = smprint("<a href=\"%s\">", fld[3]); | 
|  | }else if(strcmp(fld[2], "/href") == 0){ | 
|  | attr &= ~(1<<Anchor); | 
|  | }else if(strcmp(fld[2], "manPP") == 0){ | 
|  | didP = 1; | 
|  | emitul(Epp, 1); | 
|  | }else if(nfld>=5 && strcmp(fld[2], "manhead") == 0){ | 
|  | pagename = strdup(fld[3]); | 
|  | section = strdup(fld[4]); | 
|  | }else if(nfld<4 || strcmp(fld[2], "manref")!=0){ | 
|  | if(nfld>2 && strcmp(fld[2], "<P>")==0){	/* avoid triggering extra <br> */ | 
|  | didP = 1; | 
|  | /* clear all font attributes before paragraph */ | 
|  | emitul(' ' | (attr & ~(0xFFFF|((1<<Italic)|(1<<Bold)|(1<<CW)))), 0); | 
|  | emitstr("<P>"); | 
|  | /* next emittec char will turn font attributes back on */ | 
|  | }else if(nfld>2 && strcmp(fld[2], "<H4>")==0) | 
|  | attr |= (1<<Heading); | 
|  | else if(nfld>2 && strcmp(fld[2], "</H4>")==0) | 
|  | attr &= ~(1<<Heading); | 
|  | else if(debug) | 
|  | fprint(2, "unknown in-line html %s... at %s:%#d\n", | 
|  | fld[2], filename, cno); | 
|  | } | 
|  | return; | 
|  | } | 
|  | if(debug) | 
|  | fprint(2, "unknown or badly formatted x command %s\n", fld[0]); | 
|  | } | 
|  |  | 
|  | int | 
|  | lookup(int c, Htmlchar tab[], int ntab) | 
|  | { | 
|  | int low, high, mid; | 
|  |  | 
|  | low = 0; | 
|  | high = ntab - 1; | 
|  | while(low <= high){ | 
|  | mid = (low+high)/2; | 
|  | if(c < tab[mid].value) | 
|  | high = mid - 1; | 
|  | else if(c > tab[mid].value) | 
|  | low = mid + 1; | 
|  | else | 
|  | return mid; | 
|  | } | 
|  | return -1;	/* no match */ | 
|  | } | 
|  |  | 
|  | void | 
|  | emithtmlchar(int r) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | i = lookup(r, htmlchars, nelem(htmlchars)); | 
|  | if(i >= 0) | 
|  | emitstr(htmlchars[i].name); | 
|  | else | 
|  | emit(r); | 
|  | } | 
|  |  | 
|  | char* | 
|  | troffchar(char *s) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for(i=0; troffchars[i].name!=nil; i++) | 
|  | if(strcmp(s, troffchars[i].name) == 0) | 
|  | return troffchars[i].value; | 
|  | return strdup(s); | 
|  | } | 
|  |  | 
|  | void | 
|  | indent(void) | 
|  | { | 
|  | int nind; | 
|  |  | 
|  | didP = 0; | 
|  | if(atnewline){ | 
|  | if(hp != prevlineH){ | 
|  | prevlineH = hp; | 
|  | /* these most peculiar numbers appear in the troff -man output */ | 
|  | nind = ((prevlineH-1*res)+323)/324; | 
|  | attr &= ~((1<<Indent1)|(1<<Indent2)|(1<<Indent3)); | 
|  | if(nind >= 1) | 
|  | attr |= (1<<Indent1); | 
|  | if(nind >= 2) | 
|  | attr |= (1<<Indent2); | 
|  | if(nind >= 3) | 
|  | attr |= (1<<Indent3); | 
|  | } | 
|  | atnewline = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | process(Biobuf *b, char *name) | 
|  | { | 
|  | int c, r, v, i; | 
|  | char *p; | 
|  |  | 
|  | cno = 0; | 
|  | prevlineH = res; | 
|  | filename = name; | 
|  | for(;;){ | 
|  | c = getc(b); | 
|  | switch(c){ | 
|  | case Beof: | 
|  | /* go to ground state */ | 
|  | attr = 0; | 
|  | emit('\n'); | 
|  | return; | 
|  | case '\n': | 
|  | break; | 
|  | case '0': case '1': case '2': case '3': case '4': | 
|  | case '5': case '6': case '7': case '8': case '9': | 
|  | v = c-'0'; | 
|  | c = getc(b); | 
|  | if(c<'0' || '9'<c) | 
|  | sysfatal("illegal character motion at %s:#%d", filename, cno); | 
|  | v = v*10 + (c-'0'); | 
|  | hp += v; | 
|  | /* fall through to character case */ | 
|  | case 'c': | 
|  | indent(); | 
|  | r = getc(b); | 
|  | emithtmlchar(r); | 
|  | break; | 
|  | case 'D': | 
|  | /* draw line; ignore */ | 
|  | do | 
|  | c = getc(b); | 
|  | while(c!='\n' && c!= Beof); | 
|  | break; | 
|  | case 'f': | 
|  | v = setnum(b, "font", 0, Nfont); | 
|  | switchfont(v); | 
|  | break; | 
|  | case 'h': | 
|  | v = setnum(b, "hpos", -20000, 20000); | 
|  | /* generate spaces if motion is large and within a line */ | 
|  | if(!atnewline && v>2*72) | 
|  | for(i=0; i<v; i+=72) | 
|  | emitstr(" "); | 
|  | hp += v; | 
|  | break; | 
|  | case 'n': | 
|  | setnum(b, "n1", -10000, 10000); | 
|  | /*Bprint(&bout, " N1=%d", v); */ | 
|  | getc(b);	/* space separates */ | 
|  | setnum(b, "n2", -10000, 10000); | 
|  | atnewline = 1; | 
|  | if(!didP && hp < (Wid-1)*res)	/* if line is less than 19" long, probably need a line break */ | 
|  | emitstr("<br>"); | 
|  | emit('\n'); | 
|  | break; | 
|  | case 'p': | 
|  | page = setnum(b, "ps", -10000, 10000); | 
|  | break; | 
|  | case 's': | 
|  | ps = setnum(b, "ps", 1, 1000); | 
|  | break; | 
|  | case 'v': | 
|  | vp += setnum(b, "vpos", -10000, 10000); | 
|  | /* BUG: ignore motion */ | 
|  | break; | 
|  | case 'x': | 
|  | xcmd(b); | 
|  | break; | 
|  | case 'w': | 
|  | emit(' '); | 
|  | break; | 
|  | case 'C': | 
|  | indent(); | 
|  | p = getstr(b); | 
|  | emitstr(troffchar(p)); | 
|  | break; | 
|  | case 'H': | 
|  | hp = setnum(b, "hpos", 0, 20000); | 
|  | /*Bprint(&bout, " H=%d ", hp); */ | 
|  | break; | 
|  | case 'V': | 
|  | vp = setnum(b, "vpos", 0, 10000); | 
|  | break; | 
|  | default: | 
|  | fprint(2, "dhtml: unknown directive %c(0x%.2ux) at %s:#%d\n", c, c, filename, cno); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | HTMLfont* | 
|  | htmlfont(char *name) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for(i=0; htmlfonts[i].name!=nil; i++) | 
|  | if(strcmp(name, htmlfonts[i].name) == 0) | 
|  | return &htmlfonts[i]; | 
|  | return &htmlfonts[0]; | 
|  | } | 
|  |  | 
|  | void | 
|  | mountfont(int pos, char *name) | 
|  | { | 
|  | if(debug) | 
|  | fprint(2, "mount font %s on %d\n", name, pos); | 
|  | if(font[pos] != nil){ | 
|  | free(font[pos]->name); | 
|  | free(font[pos]); | 
|  | } | 
|  | font[pos] = emalloc(sizeof(Font)); | 
|  | font[pos]->name = estrdup(name); | 
|  | font[pos]->htmlfont = htmlfont(name); | 
|  | } | 
|  |  | 
|  | void | 
|  | switchfont(int pos) | 
|  | { | 
|  | HTMLfont *hf; | 
|  |  | 
|  | if(debug) | 
|  | fprint(2, "font change from %d (%s) to %d (%s)\n", ft, font[ft]->name, pos, font[pos]->name); | 
|  | if(pos == ft) | 
|  | return; | 
|  | hf = font[ft]->htmlfont; | 
|  | if(hf->bit != 0) | 
|  | attr &= ~(1<<hf->bit); | 
|  | ft = pos; | 
|  | hf = font[ft]->htmlfont; | 
|  | if(hf->bit != 0) | 
|  | attr |= (1<<hf->bit); | 
|  | } |