]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/mothra/mothra.c
mothra: mouse scrollwheel behave the same as in sam
[plan9front.git] / sys / src / cmd / mothra / mothra.c
index 70306d48da3cd2a9b237a8e4d1277777c27f905e..fef7c9573b94021ff329910c90c64050deb01901 100644 (file)
@@ -9,37 +9,21 @@
 #include <plumb.h>
 #include <cursor.h>
 #include <panel.h>
+#include <regexp.h>
 #include "mothra.h"
 #include "rtext.h"
-int verbose=0;         /* -v flag causes html errors to appear in error log */
+int debug=0;
+int verbose=0;         /* -v flag causes html errors to be written to file-descriptor 2 */
 int defdisplay=1;      /* is the default (initial) display visible? */
 Panel *root;   /* the whole display */
 Panel *alt;    /* the alternate display */
 Panel *alttext;        /* the alternate text window */
 Panel *cmd;    /* command entry */
-Panel *curttl; /* label giving the title of the visible text */
 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 */
-Mouse mouse;   /* current mouse data */
 char mothra[] = "mothra!";
-Url defurl={
-       "http://cat-v.org/",
-       "",
-       "http://cat-v.org/",
-       "",
-       "",
-       HTML,
-};
-Url badurl={
-       "",
-       "",
-       "No file loaded",
-       "",
-       "",
-       HTML,
-};
 Cursor patientcurs={
        0, 0,
        0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x07, 0xe0,
@@ -76,22 +60,39 @@ Cursor readingcurs={
        0x0E, 0x60, 0x1C, 0x00, 0x38, 0x00, 0x71, 0xB6,
        0x61, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 };
-char *mtpt="/mnt/web";
+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 logfile;
+int mothmode;
+int kickpipe[2];
+
 void docmd(Panel *, char *);
 void doprev(Panel *, int, int);
-void selurl(char *);
+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",
-       "snarf url",
+       "moth mode",
+       "snarf",
        "paste",
+       "search",
        "save hit",
        "hit list",
        "exit",
@@ -100,17 +101,13 @@ char *buttons[]={
 
 int wwwtop=0;
 Www *www(int index){
-       static Www a[1+NWWW];
-       return &a[1+(index % NWWW)];
+       static Www a[NWWW];
+       return &a[index % NWWW];
 }
 int nwww(void){
        return wwwtop<NWWW ? wwwtop : NWWW;
 }
 
-void err(Display *, char *msg){
-       fprint(2, "err: %s (%r)\n", msg);
-       abort();
-}
 int subpanel(Panel *obj, Panel *subj){
        if(obj==0) return 0;
        if(obj==subj) return 1;
@@ -122,36 +119,49 @@ int subpanel(Panel *obj, Panel *subj){
  * Make sure that the keyboard focus is on-screen, by adjusting it to
  * be the cmd entry if necessary.
  */
-void adjkb(void){
+int adjkb(void){
        Rtext *t;
        int yoffs;
-       extern Panel *pl_kbfocus;       /* this is a secret panel library name */
-       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.max.y) break;
-               if(t->r.min.y+yoffs>=text->r.min.y
-               && t->b==0
-               && subpanel(t->p, pl_kbfocus)) return;
+       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 scrolltext(int dy)
+void scrolltext(int dy, int whence)
 {
        Scroll s;
 
        s = plgetscroll(text);
-       s.pos.y += dy;
+       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 < 0)
                s.pos.y = 0;
        if(s.pos.y > s.size.y)
                s.pos.y = s.size.y;
        plsetscroll(text, s);
-       pldraw(root, screen);
 }
 
 void mkpanels(void){
-       Panel *p, *bar;
+       Panel *p, *bar, *swap;
+
        menu3=plmenu(0, 0, buttons, PACKN|FILLX, hit3);
        root=plpopup(root, EXPAND, 0, 0, menu3);
                p=plgroup(root, PACKN|FILLX);
@@ -163,72 +173,112 @@ void mkpanels(void){
                        bar=plscrollbar(p, PACKW);
                        list=pllist(p, PACKN|FILLX, genwww, 8, doprev);
                        plscroll(list, 0, bar);
-               p=plgroup(root, PACKN|FILLX);
-                       pllabel(p, PACKW, "Title:");
-                       curttl=pllabel(p, PACKE|EXPAND, "Initializing");
-                       plplacelabel(curttl, PLACEW);
                p=plgroup(root, PACKN|FILLX);
                        pllabel(p, PACKW, "Url:");
                        cururl=pllabel(p, PACKE|EXPAND, "---");
                        plplacelabel(cururl, PLACEW);
                p=plgroup(root, PACKN|EXPAND);
-                       bar=plscrollbar(p, PACKW);
+                       bar=plscrollbar(p, PACKW|USERFL);
                        text=pltextview(p, PACKE|EXPAND, Pt(0, 0), 0, dolink);
                        plscroll(text, 0, bar);
        plgrabkb(cmd);
        alt=plpopup(0, PACKE|EXPAND, 0, 0, menu3);
-               bar=plscrollbar(alt, PACKW);
+               bar=plscrollbar(alt, PACKW|USERFL);
                alttext=pltextview(alt, PACKE|EXPAND, Pt(0, 0), 0, dolink);
                plscroll(alttext, 0, bar);
+
+       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, getpid(), "kill\n");
+               postnote(PNGROUP, cohort, "kill\n");
                sleep(1);
        }
 }
+void catch(void*, char*){
+       noted(NCONT);
+}
 void dienow(void*, char*){
        noted(NDFLT);
 }
-int mkmfile(char *stem, int mode){
-       char *henv;
-       char filename[NNAME];
+
+char* mkhome(void){
+       static char *home;              /* where to put files */
+       char *henv, *tmp;
        int f;
-       if(home[0]=='\0'){
+
+       if(home == nil){
                henv=getenv("home");
                if(henv){
-                       sprint(home, "%s/lib", henv);
-                       f=create(home, OREAD, DMDIR|0777);
+                       tmp = smprint("%s/lib", henv);
+                       f=create(tmp, OREAD, DMDIR|0777);
                        if(f!=-1) close(f);
-                       sprint(home, "%s/lib/mothra", henv);
+                       free(tmp);
+
+                       home = smprint("%s/lib/mothra", henv);
                        f=create(home, OREAD, DMDIR|0777);
                        if(f!=-1) close(f);
                        free(henv);
                }
                else
-                       strcpy(home, "/tmp");
+                       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);
        }
-       snprint(filename, sizeof(filename), "%s/%s", home, stem);
-       f=create(filename, OWRITE, mode);
-       if(f==-1)
-               f=create(stem, OWRITE, mode);
-       return f;
 }
+
+void scrollto(char *tag);
+void search(void);
+
+extern char *mtpt; /* url */
+
 void main(int argc, char *argv[]){
        Event e;
-       enum { Eplumb = 128 };
+       enum { Eplumb = 128, Ekick = 256 };
        Plumbmsg *pm;
        Www *new;
+       Action *a;
        char *url;
-       int errfile;
        int i;
+
+       quotefmtinstall();
+       fmtinstall('U', Ufmt);
+
        ARGBEGIN{
-       case 'd': debug++; break;
+       case 'd': debug=1; break;
        case 'v': verbose=1; break;
        case 'm':
                if(mtpt = ARGF())
                        break;
+       case 'a': defdisplay=0; break;
        default:  goto Usage;
        }ARGEND
 
@@ -236,113 +286,152 @@ void main(int argc, char *argv[]){
         * so that we can stop all subprocesses with a note,
         * and to isolate rendezvous from other processes
         */
-       rfork(RFNOTEG|RFNAMEG|RFREND);
+       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 [-d] [-m mtpt] [url]\n", argv[0]);
+               fprint(2, "Usage: %s [-dva] [-m mtpt] [url]\n", argv0);
                exits("usage");
        case 0:
                url=getenv("url");
-               if(url==0 || url[0]=='\0')
-                       url=defurl.fullname;
                break;
        case 1: url=argv[0]; break;
        }
-       errfile=mkmfile("mothra.err", 0666);
-       if(errfile!=-1){
-               dup(errfile, 2);
-               close(errfile);
-       }
-       logfile=mkmfile("mothra.log", 0666|DMAPPEND);
-       
-       initdraw(err,0,"mothra");
+       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");
-       etimer(0, 1000);
+       if(pipe(kickpipe) < 0)
+               sysfatal("pipe: %r");
+       estart(Ekick, kickpipe[0], 256);
        plinit(screen->depth);
        if(debug) notify(dienow);
        getfonts();
        hrule=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 0, DWhite);
-       if(hrule==0){
-               fprint(2, "%s: can't allocimage!\n", argv[0]);
-               exits("no mem");
-       }
+       if(hrule==0)
+               sysfatal("can't allocimage!");
        draw(hrule, Rect(0,1,1280,3), display->black, 0, ZP);
        linespace=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 0, DWhite);
-       if(linespace==0){
-               fprint(2, "%s: can't allocimage!\n", argv[0]);
-               exits("no mem");
-       }
+       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);
-       new = www(-1);
-       new->url=&badurl;
-       strcpy(new->title, "See error message above");
-       plrtstr(&new->text, 0, 0, font, "See error message above", 0, 0);
-       new->alldone=1;
        mkpanels();
-
        unlockdisplay(display);
        eresized(0);
-       lockdisplay(display);
+       drawlock(1);
 
-       geturl(url, GET, 0, 1, 0);
+       if(url && url[0])
+               geturl(url, -1, 1, 0);
 
-       if(logfile==-1) message("Can't open log file");
        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);
-                               esetcursor(0);
-                       }
-                       else if(current->changed){
-                               updtext(current);
-                               current->changed=0;
+                               donecurs();
                        }
                }
 
