]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/mothra/mothra.c
mothra: fix alt display resizing, filter control characters in panel entries, use...
[plan9front.git] / sys / src / cmd / mothra / mothra.c
index 60e8ba544e88042ddb44e4d43e9163e5a8e664ec..32b09ac33d19f04b3b75b915525cd3c8f2d9a5da 100644 (file)
@@ -30,7 +30,6 @@ Url defurl={
        "http://cat-v.org/",
        "",
        "",
-       HTML,
 };
 Url badurl={
        "",
@@ -38,7 +37,6 @@ Url badurl={
        "No file loaded",
        "",
        "",
-       HTML,
 };
 Cursor patientcurs={
        0, 0,
@@ -76,12 +74,25 @@ Cursor readingcurs={
        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, }
+};
 char *mtpt="/mnt/web";
 Www *current=0;
 Url *selection=0;
 int logfile;
+int mothmode;
 void docmd(Panel *, char *);
 void doprev(Panel *, int, int);
+char *urlstr(Url *);
 void selurl(char *);
 void setcurrent(int, char *);
 char *genwww(Panel *, int);
@@ -90,9 +101,9 @@ void dolink(Panel *, int, Rtext *);
 void hit3(int, int);
 char *buttons[]={
        "alt display",
+       "moth mode",
        "snarf url",
        "paste",
-       "save url",
        "save hit",
        "hit list",
        "exit",
@@ -127,12 +138,14 @@ void 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.max.y) break;
+                       if(t->r.min.y+yoffs>=text->r.min.y
+                       && t->b==0
+                       && subpanel(t->p, pl_kbfocus)) return;
+               }
        }
        plgrabkb(cmd);
 }
@@ -226,11 +239,24 @@ int mkmfile(char *stem, int mode){
                f=create(stem, OWRITE, mode);
        return f;
 }
+
+void donecurs(void){
+       if(current == nil)
+               return;
+       if(mothmode)
+               esetcursor(&mothcurs);
+       else
+               esetcursor(current->alldone ? 0 : &readingcurs);
+}
+
+void scrollto(char *tag);
+
 void main(int argc, char *argv[]){
        Event e;
        enum { Eplumb = 128 };
        Plumbmsg *pm;
        Www *new;
+       Action *a;
        char *url;
        int errfile;
        int i;
@@ -310,11 +336,13 @@ void main(int argc, char *argv[]){
                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);
+                               donecurs();
                        }
                        else if(current->changed){
                                updtext(current);
@@ -366,6 +394,26 @@ void main(int argc, char *argv[]){
                }
        }
 }
