ref: 8be7f27b758ef9ef818f68db0d8bcb7b038eae0e
dir: /sys/src/cmd/mothra/mothra.c/
/* * Trivial web browser */ #include <u.h> #include <libc.h> #include <draw.h> #include <event.h> #include <keyboard.h> #include <plumb.h> #include <cursor.h> #include <panel.h> #include <regexp.h> #include "mothra.h" #include "rtext.h" int debug=0; int verbose=0; /* -v flag causes html errors to be written to file-descriptor 2 */ int killimgs=0; /* should mothra kill images? */ int defdisplay=1; /* is the default (initial) display visible? */ int visxbar=0; /* horizontal scrollbar visible? */ int topxbar=0; /* horizontal scrollbar at top? */ Panel *root; /* the whole display */ Panel *alt; /* the alternate display */ Panel *alttext; /* the alternate text window */ Panel *cmd; /* command entry */ Panel *cururl; /* label giving the url of the visible text */ Panel *list; /* list of previously acquired www pages */ Panel *msg; /* message display */ Panel *menu3; /* button 3 menu */ char mothra[] = "mothra!"; Cursor patientcurs={ 0, 0, 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x0F, 0xF0, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x0F, 0xF0, 0x1F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFC, 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x04, 0x20, 0x04, 0x20, 0x06, 0x60, 0x02, 0x40, 0x0C, 0x30, 0x10, 0x08, 0x14, 0x08, 0x14, 0x28, 0x12, 0x28, 0x0A, 0x50, 0x16, 0x68, 0x20, 0x04, 0x3F, 0xFC, }; Cursor confirmcursor={ 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F, 0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C, 0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90, }; Cursor readingcurs={ -10, -3, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x1F, 0xF0, 0x3F, 0xF0, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xF3, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x07, 0xE0, 0x01, 0xE0, 0x03, 0xE0, 0x07, 0x60, 0x0E, 0x60, 0x1C, 0x00, 0x38, 0x00, 0x71, 0xB6, 0x61, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; Cursor mothcurs={ {-7, -7}, {0x00, 0x00, 0x60, 0x06, 0xf8, 0x1f, 0xfc, 0x3f, 0xfe, 0x7f, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x1f, 0xf8, 0x1f, 0xf8, 0x0e, 0x70, 0x0c, 0x30, 0x00, 0x00, }, {0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x58, 0x1a, 0x5c, 0x3a, 0x64, 0x26, 0x27, 0xe4, 0x37, 0xec, 0x37, 0xec, 0x17, 0xe8, 0x1b, 0xd8, 0x0e, 0x70, 0x0c, 0x30, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, } }; Www *current=0; Url *selection=0; int mothmode; int kickpipe[2]; void docmd(Panel *, char *); void doprev(Panel *, int, int); char *urlstr(Url *); void setcurrent(int, char *); char *genwww(Panel *, int); void updtext(Www *); void dolink(Panel *, int, Rtext *); void hit3(int, int); void mothon(Www *, int); void killpix(Www *w); char *buttons[]={ "alt display", "moth mode", "snarf", "paste", "plumb", "search", "save hit", "hit list", "exit", 0 }; int wwwtop=0; Www *www(int index){ static Www a[NWWW]; return &a[index % NWWW]; } int nwww(void){ return wwwtop<NWWW ? wwwtop : NWWW; } int subpanel(Panel *obj, Panel *subj){ if(obj==0) return 0; if(obj==subj) return 1; for(obj=obj->child;obj;obj=obj->next) if(subpanel(obj, subj)) return 1; return 0; } /* * Make sure that the keyboard focus is on-screen, by adjusting it to * be the cmd entry if necessary. */ int adjkb(void){ Rtext *t; int yoffs; if(current){ yoffs=text->r.min.y-plgetpostextview(text); for(t=current->text;t;t=t->next) if(!eqrect(t->r, Rect(0,0,0,0))){ if(t->r.max.y+yoffs>=text->r.min.y && t->r.min.y+yoffs<text->r.max.y && t->b==0 && subpanel(t->p, plkbfocus)) return 1; } } plgrabkb(cmd); return 0; } void scrollpanel(Panel *p, int dy, int whence) { Scroll s; s = plgetscroll(p); switch(whence){ case 0: s.pos.y = dy; break; case 1: s.pos.y += dy; break; case 2: s.pos.y = s.size.y+dy; break; } if(s.pos.y > s.size.y) s.pos.y = s.size.y; if(s.pos.y < 0) s.pos.y = 0; plsetscroll(p, s); } void sidescroll(int dx, int whence) { Scroll s; s = plgetscroll(text); switch(whence){ case 0: s.pos.x = dx; break; case 1: s.pos.x += dx; break; case 2: s.pos.x = s.size.x+dx; break; } if(s.pos.x > s.size.x - text->size.x + 5) s.pos.x = s.size.x - text->size.x + 5; if(s.pos.x < 0) s.pos.x = 0; plsetscroll(text, s); } void mkpanels(void){ Panel *p, *xbar, *ybar, *swap; int xflags; if(topxbar) xflags=PACKN|USERFL; else xflags=PACKS|USERFL; if(!visxbar) xflags|=IGNORE; menu3=plmenu(0, 0, buttons, PACKN|FILLX, hit3); root=plpopup(root, EXPAND, 0, 0, menu3); p=plgroup(root, PACKN|FILLX); msg=pllabel(p, PACKN|FILLX, mothra); plplacelabel(msg, PLACEW); pllabel(p, PACKW, "Go:"); cmd=plentry(p, PACKN|FILLX, 0, "", docmd); p=plgroup(root, PACKN|FILLX); ybar=plscrollbar(p, PACKW); list=pllist(p, PACKN|FILLX, genwww, 8, doprev); plscroll(list, 0, ybar); p=plgroup(root, PACKN|FILLX); pllabel(p, PACKW, "Url:"); cururl=pllabel(p, PACKE|EXPAND, "---"); plplacelabel(cururl, PLACEW); p=plgroup(root, PACKN|EXPAND); ybar=plscrollbar(p, PACKW|USERFL); xbar=plscrollbar(p, xflags); text=pltextview(p, PACKE|EXPAND, Pt(0, 0), 0, dolink); plscroll(text, xbar, ybar); plgrabkb(cmd); alt=plpopup(0, PACKE|EXPAND, 0, 0, menu3); ybar=plscrollbar(alt, PACKW|USERFL); xbar=plscrollbar(alt, xflags); alttext=pltextview(alt, PACKE|EXPAND, Pt(0, 0), 0, dolink); plscroll(alttext, xbar, ybar); if(!defdisplay){ swap=root; root=alt; alt=swap; swap=text; text=alttext; alttext=swap; } } int cohort = -1; void killcohort(void){ int i; for(i=0;i!=3;i++){ /* It's a long way to the kitchen */ postnote(PNGROUP, cohort, "kill\n"); sleep(1); } } void catch(void*, char*){ noted(NCONT); } void dienow(void*, char*){ noted(NDFLT); } char* mkhome(void){ static char *home; /* where to put files */ char *henv, *tmp; int f; if(home == nil){ henv=getenv("home"); if(henv){ tmp = smprint("%s/lib", henv); f=create(tmp, OREAD, DMDIR|0777); if(f!=-1) close(f); free(tmp); home = smprint("%s/lib/mothra", henv); f=create(home, OREAD, DMDIR|0777); if(f!=-1) close(f); free(henv); } else home = strdup("/tmp"); } return home; } void donecurs(void){ if(current && current->alldone==0) esetcursor(&readingcurs); else if(mothmode) esetcursor(&mothcurs); else esetcursor(0); } void drawlock(int dolock){ static int ref = 0; if(dolock){ if(ref++ == 0) lockdisplay(display); } else { if(--ref == 0) unlockdisplay(display); } } void scrollto(char *tag); void search(void); extern char *mtpt; /* url */ void main(int argc, char *argv[]){ Event e; enum { Eplumb = 128, Ekick = 256 }; Plumbmsg *pm; char *url; int i; quotefmtinstall(); fmtinstall('U', Ufmt); ARGBEGIN{ case 'd': debug=1; break; case 'v': verbose=1; break; case 'k': killimgs=1; break; case 'm': if(mtpt = ARGF()) break; case 'a': defdisplay=0; break; default: goto Usage; }ARGEND /* * so that we can stop all subprocesses with a note, * and to isolate rendezvous from other processes */ if(cohort=rfork(RFPROC|RFNOTEG|RFNAMEG|RFREND)){ atexit(killcohort); notify(catch); waitpid(); exits(0); } cohort = getpid(); atexit(killcohort); switch(argc){ default: Usage: fprint(2, "usage: %s [-dvak] [-m mtpt] [url]\n", argv0); exits("usage"); case 0: url=getenv("url"); break; case 1: url=argv[0]; break; } if(initdraw(0, 0, mothra) < 0) sysfatal("initdraw: %r"); display->locking = 1; chrwidth=stringwidth(font, "0"); pltabsize(chrwidth, 8*chrwidth); einit(Emouse|Ekeyboard); eplumb(Eplumb, "web"); if(pipe(kickpipe) < 0) sysfatal("pipe: %r"); estart(Ekick, kickpipe[0], 256); plinit(); if(debug) notify(dienow); getfonts(); hrule=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite); if(hrule==0) sysfatal("can't allocimage!"); draw(hrule, Rect(0,1,1,3), display->black, 0, ZP); linespace=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite); if(linespace==0) sysfatal("can't allocimage!"); bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DWhite); fillellipse(bullet, Pt(4,4), 3, 3, display->black, ZP); mkpanels(); unlockdisplay(display); eresized(0); drawlock(1); if(url && url[0]) geturl(url, -1, 1, 0); mouse.buttons=0; for(;;){ if(mouse.buttons==0 && current){ if(current->finished){ updtext(current); if(current->url->tag[0]) scrollto(current->url->tag); current->finished=0; current->changed=0; current->alldone=1; message(mothra); donecurs(); } } drawlock(0); i=event(&e); drawlock(1); switch(i){ case Ekick: if(mouse.buttons==0 && current && current->changed){ if(!current->finished) updtext(current); current->changed=0; } break; case Ekeyboard: switch(e.kbdc){ default: Plkey: adjkb(); plkeyboard(e.kbdc); break; case Khome: scrollpanel(text, 0, 0); break; case Kup: scrollpanel(text, -text->size.y/4, 1); break; case Kpgup: scrollpanel(text, -text->size.y/2, 1); break; case Kdown: scrollpanel(text, text->size.y/4, 1); break; case Kpgdown: scrollpanel(text, text->size.y/2, 1); break; case Kend: scrollpanel(text, -text->size.y, 2); break; case Kack: search(); break; case Kright: if(plkbfocus) goto Plkey; sidescroll(text->size.x/4, 1); break; case Kleft: if(plkbfocus) goto Plkey; sidescroll(-text->size.x/4, 1); break; } break; case Emouse: mouse=e.mouse; if(mouse.buttons & (8|16) && ptinrect(mouse.xy, list->r) && defdisplay){ if(mouse.buttons & 8) scrollpanel(list, list->r.min.y - mouse.xy.y, 1); else scrollpanel(list, mouse.xy.y - list->r.min.y, 1); break; } if(mouse.buttons & (8|16) && ptinrect(mouse.xy, text->r)){ if(mouse.buttons & 8) scrollpanel(text, text->r.min.y - mouse.xy.y, 1); else scrollpanel(text, mouse.xy.y - text->r.min.y, 1); break; } plmouse(root, &mouse); if(mouse.buttons == 1 && root->lastmouse == root) plgrabkb(nil); break; case Eplumb: pm=e.v; if(pm->ndata > 0) geturl(pm->data, -1, 1, 0); plumbfree(pm); break; } } } int confirm(int b){ Mouse down, up; esetcursor(&confirmcursor); do down=emouse(); while(!down.buttons); do up=emouse(); while(up.buttons); donecurs(); return down.buttons==(1<<(b-1)); } void message(char *s, ...){ static char buf[1024]; char *out; va_list args; va_start(args, s); out = buf + vsnprint(buf, sizeof(buf), s, args); va_end(args); *out='\0'; plinitlabel(msg, PACKN|FILLX, buf); if(defdisplay) pldraw(msg, screen); } void htmlerror(char *name, int line, char *m, ...){ static char buf[1024]; char *out; va_list args; if(verbose){ va_start(args, m); out=buf+snprint(buf, sizeof(buf), "%s: line %d: ", name, line); out+=vsnprint(out, sizeof(buf)-(out-buf)-1, m, args); va_end(args); *out='\0'; fprint(2, "%s\n", buf); } } void eresized(int new){ Rectangle r; drawlock(1); if(new && getwindow(display, Refnone) == -1) { fprint(2, "getwindow: %r\n"); exits("getwindow"); } r=screen->r; plpack(root, r); plpack(alt, r); pldraw(cmd, screen); /* put cmd box on screen for alt display */ pldraw(root, screen); flushimage(display, 1); drawlock(0); } void *emalloc(int n){ void *v; v=malloc(n); if(v==0) sysfatal("out of memory"); memset(v, 0, n); setmalloctag(v, getcallerpc(&n)); return v; } void nstrcpy(char *to, char *from, int len){ strncpy(to, from, len); to[len-1] = 0; } char *genwww(Panel *, int index){ static char buf[1024]; Www *w; int i; if(index >= nwww()) return 0; i = wwwtop-index-1; w = www(i); if(!w->url) return 0; if(w->title[0]!='\0'){ w->gottitle=1; snprint(buf, sizeof(buf), "%2d %s", i+1, w->title); } else snprint(buf, sizeof(buf), "%2d %s", i+1, urlstr(w->url)); return buf; } void scrollto(char *tag){ Rtext *tp; Action *ap; if(current == nil || text == nil) return; if(tag && tag[0]){ for(tp=current->text;tp;tp=tp->next){ ap=tp->user; if(ap && ap->name && strcmp(ap->name, tag)==0){ current->yoffs=tp->topy; break; } } } plsetpostextview(text, current->yoffs); } /* * selected text should be a url. */ void setcurrent(int index, char *tag){ Www *new; int i; new=www(index); if(new==current && (tag==0 || tag[0]==0)) return; if(current) current->yoffs=plgetpostextview(text); current=new; plinitlabel(cururl, PACKE|EXPAND, current->url->fullname); if(defdisplay) pldraw(cururl, screen); plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink); scrollto(tag); if((i = open("/dev/label", OWRITE)) >= 0){ fprint(i, "%s %s", mothra, current->url->fullname); close(i); } donecurs(); } char *arg(char *s){ do ++s; while(*s==' ' || *s=='\t'); return s; } void save(int ifd, char *name){ char buf[NNAME+64]; int ofd; if(ifd < 0){ message("save: %s: %r", name); return; } ofd=create(name, OWRITE, 0666); if(ofd < 0){ message("save: %s: %r", name); return; } switch(rfork(RFNOTEG|RFNAMEG|RFFDG|RFMEM|RFPROC|RFNOWAIT)){ case -1: message("Can't fork: %r"); break; case 0: dup(ifd, 0); close(ifd); dup(ofd, 1); close(ofd); snprint(buf, sizeof(buf), "{tput -p || cat} |[2] {aux/statusmsg -k %q >/dev/null || cat >/dev/null}", name); execl("/bin/rc", "rc", "-c", buf, nil); exits("exec"); } close(ifd); close(ofd); donecurs(); } void screendump(char *name, int full){ Image *b; int fd; fd=create(name, OWRITE, 0666); if(fd==-1){ message("can't create %s", name); return; } if(full){ writeimage(fd, screen, 0); } else { if((b=allocimage(display, text->r, screen->chan, 0, DNofill)) == nil){ message("can't allocate image"); close(fd); return; } draw(b, b->r, screen, 0, b->r.min); writeimage(fd, b, 0); freeimage(b); } close(fd); } /* * convert a url into a local file name. */ char *urltofile(Url *url){ char *name, *slash; if(url == nil) return nil; name = urlstr(url); if(name == nil || name[0] == 0) name = "/"; if(slash = strrchr(name, '/')) name = slash+1; if(name[0] == 0) name = "index"; return name; } /* * user typed a command. */ void docmd(Panel *p, char *s){ char buf[NNAME]; int c; USED(p); while(*s==' ' || *s=='\t') s++; /* * Non-command does a get on the url */ if(s[0]!='\0' && s[1]!='\0' && s[1]!=' ') geturl(s, -1, 0, 0); else switch(c = s[0]){ default: message("Unknown command %s", s); break; case 'a': s = arg(s); if(*s=='\0' && selection) hit3(3, 0); break; case 'd': s = arg(s); if(*s){ s = smprint("https://lite.duckduckgo.com/lite/?q=%U&kd=-1", s); if(s != nil) geturl(s, -1, 0, 0); free(s); }else message("Usage: d text"); break; case 'g': s = arg(s); if(*s=='\0'){ case 'r': if(selection) s = urlstr(selection); else message("no url selected"); } geturl(s, -1, 0, 0); break; case 'j': s = arg(s); if(*s) doprev(nil, 1, wwwtop-atoi(s)); else message("Usage: j index"); break; case 'm': mothon(current, !mothmode); break; case 'k': killimgs = !killimgs; if (killimgs) killpix(current); break; case 'w': case 'W': s = arg(s); if(s==0 || *s=='\0'){ snprint(buf, sizeof(buf), "dump.bit"); if(eenter("Screendump to", buf, sizeof(buf), &mouse) <= 0) break; s = buf; } screendump(s, c == 'W'); break; case 's': s = arg(s); if(!selection){ message("no url selected"); break; } if(s==0 || *s=='\0'){ snprint(buf, sizeof(buf), "%s", urltofile(selection)); if(eenter("Save to", buf, sizeof(buf), &mouse) <= 0) break; s = buf; } save(urlget(selection, -1), s); break; case 'q': exits(0); } plinitentry(cmd, EXPAND, 0, "", docmd); pldraw(root, screen); } void regerror(char *msg) { werrstr("regerror: %s", msg); } void search(void){ static char last[256]; char buf[256]; Reprog *re; Rtext *tp; for(;;){ if(current == nil || current->text == nil || text == nil) return; strncpy(buf, last, sizeof(buf)-1); if(eenter("Search for", buf, sizeof(buf), &mouse) <= 0) return; strncpy(last, buf, sizeof(buf)-1); re = regcompnl(buf); if(re == nil){ message("%r"); continue; } for(tp=current->text;tp;tp=tp->next) if(tp->flags & PL_SEL) break; if(tp == nil) tp = current->text; else { tp->flags &= ~PL_SEL; tp = tp->next; } while(tp != nil){ tp->flags &= ~PL_SEL; if(tp->text && *tp->text) if(regexec(re, tp->text, nil, 0)){ tp->flags |= PL_SEL; plsetpostextview(text, tp->topy); break; } tp = tp->next; } free(re); updtext(current); } } void hiturl(int buttons, char *url, int map){ switch(buttons){ case 1: geturl(url, -1, 0, map); break; case 2: urlresolve(selurl(url)); break; case 4: message("Button 3 hit on url can't happen!"); break; } } /* * user selected from the list of available pages */ void doprev(Panel *p, int buttons, int index){ int i; USED(p); if(index < 0 || index >= nwww()) return; i = wwwtop-index-1; switch(buttons){ case 1: setcurrent(i, 0); /* no break ... */ case 2: selurl(www(i)->url->fullname); break; case 4: message("Button 3 hit on page can't happen!"); break; } } /* * Follow an html link */ void dolink(Panel *p, int buttons, Rtext *word){ Action *a; a=word->user; if(a == nil || (a->link == nil && a->image == nil)) return; if(mothmode) hiturl(buttons, a->image ? a->image : a->link, 0); else if(a->link){ if(a->ismap){ char mapurl[NNAME]; Point coord; int yoffs; yoffs=plgetpostextview(p); coord=subpt(subpt(mouse.xy, word->r.min), p->r.min); snprint(mapurl, sizeof(mapurl), "%s?%d,%d", a->link, coord.x, coord.y+yoffs); hiturl(buttons, mapurl, 1); } else hiturl(buttons, a->link, 0); } } void filter(int fd, char *cmd){ switch(rfork(RFFDG|RFPROC|RFMEM|RFREND|RFNOWAIT|RFNOTEG)){ case -1: message("Can't fork!"); break; case 0: dupfds(fd, 1, 2, -1); execl("/bin/rc", "rc", "-c", cmd, nil); _exits(0); } close(fd); } void gettext(Www *w, int fd, int type){ switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){ case -1: message("Can't fork, please wait"); break; case 0: if(type==HTML) plrdhtml(w->url->fullname, fd, w, killimgs); else plrdplain(w->url->fullname, fd, w); _exits(0); } close(fd); } void freetext(Rtext *t){ Rtext *tt; Action *a; tt = t; for(; t!=0; t = t->next){ t->b=0; free(t->text); t->text=0; if(a = t->user){ t->user=0; free(a->image); free(a->link); free(a->name); free(a); } } plrtfree(tt); } void dupfds(int fd, ...) { int mfd, n, i; va_list arg; Dir *dir; va_start(arg, fd); for(mfd = 0; fd >= 0; fd = va_arg(arg, int), mfd++) if(fd != mfd) if(dup(fd, mfd) < 0) sysfatal("dup: %r"); va_end(arg); if((fd = open("/fd", OREAD)) < 0) sysfatal("open: %r"); n = dirreadall(fd, &dir); for(i=0; i<n; i++){ if(strstr(dir[i].name, "ctl")) continue; fd = atoi(dir[i].name); if(fd >= mfd) close(fd); } free(dir); } int pipeline(int fd, char *fmt, ...) { char buf[80], *argv[4]; va_list arg; int pfd[2]; va_start(arg, fmt); vsnprint(buf, sizeof buf, fmt, arg); va_end(arg); if(pipe(pfd) < 0){ Err: close(fd); werrstr("pipeline for %s failed: %r", buf); return -1; } switch(rfork(RFPROC|RFMEM|RFFDG|RFREND|RFNOWAIT)){ case -1: close(pfd[0]); close(pfd[1]); goto Err; case 0: dupfds(fd, pfd[1], 2, -1); argv[0] = "rc"; argv[1] = "-c"; argv[2] = buf; argv[3] = nil; exec("/bin/rc", argv); _exits(0); } close(fd); close(pfd[1]); return pfd[0]; } char* urlstr(Url *url){ if(url->fullname[0]) return url->fullname; return url->reltext; } Url *copyurl(Url *u){ Url *v; v=emalloc(sizeof(Url)); *v=*u; v->reltext = strdup(u->reltext); v->basename = strdup(u->basename); return v; } void freeurl(Url *u){ free(u->reltext); free(u->basename); free(u); } void seturl(Url *url, char *urlname, char *base){ url->reltext = strdup(urlname); url->basename = strdup(base); url->fullname[0] = 0; url->tag[0] = 0; url->map = 0; } Url* selurl(char *urlname){ Url *last; last=selection; selection=emalloc(sizeof(Url)); seturl(selection, urlname, current ? current->url->fullname : ""); if(last) freeurl(last); message("selected: %s", urlstr(selection)); plgrabkb(cmd); /* for snarf */ return selection; } /* * get the file at the given url */ void geturl(char *urlname, int post, int plumb, int map){ int i, fd, typ; char cmd[NNAME]; ulong n; Www *w; if(*urlname == '#' && post < 0){ scrollto(urlname+1); return; } selurl(urlname); selection->map=map; message("getting %s", urlstr(selection)); esetcursor(&patientcurs); for(;;){ if((fd=urlget(selection, post)) < 0){ message("%r"); break; } message("getting %s", selection->fullname); if(mothmode && !plumb) typ = -1; else if((typ = mimetotype(selection->contenttype)) < 0) typ = snooptype(fd); switch(typ){ default: if(plumb){ message("unknown file type"); close(fd); break; } snprint(cmd, sizeof(cmd), "%s", urltofile(selection)); if(eenter("Save to", cmd, sizeof(cmd), &mouse) <= 0){ close(fd); break; } save(fd, cmd); break; case HTML: fd = pipeline(fd, "exec uhtml"); case PLAIN: n=0; for(i=wwwtop-1; i>=0 && i!=(wwwtop-NWWW-1); i--){ w = www(i); n += countpix(w->pix); if(n >= NPIXMB*1024*1024) killpix(w); } w = www(i = wwwtop++); if(i >= NWWW){ /* wait for the reader to finish the document */ while(!w->finished && !w->alldone){ drawlock(0); sleep(10); drawlock(1); } freetext(w->text); freeform(w->form); freepix(w->pix); freeurl(w->url); memset(w, 0, sizeof(*w)); } if(selection->map) w->url=copyurl(current->url); else w->url=copyurl(selection); w->finished = 0; w->alldone = 0; gettext(w, fd, typ); if(rfork(RFPROC|RFMEM|RFNOWAIT) == 0){ for(;;){ sleep(1000); if(w->finished || w->alldone) break; if(w->changed) write(kickpipe[1], "C", 1); } _exits(0); } plinitlist(list, PACKN|FILLX, genwww, 8, doprev); if(defdisplay) pldraw(list, screen); setcurrent(i, selection->tag); break; case GIF: case JPEG: case PNG: case BMP: case PAGE: filter(fd, "exec page -w"); break; } break; } donecurs(); } void updtext(Www *w){ Rtext *t; Action *a; if(defdisplay && w->gottitle==0 && w->title[0]!='\0') pldraw(list, screen); for(t=w->text;t;t=t->next){ a=t->user; if(a){ if(a->field) mkfieldpanel(t); a->field=0; } } if(w != current) return; w->yoffs=plgetpostextview(text); plinittextview(text, PACKE|EXPAND, Pt(0, 0), w->text, dolink); plsetpostextview(text, w->yoffs); pldraw(text, screen); } void finish(Www *w){ w->finished = 1; write(kickpipe[1], "F", 1); } void mothon(Www *w, int on) { Rtext *t, *x; Action *a, *ap; if(w==0 || mothmode==on) return; if(mothmode = on) message("moth mode!"); else message(mothra); /* * insert or remove artificial links to the href for * images that are also links */ for(t=w->text;t;t=t->next){ a=t->user; if(a == nil || a->image == nil) continue; if(a->link == nil){ if(on) t->flags |= PL_HOT; else t->flags &= ~PL_HOT; continue; } x = t->next; if(on){ t->next = nil; ap=emalloc(sizeof(Action)); ap->link = strdup(a->link); plrtstr(&t->next, 0, 0, 0, t->font, strdup("->"), PL_HOT, ap); t->next->next = x; } else { if(x) { t->next = x->next; x->next = nil; freetext(x); } } } updtext(w); donecurs(); } void killpix(Www *w){ Rtext *t; if(w==0 || !w->finished && !w->alldone) return; for(t=w->text; t; t=t->next) if(t->b && t->user) t->b=0; freepix(w->pix); w->pix=0; updtext(w); } void snarf(Panel *p){ if(p==0 || p==cmd){ if(selection){ plputsnarf(urlstr(selection)); plsnarf(text); }else message("no url selected"); }else plsnarf(p); } void paste(Panel *p){ if(p==0) p=cmd; plpaste(p); } void hit3(int button, int item){ char buf[1024]; char name[NNAME]; char *s; Panel *swap; int fd; USED(button); switch(item){ case 0: swap=root; root=alt; alt=swap; if(current) current->yoffs=plgetpostextview(text); swap=text; text=alttext; alttext=swap; defdisplay=!defdisplay; plpack(root, screen->r); if(current){ plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink); plsetpostextview(text, current->yoffs); } pldraw(root, screen); break; case 1: mothon(current, !mothmode); break; case 2: snarf(plkbfocus); break; case 3: paste(plkbfocus); break; case 4: if(plkbfocus==nil || plkbfocus==cmd){ if(text==nil || text->snarf==nil || selection==nil) return; if((s=text->snarf(text))==nil) s=smprint("%s", urlstr(selection)); }else if((s=plkbfocus->snarf(plkbfocus))==nil) return; if((fd=plumbopen("send", OWRITE))<0){ message("can't plumb"); free(s); return; } plumbsendtext(fd, "mothra", nil, getwd(buf, sizeof buf), s); close(fd); free(s); break; case 5: search(); break; case 6: if(!selection){ message("no url selected"); break; } snprint(name, sizeof(name), "%s/hit.html", mkhome()); fd=open(name, OWRITE); if(fd==-1){ fd=create(name, OWRITE, 0666); if(fd==-1){ message("can't open %s", name); return; } fprint(fd, "<html><head><title>Hit List</title></head>\n"); fprint(fd, "<body><h1>Hit list</h1>\n"); } seek(fd, 0, 2); fprint(fd, "<p><a href=\"%s\">%s</a>\n", urlstr(selection), urlstr(selection)); close(fd); break; case 7: snprint(name, sizeof(name), "file:%s/hit.html", mkhome()); geturl(name, -1, 1, 0); break; case 8: if(confirm(3)) exits(0); break; } }