-               unlockdisplay(display);
+               flushimage(display, 1);
+               drawlock(0);
                i=event(&e);
-               lockdisplay(display);
+               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:
                                adjkb();
                                plkeyboard(e.kbdc);
                                break;
+                       case Khome:
+                               scrolltext(0, 0);
+                               break;
                        case Kup:
-                               scrolltext(-text->size.y/4);
+                               scrolltext(-text->size.y/4, 1);
+                               break;
+                       case Kpgup:
+                               scrolltext(-text->size.y/2, 1);
                                break;
                        case Kdown:
-                               scrolltext(text->size.y/4);
+                               scrolltext(text->size.y/4, 1);
+                               break;
+                       case Kpgdown:
+                               scrolltext(text->size.y/2, 1);
+                               break;
+                       case Kend:
+                               scrolltext(-text->size.y, 2);
+                               break;
+                       case Kack:
+                               search();
                                break;
                        }
                        break;
                case Emouse:
                        mouse=e.mouse;
-                       plmouse(root, e.mouse);
+                       if(mouse.buttons & (8|16) && ptinrect(mouse.xy, text->r)){
+                               if(mouse.buttons & 8)
+                                       scrolltext(text->r.min.y - mouse.xy.y, 1);
+                               else
+                                       scrolltext(mouse.xy.y - text->r.min.y, 1);
+                               break;
+                       }
+                       plmouse(root, &mouse);
                        break;
                case Eplumb:
                        pm=e.v;
                        if(pm->ndata > 0)
