19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <draw.h>
59a747e4fSDavid du Colombier #include <regexp.h>
69a747e4fSDavid du Colombier #include <html.h>
79a747e4fSDavid du Colombier #include <ctype.h>
89a747e4fSDavid du Colombier #include "dat.h"
99a747e4fSDavid du Colombier
1037a6523bSDavid du Colombier char urlexpr[] =
1137a6523bSDavid du Colombier "^(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero)"
1237a6523bSDavid du Colombier "://([a-zA-Z0-9_@\\-]+([.:][a-zA-Z0-9_@\\-]+)*)";
139a747e4fSDavid du Colombier Reprog *urlprog;
149a747e4fSDavid du Colombier
15*9b7bf7dfSDavid du Colombier int newitextitem;
169a747e4fSDavid du Colombier int inword = 0;
179a747e4fSDavid du Colombier int col = 0;
189a747e4fSDavid du Colombier int wordi = 0;
199a747e4fSDavid du Colombier
209a747e4fSDavid du Colombier char*
loadhtml(int fd)219a747e4fSDavid du Colombier loadhtml(int fd)
229a747e4fSDavid du Colombier {
239a747e4fSDavid du Colombier URLwin *u;
249a747e4fSDavid du Colombier Bytes *b;
259a747e4fSDavid du Colombier int n;
269a747e4fSDavid du Colombier char buf[4096];
279a747e4fSDavid du Colombier
289a747e4fSDavid du Colombier u = emalloc(sizeof(URLwin));
299a747e4fSDavid du Colombier u->infd = fd;
309a747e4fSDavid du Colombier u->outfd = 1;
319a747e4fSDavid du Colombier u->url = estrdup(url);
329a747e4fSDavid du Colombier u->type = TextHtml;
339a747e4fSDavid du Colombier
349a747e4fSDavid du Colombier b = emalloc(sizeof(Bytes));
359a747e4fSDavid du Colombier while((n = read(fd, buf, sizeof buf)) > 0)
369a747e4fSDavid du Colombier growbytes(b, buf, n);
379a747e4fSDavid du Colombier if(b->b == nil)
389a747e4fSDavid du Colombier return nil; /* empty file */
399a747e4fSDavid du Colombier rendertext(u, b);
409a747e4fSDavid du Colombier freeurlwin(u);
419a747e4fSDavid du Colombier return nil;
429a747e4fSDavid du Colombier }
439a747e4fSDavid du Colombier
449a747e4fSDavid du Colombier char*
runetobyte(Rune * r,int n)459a747e4fSDavid du Colombier runetobyte(Rune *r, int n)
469a747e4fSDavid du Colombier {
479a747e4fSDavid du Colombier char *s;
489a747e4fSDavid du Colombier
499a747e4fSDavid du Colombier if(n == 0)
509a747e4fSDavid du Colombier return emalloc(1);
519a747e4fSDavid du Colombier s = smprint("%.*S", n, r);
529a747e4fSDavid du Colombier if(s == nil)
539a747e4fSDavid du Colombier error("malloc failed");
549a747e4fSDavid du Colombier return s;
559a747e4fSDavid du Colombier }
569a747e4fSDavid du Colombier
579a747e4fSDavid du Colombier int
closingpunct(char c)5837a6523bSDavid du Colombier closingpunct(char c)
599a747e4fSDavid du Colombier {
609a747e4fSDavid du Colombier return strchr(".,:;'\")]}>!?", c) != nil;
619a747e4fSDavid du Colombier }
629a747e4fSDavid du Colombier
639a747e4fSDavid du Colombier void
emitword(Bytes * b,Rune * r,int nr)649a747e4fSDavid du Colombier emitword(Bytes *b, Rune *r, int nr)
659a747e4fSDavid du Colombier {
669a747e4fSDavid du Colombier char *s;
679a747e4fSDavid du Colombier int space;
689a747e4fSDavid du Colombier
699a747e4fSDavid du Colombier if(nr == 0)
709a747e4fSDavid du Colombier return;
719a747e4fSDavid du Colombier s = smprint("%.*S", nr, r);
72*9b7bf7dfSDavid du Colombier space = b->n > 0 && !isspace(b->b[b->n-1]) && (!newitextitem || !closingpunct(*s));
739a747e4fSDavid du Colombier if(col > 0 && col+space+nr > width){
749a747e4fSDavid du Colombier growbytes(b, "\n", 1);
759a747e4fSDavid du Colombier space = 0;
769a747e4fSDavid du Colombier col = 0;
779a747e4fSDavid du Colombier }
789a747e4fSDavid du Colombier if(space && col > 0){
799a747e4fSDavid du Colombier growbytes(b, " ", 1);
809a747e4fSDavid du Colombier col++;
819a747e4fSDavid du Colombier }
829a747e4fSDavid du Colombier growbytes(b, s, strlen(s));
839a747e4fSDavid du Colombier col += nr;
849a747e4fSDavid du Colombier free(s);
859a747e4fSDavid du Colombier inword = 0;
86*9b7bf7dfSDavid du Colombier newitextitem = 0;
879a747e4fSDavid du Colombier }
889a747e4fSDavid du Colombier
899a747e4fSDavid du Colombier void
renderrunes(Bytes * b,Rune * r)909a747e4fSDavid du Colombier renderrunes(Bytes *b, Rune *r)
919a747e4fSDavid du Colombier {
929a747e4fSDavid du Colombier int i, n;
939a747e4fSDavid du Colombier
94*9b7bf7dfSDavid du Colombier newitextitem = 1;
95*9b7bf7dfSDavid du Colombier
969a747e4fSDavid du Colombier n = runestrlen(r);
979a747e4fSDavid du Colombier for(i=0; i<n; i++){
989a747e4fSDavid du Colombier switch(r[i]){
999a747e4fSDavid du Colombier case '\n':
1009a747e4fSDavid du Colombier if(inword)
1019a747e4fSDavid du Colombier emitword(b, r+wordi, i-wordi);
1029a747e4fSDavid du Colombier col = 0;
1039a747e4fSDavid du Colombier if(b->n == 0)
1049a747e4fSDavid du Colombier break; /* don't start with blank lines */
1059a747e4fSDavid du Colombier if(b->n<2 || b->b[b->n-1]!='\n' || b->b[b->n-2]!='\n')
1069a747e4fSDavid du Colombier growbytes(b, "\n", 1);
1079a747e4fSDavid du Colombier break;
1089a747e4fSDavid du Colombier case ' ':
1099a747e4fSDavid du Colombier if(inword)
1109a747e4fSDavid du Colombier emitword(b, r+wordi, i-wordi);
1119a747e4fSDavid du Colombier break;
1129a747e4fSDavid du Colombier default:
1139a747e4fSDavid du Colombier if(!inword)
1149a747e4fSDavid du Colombier wordi = i;
1159a747e4fSDavid du Colombier inword = 1;
1169a747e4fSDavid du Colombier break;
1179a747e4fSDavid du Colombier }
1189a747e4fSDavid du Colombier }
1199a747e4fSDavid du Colombier if(inword)
1209a747e4fSDavid du Colombier emitword(b, r+wordi, i-wordi);
1219a747e4fSDavid du Colombier }
1229a747e4fSDavid du Colombier
1239a747e4fSDavid du Colombier void
renderbytes(Bytes * b,char * fmt,...)1249a747e4fSDavid du Colombier renderbytes(Bytes *b, char *fmt, ...)
1259a747e4fSDavid du Colombier {
1269a747e4fSDavid du Colombier Rune *r;
1279a747e4fSDavid du Colombier va_list arg;
1289a747e4fSDavid du Colombier
1299a747e4fSDavid du Colombier va_start(arg, fmt);
1309a747e4fSDavid du Colombier r = runevsmprint(fmt, arg);
1319a747e4fSDavid du Colombier va_end(arg);
1329a747e4fSDavid du Colombier renderrunes(b, r);
1339a747e4fSDavid du Colombier free(r);
1349a747e4fSDavid du Colombier }
1359a747e4fSDavid du Colombier
1369a747e4fSDavid du Colombier char*
baseurl(char * url)1379a747e4fSDavid du Colombier baseurl(char *url)
1389a747e4fSDavid du Colombier {
1399a747e4fSDavid du Colombier char *base, *slash;
1409a747e4fSDavid du Colombier Resub rs[10];
1419a747e4fSDavid du Colombier
1429a747e4fSDavid du Colombier if(url == nil)
1439a747e4fSDavid du Colombier return nil;
1449a747e4fSDavid du Colombier if(urlprog == nil){
1459a747e4fSDavid du Colombier urlprog = regcomp(urlexpr);
1469a747e4fSDavid du Colombier if(urlprog == nil)
1479a747e4fSDavid du Colombier error("can't compile URL regexp");
1489a747e4fSDavid du Colombier }
1499a747e4fSDavid du Colombier memset(rs, 0, sizeof rs);
1509a747e4fSDavid du Colombier if(regexec(urlprog, url, rs, nelem(rs)) == 0)
1519a747e4fSDavid du Colombier return nil;
1529a747e4fSDavid du Colombier base = estrdup(url);
1539a747e4fSDavid du Colombier slash = strrchr(base, '/');
1549a747e4fSDavid du Colombier if(slash!=nil && slash>=&base[rs[0].ep-rs[0].sp])
1559a747e4fSDavid du Colombier *slash = '\0';
1569a747e4fSDavid du Colombier else
1579a747e4fSDavid du Colombier base[rs[0].ep-rs[0].sp] = '\0';
1589a747e4fSDavid du Colombier return base;
1599a747e4fSDavid du Colombier }
1609a747e4fSDavid du Colombier
1619a747e4fSDavid du Colombier char*
fullurl(URLwin * u,Rune * rhref)1629a747e4fSDavid du Colombier fullurl(URLwin *u, Rune *rhref)
1639a747e4fSDavid du Colombier {
1649a747e4fSDavid du Colombier char *base, *href, *hrefbase;
1659a747e4fSDavid du Colombier char *result;
1669a747e4fSDavid du Colombier
1679a747e4fSDavid du Colombier if(rhref == nil)
1689a747e4fSDavid du Colombier return estrdup("NULL URL");
1699a747e4fSDavid du Colombier href = runetobyte(rhref, runestrlen(rhref));
1709a747e4fSDavid du Colombier hrefbase = baseurl(href);
1719a747e4fSDavid du Colombier result = nil;
1729a747e4fSDavid du Colombier if(hrefbase==nil && (base = baseurl(u->url))!=nil){
1739a747e4fSDavid du Colombier result = estrdup(base);
1749a747e4fSDavid du Colombier if(base[strlen(base)-1]!='/' && (href==nil || href[0]!='/'))
1759a747e4fSDavid du Colombier result = eappend(result, "/", "");
1769a747e4fSDavid du Colombier free(base);
1779a747e4fSDavid du Colombier }
1789a747e4fSDavid du Colombier if(href){
1799a747e4fSDavid du Colombier if(result)
1809a747e4fSDavid du Colombier result = eappend(result, "", href);
1819a747e4fSDavid du Colombier else
1829a747e4fSDavid du Colombier result = estrdup(href);
1839a747e4fSDavid du Colombier }
1849a747e4fSDavid du Colombier free(hrefbase);
1859a747e4fSDavid du Colombier if(result == nil)
1869a747e4fSDavid du Colombier return estrdup("***unknown***");
1879a747e4fSDavid du Colombier return result;
1889a747e4fSDavid du Colombier }
1899a747e4fSDavid du Colombier
1909a747e4fSDavid du Colombier void
render(URLwin * u,Bytes * t,Item * items,int curanchor)1919a747e4fSDavid du Colombier render(URLwin *u, Bytes *t, Item *items, int curanchor)
1929a747e4fSDavid du Colombier {
1939a747e4fSDavid du Colombier Item *il;
1949a747e4fSDavid du Colombier Itext *it;
1959a747e4fSDavid du Colombier Ifloat *ifl;
1969a747e4fSDavid du Colombier Ispacer *is;
1979a747e4fSDavid du Colombier Itable *ita;
1989a747e4fSDavid du Colombier Iimage *im;
1999a747e4fSDavid du Colombier Anchor *a;
2009a747e4fSDavid du Colombier Table *tab;
2019a747e4fSDavid du Colombier Tablecell *cell;
2029a747e4fSDavid du Colombier char *href;
2039a747e4fSDavid du Colombier
2049a747e4fSDavid du Colombier inword = 0;
2059a747e4fSDavid du Colombier col = 0;
2069a747e4fSDavid du Colombier wordi = 0;
2079a747e4fSDavid du Colombier
2089a747e4fSDavid du Colombier for(il=items; il!=nil; il=il->next){
2099a747e4fSDavid du Colombier if(il->state & IFbrk)
2109a747e4fSDavid du Colombier renderbytes(t, "\n");
2119a747e4fSDavid du Colombier if(il->state & IFbrksp)
2129a747e4fSDavid du Colombier renderbytes(t, "\n");
2139a747e4fSDavid du Colombier
2149a747e4fSDavid du Colombier switch(il->tag){
2159a747e4fSDavid du Colombier case Itexttag:
2169a747e4fSDavid du Colombier it = (Itext*)il;
217c978705dSDavid du Colombier if(it->state & IFwrap)
2189a747e4fSDavid du Colombier renderrunes(t, it->s);
219*9b7bf7dfSDavid du Colombier else {
220*9b7bf7dfSDavid du Colombier newitextitem = 1;
221c978705dSDavid du Colombier emitword(t, it->s, runestrlen(it->s));
222*9b7bf7dfSDavid du Colombier }
2239a747e4fSDavid du Colombier break;
2249a747e4fSDavid du Colombier case Iruletag:
2259a747e4fSDavid du Colombier if(t->n>0 && t->b[t->n-1]!='\n')
2269a747e4fSDavid du Colombier renderbytes(t, "\n");
2279a747e4fSDavid du Colombier renderbytes(t, "=======\n");
2289a747e4fSDavid du Colombier break;
2299a747e4fSDavid du Colombier case Iimagetag:
2309a747e4fSDavid du Colombier if(!aflag)
2319a747e4fSDavid du Colombier break;
2329a747e4fSDavid du Colombier im = (Iimage*)il;
2339a747e4fSDavid du Colombier if(im->imsrc){
2349a747e4fSDavid du Colombier href = fullurl(u, im->imsrc);
2359a747e4fSDavid du Colombier renderbytes(t, "[image %s]", href);
2369a747e4fSDavid du Colombier free(href);
2379a747e4fSDavid du Colombier }
2389a747e4fSDavid du Colombier break;
2399a747e4fSDavid du Colombier case Iformfieldtag:
2409a747e4fSDavid du Colombier if(aflag)
2419a747e4fSDavid du Colombier renderbytes(t, "[formfield]");
2429a747e4fSDavid du Colombier break;
2439a747e4fSDavid du Colombier case Itabletag:
2449a747e4fSDavid du Colombier ita = (Itable*)il;
2459a747e4fSDavid du Colombier tab = ita->table;
2469a747e4fSDavid du Colombier for(cell=tab->cells; cell!=nil; cell=cell->next){
2479a747e4fSDavid du Colombier render(u, t, cell->content, curanchor);
2489a747e4fSDavid du Colombier }
2499a747e4fSDavid du Colombier if(t->n>0 && t->b[t->n-1]!='\n')
2509a747e4fSDavid du Colombier renderbytes(t, "\n");
2519a747e4fSDavid du Colombier break;
2529a747e4fSDavid du Colombier case Ifloattag:
2539a747e4fSDavid du Colombier ifl = (Ifloat*)il;
2549a747e4fSDavid du Colombier render(u, t, ifl->item, curanchor);
2559a747e4fSDavid du Colombier break;
2569a747e4fSDavid du Colombier case Ispacertag:
2579a747e4fSDavid du Colombier is = (Ispacer*)il;
2589a747e4fSDavid du Colombier if(is->spkind != ISPnull)
2599a747e4fSDavid du Colombier renderbytes(t, " ");
2609a747e4fSDavid du Colombier break;
2619a747e4fSDavid du Colombier default:
2629a747e4fSDavid du Colombier error("unknown item tag %d\n", il->tag);
2639a747e4fSDavid du Colombier }
2649a747e4fSDavid du Colombier if(il->anchorid != 0 && il->anchorid!=curanchor){
2659a747e4fSDavid du Colombier for(a=u->docinfo->anchors; a!=nil; a=a->next)
2669a747e4fSDavid du Colombier if(aflag && a->index == il->anchorid){
2679a747e4fSDavid du Colombier href = fullurl(u, a->href);
2689a747e4fSDavid du Colombier renderbytes(t, "[%s]", href);
2699a747e4fSDavid du Colombier free(href);
2709a747e4fSDavid du Colombier break;
2719a747e4fSDavid du Colombier }
2729a747e4fSDavid du Colombier curanchor = il->anchorid;
2739a747e4fSDavid du Colombier }
2749a747e4fSDavid du Colombier }
2759a747e4fSDavid du Colombier if(t->n>0 && t->b[t->n-1]!='\n')
2769a747e4fSDavid du Colombier renderbytes(t, "\n");
2779a747e4fSDavid du Colombier }
2789a747e4fSDavid du Colombier
2799a747e4fSDavid du Colombier void
rerender(URLwin * u)2809a747e4fSDavid du Colombier rerender(URLwin *u)
2819a747e4fSDavid du Colombier {
2829a747e4fSDavid du Colombier Bytes *t;
2839a747e4fSDavid du Colombier
2849a747e4fSDavid du Colombier t = emalloc(sizeof(Bytes));
2859a747e4fSDavid du Colombier
2869a747e4fSDavid du Colombier render(u, t, u->items, 0);
2879a747e4fSDavid du Colombier
2889a747e4fSDavid du Colombier if(t->n)
2899a747e4fSDavid du Colombier write(u->outfd, (char*)t->b, t->n);
2909a747e4fSDavid du Colombier free(t->b);
2919a747e4fSDavid du Colombier free(t);
2929a747e4fSDavid du Colombier }
2939a747e4fSDavid du Colombier
2949a747e4fSDavid du Colombier /*
2959a747e4fSDavid du Colombier * Somewhat of a hack. Not a full parse, just looks for strings in the beginning
2969a747e4fSDavid du Colombier * of the document (cistrstr only looks at first somewhat bytes).
2979a747e4fSDavid du Colombier */
2989a747e4fSDavid du Colombier int
charset(char * s)2999a747e4fSDavid du Colombier charset(char *s)
3009a747e4fSDavid du Colombier {
3019a747e4fSDavid du Colombier char *meta, *emeta, *charset;
3029a747e4fSDavid du Colombier
303d9306527SDavid du Colombier if(defcharset == 0)
304d9306527SDavid du Colombier defcharset = ISO_8859_1;
3059a747e4fSDavid du Colombier meta = cistrstr(s, "<meta");
3069a747e4fSDavid du Colombier if(meta == nil)
307d9306527SDavid du Colombier return defcharset;
3089a747e4fSDavid du Colombier for(emeta=meta; *emeta!='>' && *emeta!='\0'; emeta++)
3099a747e4fSDavid du Colombier ;
3109a747e4fSDavid du Colombier charset = cistrstr(s, "charset=");
3119a747e4fSDavid du Colombier if(charset == nil)
312d9306527SDavid du Colombier return defcharset;
3139a747e4fSDavid du Colombier charset += 8;
3149a747e4fSDavid du Colombier if(*charset == '"')
3159a747e4fSDavid du Colombier charset++;
3169a747e4fSDavid du Colombier if(cistrncmp(charset, "utf-8", 5) || cistrncmp(charset, "utf8", 4))
3179a747e4fSDavid du Colombier return UTF_8;
318d9306527SDavid du Colombier return defcharset;
3199a747e4fSDavid du Colombier }
3209a747e4fSDavid du Colombier
3219a747e4fSDavid du Colombier void
rendertext(URLwin * u,Bytes * b)3229a747e4fSDavid du Colombier rendertext(URLwin *u, Bytes *b)
3239a747e4fSDavid du Colombier {
3249a747e4fSDavid du Colombier Rune *rurl;
3259a747e4fSDavid du Colombier
3269a747e4fSDavid du Colombier rurl = toStr((uchar*)u->url, strlen(u->url), ISO_8859_1);
3279a747e4fSDavid du Colombier u->items = parsehtml(b->b, b->n, rurl, u->type, charset((char*)b->b), &u->docinfo);
3289a747e4fSDavid du Colombier // free(rurl);
3299a747e4fSDavid du Colombier
3309a747e4fSDavid du Colombier rerender(u);
3319a747e4fSDavid du Colombier }
3329a747e4fSDavid du Colombier
3339a747e4fSDavid du Colombier
3349a747e4fSDavid du Colombier void
freeurlwin(URLwin * u)3359a747e4fSDavid du Colombier freeurlwin(URLwin *u)
3369a747e4fSDavid du Colombier {
3379a747e4fSDavid du Colombier freeitems(u->items);
3389a747e4fSDavid du Colombier u->items = nil;
3399a747e4fSDavid du Colombier freedocinfo(u->docinfo);
3409a747e4fSDavid du Colombier u->docinfo = nil;
3419a747e4fSDavid du Colombier free(u);
3429a747e4fSDavid du Colombier }
343