Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/mothra/rdhtml.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "mothra.h"
#include "html.h"
#include "rtext.h"
typedef struct Fontdata Fontdata;
struct Fontdata{
	char *name;
	Font *font;
	int space;
}fontlist[4][4]={
	"lucidasans/unicode.7", 0, 0,
	"lucidasans/unicode.8", 0, 0,
	"lucidasans/unicode.10", 0, 0,
	"lucidasans/unicode.13", 0, 0,

	"lucidasans/italicunicode.7", 0, 0,
	"lucidasans/italicunicode.8", 0, 0,
	"lucidasans/italicunicode.10", 0, 0,
	"lucidasans/italicunicode.13", 0, 0,

	"lucidasans/boldunicode.7", 0, 0,
	"lucidasans/boldunicode.8", 0, 0,
	"lucidasans/boldunicode.10", 0, 0,
	"lucidasans/boldunicode.13", 0, 0,

	"lucidasans/typeunicode.7", 0, 0,
	"pelm/unicode.8", 0, 0,
	"lucidasans/typeunicode.12", 0, 0,
	"lucidasans/typeunicode.16", 0, 0,
};
Fontdata *pl_whichfont(int f, int s){
	char name[100];
	if(fontlist[f][s].font==0){
		sprint(name, "/lib/font/bit/%s.font",
			fontlist[f][s].name);
		fontlist[f][s].font=openfont(display, name);
		if(fontlist[f][s].font==0) fontlist[f][s].font=font;
		fontlist[f][s].space=stringwidth(fontlist[f][s].font, "0");
	}
	return &fontlist[f][s];
	
}
void getfonts(void){
	int f, s;
	for(f=0;f!=4;f++)
		for(s=0;s!=4;s++)
			pl_whichfont(f, s);
}
void pl_pushstate(Hglob *g, int t){
	++g->state;
	if(g->state==&g->stack[NSTACK]){
		htmlerror(g->name, g->lineno, "stack overflow at <%s>", tag[t].name);
		--g->state;
	}
	g->state[0]=g->state[-1];
	g->state->tag=t;
}
void pl_linespace(Hglob *g){
	plrtbitmap(&g->dst->text, 1000000, 0, linespace, 0, 0);
	g->para=0;
	g->linebrk=0;
}
void pl_htmloutput(Hglob *g, int nsp, char *s, Field *field){
	Fontdata *f;
	int space, indent;
	Action *ap;
	if(g->state->tag==Tag_title
/*	|| g->state->tag==Tag_textarea */
	|| g->state->tag==Tag_select){
		if(s){
			if(g->tp!=g->text && g->tp!=g->etext && g->tp[-1]!=' ')
				*g->tp++=' ';
			while(g->tp!=g->etext && *s) *g->tp++=*s++;
			if(g->state->tag==Tag_title) g->dst->changed=1;
			*g->tp='\0';
		}
		return;
	}
	f=pl_whichfont(g->state->font, g->state->size);
	space=f->space;
	indent=g->state->margin;
	if(g->para){
		space=1000000;
		indent+=g->state->indent;
	}
	else if(g->linebrk)
		space=1000000;
	else if(nsp<=0)
		space=0;
	if(g->state->image==0 && g->state->link==0 && g->state->name==0 && field==0)
		ap=0;
	else{
		ap=malloc(sizeof(Action));
		if(ap!=0){
			ap->image=g->state->image;
			ap->link=g->state->link;
			ap->name=g->state->name;
			ap->ismap=g->state->ismap;
			ap->imagebits=0;
			ap->field=field;
		}
	}
	if(space<0) space=0;
	if(indent<0) indent=0;
	if(g->state->pre && s[0]=='\t'){
		space=0;
		while(s[0]=='\t'){
			space++;
			s++;
		}
		space=PL_TAB|space;
		if(g->linebrk){
			indent=space;
			space=1000000;
		}
	}
	plrtstr(&g->dst->text, space, indent, f->font, strdup(s), g->state->link!=0, ap);
	g->para=0;
	g->linebrk=0;
	g->dst->changed=1;
}
/*
 * Buffered read, no translation
 * Save in cache.
 */
