#include #include #include #include #include int br, wrap, nfn, nallocfn = 64, width = 70; char *defcharset, **fn; Rune *url, *l; Biobuf out; Anchor *curanchor; void* emalloc(ulong n){ void *p; p = malloc(n); if(p == nil) sysfatal("malloc: %r"); memset(p, 0, n); return p; } void* erealloc(void *p, ulong n){ p = realloc(p, n); if(p == nil) sysfatal("realloc: %r"); return p; } void spew(Rune *s){ static int off; Rune *sp; int n; if(br & IFbrk){ Bprint(&out, "%.*S\n", off, l); if(br & IFbrksp) Bputc(&out, '\n'); br = off = 0; } if(s == nil || *s == 0) return; if(!wrap){ Bprint(&out, "%S", s); return; } for(;;){ n = runestrlen(s); if(off + n <= width){ runestrncpy(l + off, s, n); off += n; return; } n = width - off; runestrncpy(l + off, s, n); s += n; sp = runestrrchr(l, ' '); if(sp == nil){ /*long line */ off = 0; Bprint(&out, "%.*S", width, l); sp = runestrchr(s, ' '); if(sp == nil){ Bprint(&out, "%S\n", s); return; } Bprint(&out, "%.*S\n", (int)(sp - s), s); s = sp + 1; continue; } n = sp - l; Bprint(&out, "%.*S\n", n, l); off = width - n - 1; runestrncpy(l, sp + 1, off); } } void addfn(Rune *ref){ static Rune *a = L"⁰¹²³⁴⁵⁶⁷⁸⁹"; Rune *num, *r; num = runesmprint("%d", nfn + 1); if(num == nil) sysfatal("smprint: %r"); for(r = num; *r; r++) *r = a[*r - '0']; if(nfn >= nallocfn) fn = erealloc(fn, (nallocfn <<= 1) * sizeof(char*)); fn[nfn] = smprint("%S %S\n", num, ref); if(fn[nfn] == nil) sysfatal("smprint: %r"); spew(num); free(num); nfn++; } void render(Item *item){ static Rune *ftype[] = { L"§ input: ", L"§ password: ", L"□ ", L"○ ", L"[submit] ", L"§ hidden: ", L"§ image: ", L"§ reset: ", L"§ file: ", L"§ button: ", L"§ dropdown: ", L"§ textarea: " }; Itext *txt; Iimage *img; Area *area; Formfield *field; Option *opt; Table *table; Tablecell *cell; Item *subitem; br |= item->state; if(wrap ^ item->state & IFwrap){ br |= IFbrk; wrap = item->state & IFwrap; } if(item->anchor != curanchor){ if(curanchor != nil) addfn(curanchor->href); curanchor = item->anchor; return; } switch(item->tag){ case Itexttag: txt = (Itext*)item; if(txt->ul == ULmid){ spew(L""); spew(txt->s); spew(L""); }else spew(txt->s); if(item->state & IFhangmask) /* list marker */ spew(L" "); break; case Iruletag: br = IFbrk | IFbrksp; spew(nil); Bprint(&out, " %0*d ", width - 2, 0); /* hahaha */ br = IFbrk | IFbrksp; break; case Iimagetag: img = (Iimage*)item; if(img->map != nil){ if(curanchor != nil){ addfn(curanchor->href); curanchor = nil; } br = IFbrk | IFbrksp; spew(L"§imagemap:"); br = IFbrk; } fimage: spew(L"{"); spew(img->altrep); addfn(img->imsrc); spew(L"}"); if(img->map == nil) break; for(area = img->map->areas; area != nil; area = area->next){ br = IFbrk; spew(area->anchor->name); addfn(area->anchor->href); } br = IFbrk | IFbrksp; break; case Iformfieldtag: br |= IFbrk; field = ((Iformfield*)item)->formfield; spew(ftype[field->ftype]); spew(field->name); spew(L": "); spew(field->value); if(field->ftype == Fimage){ img = (Iimage*)field->image; goto fimage; } br = IFbrk; if(field->ftype == Fselect){ spew(nil); for(opt = field->options; opt != nil; opt = opt->next) Bprint(&out, "→ %S (%S)\n", opt->display, opt->value); } break; case Itabletag: table = ((Itable*)item)->table; if(table->caption_place == ALtop) spew(table->caption); br |= IFbrk; cell = table->cells; while(cell != nil){ for(subitem = cell->content; subitem != nil; subitem = subitem->next) render(subitem); if(cell->nextinrow == nil){ br |= IFbrk; cell = cell->next; continue; } spew(L"\t"); cell = cell->nextinrow; } br |= IFbrk; if(table->caption_place == ALbottom) spew(table->caption); break; case Ifloattag: for(subitem = ((Ifloat*)item)->item; subitem != nil; subitem = subitem->next) render(subitem); break; case Ispacertag: switch(((Ispacer*)item)->spkind){ case ISPvline: br = IFbrk | IFbrksp; break; case ISPhspace: case ISPgeneral: default: if(!(br & IFbrk)) spew(L" "); case ISPnull: break; } break; default: fprint(2, "unknown item tag %d\n", item->tag); } } void frames(Kidinfo *k){ while(k != nil){ if(k->isframeset){ Bprint(&out, "\nFrameset: %S", k->name); frames(k->kidinfos); } else Bprint(&out, "\nFrame: %S: %S", k->name, k->src); k = k->next; } } void loadhtml(int fd){ Docinfo *d; Item *items, *i; uchar *buf; long n, nbuf, nalloc; int p[2]; if(pipe(p) < 0) sysfatal("pipe: %r"); switch(fork()){ case -1: sysfatal("fork: %r"); case 0: dup(fd, 0); dup(p[1], 1); close(p[1]); close(p[0]); if(defcharset) execl("/bin/uhtml", "uhtml", "-c", defcharset, nil); execl("/bin/uhtml", "uhtml", nil); execl("/bin/cat", "cat", nil); sysfatal("execl: %r"); } close(p[1]); buf = erealloc(nil, nalloc = 8192); nbuf = 0; while(n = read(p[0], buf + nbuf, nalloc - nbuf)) if((nbuf += n) > nalloc / 2) buf = erealloc(buf, nalloc <<= 1); close(p[0]); close(fd); if(nbuf == 0){ free(buf); return; } buf = erealloc(buf, nbuf); items = parsehtml(buf, nbuf, url, TextHtml, UTF_8, &d); free(buf); Bprint(&out, "%S", d->doctitle); frames(d->kidinfo); br = IFbrk | IFbrksp; for(i = items; i != nil; i = i->next) render(i); if(curanchor != nil) addfn(curanchor->href); br = IFbrk; spew(nil); freeitems(items); freedocinfo(d); } void usage(void){ fprint(2, "usage: %s [-c charset] [-u URL] [-l length] [file ...]\n", argv0); exits("usage"); } void main(int argc, char *argv[]){ int i, fd; char *s; ARGBEGIN{ case 'c': defcharset = EARGF(usage()); break; case 'l': case 'w': s = EARGF(usage()); width = atoi(s); if(width < 1) usage(); break; case 'u': s = EARGF(usage()); free(url); url = emalloc((utflen(s) + 1) * sizeof(Rune)); for(i = 0; *s; i++) s += chartorune(url + i, s); break; default: usage(); }ARGEND Binit(&out, 1, OWRITE); l = emalloc(width * sizeof(Rune)); fn = erealloc(nil, nallocfn * sizeof(char*)); s = nil; if(argc == 0) loadhtml(0); else for(i = 0; i < argc; i++){ fd = open(argv[i], OREAD); if(fd < 0){ fprint(2, "skipping %s: open: %r\n", argv[i]); s = "open error"; continue; } if(i) Bputc(&out, '\n'); if(argc > 1) Bprint(&out, "→ %s: ", argv[i]); loadhtml(fd); } free(l); if(nfn){ Bwrite(&out, "\nReferences:\n", 13); for(i = 0; i < nfn; i++){ Bwrite(&out, fn[i], strlen(fn[i])); free(fn[i]); } } free(fn); exits(s); }