-                               geturl(pm->data, GET, 0, 1, 0);
+                               geturl(pm->data, -1, 1, 0);
                        plumbfree(pm);
                        break;
                }
        }
 }
+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,
+};
+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;
@@ -370,126 +459,130 @@ void htmlerror(char *name, int line, char *m, ...){
 void eresized(int new){
        Rectangle r;
 
-       lockdisplay(display);
+       drawlock(1);
        if(new && getwindow(display, Refnone) == -1) {
                fprint(2, "getwindow: %r\n");
                exits("getwindow");
        }
        r=screen->r;
-       plinitlabel(curttl, PACKE|EXPAND, "---");
-       plinitlabel(cururl, PACKE|EXPAND, "---");
        plpack(root, r);
-       if(current){
-               plinitlabel(curttl, PACKE|EXPAND, current->title);
-               plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
-       }
-       draw(screen, r, display->white, 0, ZP);
+       plpack(alt, r);
+       pldraw(cmd, screen);    /* put cmd box on screen for alt display */
        pldraw(root, screen);
-       unlockdisplay(display);
+       flushimage(display, 1);
+       drawlock(0);
 }
 void *emalloc(int n){
        void *v;
        v=malloc(n);
-       if(v==0){
-               fprint(2, "out of space\n");
-               exits("no mem");
-       }
+       if(v==0)
+               sysfatal("out of memory");
+       memset(v, 0, n);
+       setmalloctag(v, getcallerpc(&n));
        return v;
 }
-void *emallocz(int n, int z){
-       void *v;
-       v = emalloc(n);
-       if(z)
-               memset(v, 0, 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;
-       snprint(buf, sizeof(buf), "%2d %s", i+1, www(i)->title);
+       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 donecurs(void){
-       esetcursor(current && current->alldone?0:&readingcurs);
+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);
+       flushimage(display, 1);
 }
+
 /*
  * selected text should be a url.
- * get the document, scroll to the given tag
  */
 void setcurrent(int index, char *tag){
        Www *new;
-       Rtext *tp;
-       Action *ap;
        int i;
        new=www(index);
        if(new==current && (tag==0 || tag[0]==0)) return;
        if(current)
                current->yoffs=plgetpostextview(text);
        current=new;
-       plinitlabel(curttl, PACKE|EXPAND, current->title);
-       if(defdisplay) pldraw(curttl, screen);
        plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
        if(defdisplay) pldraw(cururl, screen);
        plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
-       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;
-                       }
-               }
+       scrollto(tag);
+       if((i = open("/dev/label", OWRITE)) >= 0){
+               fprint(i, "%s %s", mothra, current->url->fullname);
+               close(i);
        }
-       plsetpostextview(text, current->yoffs);
        donecurs();
-       flushimage(display, 1);
 }
 char *arg(char *s){
        do ++s; while(*s==' ' || *s=='\t');
        return s;
 }
