10 typedef struct Fontdata Fontdata;
16 "dejavusans/unicode.12", 0, 0,
17 "dejavusans/unicode.12", 0, 0,
18 "dejavusans/unicode.14", 0, 0,
19 "dejavusans/unicode.16", 0, 0,
21 "dejavusansit/unicode.12", 0, 0,
22 "dejavusansit/unicode.12", 0, 0,
23 "dejavusansit/unicode.14", 0, 0,
24 "dejavusansit/unicode.16", 0, 0,
26 "dejavusansbd/unicode.12", 0, 0,
27 "dejavusansbd/unicode.12", 0, 0,
28 "dejavusansbd/unicode.14", 0, 0,
29 "dejavusansbd/unicode.16", 0, 0,
32 "terminus/unicode.14", 0, 0,
33 "terminus/unicode.16", 0, 0,
34 "terminus/unicode.18", 0, 0,
36 Fontdata *pl_whichfont(int f, int s){
39 assert(f >= 0 && f < 4);
40 assert(s >= 0 && s < 4);
42 if(fontlist[f][s].font==0){
43 snprint(name, sizeof(name), "/lib/font/bit/%s.font", fontlist[f][s].name);
44 fontlist[f][s].font=openfont(display, name);
45 if(fontlist[f][s].font==0) fontlist[f][s].font=font;
46 fontlist[f][s].space=stringwidth(fontlist[f][s].font, "0");
48 return &fontlist[f][s];
57 void pl_pushstate(Hglob *g, int t){
59 if(g->state==&g->stack[NSTACK]){
60 htmlerror(g->name, g->lineno, "stack overflow at <%s>", tag[t].name);
63 g->state[0]=g->state[-1];
66 void pl_linespace(Hglob *g){
67 plrtbitmap(&g->dst->text, 1000000, 0, linespace, 0, 0);
75 int strtolength(Hglob *g, int dir, char *str)
80 if(cistrstr(str, "px"))
82 if(cistrstr(str, "%"))
83 return floor(f*((dir==HORIZ) ? Dx(g->dst->text->r) : Dy(g->dst->text->r))/100);
84 if(cistrstr(str, "em")){
86 z = stringsize(g->dst->text->font, "M");
87 return floor(f*((dir==HORIZ) ? z.x : z.y));
92 void pl_htmloutput(Hglob *g, int nsp, char *s, Field *field){
96 if(g->state->tag==Tag_title
97 /* || g->state->tag==Tag_textarea */
98 || g->state->tag==Tag_select){
100 if(g->tp!=g->text && g->tp!=g->etext && g->tp[-1]!=' ')
102 while(g->tp!=g->etext && *s) *g->tp++=*s++;
103 if(g->state->tag==Tag_title) update(g->dst);
108 f=pl_whichfont(g->state->font, g->state->size);
110 indent=g->state->margin;
113 indent+=g->state->indent;
119 if(g->state->image[0]==0 && g->state->link[0]==0 && g->state->name[0]==0 && field==0)
122 ap=mallocz(sizeof(Action), 1);
124 if(g->state->image[0])
125 ap->image = strdup(g->state->image);
126 if(g->state->link[0])
127 ap->link = strdup(g->state->link);
128 if(g->state->name[0])
129 ap->name = strdup(g->state->name);
130 ap->ismap=g->state->ismap;
131 ap->width=g->state->width;
132 ap->height=g->state->height;
137 if(indent<0) indent=0;
138 if(g->state->pre && s[0]=='\t'){
150 plrtstr(&g->dst->text, space, indent, f->font, strdup(s),
151 g->state->link[0] || g->state->image[0], ap);
158 * Buffered read, no translation
161 int pl_bread(Hglob *g){
164 if(g->hbufp==g->ehbuf){
165 n=read(g->hfd, g->hbuf, NHBUF);
168 snprint(err, sizeof(err), "%r reading %s", g->name);
169 pl_htmloutput(g, 1, err, 0);
178 if(c=='\n') g->lineno++;
182 * Read a character, translating \r\n, \n\r, \r and \n into \n
184 int pl_readc(Hglob *g){
205 void pl_putback(Hglob *g, int c){
206 if(g->npeekc==NPEEKC) htmlerror(g->name, g->lineno, "too much putback!");
207 else if(c!=EOF) g->peekc[g->npeekc++]=c;
209 int pl_nextc(Hglob *g){
213 char crune[UTFmax+1];
214 if(g->heof) return EOF;
215 if(g->npeekc!=0) return g->peekc[--g->npeekc];
223 if('a'<=c && c<='z' || 'A'<=c && c<='Z') return STAG;
227 if(c=='!' || 'a'<=c && c<='z' || 'A'<=c && c<='Z' || c=='?') return STAG;
230 if(c=='>') return ETAG;
232 for (n=1; n<=sizeof(crune); n++){
234 if(fullrune(crune, n)){
235 chartorune(&r, crune);
244 char *unquot(char *dst, char *src, int len){
248 while(strchr("\n\r\t ", *src))
250 if(*src=='\'' || *src=='"'){
251 e=strrchr(src+1, *src);
254 if(e==0) e=strchr(src, 0);
258 if(len>0) memmove(dst, src, len);
262 int alnumchar(int c){
263 return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9';
266 return c=='#' || alnumchar(c);
269 /* return url if text token looks like a hyperlink */
270 char *linkify(char *s){
271 if(!cistrncmp(s, "http://", 7))
273 if(!cistrncmp(s, "https://", 8))
275 if(!cistrncmp(s, "www.", 4)){
284 } else if(!alnumchar(s[i]))
288 return smprint("http://%s", s);
294 * remove entity references, in place.
296 * This doesn't work if removing an entity reference can lengthen the string!
297 * Fortunately, this doesn't happen.
299 void pl_rmentities(Hglob *g, char *s){
307 && ((*s=='#' && strchr("0123456789Xx", s[1]))
308 || 'a'<=*s && *s<='z'
309 || 'A'<=*s && *s<='Z')){
311 while(entchar(*s)) s++;
315 if(strcmp(u, "lt") == 0)
317 else if(strcmp(u, "gt") == 0)
319 else if(strcmp(u, "quot") == 0)
321 else if(strcmp(u, "apos") == 0)
323 else if(strcmp(u, "amp") == 0)
337 * Skip over white space
339 char *pl_white(char *s){
340 while(*s==' ' || *s=='\t' || *s=='\n' || *s=='\r') s++;
344 * Skip over HTML word
346 char *pl_word(char *s){
347 if ('a'<=*s && *s<='z' || 'A'<=*s && *s<='Z') {
349 while('a'<=*s && *s<='z' || 'A'<=*s && *s<='Z' || '0'<=*s && *s<='9' || *s=='-' || *s=='.') s++;
354 * Skip to matching quote
356 char *pl_quote(char *s){
359 while(*s!=q && *s!='\0') s++;
362 void pl_dnl(char *s){
364 for(t=s;*s;s++) if(*s!='\r' && *s!='\n') *t++=*s;
367 void pl_tagparse(Hglob *g, char *str){
368 char *s, *t, *name, c;
373 if(str[0]=='!'){ /* test should be strncmp(str, "!--", 3)==0 */
378 if(str[0]=='/') str++;
381 if(*s!=' ' && *s!='\n' && *s!='\t' && *s!='\0'){
382 htmlerror(g->name, g->lineno, "bad tag name in %s", str);
386 if(*s!='\0') *s++='\0';
387 for(t=name;t!=s;t++) if('A'<=*t && *t<='Z') *t+='a'-'A';
389 * Binary search would be faster here
391 for(tagp=tag;tagp->name;tagp++) if(strcmp(name, tagp->name)==0) break;
393 if(g->tag==Tag_end) htmlerror(g->name, g->lineno, "no tag %s", name);
405 for(s=ap->name;*s;s++) if('A'<=*s && *s<='Z') *s+='a'-'A';
408 if(*s=='\'' || *s=='"'){
412 htmlerror(g->name, g->lineno,
413 "No terminating quote in rhs of attribute %s",
422 /* read up to white space or > */
424 while(*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0') s++;
425 if(*s!='\0') *s++='\0';
427 pl_rmentities(g, ap->value);
433 if(ap==&g->attr[NATTR-1])
434 htmlerror(g->name, g->lineno, "too many attributes!");
438 int pl_getcomment(Hglob *g){
440 if((c=pl_nextc(g))=='-' && (c=pl_nextc(g))=='-'){
441 /* <!-- eats everything until --> or EOF */
443 while((c=pl_nextc(g))!='-' && c!=EOF)
447 if((c=pl_nextc(g))=='-'){
448 while((c=pl_nextc(g))=='-')
450 if(c==ETAG || c==EOF)
455 /* <! eats everything until > or EOF */
456 while(c!=ETAG && c!=EOF)
460 htmlerror(g->name, g->lineno, "EOF in comment");
466 int lrunetochar(char *p, int v)
471 return runetochar(p, &r);
475 * Read a start or end tag -- the caller has read the initial <
477 int pl_gettag(Hglob *g){
481 if((c=pl_nextc(g))=='!' || c=='?')
482 return pl_getcomment(g);
484 while((c=pl_nextc(g))!=ETAG && c!=EOF)
485 if(tokp < &g->token[NTOKEN-UTFmax-1]) tokp += lrunetochar(tokp, c);
487 if(c==EOF) htmlerror(g->name, g->lineno, "EOF in tag");
488 pl_tagparse(g, g->token);
489 if(g->token[0]!='/') return TAG;
490 if(g->attr[0].name!=0)
491 htmlerror(g->name, g->lineno, "end tag should not have attributes");
495 * The next token is a tag, an end tag or a sequence of
496 * non-white characters.
497 * If inside <pre>, newlines are converted to <br> and spaces are preserved.
498 * Otherwise, spaces and newlines are noted and discarded.
500 int pl_gettoken(Hglob *g){
503 if(g->state->pre) switch(c=pl_nextc(g)){
504 case STAG: return pl_gettag(g);
505 case EOF: return EOF;
507 pl_tagparse(g, "br");
512 if(tokp < &g->token[NTOKEN-UTFmax-1]) tokp += lrunetochar(tokp, c);
515 while(c!='\t' && c!='\n' && c!=STAG && c!=EOF){
517 if(tokp < &g->token[NTOKEN-UTFmax-1]) tokp += lrunetochar(tokp, c);
521 pl_rmentities(g, g->token);
527 while((c=pl_nextc(g))==' ' || c=='\t' || c=='\n')
531 case STAG: return pl_gettag(g);
532 case EOF: return EOF;
537 if(tokp < &g->token[NTOKEN-UTFmax-1]) tokp += lrunetochar(tokp, c);
539 }while(c!=' ' && c!='\t' && c!='\n' && c!=STAG && c!=EOF);
541 pl_rmentities(g, g->token);
548 char *pl_getattr(Pair *attr, char *name){
549 for(;attr->name;attr++)
550 if(strcmp(attr->name, name)==0)
554 int pl_hasattr(Pair *attr, char *name){
555 for(;attr->name;attr++)
556 if(strcmp(attr->name, name)==0)
560 void plaintext(Hglob *g){
564 g->state->font=CWIDTH;
565 g->state->size=NORMAL;
566 elp=&line[NLINE-UTFmax-1];
571 if(c=='\n' || lp>=elp){
574 pl_htmloutput(g, 0, line, 0);
578 do *lp++=' '; while(lp<elp && utfnlen(line, lp-line)%8!=0);
581 lp += lrunetochar(lp, c);
586 pl_htmloutput(g, 0, line, 0);
589 void plrdplain(char *name, int fd, Www *dst){
592 g.state->tag=Tag_html;
593 g.state->font=CWIDTH;
594 g.state->size=NORMAL;
605 g.ehbuf=g.hbufp=g.hbuf;
613 g.etext=g.text+NTITLE-1;
616 strncpy(g.text, name, NTITLE);
620 void plrdhtml(char *name, int fd, Www *dst){
629 g.state->tag=Tag_html;
631 g.state->size=NORMAL;
644 g.ehbuf=g.hbufp=g.hbuf;
652 g.etext=g.text+NTITLE-1;
657 for(;;) switch(pl_gettoken(&g)){
659 switch(tag[g.tag].action){
661 for(sp=g.state;sp!=g.stack && sp->tag!=g.tag;--sp);
663 pl_pushstate(&g, g.tag);
665 for(;g.state!=sp;--g.state)
666 if(tag[g.state->tag].action!=OPTEND)
667 htmlerror(g.name, g.lineno,
668 "end tag </%s> missing",
669 tag[g.state->tag].name);
672 pl_pushstate(&g, g.tag);
675 if(str=pl_getattr(g.attr, "id")){
678 strncpy(swap, g.state->name, sizeof(swap));
679 strncpy(g.state->name, str, sizeof(g.state->name));
680 pl_htmloutput(&g, 0, "", 0);
681 strncpy(g.state->name, swap, sizeof(g.state->name));
685 htmlerror(g.name, g.lineno,
686 "unimplemented tag <%s>", tag[g.tag].name);
688 case Tag_end: /* unrecognized start tag */
691 if(str=pl_getattr(g.attr, "src"))
692 strncpy(g.state->image, str, sizeof(g.state->image));
693 g.state->ismap=pl_hasattr(g.attr, "ismap");
694 if(str=pl_getattr(g.attr, "width"))
695 g.state->width = strtolength(&g, HORIZ, str);
696 if(str=pl_getattr(g.attr, "height"))
697 g.state->height = strtolength(&g, VERT, str);
698 str=pl_getattr(g.attr, "alt");
700 if(g.state->image[0])
705 pl_htmloutput(&g, 0, str, 0);
729 if(str=pl_getattr(g.attr, "href"))
730 strncpy(g.state->link, str, sizeof(g.state->link));
731 if(str=pl_getattr(g.attr, "name")){
732 strncpy(g.state->name, str, sizeof(g.state->name));
733 pl_htmloutput(&g, 0, "", 0);
737 if((str=pl_getattr(g.attr, "http-equiv"))==0)
739 if(cistrcmp(str, "refresh"))
741 if((str=pl_getattr(g.attr, "content"))==0)
743 if((str=strchr(str, '='))==0)
746 str=unquot(g.state->link, str, sizeof(g.state->link));
747 pl_htmloutput(&g, 0, "refresh: ", 0);
748 pl_htmloutput(&g, 0, str, 0);
759 if(str=pl_getattr(g.attr, "src"))
760 strncpy(g.state->link, str, sizeof(g.state->link));
761 if(str=pl_getattr(g.attr, "name"))
762 strncpy(g.state->name, str, sizeof(g.state->name));
765 pl_htmloutput(&g, 0, tag[g.tag].name, 0);
766 pl_htmloutput(&g, 0, ": ", 0);
767 pl_htmloutput(&g, 0, str, 0);
777 g.state->size=NORMAL;
795 g.state->size=NORMAL;
810 g.state->font=ITALIC;
811 g.state->size=NORMAL;
814 g.state->font=CWIDTH;
815 g.state->size=NORMAL;
824 htmlerror(g.name, g.lineno, "<dfn> deprecated");
827 g.state->size=NORMAL;
831 g.state->size=NORMAL;
845 htmlerror(g.name, g.lineno, "<u> deprecated");
849 g.state->font=ITALIC;
854 g.state->size=ENORMOUS;
855 g.state->margin+=100;
861 g.state->size=ENORMOUS;
867 g.state->font=ITALIC;
868 g.state->size=ENORMOUS;
881 g.state->font=ITALIC;
894 plrtbitmap(&g.dst->text, 1000000, g.state->margin, hrule, 0, 0);
897 htmlerror(g.name, g.lineno, "<key> deprecated");
899 g.state->font=CWIDTH;
913 switch(g.state->tag){
915 htmlerror(g.name, g.lineno, "can't have <li> in <%s>",
916 tag[g.state->tag].name);
917 case Tag_dir: /* supposed to be multi-columns, can't do! */
923 snprint(buf, sizeof(buf), "%2d ", ++g.state->number);
924 pl_htmloutput(&g, 0, buf, 0);
930 plrtbitmap(&g.dst->text, 100000,
931 g.state->margin+g.state->indent, bullet, 0, 0);
942 htmlerror(g.name, g.lineno, "<%s> deprecated", tag[g.tag].name);
947 g.state->font=CWIDTH;
948 g.state->size=NORMAL;
952 g.state->font=CWIDTH;
953 g.state->size=NORMAL;
956 g.text=dst->title+strlen(dst->title);
958 g.etext=dst->title+NTITLE-1;
974 * ignore the content of these tags, eat tokens until we
975 * reach a matching endtag.
979 switch(pl_gettoken(&g)){
996 * If the end tag doesn't match the top, we try to uncover a match
999 if(g.state->tag!=g.tag){
1001 for(sp=g.state;sp!=g.stack;--sp){
1004 if(tag[g.state->tag].action!=OPTEND) tagerr++;
1008 htmlerror(g.name, g.lineno,
1009 "end tag mismatch <%s>...</%s>, ignored",
1010 tag[g.state->tag].name, tag[g.tag].name);
1014 htmlerror(g.name, g.lineno,
1015 "end tag mismatch <%s>...</%s>, "
1016 "intervening tags popped",
1017 tag[g.state->tag].name, tag[g.tag].name);
1021 else if(g.state==g.stack)
1022 htmlerror(g.name, g.lineno, "end tag </%s> at stack bottom",
1062 if(g.state->link[0]==0 && (str = linkify(g.token))){
1063 strncpy(g.state->link, str, sizeof(g.state->link));
1064 pl_htmloutput(&g, g.nsp, g.token, 0);
1065 g.state->link[0] = 0;
1068 pl_htmloutput(&g, g.nsp, g.token, 0);
1071 for(;g.state!=g.stack;--g.state)
1072 if(tag[g.state->tag].action!=OPTEND)
1073 htmlerror(g.name, g.lineno,
1074 "missing </%s> at EOF", tag[g.state->tag].name);
1077 getpix(dst->text, dst);