+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;
@@ -402,6 +450,7 @@ void eresized(int new){
        plinitlabel(curttl, PACKE|EXPAND, "---");
        plinitlabel(cururl, PACKE|EXPAND, "---");
        plpack(root, r);
+       plpack(alt, r);
        if(current){
                plinitlabel(curttl, PACKE|EXPAND, current->title);
                plinitlabel(cururl, PACKE|EXPAND, current->url->fullname);
@@ -438,17 +487,29 @@ char *genwww(Panel *, int index){
        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;
@@ -460,54 +521,67 @@ void setcurrent(int index, char *tag){
        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;
-                       }
-               }
-       }
-       plsetpostextview(text, current->yoffs);
+       scrollto(tag);
        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;
+void save(int ifd, char *name){
        char buf[4096];
-       ofd=create(name, OWRITE, 0666);
-       if(ofd==-1){
+       int 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, OEXCL|OWRITE, 0666);
+       if(ofd < 0){
+               message("save: %s: %r", name);
+               return;
        }
-       switch(rfork(RFNOTEG|RFFDG|RFPROC|RFNOWAIT)){
+       switch(rfork(RFNOTEG|RFNAMEG|RFFDG|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);
+               snprint(buf, sizeof(buf), "-pid %d", getpid());
+               if(newwindow(buf) != -1){
+                       int blk, cfd, n;
+                       vlong off;
+
+                       close(1); open("/dev/cons", OWRITE);
+                       if((cfd = open("/dev/label", OWRITE)) >= 0){
+                               fprint(cfd, "save %s", name);
+                               close(cfd);
+                       }
+                       if((cfd = open("/dev/wctl", OWRITE)) >= 0){
+                               fprint(cfd, "scroll\n");
+                               close(cfd);
+                       }
+                       off = 0;
+                       blk = 0;
+                       werrstr("");
+                       for(;;){
+                               if((blk++ % 4) == 0){
+                                       if(off > 0)
+                                               print("\n");
+                                       print("%s: ", name);
+                               }
+                               if((n=read(ifd, buf, sizeof(buf))) <= 0)
+                                       break;
+                               if(write(ofd, buf, n) != n)
+                                       break;
+                               off += n;
+                               print("%lldK... ", off/1024);
+                       }
+                       print("%r\n");
+               }
+               exits(0);
        }
        close(ifd);
        close(ofd);
+       donecurs();
 }
 void screendump(char *name, int full){
        Image *b;
@@ -532,6 +606,24 @@ 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.
  */
@@ -542,7 +634,7 @@ void docmd(Panel *p, char *s){
         * Non-command does a get on the url
         */
        if(s[0]!='\0' && s[1]!='\0' && s[1]!=' ')
-               geturl(s, GET, 0, 1, 0);
+               geturl(s, GET, 0, 0, 0);
        else switch(s[0]){
        default:
                message("Unknown command %s, type h for help", s);
@@ -555,12 +647,13 @@ void docmd(Panel *p, char *s){
        case 'g':
                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, GET, 0, 0, 0);
                break;
        case 'j':
                s = arg(s);
@@ -569,11 +662,6 @@ void docmd(Panel *p, char *s){
                else
                        message("Usage: j index");
                break;
-       case 'r':
-               s = arg(s);
-               if(*s=='\0' && selection)
-                       geturl(selection->fullname, GET, 0, 0, 0);
-               break;
        case 'W':
                s = arg(s);
                if(s=='\0'){
@@ -592,17 +680,17 @@ void docmd(Panel *p, char *s){
                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");
-                               break;
-                       }
+               if(!selection){
+                       message("no url selected");
+                       break;
+               }
+               if(s==0 || *s=='\0')
+                       s = urltofile(selection);
+               if(s==0 || *s=='\0'){
+                       message("Usage: s file");
+                       break;
                }
-               save(selection, s);
+               save(urlopen(selection, GET, 0), s);
                break;
        case 'q':
                draw(screen, screen->r, display->white, 0, ZP);
@@ -613,7 +701,7 @@ void docmd(Panel *p, char *s){
 }
 void hiturl(int buttons, char *url, int map){
        switch(buttons){
-       case 1: geturl(url, GET, 0, 1, map); break;
+       case 1: geturl(url, GET, 0, 0, map); break;
        case 2: selurl(url); break;
        case 4: message("Button 3 hit on url can't happen!"); break;
        }
@@ -638,23 +726,25 @@ 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;
+
        a=word->user;
-       if(a && 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
-                       hiturl(buttons, a->link, 0);
-       }
+       if(a == nil || a->image == nil && a->link == nil)
+               return;
+       if(mothmode)
+               hiturl(buttons, a->image ? a->image : a->link, 0);
+       else 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
+               hiturl(buttons, a->link ? a->link : a->image, 0);
 }
+
 void filter(char *cmd, int fd){
        flushimage(display, 1);
        switch(rfork(RFFDG|RFPROC|RFNOWAIT)){
@@ -678,10 +768,6 @@ void gettext(Www *w, int fd, int type){
        switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFMEM)){
        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)
@@ -713,39 +799,6 @@ 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)
-{
-       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 fileurlopen(Url *url){
        char *rel, *base, *x;
        int fd;
@@ -777,10 +830,24 @@ int fileurlopen(Url *url){
        memset(url->fullname, 0, sizeof(url->fullname));
        strcpy(url->fullname, "file:");
        fd2path(fd, url->fullname+5, sizeof(url->fullname)-6);
-       url->type = content2type("application/octet-stream", url->fullname);
        return fd;
 }
 
+int readstr(char *buf, int nbuf, char *base, char *name){
+       char path[128];
+       int n, fd;
+
+       n = 0;
+       snprint(path, sizeof path, "%s/%s", base, name);
+       if((fd = open(path, OREAD)) >= 0){
+               if((n = read(fd, buf, nbuf-1)) < 0)
+                       n = 0;
+               close(fd);
+       }
+       buf[n] = 0;
+       return n;
+}
+
 int urlopen(Url *url, int method, char *body){
        int conn, ctlfd, fd, n;
        char buf[1024+1], *p;
@@ -790,7 +857,6 @@ int urlopen(Url *url, int method, char *body){
        if(method == GET)
                if((fd = fileurlopen(url)) >= 0)
                        return fd;
-
        snprint(buf, sizeof buf, "%s/clone", mtpt);
        if((ctlfd = open(buf, ORDWR)) < 0)
                return -1;
@@ -800,7 +866,6 @@ int urlopen(Url *url, int method, char *body){
        }
        buf[n] = 0;
        conn = atoi(buf);
-
        if(url->basename[0]){
                n = snprint(buf, sizeof buf, "baseurl %s", url->basename);
                write(ctlfd, buf, n);
@@ -811,7 +876,6 @@ int urlopen(Url *url, int method, char *body){
                close(ctlfd);
                return -1;
        }
-
        if(method == POST && body){
                snprint(buf, sizeof buf, "%s/%d/postbody", mtpt, conn);
                if((fd = open(buf, OWRITE)) < 0)
@@ -823,28 +887,12 @@ int urlopen(Url *url, int method, char *body){
                }
                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->charset[0] = 0;
-       if(p = cistrstr(buf, "charset=")){
-               p += 8;
-               strncpy(url->charset, p, sizeof(url->charset));
-               if(p = strchr(url->charset, ';'))
-                       *p = 0;
-       }
-       if(p = strchr(buf, ';'))
-               *p = 0;
-       url->type = content2type(buf, url->fullname);
-
        close(ctlfd);
        return fd;
 }
@@ -877,24 +925,27 @@ Err:
        return pfd[1];
 }
 
-/*
- * select the file at the given url
- */
+char*
+urlstr(Url *url){
+       if(url->fullname[0])
+               return url->fullname;
+       if(url->reltext[0])
+               return url->reltext;
+       return nil;
+}
 void selurl(char *urlname){
        static Url url;
        seturl(&url, urlname, current?
                current->url->fullname :
                defurl.fullname);
        selection=&url;
-       message("selected: %s", selection->fullname[0] ? selection->fullname : selection->reltext);
+       message("selected: %s", urlstr(selection));
 }
 void seturl(Url *url, char *urlname, char *base){
        strncpy(url->reltext, urlname, sizeof(url->reltext));
        strncpy(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){
@@ -911,12 +962,17 @@ void freeurl(Url *u){
 /*
  * get the file at the given url
  */
-void geturl(char *urlname, int method, char *body, int cache, int map){
-       int i, fd;
-       char cmd[NNAME];
+void geturl(char *urlname, int method, char *body, int plumb, int map){
+       int i, fd, typ;
+       char *file, cmd[NNAME];
        int pfd[2];
        Www *w;
 
+       if(*urlname == '#'){
+               scrollto(urlname+1);
+               return;
+       }
+
        selurl(urlname);
        selection->map=map;
 
@@ -929,18 +985,39 @@ void geturl(char *urlname, int method, char *body, int cache, int map){
                        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);
+                       if(typ == GUNZIP){
+                               fd=pipeline("/bin/gunzip", fd);
+                               typ = snooptype(fd);
+                       }
+                       if(typ == COMPRESS){
+                               fd=pipeline("/bin/uncompress", fd);
+                               typ = snooptype(fd);
+                       }
+               }
+               switch(typ){
                default:
-                       message("Bad type %x in geturl", selection->type);
+                       if(plumb){
+                               message("unknown file type");
+                               close(fd);
+                               break;
+                       }
+                       file = urltofile(selection);
+                       if(!mothmode){
+                               message("save to '%s' ?", file);
+                               if(!confirm(1)){
+                                       message(mothra);
+                                       close(fd);
+                                       break;
+                               }
+                       }
+                       save(fd, file);
                        break;
                case HTML:
-                       snprint(cmd, sizeof(cmd), selection->charset[0] ?
-                               "/bin/uhtml -c %s" : "/bin/uhtml", selection->charset);
-                       fd = pipeline(cmd, fd);
+                       fd = pipeline("/bin/uhtml", fd);
                case PLAIN:
                        w = www(i = wwwtop++);
                        if(i >= NWWW){
@@ -966,24 +1043,29 @@ 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);
                        plinitlist(list, PACKN|FILLX, genwww, 8, doprev);
                        if(defdisplay) pldraw(list, screen);
                        setcurrent(i, selection->tag);
                        break;
-               case POSTSCRIPT:
                case GIF:
+                       if(rfork(RFFDG|RFNOTEG|RFPROC|RFNAMEG|RFNOWAIT) == 0){
+                               snprint(cmd, sizeof(cmd), "-pid %d", getpid());
+                               if(newwindow(cmd) != -1){
+                                       close(1); open("/dev/cons", OWRITE);
+                                       print("reading gif...\n");
+                                       filter("gif", fd);
+                               }
+                               exits(0);
+                       }
+                       close(fd);
+                       break;
                case JPEG:
                case PNG:
-               case PDF:
+               case BMP:
+               case PAGE:
                        filter("page -w", fd);
                        break;
-               case TIFF:
-                       filter("/sys/lib/mothra/tiffview", fd);
-                       break;
-               case XBM:
-                       filter("fb/xbm2pic|fb/9v", fd);
-                       break;
                }
                break;
        }
@@ -1005,31 +1087,49 @@ void updtext(Www *w){
        plsetpostextview(text, w->yoffs);
        pldraw(root, 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
+mothon(Www *w, int on)
+{
+       Rtext *t, *x;
+       Action *a, *ap;
+
+       if(current == nil || 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 || a->link == nil)
+                       continue;
+               x = t->next;
+               if(on){
+                       t->next = nil;
+                       ap=mallocz(sizeof(Action), 1);
+                       ap->link = strdup(a->link);
+                       plrtstr(&t->next, 0, 0, t->font, strdup("->"), 1, ap);
+                       t->next->next = x;
+               } else {
+                       t->next = x->next;
+                       x->next = nil;
+                       freetext(x);
+               }
+       }
+       updtext(w);
        donecurs();
-       return down.buttons==(1<<(b-1));
 }
+
 void snarf(Panel *p){
        int fd;
        fd=create("/dev/snarf", OWRITE, 0666);
        if(fd>=0){
-               fprint(fd, "%s", selection->fullname[0] ? selection->fullname : selection->reltext);
+               fprint(fd, "%s", urlstr(selection));
                close(fd);
        }
 }
@@ -1069,20 +1169,13 @@ void hit3(int button, int item){
                pldraw(root, screen);
                break;
        case 1:
-               snarf(cmd);
+               mothon(current, !mothmode);
                break;
        case 2:
-               paste(cmd);
+               snarf(cmd);
                break;
        case 3:
-               if(strrchr(selection->reltext, '/')){
-                       snprint(file, sizeof(file), "%s", strrchr(selection->reltext, '/')+1);
-                       if(file[0]==0)
-                               strcpy(file, "index");
-               } else
-                       snprint(file, sizeof(file), "%s", selection->reltext);
-               save(selection, file);
-               message("saved %s", file);
+               paste(cmd);
                break;
        case 4:
                snprint(name, sizeof(name), "%s/hit.html", home);
@@ -1093,12 +1186,11 @@ 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 5: