URI:
       ttroff2html.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       ttroff2html.c (16311B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <ctype.h>
            5 
            6 enum{
            7         Nfont = 11,
            8         Wid = 20        /* tmac.anhtml sets page width to 20" so we can recognize .nf text */
            9 };
           10 
           11 typedef ulong Char;
           12 typedef struct Troffchar Troffchar;
           13 typedef struct Htmlchar Htmlchar;
           14 typedef struct Font Font;
           15 typedef struct HTMLfont HTMLfont;
           16 
           17 /* a Char is 32 bits. low 16 bits are the rune. higher are attributes */
           18 enum
           19 {
           20         Italic        =        16,
           21         Bold,
           22         CW,
           23         Indent1,
           24         Indent2,
           25         Indent3,
           26         Heading =        25,
           27         Anchor =        26        /* must be last */
           28 };
           29 
           30 enum        /* magic emissions */
           31 {
           32         Estring = 0,
           33         Epp = 1<<16
           34 };
           35 
           36 int attrorder[] = { Indent1, Indent2, Indent3, Heading, Anchor, Italic, Bold, CW };
           37 
           38 int nest[10];
           39 int nnest;
           40 
           41 struct Troffchar
           42 {
           43         char *name;
           44         char *value;
           45 };
           46 
           47 struct Htmlchar
           48 {
           49         char *utf;
           50         char *name;
           51         int value;
           52 };
           53 
           54 #include "chars.h"
           55 
           56 struct Font{
           57         char                *name;
           58         HTMLfont        *htmlfont;
           59 };
           60 
           61 struct HTMLfont{
           62         char        *name;
           63         char        *htmlname;
           64         int        bit;
           65 };
           66 
           67 /* R must be first; it's the default representation for fonts we don't recognize */
           68 HTMLfont htmlfonts[] =
           69 {
           70         "R",                        nil,                0,
           71         "LuxiSans",        nil,                0,
           72         "I",                        "i",        Italic,
           73         "LuxiSans-Oblique",        "i",        Italic,
           74         "CW",                "tt",                CW,
           75         "LuxiMono",        "tt",                CW,
           76         nil,        nil
           77 };
           78 
           79 #define TABLE "<table border=0 cellpadding=0 cellspacing=0>"
           80 
           81 char*
           82 onattr[8*sizeof(ulong)] =
           83 {
           84         0, 0, 0, 0, 0, 0, 0, 0,
           85         0, 0, 0, 0, 0, 0, 0, 0,
           86         "<i>",        /* italic */
           87         "<b>",        /* bold */
           88         "<tt><font size=+1>",        /* cw */
           89         "<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",        /* indent1 */
           90         "<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",        /* indent2 */
           91         "<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n",        /* indent3 */
           92         0,
           93         0,
           94         0,
           95         "<p><font size=+1><b>",        /* heading 25 */
           96         "<unused>",        /* anchor 26 */
           97 };
           98 
           99 char*
          100 offattr[8*sizeof(ulong)] =
          101 {
          102         0, 0, 0, 0, 0, 0, 0, 0,
          103         0, 0, 0, 0, 0, 0, 0, 0,
          104         "</i>",        /* italic */
          105         "</b>",        /* bold */
          106         "</font></tt>",        /* cw */
          107         "<-/table>",        /* indent1 */
          108         "<-/table>",        /* indent2 */
          109         "<-/table>",        /* indent3 */
          110         0,
          111         0,
          112         0,
          113         "</b></font>",        /* heading 25 */
          114         "</a>",        /* anchor 26 */
          115 };
          116 
          117 Font *font[Nfont];
          118 
          119 Biobuf bout;
          120 int        debug = 0;
          121 
          122 /* troff state */
          123 int        page = 1;
          124 int        ft = 1;
          125 int        vp = 0;
          126 int        hp = 0;
          127 int        ps = 1;
          128 int        res = 720;
          129 
          130 int                didP = 0;
          131 int                atnewline = 1;
          132 int                prevlineH = 0;
          133 ulong        attr = 0;        /* or'ed into each Char */
          134 
          135 Char                *chars;
          136 int                nchars;
          137 int                nalloc;
          138 char**        anchors;        /* allocated in order */
          139 int                nanchors;
          140 
          141 char *pagename;
          142 char *section;
          143 
          144 char        *filename;
          145 int        cno;
          146 char        buf[8192];
          147 char        *title = "Plan 9 man page";
          148 
          149 void        process(Biobuf*, char*);
          150 void        mountfont(int, char*);
          151 void        switchfont(int);
          152 void        header(char*);
          153 void        flush(void);
          154 void        trailer(void);
          155 
          156 void*
          157 emalloc(ulong n)
          158 {
          159         void *p;
          160 
          161         p = malloc(n);
          162         if(p == nil)
          163                 sysfatal("malloc failed: %r");
          164         return p;
          165 }
          166 
          167 void*
          168 erealloc(void *p, ulong n)
          169 {
          170 
          171         p = realloc(p, n);
          172         if(p == nil)
          173                 sysfatal("realloc failed: %r");
          174         return p;
          175 }
          176 
          177 char*
          178 estrdup(char *s)
          179 {
          180         char *t;
          181 
          182         t = strdup(s);
          183         if(t == nil)
          184                 sysfatal("strdup failed: %r");
          185         return t;
          186 }
          187 
          188 void
          189 usage(void)
          190 {
          191         fprint(2, "usage: troff2html [-d] [-t title] [file ...]\n");
          192         exits("usage");
          193 }
          194 
          195 int
          196 hccmp(const void *va, const void *vb)
          197 {
          198         Htmlchar *a, *b;
          199 
          200         a = (Htmlchar*)va;
          201         b = (Htmlchar*)vb;
          202         return a->value - b->value;
          203 }
          204 
          205 void
          206 main(int argc, char *argv[])
          207 {
          208         int i;
          209         Biobuf in, *inp;
          210         Rune r;
          211 
          212         for(i=0; i<nelem(htmlchars); i++){
          213                 chartorune(&r, htmlchars[i].utf);
          214                 htmlchars[i].value = r;
          215         }
          216         qsort(htmlchars, nelem(htmlchars), sizeof(htmlchars[0]), hccmp);
          217 
          218         ARGBEGIN{
          219         case 't':
          220                 title = ARGF();
          221                 if(title == nil)
          222                         usage();
          223                 break;
          224         case 'd':
          225                 debug++;
          226                 break;
          227         default:
          228                 usage();
          229         }ARGEND
          230 
          231         Binit(&bout, 1, OWRITE);
          232         if(argc == 0){
          233                 Binit(&in, 0, OREAD);
          234                 process(&in, "<stdin>");
          235         }else{
          236                 for(i=0; i<argc; i++){
          237                         inp = Bopen(argv[i], OREAD);
          238                         if(inp == nil)
          239                                 sysfatal("can't open %s: %r", argv[i]);
          240                         process(inp, argv[i]);
          241                         Bterm(inp);
          242                 }
          243         }
          244         header(title);
          245         flush();
          246         trailer();
          247         exits(nil);
          248 }
          249 
          250 void
          251 emitul(ulong ul, int special)
          252 {
          253         ulong a, c;
          254 
          255         if(nalloc == nchars){
          256                 nalloc += 10000;
          257                 chars = realloc(chars, nalloc*sizeof(chars[0]));
          258                 if(chars == nil)
          259                         sysfatal("malloc failed: %r");
          260         }
          261 
          262         if(!special){
          263                 a = ul&~0xFFFF;
          264                 c = ul&0xFFFF;
          265                 /*
          266                  * Attr-specific transformations.
          267                  */
          268                 if((a&(1<<CW)) && c=='-')
          269                         c = 0x2212;
          270                 if(!(a&(1<<CW))){
          271                         if(c == '`')
          272                                 c = 0x2018;
          273                         if(c == '\'')
          274                                 c = 0x2019;
          275                 }
          276                 ul = a|c;
          277 
          278                 /*
          279                  * Turn single quotes into double quotes.
          280                  */
          281                 if(nchars > 0){
          282                         if(c == 0x2018 && (chars[nchars-1]&0xFFFF) == 0x2018
          283                         && a==(chars[nchars-1]&~0xFFFF)){
          284                                 chars[nchars-1] = (ul&~0xFFFF) | 0x201C;
          285                                 return;
          286                         }
          287                         if(c == 0x2019 && (chars[nchars-1]&0xFFFF) == 0x2019
          288                         && a==(chars[nchars-1]&~0xFFFF)){
          289                                 chars[nchars-1] = (ul&~0xFFFF) | 0x201D;
          290                                 return;
          291                         }
          292                 }
          293         }
          294         chars[nchars++] = ul;
          295 }
          296 
          297 void
          298 emit(Rune r)
          299 {
          300         emitul(r | attr, 0);
          301         /*
          302          * Close man page references early, so that
          303          * .IR proof (1),
          304          * doesn't make the comma part of the link.
          305          */
          306         if(r == ')')
          307                 attr &= ~(1<<Anchor);
          308 }
          309 
          310 void
          311 emitstr(char *s)
          312 {
          313         emitul(Estring | attr, 0);
          314         emitul((ulong)s, 1);
          315 }
          316 
          317 int indentlevel;
          318 int linelen;
          319 
          320 void
          321 iputrune(Biobuf *b, Rune r)
          322 {
          323         int i;
          324 
          325         if(linelen++ > 60 && r == ' ')
          326                 r = '\n';
          327         if(r >= 0x80)
          328                 Bprint(b, "&#%d;", r);
          329         else
          330                 Bputrune(b, r);
          331         if(r == '\n'){
          332                 for(i=0; i<indentlevel; i++)
          333                         Bprint(b, "    ");
          334                 linelen = 0;
          335         }
          336 }
          337 
          338 void
          339 iputs(Biobuf *b, char *s)
          340 {
          341         if(s[0]=='<' && s[1]=='+'){
          342                 iputrune(b, '\n');
          343                 Bprint(b, "<%s", s+2);
          344                 indentlevel++;
          345                 iputrune(b, '\n');
          346         }else if(s[0]=='<' && s[1]=='-'){
          347                 indentlevel--;
          348                 iputrune(b, '\n');
          349                 Bprint(b, "<%s", s+2);
          350                 iputrune(b, '\n');
          351         }else
          352                 Bprint(b, "%s", s);
          353 }
          354 
          355 void
          356 setattr(ulong a)
          357 {
          358         int on, off, i, j;
          359 
          360         on = a & ~attr;
          361         off = attr & ~a;
          362 
          363         /* walk up the nest stack until we reach something we need to turn off. */
          364         for(i=0; i<nnest; i++)
          365                 if(off&(1<<nest[i]))
          366                         break;
          367 
          368         /* turn off everything above that */
          369         for(j=nnest-1; j>=i; j--)
          370                 iputs(&bout, offattr[nest[j]]);
          371 
          372         /* turn on everything we just turned off but didn't want to */
          373         for(j=i; j<nnest; j++)
          374                 if(a&(1<<nest[j]))
          375                         iputs(&bout, onattr[nest[j]]);
          376                 else
          377                         nest[j] = 0;
          378 
          379         /* shift the zeros (turned off things) up */
          380         for(i=j=0; i<nnest; i++)
          381                 if(nest[i] != 0)
          382                         nest[j++] = nest[i];
          383         nnest = j;
          384 
          385         /* now turn on the new attributes */
          386         for(i=0; i<nelem(attrorder); i++){
          387                 j = attrorder[i];
          388                 if(on&(1<<j)){
          389                         if(j == Anchor)
          390                                 onattr[j] = anchors[nanchors++];
          391                         iputs(&bout, onattr[j]);
          392                         nest[nnest++] = j;
          393                 }
          394         }
          395         attr = a;
          396 }
          397 
          398 void
          399 flush(void)
          400 {
          401         int i;
          402         ulong c, a;
          403 
          404         nanchors = 0;
          405         for(i=0; i<nchars; i++){
          406                 c = chars[i];
          407                 if(c == Epp){
          408                         iputrune(&bout, '\n');
          409                         iputs(&bout, TABLE "<tr height=5><td></table>");
          410                         iputrune(&bout, '\n');
          411                         continue;
          412                 }
          413                 a = c & ~0xFFFF;
          414                 c &= 0xFFFF;
          415                 /*
          416                  * If we're going to something off after a space,
          417                  * let's just turn it off before.
          418                  */
          419                 if(c==' ' && i<nchars-1 && (chars[i+1]&0xFFFF) >= 32)
          420                         a ^= a & ~chars[i+1];
          421                 setattr(a);
          422                 if(c == Estring){
          423                         /* next word is string to print */
          424                         iputs(&bout, (char*)chars[++i]);
          425                         continue;
          426                 }
          427                 iputrune(&bout, c & 0xFFFF);
          428         }
          429 }
          430 
          431 void
          432 header(char *s)
          433 {
          434         char *p;
          435 
          436         Bprint(&bout, "<head>\n");
          437         if(pagename && section){
          438                 char buf[512];
          439                 strecpy(buf, buf+sizeof buf, pagename);
          440                 for(p=buf; *p; p++)
          441                         *p = tolower((uchar)*p);
          442                 Bprint(&bout, "<title>%s(%s) - %s</title>\n", buf, section, s);
          443         }else
          444                 Bprint(&bout, "<title>%s</title>\n", s);
          445         Bprint(&bout, "<meta content=\"text/html; charset=utf-8\" http-equiv=Content-Type>\n");
          446         Bprint(&bout, "</head>\n");
          447         Bprint(&bout, "<body bgcolor=#ffffff>\n");
          448         Bprint(&bout, "<table border=0 cellpadding=0 cellspacing=0 width=100%%>\n");
          449         Bprint(&bout, "<tr height=10><td>\n");
          450         Bprint(&bout, "<tr><td width=20><td>\n");
          451         if(pagename && section){
          452                 Bprint(&bout, "<tr><td width=20><td><b>%s(%s)</b><td align=right><b>%s(%s)</b>\n",
          453                         pagename, section, pagename, section);
          454         }
          455         Bprint(&bout, "<tr><td width=20><td colspan=2>\n");
          456 }
          457 
          458 void
          459 trailer(void)
          460 {
          461         Bprint(&bout, "<td width=20>\n");
          462         Bprint(&bout, "<tr height=20><td>\n");
          463         Bprint(&bout, "</table>\n");
          464 
          465 #ifdef LUCENT
          466     {
          467         Tm *t;
          468 
          469         t = localtime(time(nil));
          470         Bprint(&bout, TABLE "<tr height=20><td></table>\n");
          471         Bprint(&bout, "<font size=-1><a href=\"http:/*www.lucent.com/copyright.html\">\n"); */
          472         Bprint(&bout, "Portions Copyright</A> &#169; %d Lucent Technologies.  All rights reserved.</font>\n", t->year+1900);
          473     }
          474 #endif
          475         Bprint(&bout, "<!-- TRAILER -->\n");
          476         Bprint(&bout, "</body></html>\n");
          477 }
          478 
          479 int
          480 getc(Biobuf *b)
          481 {
          482         cno++;
          483         return Bgetrune(b);
          484 }
          485 
          486 void
          487 ungetc(Biobuf *b)
          488 {
          489         cno--;
          490         Bungetrune(b);
          491 }
          492 
          493 char*
          494 getline(Biobuf *b)
          495 {
          496         int i, c;
          497 
          498         for(i=0; i<sizeof buf; i++){
          499                 c = getc(b);
          500                 if(c == Beof)
          501                         return nil;
          502                 buf[i] = c;
          503                 if(c == '\n'){
          504                         buf[i] = '\0';
          505                         break;
          506                 }
          507         }
          508         return buf;
          509 }
          510 
          511 int
          512 getnum(Biobuf *b)
          513 {
          514         int i, c;
          515 
          516         i = 0;
          517         for(;;){
          518                 c = getc(b);
          519                 if(c<'0' || '9'<c){
          520                         ungetc(b);
          521                         break;
          522                 }
          523                 i = i*10 + (c-'0');
          524         }
          525         return i;
          526 }
          527 
          528 char*
          529 getstr(Biobuf *b)
          530 {
          531         int i, c;
          532 
          533         for(i=0; i<sizeof buf; i++){
          534                 /* must get bytes not runes */
          535                 cno++;
          536                 c = Bgetc(b);
          537                 if(c == Beof)
          538                         return nil;
          539                 buf[i] = c;
          540                 if(c == '\n' || c==' ' || c=='\t'){
          541                         ungetc(b);
          542                         buf[i] = '\0';
          543                         break;
          544                 }
          545         }
          546         return buf;
          547 }
          548 
          549 int
          550 setnum(Biobuf *b, char *name, int min, int max)
          551 {
          552         int i;
          553 
          554         i = getnum(b);
          555         if(debug > 2)
          556                 fprint(2, "set %s = %d\n", name, i);
          557         if(min<=i && i<max)
          558                 return i;
          559         sysfatal("value of %s is %d; min %d max %d at %s:#%d", name, i, min, max, filename, cno);
          560         return i;
          561 }
          562 
          563 void
          564 xcmd(Biobuf *b)
          565 {
          566         char *p, *fld[16], buf[1024];
          567 
          568         int i, nfld;
          569 
          570         p = getline(b);
          571         if(p == nil)
          572                 sysfatal("xcmd error: %r");
          573         if(debug)
          574                 fprint(2, "x command '%s'\n", p);
          575         nfld = tokenize(p, fld, nelem(fld));
          576         if(nfld == 0)
          577                 return;
          578         switch(fld[0][0]){
          579         case 'f':
          580                 /* mount font */
          581                 if(nfld != 3)
          582                         break;
          583                 i = atoi(fld[1]);
          584                 if(i<0 || Nfont<=i)
          585                         sysfatal("font %d out of range at %s:#%d", i, filename, cno);
          586                 mountfont(i, fld[2]);
          587                 return;
          588         case 'i':
          589                 /* init */
          590                 return;
          591         case 'r':
          592                 if(nfld<2 || atoi(fld[1])!=res)
          593                         sysfatal("typesetter has unexpected resolution %s", fld[1]? fld[1] : "<unspecified>");
          594                 return;
          595         case 's':
          596                 /* stop */
          597                 return;
          598         case 't':
          599                 /* trailer */
          600                 return;
          601         case 'T':
          602                 if(nfld!=2 || strcmp(fld[1], "utf")!=0)
          603                         sysfatal("output for unknown typesetter type %s", fld[1]);
          604                 return;
          605         case 'X':
          606                 if(nfld<3 || strcmp(fld[1], "html")!=0)
          607                         break;
          608                 /* is it a man reference of the form cp(1)? */
          609                 /* X manref start/end cp (1) */
          610                 if(nfld==6 && strcmp(fld[2], "manref")==0){
          611                         /* was the right macro; is it the right form? */
          612                         if(strlen(fld[5])>=3 &&
          613                            fld[5][0]=='('/*)*/ && (fld[5][2]==/*(*/')' || (isalpha((uchar)fld[5][2]) && fld[5][3]==/*(*/')')) &&
          614                            '0'<=fld[5][1] && fld[5][1]<='9'){
          615                                 if(strcmp(fld[3], "start") == 0){
          616                                         /* set anchor attribute and remember string */
          617                                         attr |= (1<<Anchor);
          618 #if 0
          619                                         snprint(buf, sizeof buf,
          620                                                 "<a href=\"/magic/man2html/man%c/%s\">",
          621                                                 fld[5][1], fld[4]);
          622 #else
          623                                         snprint(buf, sizeof buf,
          624                                                 "<a href=\"../man%c/%s.html\">", fld[5][1], fld[4]);
          625                                         for(p=buf; *p; p++)
          626                                                 if('A' <= *p && *p <= 'Z')
          627                                                         *p += 'a'-'A';
          628 #endif
          629                                         nanchors++;
          630                                         anchors = erealloc(anchors, nanchors*sizeof(char*));
          631                                         anchors[nanchors-1] = estrdup(buf);
          632                                 }else if(strcmp(fld[3], "end") == 0)
          633                                         attr &= ~(1<<Anchor);
          634                         }
          635                 }else if(nfld >= 4 && strcmp(fld[2], "href") == 0){
          636                         attr |= 1<<Anchor;
          637                         nanchors++;
          638                         anchors = erealloc(anchors, nanchors*sizeof(char*));
          639                         anchors[nanchors-1] = smprint("<a href=\"%s\">", fld[3]);
          640                 }else if(strcmp(fld[2], "/href") == 0){
          641                         attr &= ~(1<<Anchor);
          642                 }else if(strcmp(fld[2], "manPP") == 0){
          643                         didP = 1;
          644                         emitul(Epp, 1);
          645                 }else if(nfld>=5 && strcmp(fld[2], "manhead") == 0){
          646                         pagename = strdup(fld[3]);
          647                         section = strdup(fld[4]);
          648                 }else if(nfld<4 || strcmp(fld[2], "manref")!=0){
          649                         if(nfld>2 && strcmp(fld[2], "<P>")==0){        /* avoid triggering extra <br> */
          650                                 didP = 1;
          651                                 /* clear all font attributes before paragraph */
          652                                 emitul(' ' | (attr & ~(0xFFFF|((1<<Italic)|(1<<Bold)|(1<<CW)))), 0);
          653                                 emitstr("<P>");
          654                                 /* next emittec char will turn font attributes back on */
          655                         }else if(nfld>2 && strcmp(fld[2], "<H4>")==0)
          656                                 attr |= (1<<Heading);
          657                         else if(nfld>2 && strcmp(fld[2], "</H4>")==0)
          658                                 attr &= ~(1<<Heading);
          659                         else if(debug)
          660                                 fprint(2, "unknown in-line html %s... at %s:%#d\n",
          661                                         fld[2], filename, cno);
          662                 }
          663                 return;
          664         }
          665         if(debug)
          666                 fprint(2, "unknown or badly formatted x command %s\n", fld[0]);
          667 }
          668 
          669 int
          670 lookup(int c, Htmlchar tab[], int ntab)
          671 {
          672         int low, high, mid;
          673 
          674         low = 0;
          675         high = ntab - 1;
          676         while(low <= high){
          677                 mid = (low+high)/2;
          678                 if(c < tab[mid].value)
          679                         high = mid - 1;
          680                 else if(c > tab[mid].value)
          681                         low = mid + 1;
          682                 else
          683                         return mid;
          684         }
          685         return -1;        /* no match */
          686 }
          687 
          688 void
          689 emithtmlchar(int r)
          690 {
          691         int i;
          692 
          693         i = lookup(r, htmlchars, nelem(htmlchars));
          694         if(i >= 0)
          695                 emitstr(htmlchars[i].name);
          696         else
          697                 emit(r);
          698 }
          699 
          700 char*
          701 troffchar(char *s)
          702 {
          703         int i;
          704 
          705         for(i=0; troffchars[i].name!=nil; i++)
          706                 if(strcmp(s, troffchars[i].name) == 0)
          707                         return troffchars[i].value;
          708         return strdup(s);
          709 }
          710 
          711 void
          712 indent(void)
          713 {
          714         int nind;
          715 
          716         didP = 0;
          717         if(atnewline){
          718                 if(hp != prevlineH){
          719                         prevlineH = hp;
          720                         /* these most peculiar numbers appear in the troff -man output */
          721                         nind = ((prevlineH-1*res)+323)/324;
          722                         attr &= ~((1<<Indent1)|(1<<Indent2)|(1<<Indent3));
          723                         if(nind >= 1)
          724                                 attr |= (1<<Indent1);
          725                         if(nind >= 2)
          726                                 attr |= (1<<Indent2);
          727                         if(nind >= 3)
          728                                 attr |= (1<<Indent3);
          729                 }
          730                 atnewline = 0;
          731         }
          732 }
          733 
          734 void
          735 process(Biobuf *b, char *name)
          736 {
          737         int c, r, v, i;
          738         char *p;
          739 
          740         cno = 0;
          741         prevlineH = res;
          742         filename = name;
          743         for(;;){
          744                 c = getc(b);
          745                 switch(c){
          746                 case Beof:
          747                         /* go to ground state */
          748                         attr = 0;
          749                         emit('\n');
          750                         return;
          751                 case '\n':
          752                         break;
          753                 case '0': case '1': case '2': case '3': case '4':
          754                 case '5': case '6': case '7': case '8': case '9':
          755                         v = c-'0';
          756                         c = getc(b);
          757                         if(c<'0' || '9'<c)
          758                                 sysfatal("illegal character motion at %s:#%d", filename, cno);
          759                         v = v*10 + (c-'0');
          760                         hp += v;
          761                         /* fall through to character case */
          762                 case 'c':
          763                         indent();
          764                         r = getc(b);
          765                         emithtmlchar(r);
          766                         break;
          767                 case 'D':
          768                         /* draw line; ignore */
          769                         do
          770                                 c = getc(b);
          771                         while(c!='\n' && c!= Beof);
          772                         break;
          773                 case 'f':
          774                         v = setnum(b, "font", 0, Nfont);
          775                         switchfont(v);
          776                         break;
          777                 case 'h':
          778                         v = setnum(b, "hpos", -20000, 20000);
          779                         /* generate spaces if motion is large and within a line */
          780                         if(!atnewline && v>2*72)
          781                                 for(i=0; i<v; i+=72)
          782                                         emitstr("&nbsp;");
          783                         hp += v;
          784                         break;
          785                 case 'n':
          786                         setnum(b, "n1", -10000, 10000);
          787                         /*Bprint(&bout, " N1=%d", v); */
          788                         getc(b);        /* space separates */
          789                         setnum(b, "n2", -10000, 10000);
          790                         atnewline = 1;
          791                         if(!didP && hp < (Wid-1)*res)        /* if line is less than 19" long, probably need a line break */
          792                                 emitstr("<br>");
          793                         emit('\n');
          794                         break;
          795                 case 'p':
          796                         page = setnum(b, "ps", -10000, 10000);
          797                         break;
          798                 case 's':
          799                         ps = setnum(b, "ps", 1, 1000);
          800                         break;
          801                 case 'v':
          802                         vp += setnum(b, "vpos", -10000, 10000);
          803                         /* BUG: ignore motion */
          804                         break;
          805                 case 'x':
          806                         xcmd(b);
          807                         break;
          808                 case 'w':
          809                         emit(' ');
          810                         break;
          811                 case 'C':
          812                         indent();
          813                         p = getstr(b);
          814                         emitstr(troffchar(p));
          815                         break;
          816                 case 'H':
          817                         hp = setnum(b, "hpos", 0, 20000);
          818                         /*Bprint(&bout, " H=%d ", hp); */
          819                         break;
          820                 case 'V':
          821                         vp = setnum(b, "vpos", 0, 10000);
          822                         break;
          823                 default:
          824                         fprint(2, "dhtml: unknown directive %c(0x%.2ux) at %s:#%d\n", c, c, filename, cno);
          825                         return;
          826                 }
          827         }
          828 }
          829 
          830 HTMLfont*
          831 htmlfont(char *name)
          832 {
          833         int i;
          834 
          835         for(i=0; htmlfonts[i].name!=nil; i++)
          836                 if(strcmp(name, htmlfonts[i].name) == 0)
          837                         return &htmlfonts[i];
          838         return &htmlfonts[0];
          839 }
          840 
          841 void
          842 mountfont(int pos, char *name)
          843 {
          844         if(debug)
          845                 fprint(2, "mount font %s on %d\n", name, pos);
          846         if(font[pos] != nil){
          847                 free(font[pos]->name);
          848                 free(font[pos]);
          849         }
          850         font[pos] = emalloc(sizeof(Font));
          851         font[pos]->name = estrdup(name);
          852         font[pos]->htmlfont = htmlfont(name);
          853 }
          854 
          855 void
          856 switchfont(int pos)
          857 {
          858         HTMLfont *hf;
          859 
          860         if(debug)
          861                 fprint(2, "font change from %d (%s) to %d (%s)\n", ft, font[ft]->name, pos, font[pos]->name);
          862         if(pos == ft)
          863                 return;
          864         hf = font[ft]->htmlfont;
          865         if(hf->bit != 0)
          866                 attr &= ~(1<<hf->bit);
          867         ft = pos;
          868         hf = font[ft]->htmlfont;
          869         if(hf->bit != 0)
          870                 attr |= (1<<hf->bit);
          871 }