int pl_bread(Hglob *g){
	int n, c;
	char err[1024];
	if(g->hbufp==g->ehbuf){
		n=read(g->hfd, g->hbuf, NHBUF);
		if(n<=0){
			if(n<0){
				sprint(err, "%r reading %s", g->name);
				pl_htmloutput(g, 1, err, 0);
			}
			g->heof=1;
			return EOF;
		}
		g->hbufp=g->hbuf;
		g->ehbuf=g->hbuf+n;
	}
	c=*g->hbufp++&255;
	if(c=='\n') g->lineno++;
	return c;
}
/*
 * Read a character, translating \r\n, \n\r, \r and \n into \n
 */
int pl_readc(Hglob *g){
	int c;
	static int peek=-1;
	if(peek!=-1){
		c=peek;
		peek=-1;
	}
	else
		c=pl_bread(g);
	if(c=='\r'){
		c=pl_bread(g);
		if(c!='\n') peek=c;
		return '\n';
	}
	if(c=='\n'){
		c=pl_bread(g);
		if(c!='\r') peek=c;
		return '\n';
	}
	return c;
}
void pl_putback(Hglob *g, int c){
	if(g->npeekc==NPEEKC) htmlerror(g->name, g->lineno, "too much putback!");
	else if(c!=EOF) g->peekc[g->npeekc++]=c;
}
static Rune ms1252[32] = {
	0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
	0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0xFFFD, 0x017D, 0xFFFD,
	0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
	0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0xFFFD, 0x017E, 0x0178,
};
int pl_nextc(Hglob *g){
	int c;
	int n;
	Rune r;
	char crune[4];
	if(g->heof) return EOF;
	if(g->npeekc!=0) return g->peekc[--g->npeekc];
	c=pl_readc(g);
	if(c=='<'){
		c=pl_readc(g);
		if(c=='/'){
			c=pl_readc(g);
			pl_putback(g, c);
			pl_putback(g, '/');
			if('a'<=c && c<='z' || 'A'<=c && c<='Z') return STAG;
			return '<';
		}
		pl_putback(g, c);
		if(c=='!' || 'a'<=c && c<='z' || 'A'<=c && c<='Z' || c=='?') return STAG;
		return '<';
	}
	if(c=='>') return ETAG;
	if(c==EOF) return c;
	if(c&0x80){
		if(g->isutf){
			n=0;
			for (;;){
				crune[n++]=c;
				if(fullrune(crune, n)){
					chartorune(&r, crune);
					return r;
				}
				c=pl_readc(g);
				if(c==EOF)
					return EOF;
			}
		} else if(c < 0xA0)
			return ms1252[c-0x80];
	}
	return c;
}
int entchar(int c){
	return c=='#' || 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9';
}
Entity *entsearch(char *s){
	int i, m, n, r;
	i=0;
	n=pl_entities;
	while ((n-i) > 0) {
		m=i+(n-i)/2;
		r=strcmp(s, pl_entity[m].name);
		if (r > 0)
			i=m+1;
		else if (r < 0)
			n=m;
		else
			return &pl_entity[m];
	}
	return 0;
}
/*
 * remove entity references, in place.
 * Potential bug:
 *	This doesn't work if removing an entity reference can lengthen the string!
 *	Fortunately, this doesn't happen.
 */
void pl_rmentities(Hglob *g, char *s){
	char *t, *u, c, svc;
	Entity *ep;
	Rune r;
	t=s;
	do{
		c=*s++;
		if(c=='&'
		&& ((*s=='#' && strchr("0123456789Xx", s[1]))
		  || 'a'<=*s && *s<='z'
		  || 'A'<=*s && *s<='Z')){
			u=s;
			while(entchar(*s)) s++;
			svc=*s;
			if(svc!=';')
				htmlerror(g->name, g->lineno, "entity syntax error");
			*s++='\0';
			if(*u=='#'){
				if (u[1]=='X' || u[1]=='x')
					r=strtol(u+2, 0, 16);
				else
					r=atoi(u+1);
				if(r==0xa0) r=' ';
				else if(0x80<=r && r<0xa0) r=ms1252[r-0x80];
				t+=runetochar(t, &r);
				if(svc!=';') *--s=svc;
			}
			else{
				ep=entsearch(u);
				if(ep && ep->name){
					t+=runetochar(t, &ep->value);
					if(svc!=';') *--s=svc;
				}
				else{
					htmlerror(g->name, g->lineno,
						"unknown entity %s", u);
					s[-1]=svc;
					s=u;
					*t++='&';
				}
			}
		}	
		else *t++=c;
	}while(c);
}
/*
 * Skip over white space
 */