-void save(Url *url, char *name){
-       int ofd, ifd, n;
-       char buf[4096];
-       ofd=create(name, OWRITE, 0666);
-       if(ofd==-1){
+void save(int ifd, char *name){
+       char buf[NNAME+64];
+       int cfd, ofd;
+       if(ifd < 0){
                message("save: %s: %r", name);
                return;
        }
-       esetcursor(&patientcurs);
-       ifd=urlopen(url, GET, 0);
-       donecurs();
-       if(ifd==-1){
-               message("save: %s: %r", selection->fullname);
-               close(ofd);
+       ofd=create(name, OWRITE, 0666);
+       if(ofd < 0){
+               message("save: %s: %r", name);
+               return;
        }
-       switch(rfork(RFNOTEG|RFFDG|RFPROC|RFNOWAIT)){
+       switch(rfork(RFNOTEG|RFNAMEG|RFFDG|RFMEM|RFPROC|RFNOWAIT)){
        case -1:
-               message("Can't fork -- please wait");
-               esetcursor(&patientcurs);
-               while((n=read(ifd, buf, 4096))>0)
-                       write(ofd, buf, n);
-               donecurs();
+               message("Can't fork: %r");
                break;
        case 0:
-               while((n=read(ifd, buf, 4096))>0)
-                       write(ofd, buf, n);
-               if(n==-1) fprint(2, "save: %s: %r\n", url->fullname);
-               _exits(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|OTRUNC, 0666);
+       fd=create(name, OWRITE, 0666);
        if(fd==-1){
                message("can't create %s", name);
                return;
@@ -509,87 +602,158 @@ void screendump(char *name, int full){
        close(fd);
 }
 
+/*
+ * convert a url into a local file name.
+ */
+char *urltofile(Url *url){
+       char *name, *slash;
+       if(url == nil)
+               return nil;
+       if(url->fullname[0] || url->reltext[0])
+               name = urlstr(url);
+       else
+               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, GET, 0, 1, 0);
-       else switch(s[0]){
+               geturl(s, -1, 0, 0);
+       else switch(c = s[0]){
        default:
-               message("Unknown command %s, type h for help", s);
+               message("Unknown command %s", s);
+               break;
+       case 'a':
+               s = arg(s);
+               if(*s=='\0' && selection)
+                       hit3(3, 0);
                break;
        case 'g':
-               s=arg(s);
+               s = arg(s);
                if(*s=='\0'){
+       case 'r':
                        if(selection)
-                               geturl(selection->fullname, GET, 0, 1, 0);
+                               s = urlstr(selection);
                        else
                                message("no url selected");
                }
-               else geturl(s, GET, 0, 1, 0);
+               geturl(s, -1, 0, 0);
                break;
-       case 'r':
+       case 'j':
                s = arg(s);
-               if(*s == '\0' && selection)
-                       geturl(selection->fullname, GET, 0, 0, 0);
+               if(*s)
+                       doprev(nil, 1, wwwtop-atoi(s));
+               else
+                       message("Usage: j index");
                break;
-       case 'W':
-               s=arg(s);
-               if(s=='\0'){
-                       message("Usage: W file");
-                       break;
-               }
-               screendump(s, 1);
+       case 'm':
+               mothon(current, !mothmode);
+               break;
+       case 'k':
+               killpix(current);
                break;
        case 'w':
-               s=arg(s);
-               if(s=='\0'){
-                       message("Usage: w file");
-                       break;
+       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, 0);
+               screendump(s, c == 'W');
                break;
        case 's':
-               s=arg(s);
-               if(*s=='\0'){
-                       if(selection){
-                               s=strrchr(selection->fullname, '/');
-                               if(s) s++;
-                       }
-                       if(s==0 || *s=='\0'){
-                               message("Usage: s file");
+               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(selection, s);
+               save(urlget(selection, -1), s);
                break;
        case 'q':
-               draw(screen, screen->r, display->white, 0, ZP);
                exits(0);
        }
        plinitentry(cmd, EXPAND, 0, "", docmd);
-       if(defdisplay) pldraw(cmd, screen);
+       pldraw(root, screen);
+}
+
+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;
+               re = regcompnl(buf);
+               if(re == nil)
+                       return;
+               strncpy(last, buf, sizeof(buf)-1);
+               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, GET, 0, 1, map); break;
+       case 1: geturl(url, -1, 0, map); break;
        case 2: 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 >= nwww())
+       if(index < 0 || index >= nwww())
                return;
        i = wwwtop-index-1;
        switch(buttons){
@@ -603,50 +767,50 @@ void doprev(Panel *p, int buttons, int index){
  * Follow an html link
  */
 void dolink(Panel *p, int buttons, Rtext *word){
-       char mapurl[NNAME];
-       Action *a;
+       char *file, mapurl[NNAME];
        Point coord;
        int yoffs;
-       USED(p);
+       Action *a;
+
+       /* really a button, hit it */
+       if(word->p != nil && word->p != p && strcmp(word->p->kind, "button") == 0){
+               extern void pl_buttonhit(Panel *p, int buttons, int check);
+               pl_buttonhit(word->p, buttons, 0);
+               return;
+       }
+
        a=word->user;
-       if(a && a->link){
+       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){
                        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
+               } else
                        hiturl(buttons, a->link, 0);
        }
 }
-void filter(char *cmd, int fd){
-       flushimage(display, 1);
-       switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
+
+void filter(int fd, char *cmd){
+       switch(rfork(RFFDG|RFPROC|RFMEM|RFREND|RFNOWAIT|RFNOTEG)){
        case -1:
                message("Can't fork!");
                break;
        case 0:
-               close(0);
-               dup(fd, 0);
-               close(fd);
+               dupfds(fd, 1, 2, -1);
                execl("/bin/rc", "rc", "-c", cmd, 0);
-               message("Can't exec /bin/rc!");
                _exits(0);
-       default:
-               break;
        }
        close(fd);
 }
 void gettext(Www *w, int fd, int type){
-       flushimage(display, 1);
-       switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFMEM)){
+       switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){
        case -1:
                message("Can't fork, please wait");
-               if(type==HTML)
-                       plrdhtml(w->url->fullname, fd, w);
-               else
-                       plrdplain(w->url->fullname, fd, w);
                break;
        case 0:
                if(type==HTML)
@@ -678,138 +842,86 @@ void freetext(Rtext *t){
        plrtfree(tt);
 }
 
-void popwin(char *cmd){
-       flushimage(display, 1);
-       switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
-       case -1:
-               message("sorry, can't fork to %s", cmd);
-               break;
-       case 0:
-               execl("/bin/window", "window", "100 100 800 800", "rc", "-c", cmd, 0);
-               _exits(0);
-       }
-}
-
-int readstr(char *buf, int nbuf, char *base, char *name)
+void
+dupfds(int fd, ...)
 {
-       char path[128];
-       int n, fd;
-
-       snprint(path, sizeof path, "%s/%s", base, name);
-       if((fd = open(path, OREAD)) < 0){
-       ErrOut:
-               memset(buf, 0, nbuf);
-               return 0;
-       }
-       n = read(fd, buf, nbuf-1);
-       close(fd);
-       if(n <= 0){
-               close(fd);
-               goto ErrOut;
-       }
-       buf[n] = 0;
-       return n;
-}
+       int mfd, n, i;
+       va_list arg;
+       Dir *dir;
 
-int urlopen(Url *url, int method, char *body){
-       int conn, ctlfd, fd, n;
-       char buf[1024+1];
-
-       snprint(buf, sizeof buf, "%s/clone", mtpt);
-       if((ctlfd = open(buf, ORDWR)) < 0)
-               return -1;
-       if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
-               close(ctlfd);
-               return -1;
-       }
-       buf[n] = 0;
-       conn = atoi(buf);
-
-       if(url->basename[0]){
-               n = snprint(buf, sizeof buf, "baseurl %s", url->basename);
-               write(ctlfd, buf, n);
-       }
-       n = snprint(buf, sizeof buf, "url %s", url->reltext);
-       if(write(ctlfd, buf, n) != n){
-       ErrOut:
-               close(ctlfd);
-               return -1;
-       }
-
-       if(method == POST && body){
-               snprint(buf, sizeof buf, "%s/%d/postbody", mtpt, conn);
-               if((fd = open(buf, OWRITE)) < 0)
-                       goto ErrOut;
-               n = strlen(body);
-               if(write(fd, body, n) != n){
+       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);
-                       goto ErrOut;
-               }
-               close(fd);
        }
-
-       snprint(buf, sizeof buf, "%s/%d/body", mtpt, conn);
-       if((fd = open(buf, OREAD)) < 0)
-               goto ErrOut;
-
-       snprint(buf, sizeof buf, "%s/%d/parsed", mtpt, conn);
-       readstr(url->fullname, sizeof(url->fullname), buf, "url");
-       readstr(url->tag, sizeof(url->tag), buf, "fragment");
-
-       snprint(buf, sizeof buf, "%s/%d", mtpt, conn);
-       readstr(buf, sizeof buf, buf, "contenttype");
-       url->type = content2type(buf, url->fullname);
-
-       close(ctlfd);
-       return fd;
+       free(dir);
 }
 
-int pipeline(char *cmd, int fd)
+int pipeline(int fd, char *fmt, ...)
 {
+       char buf[80], *argv[4];
+       va_list arg;
        int pfd[2];
 
-       if(pipe(pfd)==-1){
-Err:
+       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", cmd);
+               werrstr("pipeline for %s failed: %r", buf);
                return -1;
        }
-       switch(fork()){
+       switch(rfork(RFPROC|RFMEM|RFFDG|RFREND|RFNOWAIT)){
        case -1:
                close(pfd[0]);
                close(pfd[1]);
                goto Err;
        case 0:
-               dup(fd, 0);
-               dup(pfd[0], 1);
-               close(pfd[0]);
-               close(pfd[1]);
-               execl("/bin/rc", "rc", "-c", cmd, 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(pfd[0]);
        close(fd);
-       return pfd[1];
+       close(pfd[1]);
+       return pfd[0];
 }
 
-/*
- * select the file at the given url
- */
-void selurl(char *urlname){
+char*
+urlstr(Url *url){
+       if(url->fullname[0])
+               return url->fullname;
+       return url->reltext;
+}
+Url* selurl(char *urlname){
        static Url url;
-       seturl(&url, urlname, current?
-               current->url->fullname :
-               defurl.fullname);
+       seturl(&url, urlname, current ? current->url->fullname : "");
        selection=&url;
-       message("selected: %s", selection->fullname);
+       message("selected: %s", urlstr(selection));
+       plgrabkb(cmd);          /* for snarf */
+       return selection;
 }
 void seturl(Url *url, char *urlname, char *base){
-       strncpy(url->reltext, urlname, sizeof(url->reltext));
-       strncpy(url->basename, base, sizeof(url->basename));
+       nstrcpy(url->reltext, urlname, sizeof(url->reltext));
+       nstrcpy(url->basename, base, sizeof(url->basename));
        url->fullname[0] = 0;
-       url->charset[0] = 0;
        url->tag[0] = 0;
-       url->type = 0;
        url->map = 0;
 }
 Url *copyurl(Url *u){
@@ -819,53 +931,70 @@ Url *copyurl(Url *u){
        return v;
 }
 void freeurl(Url *u){
-       if(u!=&defurl && u!=&badurl)
-               free(u);
+       free(u);
 }
 
 /*
  * get the file at the given url
  */
-void geturl(char *urlname, int method, char *body, int cache, int map){
-       int i, fd;
+void geturl(char *urlname, int post, int plumb, int map){
+       int i, fd, typ, pfd[2];
        char cmd[NNAME];
-       int pfd[2];
+       ulong n;
        Www *w;
 
+       if(*urlname == '#' && post < 0){
+               scrollto(urlname+1);
+               return;
+       }
+
        selurl(urlname);
        selection->map=map;
 
        message("getting %s", selection->reltext);
        esetcursor(&patientcurs);
        for(;;){
-               if((fd=urlopen(selection, method, body)) < 0){
+               if((fd=urlget(selection, post)) < 0){
                        message("%r");
-                       setcurrent(-1, 0);
                        break;
                }
                message("getting %s", selection->fullname);
-               if(selection->type&COMPRESS)
-                       fd=pipeline("/bin/uncompress", fd);
-               else if(selection->type&GUNZIP)
-                       fd=pipeline("/bin/gunzip", fd);
-               switch(selection->type&~COMPRESSION){
+               if(mothmode && !plumb)
+                       typ = -1;
+               else
+                       typ = snooptype(fd);
+               switch(typ){
                default:
-                       message("Bad type %x in geturl", selection->type);
+                       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 PLAIN:
                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){
-                               extern void freeform(void *p);
-                               extern void freepix(void *p);
-
                                /* wait for the reader to finish the document */
                                while(!w->finished && !w->alldone){
-                                       unlockdisplay(display);
+                                       drawlock(0);
                                        sleep(10);
-                                       lockdisplay(display);
+                                       drawlock(1);
                                }
-
                                freetext(w->text);
                                freeform(w->form);
                                freepix(w->pix);
@@ -878,23 +1007,27 @@ void geturl(char *urlname, int method, char *body, int cache, int map){
                                w->url=copyurl(selection);
                        w->finished = 0;
                        w->alldone = 0;
-                       gettext(w, fd, selection->type&~COMPRESSION);
+                       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 POSTSCRIPT:
                case GIF:
                case JPEG:
                case PNG:
-               case PDF:
-                       filter("page -w", fd);
-                       break;
-               case TIFF:
-                       filter("/sys/lib/mothra/tiffview", fd);
-                       break;
-               case XBM:
-                       filter("fb/xbm2pic|fb/9v", fd);
+               case BMP:
+               case PAGE:
+                       filter(fd, "exec page -w");
                        break;
                }
                break;
@@ -904,6 +1037,8 @@ void geturl(char *urlname, int method, char *body, int cache, int map){
 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){
@@ -912,55 +1047,94 @@ void updtext(Www *w){
                        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(root, screen);
+       pldraw(text, screen);
 }
-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,
-};
-int confirm(int b){
-       Mouse down, up;
-       esetcursor(&confirmcursor);
-       do down=emouse(); while(!down.buttons);
-       do up=emouse(); while(up.buttons);
+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, 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();
-       return down.buttons==(1<<(b-1));
+}
+
+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){
-       int fd;
-       fd=create("/dev/snarf", OWRITE, 0666);
-       if(fd>=0){
-               fprint(fd, "%s", selection->fullname);
-               close(fd);
-       }
+       if(p==0 || p==cmd){
+               if(selection){
+                       plputsnarf(urlstr(selection));
+                       plsnarf(text);
+               }else
+                       message("no url selected");
+       }else
+               plsnarf(p);
 }
 void paste(Panel *p){
-       char buf[1024];
-       int n, len, fd;
-       fd=open("/dev/snarf", OREAD);
-       strncpy(buf, plentryval(p), sizeof(buf));
-       len=strlen(buf);
-       n=read(fd, buf+len, 1023-len);
-       if(n>0){
-               buf[len+n]='\0';
-               plinitentry(cmd, PACKE|EXPAND, 0, buf, docmd);
-               pldraw(cmd, screen);
-       }
-       close(fd);
+       if(p==0) p=cmd;
+       plpaste(p);
 }
 void hit3(int button, int item){
-       char name[100], *home;
+       char name[NNAME];
+       char file[128];
        Panel *swap;
        int fd;
        USED(button);
@@ -969,29 +1143,37 @@ void hit3(int button, int item){
                swap=root;
                root=alt;
                alt=swap;
-               current->yoffs=plgetpostextview(text);
+               if(current)
+                       current->yoffs=plgetpostextview(text);
                swap=text;
                text=alttext;
                alttext=swap;
                defdisplay=!defdisplay;
                plpack(root, screen->r);
-               plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
-               plsetpostextview(text, current->yoffs);
+               if(current){
+                       plinittextview(text, PACKE|EXPAND, Pt(0, 0), current->text, dolink);
+                       plsetpostextview(text, current->yoffs);
+               }
                pldraw(root, screen);
                break;
        case 1:
-               snarf(cmd);
+               mothon(current, !mothmode);
                break;
        case 2:
-               paste(cmd);
+               snarf(plkbfocus);
                break;
        case 3:
-               home=getenv("home");
-               if(home==0){
-                       message("no $home");
-                       return;
+               paste(plkbfocus);
+               break;
+       case 4:
+               search();
+               break;
+       case 5:
+               if(!selection){
+                       message("no url selected");
+                       break;
                }
-               snprint(name, sizeof(name), "%s/lib/mothra/hit.html", home);
+               snprint(name, sizeof(name), "%s/hit.html", mkhome());
                fd=open(name, OWRITE);
                if(fd==-1){
                        fd=create(name, OWRITE, 0666);
@@ -999,28 +1181,20 @@ void hit3(int button, int item){
                                message("can't open %s", name);
                                return;
                        }
-                       fprint(fd, "<head><title>Hit List</title></head>\n");
+                       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",
-                       selection->fullname, selection->fullname);
+               fprint(fd, "<p><a href=\"%s\">%s</a>\n", urlstr(selection), urlstr(selection));
                close(fd);
                break;
-       case 4:
-               home=getenv("home");
-               if(home==0){
-                       message("no $home");
-                       return;
-               }
-               snprint(name, sizeof(name), "file:%s/lib/mothra/hit.html", home);
-               geturl(name, GET, 0, 1, 0);
+       case 6:
+               snprint(name, sizeof(name), "file:%s/hit.html", mkhome());
+               geturl(name, -1, 1, 0);
                break;
-       case 5:
-               if(confirm(3)){
-                       draw(screen, screen->r, display->white, 0, ZP);
+       case 7:
+               if(confirm(3))
                        exits(0);
-               }
                break;
        }
 }