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