char *pl_white(char *s){
	while(*s==' ' || *s=='\t' || *s=='\n' || *s=='\r') s++;
	return s;
}
/*
 * Skip over HTML word
 */
char *pl_word(char *s){
	if ('a'<=*s && *s<='z' || 'A'<=*s && *s<='Z') {
		s++;
		while('a'<=*s && *s<='z' || 'A'<=*s && *s<='Z' || '0'<=*s && *s<='9' || *s=='-' || *s=='.') s++;
	}
	return s;
}
/*
 * Skip to matching quote
 */
char *pl_quote(char *s){
	char q;
	q=*s++;
	while(*s!=q && *s!='\0') s++;
	return s;
}
void pl_dnl(char *s){
	char *t;
	for(t=s;*s;s++) if(*s!='\r' && *s!='\n') *t++=*s;
	*t='\0';
}
void pl_tagparse(Hglob *g, char *str){
	char *s, *t, *name, c;
	Pair *ap;
	Tag *tagp;
	g->tag=Tag_end;
	ap=g->attr;
	if(str[0]=='!'){	/* test should be strncmp(str, "!--", 3)==0 */
		g->tag=Tag_comment;
		ap->name=0;
		return;
	}
	if(str[0]=='/') str++;
	name=str;
	s=pl_word(str);
	if(*s!=' ' && *s!='\n' && *s!='\t' && *s!='\0'){
		htmlerror(g->name, g->lineno, "bad tag name in %s", str);
		ap->name=0;
		return;
	}
	if(*s!='\0') *s++='\0';
	for(t=name;t!=s;t++) if('A'<=*t && *t<='Z') *t+='a'-'A';
	/*
	 * Binary search would be faster here
	 */
	for(tagp=tag;tagp->name;tagp++) if(strcmp(name, tagp->name)==0) break;
	g->tag=tagp-tag;
	if(g->tag==Tag_end) htmlerror(g->name, g->lineno, "no tag %s", name);
	for(;;){
		s=pl_white(s);
		if(*s=='\0'){
			ap->name=0;
			return;
		}
		ap->name=s;
		s=pl_word(s);
		t=pl_white(s);
		c=*t;
		*s='\0';
		for(s=ap->name;*s;s++) if('A'<=*s && *s<='Z') *s+='a'-'A';
		if(c=='='){
			s=pl_white(t+1);
			if(*s=='\'' || *s=='"'){
				ap->value=s+1;
				s=pl_quote(s);
				if(*s=='\0'){
					htmlerror(g->name, g->lineno,
						"No terminating quote in rhs of attribute %s",
						ap->name);
					ap->name=0;
					return;
				}
				*s++='\0';
				pl_dnl(ap->value);
			}
			else{
				/* read up to white space or > */
				ap->value=s;
				while(*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0') s++;
				if(*s!='\0') *s++='\0';
			}
			pl_rmentities(g, ap->value);
		}
		else{
			if(c!='\0') s++;
			ap->value="";
		}
		if(ap==&g->attr[NATTR-1])
			htmlerror(g->name, g->lineno, "too many attributes!");
		else ap++;
	}
}
int pl_getcomment(Hglob *g){
	int c;
	if((c=pl_nextc(g))=='-' && (c=pl_nextc(g))=='-'){
		/* <!-- eats everything until --> or EOF */
		for(;;){
			while((c=pl_nextc(g))!='-' && c!=EOF)
				;
			if(c==EOF)
				break;
			if((c=pl_nextc(g))=='-'){
				while((c=pl_nextc(g))=='-')
					;
				if(c==ETAG || c==EOF)
					break;
			}
		}
	} else {
		/* <! eats everything until > or EOF */
		while(c!=ETAG && c!=EOF)
			c=pl_nextc(g);
	}
	if(c==EOF)
		htmlerror(g->name, g->lineno, "EOF in comment");
	g->tag=Tag_comment;
	g->attr->name=0;
	g->token[0]='\0';
	return TAG;
}
int lrunetochar(char *p, int v)
{
	Rune r;

	r=v;
	return runetochar(p, &r);
}

/*
 * Read a start or end tag -- the caller has read the initial <
 */
int pl_gettag(Hglob *g){
	char *tokp;
	int c;
	tokp=g->token;
	if((c=pl_nextc(g))=='!' || c=='?')
		return pl_getcomment(g);
	pl_putback(g, c);
	while((c=pl_nextc(g))!=ETAG && c!=EOF)
		if(tokp!=&g->token[NTOKEN-3]) tokp += lrunetochar(tokp, c);
	*tokp='\0';
	if(c==EOF) htmlerror(g->name, g->lineno, "EOF in tag");
	pl_tagparse(g, g->token);
	if(g->token[0]!='/') return TAG;
	if(g->attr[0].name!=0)
		htmlerror(g->name, g->lineno, "end tag should not have attributes");
	return ENDTAG;
}
/*
 * The next token is a tag, an end tag or a sequence of
 * non-white characters.
 * If inside <pre>, newlines are converted to <br> and spaces are preserved.
 * Otherwise, spaces and newlines are noted and discarded.
 */
int pl_gettoken(Hglob *g){
	char *tokp;
	int c;
	if(g->state->pre) switch(c=pl_nextc(g)){
	case STAG: return pl_gettag(g);
	case EOF: return EOF;
	case '\n':
		pl_tagparse(g, "br");
		return TAG;
	default:
		tokp=g->token;
		while(c=='\t'){
			if(tokp!=&g->token[NTOKEN-3]) tokp += lrunetochar(tokp, c);
			c=pl_nextc(g);
		}
		while(c!='\t' && c!='\n' && c!=STAG && c!=EOF){
			if(c==ETAG) c='>';
			if(tokp!=&g->token[NTOKEN-3]) tokp += lrunetochar(tokp, c);
			c=pl_nextc(g);
		}
		*tokp='\0';
		pl_rmentities(g, g->token);
		pl_putback(g, c);
		g->nsp=0;
		g->spacc=0;
		return TEXT;
	}
	while((c=pl_nextc(g))==' ' || c=='\t' || c=='\n')
		if(g->spacc!=-1)
			g->spacc++;
	switch(c){
	case STAG: return pl_gettag(g);
	case EOF: return EOF;
	default:
		tokp=g->token;
		do{
			if(c==ETAG) c='>';
			if(tokp!=&g->token[NTOKEN-3]) tokp += lrunetochar(tokp, c);
			c=pl_nextc(g);
		}while(c!=' ' && c!='\t' && c!='\n' && c!=STAG && c!=EOF);
		*tokp='\0';
		pl_rmentities(g, g->token);
		pl_putback(g, c);
		g->nsp=g->spacc;
		g->spacc=0;
		return TEXT;
	}
}
char *pl_getattr(Pair *attr, char *name){
	for(;attr->name;attr++)
		if(strcmp(attr->name, name)==0)
			return attr->value;
	return 0;
}
int pl_hasattr(Pair *attr, char *name){
	for(;attr->name;attr++)
		if(strcmp(attr->name, name)==0)
			return 1;
	return 0;
}
#define	NLINE	256
void plaintext(Hglob *g){
	char line[NLINE];
	char *lp, *elp;
	int c;
	g->state->font=CWIDTH;
	g->state->size=NORMAL;
	elp=&line[NLINE+1];
	lp=line;
	for(;;){
		c=pl_readc(g);
		if(c==EOF) break;
		if(c=='\n' || lp==elp){
			*lp='\0';
			g->linebrk=1;
			pl_htmloutput(g, 0, line, 0);
			lp=line;
		}
		if(c=='\t'){
			do *lp++=' '; while(lp!=elp && utfnlen(line, lp-line)%8!=0);
		}
		else if(c!='\n')
			lp += lrunetochar(lp, c);
	}
	if(lp!=line){
		*lp='\0';
		g->linebrk=1;
		pl_htmloutput(g, 0, line, 0);
	}
}
void plrdplain(char *name, int fd, Www *dst){
	Hglob g;
	g.state=g.stack;
	g.state->tag=Tag_html;
	g.state->font=CWIDTH;
	g.state->size=NORMAL;
	g.state->pre=0;
	g.state->image=0;
	g.state->link=0;
	g.state->name=0;
	g.state->margin=0;
	g.state->indent=20;
	g.state->ismap=0;
	g.state->table=0;
	g.dst=dst;
	g.hfd=fd;
	g.name=name;
	g.ehbuf=g.hbufp=g.hbuf;
	g.npeekc=0;
	g.heof=0;
	g.lineno=1;
	g.linebrk=1;
	g.para=0;
	g.text=dst->title;
	g.tp=g.text;
	g.etext=g.text+NTITLE-1;
	g.spacc=0;
	g.form=0;
	strncpy(g.text, name, NTITLE);
	plaintext(&g);
	dst->finished=1;
}
void plrdhtml(char *name, int fd, Www *dst){
	Stack *sp;
	char buf[20];
	char *str;
	Hglob g;
	int tagerr;
	g.state=g.stack;
	g.state->tag=Tag_html;
	g.state->font=ROMAN;
	g.state->size=NORMAL;
	g.state->pre=0;
	g.state->image=0;
	g.state->link=0;
	g.state->name=0;
	g.state->margin=0;
	g.state->indent=25;
	g.state->ismap=0;
	g.state->table=0;
	g.dst=dst;
	g.hfd=fd;
	g.name=name;
	g.ehbuf=g.hbufp=g.hbuf;
	g.npeekc=0;
	g.heof=0;
	g.lineno=1;
	g.linebrk=1;
	g.para=0;
	g.text=dst->title;
	g.tp=g.text;
	g.etext=g.text+NTITLE-1;
	dst->title[0]='\0';
	dst->base=dst->url;
	g.spacc=0;
	g.form=0;
	g.isutf=0;
	for(;;) switch(pl_gettoken(&g)){
	case TAG:
		switch(tag[g.tag].action){
		case OPTEND:
			for(sp=g.state;sp!=g.stack && sp->tag!=g.tag;--sp);
			if(sp->tag!=g.tag)
				pl_pushstate(&g, g.tag);
			else
				for(;g.state!=sp;--g.state)
					if(tag[g.state->tag].action!=OPTEND)
						htmlerror(g.name, g.lineno,
							"end tag </%s> missing",
							tag[g.state->tag].name);
			break;
		case END:
			pl_pushstate(&g, g.tag);
			break;
		}
		switch(g.tag){
		default:
			htmlerror(g.name, g.lineno,
				"unimplemented tag <%s>", tag[g.tag].name);
			break;
		case Tag_end:	/* unrecognized start tag */
			break;
		case Tag_meta:
			if((str=pl_getattr(g.attr, "http-equiv")) &&
			   (strcmp(str, "content-type"))==0 &&
			   (str=pl_getattr(g.attr, "content")) &&
			   (str=strstr(str, "charset="))){
				str+=8;
				if(cistrcmp(str, "utf-8")==0)
					g.isutf=1;
			}
			break;
		case Tag_img:
			str=pl_getattr(g.attr, "src");
			if(str) g.state->image=strdup(str);
			g.state->ismap=pl_hasattr(g.attr, "ismap");
			str=pl_getattr(g.attr, "alt");
			if(str==0){
				if(g.state->image)
					str=strdup(g.state->image);
				else
					str="[[image]]";
			}
			pl_htmloutput(&g, 0, str, 0);
			g.state->image=0;
			g.state->ismap=0;
			break;
		case Tag_plaintext:
			g.spacc=0;
			plaintext(&g);
			break;
		case Tag_comment:
		case Tag_html:
		case Tag_link:
		case Tag_nextid:
			break;
		case Tag_table:
			g.state->table++;
			break;
		case Tag_tr:
			if(g.state->table==1){
				g.spacc=0;
				g.linebrk=1;
			} else
				g.spacc++;
			break;
		case Tag_td:
			g.spacc++;
			break;
		case Tag_a:
			str=pl_getattr(g.attr, "href");
			if(str)
				g.state->link=strdup(str);
			str=pl_getattr(g.attr, "name");
			if(str){
				g.state->name=strdup(str);
				pl_htmloutput(&g, 0, "", 0);
			}
			break;
		case Tag_frame:
			pl_htmloutput(&g, 0, "FRAME: ", 0);
			str=pl_getattr(g.attr, "src");
			if(str)
				g.state->link=strdup(str);
			str=pl_getattr(g.attr, "name");
			if(str)
				g.state->name=strdup(str);
			else
				str = g.state->link;
			pl_htmloutput(&g, 0, str, 0);
			g.state->link= 0;
			g.state->name = 0;
			g.spacc=0;
			g.linebrk=1;
			break;
		case Tag_address:
			g.spacc=0;
			g.linebrk=1;
			g.state->font=ROMAN;
			g.state->size=NORMAL;
			g.state->margin=300;
			g.state->indent=50;
			break;
		case Tag_b:
		case Tag_strong:
			g.state->font=BOLD;
			break;
		case Tag_base:
			str=pl_getattr(g.attr, "href");
			if(str){
				dst->base=emalloc(sizeof(Url));
				crackurl(dst->base, str, dst->url);
			}
			break;
		case Tag_blockquot:
			g.spacc=0;
			g.linebrk=1;
			g.state->margin+=50;
			g.state->indent=20;
			break;
		case Tag_head:
		case Tag_body:
			g.state->font=ROMAN;
			g.state->size=NORMAL;
			g.state->margin=0;
			g.state->indent=20;
			g.spacc=0;
			break;
		case Tag_br:
			g.spacc=0;
			g.linebrk=1;
			break;
		case Tag_center:
			/* more to come */
			break;
		case Tag_cite:
			g.state->font=ITALIC;
			g.state->size=NORMAL;
			break;
		case Tag_code:
			g.state->font=CWIDTH;
			g.state->size=NORMAL;
			break;
		case Tag_dd:
			g.linebrk=1;
			g.state->indent=0;
			g.state->font=ROMAN;
			g.spacc=0;
			break;
		case Tag_dfn:
			htmlerror(g.name, g.lineno, "<dfn> deprecated");
			g.state->font=BOLD;
			g.state->size=NORMAL;
			break;
		case Tag_dl:
			g.state->font=BOLD;
			g.state->size=NORMAL;
			g.state->margin+=40;
			g.spacc=0;
			break;
		case Tag_dt:
			g.para=1;
			g.state->indent=-40;
			g.state->font=BOLD;
			g.spacc=0;
			break;
		case Tag_font:
			/* more to come */
			break;
		case Tag_u:
			htmlerror(g.name, g.lineno, "<u> deprecated");
		case Tag_em:
		case Tag_i:
		case Tag_var:
			g.state->font=ITALIC;
			break;
		case Tag_h1:
			g.linebrk=1;
			g.state->font=BOLD;
			g.state->size=ENORMOUS;
			g.state->margin+=100;
			g.spacc=0;
			break;
		case Tag_h2:
			pl_linespace(&g);
			g.state->font=BOLD;
			g.state->size=ENORMOUS;
			g.spacc=0;
			break;
		case Tag_h3:
			g.linebrk=1;
			pl_linespace(&g);
			g.state->font=ITALIC;
			g.state->size=ENORMOUS;
			g.state->margin+=20;
			g.spacc=0;
			break;
		case Tag_h4:
			pl_linespace(&g);
			g.state->font=BOLD;
			g.state->size=LARGE;
			g.state->margin+=10;
			g.spacc=0;
			break;
		case Tag_h5:
			pl_linespace(&g);
			g.state->font=ITALIC;
			g.state->size=LARGE;
			g.state->margin+=10;
			g.spacc=0;
			break;
		case Tag_h6:
			pl_linespace(&g);
			g.state->font=BOLD;
			g.state->size=LARGE;
			g.spacc=0;
			break;
		case Tag_hr:
			g.spacc=0;
			plrtbitmap(&g.dst->text, 1000000, g.state->margin, hrule, 0, 0);
			break;
		case Tag_key:
			htmlerror(g.name, g.lineno, "<key> deprecated");
		case Tag_kbd:
			g.state->font=CWIDTH;
			break;
		case Tag_dir:
		case Tag_menu:
		case Tag_ol:
		case Tag_ul:
			g.state->number=0;
			g.linebrk=1;
			g.state->margin+=25;
			g.state->indent=-25;
			g.spacc=0;
			break;
		case Tag_li:
			g.spacc=0;
			switch(g.state->tag){
			default:
				htmlerror(g.name, g.lineno, "can't have <li> in <%s>",
					tag[g.state->tag].name);
			case Tag_dir:	/* supposed to be multi-columns, can't do! */
			case Tag_menu:
				g.linebrk=1;
				break;
			case Tag_ol:
				g.para=1;
				sprint(buf, "%2d  ", ++g.state->number);
				pl_htmloutput(&g, 0, buf, 0);
				break;
			case Tag_ul:
				g.para=0;
				g.linebrk=0;
				g.spacc=-1;
				plrtbitmap(&g.dst->text, 100000,
					g.state->margin+g.state->indent, bullet, 0, 0);
				break;
			}
			break;
		case Tag_p:
			g.para=1;
			g.spacc=0;
			break;
		case Tag_listing:
		case Tag_xmp:
			htmlerror(g.name, g.lineno, "<%s> deprecated", tag[g.tag].name);
		case Tag_pre:
		case Tag_samp:
			g.state->indent=0;
			g.state->pre=1;
			g.state->font=CWIDTH;
			g.state->size=NORMAL;
			pl_linespace(&g);
			break;
		case Tag_tt:
			g.state->font=CWIDTH;
			g.state->size=NORMAL;
			break;
		case Tag_title:
			g.text=dst->title+strlen(dst->title);
			g.tp=g.text;
			g.etext=dst->title+NTITLE-1;
/*			if(g.text>g.etext) g.text=g.etext;	/* superfluous? */
			break;
		case Tag_form:
		case Tag_input:
		case Tag_select:
		case Tag_option:
		case Tag_textarea:
		case Tag_isindex:
			rdform(&g);
			break;
		}
		break;
	case ENDTAG:
		/*
		 * If the end tag doesn't match the top, we try to uncover a match
		 * on the stack.
		 */
		if(g.state->tag!=g.tag){
			tagerr=0;
			for(sp=g.state;sp!=g.stack;--sp){
				if(sp->tag==g.tag)
					break;
				if(tag[g.state->tag].action!=OPTEND) tagerr++;
			}
			if(sp==g.stack){
				if(tagerr)
					htmlerror(g.name, g.lineno,
						"end tag mismatch <%s>...</%s>, ignored",
						tag[g.state->tag].name, tag[g.tag].name);
			}
			else{
				if(tagerr)
					htmlerror(g.name, g.lineno,
						"end tag mismatch <%s>...</%s>, "
						"intervening tags popped",
						tag[g.state->tag].name, tag[g.tag].name);
				g.state=sp-1;
			}
		}
		else if(g.state==g.stack)
			htmlerror(g.name, g.lineno, "end tag </%s> at stack bottom",
				tag[g.tag].name);
		else
			--g.state;
		switch(g.tag){
		case Tag_select:
		case Tag_form:
		case Tag_textarea:
			endform(&g);
			break;
		case Tag_h1:
		case Tag_h2:
		case Tag_h3:
		case Tag_h4:
			pl_linespace(&g);
			break;
		case Tag_address:
		case Tag_blockquot:
		case Tag_body:
		case Tag_dir:
		case Tag_dl:
		case Tag_dt:
		case Tag_h5:
		case Tag_h6:
		case Tag_listing:
		case Tag_menu:
		case Tag_ol:
		case Tag_samp:
		case Tag_title:
		case Tag_ul:
		case Tag_xmp:
			g.linebrk=1;
			break;
		case Tag_table:
			if(g.state->table==0)
				g.linebrk=1;
			break;
		case Tag_pre:
			pl_linespace(&g);
			break;
		}
		break;
	case TEXT:
		pl_htmloutput(&g, g.nsp, g.token, 0);
		break;
	case EOF:
		for(;g.state!=g.stack;--g.state)
			if(tag[g.state->tag].action!=OPTEND)
				htmlerror(g.name, g.lineno,
					"missing </%s> at EOF", tag[g.state->tag].name);
		*g.tp='\0';
		dst->changed=1;
		getpix(dst->text, dst);
		dst->finished=1;
		return;
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.