]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/page.c
ndb/dnsquery, ndb/csquery: handle long lines
[plan9front.git] / sys / src / cmd / page.c
index 23c3863ad584598cff9c1d4eca1741d5f3580c74..49478d3a41770cc136cd1b39c060308847691ea4 100644 (file)
@@ -8,20 +8,24 @@
 
 typedef struct Page Page;
 struct Page {
-       char    *label;
+       char    *name;
+       char    *delim;
 
        QLock;
+       char    *ext;
        void    *data;
        int     (*open)(Page *);
 
        Image   *image;
        int     fd;
-       int     gen;
 
        Page    *up;
        Page    *next;
        Page    *down;
        Page    *tail;
+
+       Page    *lnext;
+       Page    *lprev;
 };
 
 int zoom = 1;
@@ -30,11 +34,20 @@ int imode;
 int newwin;
 int rotate;
 int viewgen;
-int pagegen;
+int forward;   /* read ahead direction: >= 0 forwards, < 0 backwards */
 Point resize, pos;
 Page *root, *current;
+Page lru;
 QLock pagelock;
 int nullfd;
+char *pagewalk = nil;
+
+enum {
+       MiB     = 1024*1024,
+};
+
+ulong imemlimit = 16*MiB;
+ulong imemsize;
 
 Image *frame, *paper, *ground;
 
@@ -42,7 +55,6 @@ char pagespool[] = "/tmp/pagespool.";
 
 enum {
        NPROC = 4,
-       NAHEAD = 2,
        NBUF = 8*1024,
        NPATH = 1024,
 };
@@ -58,8 +70,10 @@ enum {
        Cdummy1,
        Cnext,
        Cprev,
+       Csnarf,
        Czerox,
        Cwrite,
+       Cext,
        Cdummy2,
        Cquit,
 };
@@ -80,8 +94,10 @@ struct {
        [Cdummy1]       "",             0, 0, 0,
        [Cnext]         "next",         Kright, ' ', '\n', 
        [Cprev]         "prev",         Kleft, Kbs, 0,
+       [Csnarf]        "snarf",        's', 0, 0,
        [Czerox]        "zerox",        'z', 0, 0,
        [Cwrite]        "write",        'w', 0, 0,
+       [Cext]          "ext",          'x', 0, 0,
        [Cdummy2]       "",             0, 0, 0,
        [Cquit]         "quit",         'q', Kdel, Keof,
 };
@@ -113,27 +129,25 @@ Cursor reading = {
         0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
 };
 
+int pagewalk1(Page *p);
+void showpage1(Page *);
 void showpage(Page *);
 void drawpage(Page *);
 Point pagesize(Page *);
 
 Page*
-addpage(Page *up, char *label, int (*popen)(Page *), void *pdata, int fd)
+addpage(Page *up, char *name, int (*popen)(Page *), void *pdata, int fd)
 {
        Page *p;
 
        p = mallocz(sizeof(*p), 1);
-       p->label = strdup(label);
-       p->gen = pagegen;
-       p->image = nil;
+       p->name = strdup(name);
+       p->delim = "!";
+       p->image = nil;
        p->data = pdata;
        p->open = popen;
        p->fd = fd;
 
-       p->down = nil;
-       p->tail = nil;
-       p->next = nil;
-
        qlock(&pagelock);
        if(p->up = up){
                if(up->tail == nil)
@@ -145,8 +159,11 @@ addpage(Page *up, char *label, int (*popen)(Page *), void *pdata, int fd)
        }
        qunlock(&pagelock);
 
-       if(up && current == up)
-               showpage(p);
+       if(up && current == up){
+               if(!pagewalk1(p))
+                       return p;
+               showpage1(p);
+       }
        return p;
 }
 
@@ -159,7 +176,7 @@ resizewin(Point size)
                return;
        /* add rio border */
        size = addpt(size, Pt(Borderwidth*2, Borderwidth*2));
-       if(display->image){
+       if(display->image != nil){
                Point dsize = subpt(display->image->r.max, display->image->r.min);
                if(size.x > dsize.x)
                        size.x = dsize.x;
@@ -191,6 +208,10 @@ catchnote(void *, char *msg)
                return 1;
        if(strstr(msg, "alarm"))
                return 1;
+       if(strstr(msg, "interrupt"))
+               return 1;
+       if(strstr(msg, "kill"))
+               exits("killed");
        return 0;
 }
 
@@ -254,14 +275,50 @@ pipeline(int fd, char *fmt, ...)
        close(pfd[0]);
 }
 
+static char*
+shortlabel(char *s)
+{
+       enum { NR=60 };
+       static char buf[NR*UTFmax];
+       int i, k, l;
+       Rune r;
+
+       l = utflen(s);
+       if(l < NR-2)
+               return s;
+       k = i = 0;
+       while(i < NR/2){
+               k += chartorune(&r, s+k);
+               i++;
+       }
+       strncpy(buf, s, k);
+       strcpy(buf+k, "...");
+       while((l-i) >= NR/2-4){
+               k += chartorune(&r, s+k);
+               i++;
+       }
+       strcat(buf, s+k);
+       return buf;
+}
+
+static char*
+pageaddr1(Page *p, char *s, char *e)
+{
+       if(p == nil || p == root)
+               return s;
+       return seprint(pageaddr1(p->up, s, e), e, "%s%s", p->up->delim, p->name);
+}
+
+/*
+ * returns address string of a page in the form:
+ * /dir/filename!page!subpage!...
+ */
 char*
-shortname(char *s)
+pageaddr(Page *p, char *buf, int nbuf)
 {
-       char *x;
-       if(x = strrchr(s, '/'))
-               if(x[1] != 0)
-                       return x+1;
-       return s;
+       buf[0] = 0;
+       pageaddr1(p, buf, buf+nbuf);
+       return buf;
 }
 
 int
@@ -280,8 +337,13 @@ popenimg(Page *p)
        }
 
        seek(fd, 0, 0);
-       if(p->data)
-               pipeline(fd, "%s", (char*)p->data);
+       if(p->data){
+               p->ext = p->data;
+               if(strcmp(p->ext, "ico") == 0)
+                       pipeline(fd, "exec %s -c", p->ext);
+               else
+                       pipeline(fd, "exec %s -t9", p->ext);
+       }
 
        /*
         * dont keep the file descriptor arround if it can simply
@@ -303,7 +365,7 @@ popenfilter(Page *p)
 {
        seek(p->fd, 0, 0);
        if(p->data){
-               pipeline(p->fd, "%s", (char*)p->data);
+               pipeline(p->fd, "exec %s", (char*)p->data);
                p->data = nil;
        }
        p->open = popenfile;
@@ -316,8 +378,8 @@ popentape(Page *p)
        char mnt[32], cmd[64], *argv[4];
 
        seek(p->fd, 0, 0);
-       snprint(mnt, sizeof(mnt), "/n/tapefs.%.12d%.8lux", getpid(), (ulong)p);
-       snprint(cmd, sizeof(cmd), "%s -m %s /fd/0", p->data, mnt);
+       snprint(mnt, sizeof(mnt), "/n/tapefs.%.12d%.8lux", getpid(), (ulong)(uintptr)p);
+       snprint(cmd, sizeof(cmd), "exec %s -m %s /fd/0", (char*)p->data, mnt);
        switch(rfork(RFPROC|RFMEM|RFFDG|RFREND)){
        case -1:
                close(p->fd);
@@ -380,7 +442,7 @@ popenepub(Page *p)
                while(n > 0 && s[n-1] == '\n')
                        n--;
                s[n] = 0;
-               addpage(p, shortname(buf), popenfile, strdup(buf), -1);
+               addpage(p, s, popenfile, strdup(buf), -1);
        }
        close(fd);
        return -1;
@@ -418,7 +480,7 @@ popenpdf(Page *p)
                        "(/fd/3) (w) file "
                        "dup flushfile "
                        "dup (THIS IS NOT AN INFERNO BITMAP\\n) writestring "
-                       "flushfile\n", p->label);
+                       "flushfile\n", p->name);
                while((n = read(0, buf, sizeof buf)) > 0){
                        if(memcmp(buf, "THIS IS NOT AN INFERNO BITMAP\n", 30) == 0)
                                break;
@@ -456,6 +518,7 @@ popengs(Page *p)
        pdf = 0;
        ifd = p->fd;
        p->fd = -1;
+       p->open = nil;
        seek(ifd, 0, 0);
        if(read(ifd, buf, 5) != 5)
                goto Err0;
@@ -480,7 +543,7 @@ popengs(Page *p)
                goto Err1;
        }
 
-       argv[0] = p->data;
+       argv[0] = (char*)p->data;
        switch(rfork(RFPROC|RFMEM|RFFDG|RFREND|RFNOWAIT)){
        case -1:
                goto Err2;
@@ -658,11 +721,14 @@ popenfile(Page *p)
        "application/x-compress",       popenfilter,    "uncompress",
        "application/x-gzip",           popenfilter,    "gunzip",
        "application/x-bzip2",          popenfilter,    "bunzip2",
-       "image/gif",                    popenimg,       "gif -t9",
-       "image/jpeg",                   popenimg,       "jpg -t9",
-       "image/png",                    popenimg,       "png -t9",
-       "image/ppm",                    popenimg,       "ppm -t9",
-       "image/bmp",                    popenimg,       "bmp -t9",
+       "image/gif",                    popenimg,       "gif",
+       "image/jpeg",                   popenimg,       "jpg",
+       "image/png",                    popenimg,       "png",
+       "image/tiff",                   popenimg,       "tif",
+       "image/ppm",                    popenimg,       "ppm",
+       "image/bmp",                    popenimg,       "bmp",
+       "image/tga",                    popenimg,       "tga",
+       "image/x-icon",                 popenimg,       "ico",
        "image/p9bit",                  popenimg,       nil,
        };
 
@@ -672,8 +738,10 @@ popenfile(Page *p)
 
        fd = p->fd;
        p->fd = -1;
+       p->ext = nil;
        file = p->data;
        p->data = nil;
+       p->open = nil;
        if(fd < 0){
                if((fd = open(file, OREAD)) < 0){
                Err0:
@@ -699,7 +767,8 @@ popenfile(Page *p)
                        p->open = popenepub;
                        return p->open(p);
                }
-
+               if(strcmp(pageaddr(p, buf, sizeof(buf)), file) == 0)
+                       p->delim = "/";
                if((n = dirreadall(fd, &d)) < 0)
                        goto Err1;
                qsort(d, n, sizeof d[0], dircmp);
@@ -753,10 +822,10 @@ popenfile(Page *p)
 Page*
 nextpage(Page *p)
 {
-       if(p && p->down)
+       if(p != nil && p->down != nil)
                return p->down;
-       while(p){
-               if(p->next)
+       while(p != nil){
+               if(p->next != nil)
                        return p->next;
                p = p->up;
        }
@@ -768,8 +837,8 @@ prevpage(Page *x)
 {
        Page *p, *t;
 
-       if(x){
-               for(p = root->down; p; p = t)
+       if(x != nil){
+               for(p = root->down; p != nil; p = t)
                        if((t = nextpage(p)) == x)
                                return p;
        }
@@ -786,91 +855,133 @@ openpage(Page *p)
                p->open = nil;
        else {
                if(rotate)
-                       pipeline(fd, "rotate -r %d", rotate);
+                       pipeline(fd, "exec rotate -r %d", rotate);
                if(resize.x)
-                       pipeline(fd, "resize -x %d", resize.x);
+                       pipeline(fd, "exec resize -x %d", resize.x);
                else if(resize.y)
-                       pipeline(fd, "resize -y %d", resize.y);
+                       pipeline(fd, "exec resize -y %d", resize.y);
        }
        return fd;
 }
 
+static ulong
+imagesize(Image *i)
+{
+       if(i == nil)
+               return 0;
+       return Dy(i->r)*bytesperline(i->r, i->depth);
+}
+
+static void
+lunlink(Page *p)
+{
+       if(p->lnext == nil || p->lnext == p)
+               return;
+       p->lnext->lprev = p->lprev;
+       p->lprev->lnext = p->lnext;
+       p->lnext = nil;
+       p->lprev = nil;
+}
+
+static void
+llinkhead(Page *p)
+{
+       lunlink(p);
+       p->lnext = lru.lnext;
+       p->lprev = &lru;
+       p->lnext->lprev = p;
+       p->lprev->lnext = p;
+}
+
 void
 loadpage(Page *p)
 {
        int fd;
 
-       if(p->open && p->image == nil){
-               if((fd = openpage(p)) >= 0){
-                       pagegen++;
+       qlock(&lru);
+       llinkhead(p);
+       qunlock(&lru);
+
+       if(p->open != nil && p->image == nil){
+               fd = openpage(p);
+               if(fd >= 0){
                        if((p->image = readimage(display, fd, 1)) == nil)
                                fprint(2, "readimage: %r\n");
                        close(fd);
                }
                if(p->image == nil)
                        p->open = nil;
+               else {
+                       lockdisplay(display);
+                       imemsize += imagesize(p->image);
+                       unlockdisplay(display);
+               }
        }
-       p->gen = pagegen;
 }
 
 void
 unloadpage(Page *p)
 {
+       qlock(&lru);
+       lunlink(p);
+       qunlock(&lru);
+
        if(p->open == nil || p->image == nil)
                return;
        lockdisplay(display);
+       imemsize -= imagesize(p->image);
        freeimage(p->image);
        unlockdisplay(display);
        p->image = nil;
 }
 
 void
-unloadpages(int age)
+unloadpages(ulong limit)
 {
        Page *p;
 
-       for(p = root->down; p; p = nextpage(p)){
-               if(age == 0)    /* synchronous flush */
-                       qlock(p);
-               else if(!canqlock(p))
-                       continue;
-               if((pagegen - p->gen) >= age)
-                       unloadpage(p);
+       while(imemsize >= limit && (p = lru.lprev) != &lru){
+               qlock(p);
+               unloadpage(p);
                qunlock(p);
        }
 }
 
 void
-loadpages(Page *p, int ahead, int oviewgen)
+loadpages(Page *p, int oviewgen)
 {
-       int i;
-
-       ahead++;        /* load at least one */
-       unloadpages(ahead*2);
-       for(i = 0; i < ahead && p; p = nextpage(p), i++){
-               if(viewgen != oviewgen)
+       while(p != nil && viewgen == oviewgen){
+               qlock(p);
+               loadpage(p);
+               if(viewgen != oviewgen){
+                       unloadpage(p);
+                       qunlock(p);
                        break;
-               if(canqlock(p)){
-                       loadpage(p);
-                       if(viewgen != oviewgen){
-                               unloadpage(p);
-                               qunlock(p);
-                               break;
-                       }
-                       if(p == current){
-                               Point size;
-
-                               esetcursor(nil);
-                               size = pagesize(p);
-                               if(size.x && size.y && newwin){
-                                       newwin = 0;
-                                       resizewin(size);
-                               }
-                               lockdisplay(display);
-                               drawpage(p);
-                               unlockdisplay(display);
+               }
+               if(p == current){
+                       Point size;
+
+                       esetcursor(nil);
+                       size = pagesize(p);
+                       if(size.x && size.y && newwin){
+                               newwin = 0;
+                               resizewin(size);
                        }
-                       qunlock(p);
+                       lockdisplay(display);
+                       drawpage(p);
+                       unlockdisplay(display);
+               }
+               qunlock(p);
+               if(p != current && imemsize >= imemlimit)
+                       break;          /* only one page ahead once we reach the limit */
+               if(forward < 0){
+                       if(p->up == nil || p->up->down == p)
+                               break;
+                       p = prevpage(p);
+               } else {
+                       if(p->next == nil)
+                               break;
+                       p = nextpage(p);
                }
        }
 }
@@ -989,7 +1100,7 @@ zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int
        dr = r;
        for(sp=dr.min; dr.min.x < r.max.x; sp.x++){
                dr.max.x = dr.min.x+1;
-               if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
+               if(b != nil) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
                gendrawdiff(d, dr, top, t, sp, nil, ZP, SoverD);
                for(dr.min.x++; ++a.x < f && dr.min.x < r.max.x; dr.min.x++){
                        dr.max.x = dr.min.x+1;
@@ -1003,7 +1114,7 @@ zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int
 Point
 pagesize(Page *p)
 {
-       return p->image ? mulpt(subpt(p->image->r.max, p->image->r.min), zoom) : ZP;
+       return p->image != nil ? mulpt(subpt(p->image->r.max, p->image->r.min), zoom) : ZP;
 }
 
 void
@@ -1020,15 +1131,15 @@ drawpage(Page *p)
        Rectangle r;
        Image *i;
 
-       if(i = p->image){
+       if((i = p->image) != nil){
                r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min));
                zoomdraw(screen, r, ZR, paper, i, i->r.min, zoom);
        } else {
-               r = Rpt(ZP, stringsize(font, p->label));
+               r = Rpt(ZP, stringsize(font, p->name));
                r = rectaddpt(r, addpt(subpt(divpt(subpt(screen->r.max, screen->r.min), 2),
                        divpt(r.max, 2)), screen->r.min));
                draw(screen, r, paper, nil, ZP);
-               string(screen, r.min, display->black, ZP, font, p->label);
+               string(screen, r.min, display->black, ZP, font, p->name);
        }
        drawframe(r);
 }
@@ -1040,35 +1151,106 @@ translate(Page *p, Point d)
        Image *i;
 
        i = p->image;
-       if(i==0 || d.x==0 && d.y==0)
+       if(i==nil || d.x==0 && d.y==0)
                return;
        r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min));
        pos = addpt(pos, d);
        nr = rectaddpt(r, d);
-       rectclip(&r, screen->r);
-       draw(screen, rectaddpt(r, d), screen, nil, r.min);
+       if(rectclip(&r, screen->r))
+               draw(screen, rectaddpt(r, d), screen, nil, r.min);
+       else
+               r = ZR;
        zoomdraw(screen, nr, rectaddpt(r, d), paper, i, i->r.min, zoom);
        drawframe(nr);
 }
 
+int
+pagewalk1(Page *p)
+{
+       char *s;
+       int n;
+
+       if((s = pagewalk) == nil || *s == 0)
+               return 1;
+       n = strlen(p->name);
+       if(n == 0 || strncmp(s, p->name, n) != 0)
+               return 0;
+       if(s[n] == 0){
+               pagewalk = nil;
+               return 1;
+       }
+       if(s[n] == '/' || s[n] == '!'){
+               pagewalk = s + n+1;
+               return 1;
+       }
+       return 0;
+}
+
+Page*
+trywalk(char *name, char *addr)
+{
+       static char buf[NPATH];
+       Page *p, *a;
+
+       pagewalk = nil;
+       memset(buf, 0, sizeof(buf));
+       snprint(buf, sizeof(buf), "%s%s%s",
+               name != nil ? name : "",
+               (name != nil && addr != nil) ? "!" : "", 
+               addr != nil ? addr : "");
+       pagewalk = buf;
+
+       a = nil;
+       if(root != nil){
+               p = root->down;
+       Loop:
+               for(; p != nil; p = p->next)
+                       if(pagewalk1(p)){
+                               a = p;
+                               p = p->down;
+                               goto Loop;
+                       }
+       }
+       return a;
+}
+
 Page*
 findpage(char *name)
 {
        Page *p;
        int n;
 
+       if(name == nil)
+               return nil;
+
        n = strlen(name);
-       /* look in current document first */
-       if(current && current->up){
-               for(p = current->up->down; p; p = p->next)
-                       if(cistrncmp(p->label, name, n) == 0)
+       /* look in current document */
+       if(current != nil && current->up != nil){
+               for(p = current->up->down; p != nil; p = p->next)
+                       if(cistrncmp(p->name, name, n) == 0)
                                return p;
        }
        /* look everywhere */
-       for(p = root->down; p; p = nextpage(p))
-               if(cistrncmp(p->label, name, n) == 0)
-                       return p;
-       return nil;
+       if(root != nil){
+               for(p = root->down; p != nil; p = nextpage(p))
+                       if(cistrncmp(p->name, name, n) == 0)
+                               return p;
+       }
+       /* try bookmark */
+       return trywalk(name, nil);
+}
+
+void
+writeaddr(Page *p, char *file)
+{
+       char buf[NPATH], *s;
+       int fd;
+
+       s = pageaddr(p, buf, sizeof(buf));
+       if((fd = open(file, OWRITE)) >= 0){
+               write(fd, s, strlen(s));
+               close(fd);
+       }
 }
 
 Page*
@@ -1076,7 +1258,7 @@ pageat(int i)
 {
        Page *p;
 
-       for(p = root->down; i > 0 && p; p = nextpage(p))
+       for(p = root->down; i > 0 && p != nil; p = nextpage(p))
                i--;
        return i ? nil : p;
 }
@@ -1087,7 +1269,7 @@ pageindex(Page *x)
        Page *p;
        int i;
 
-       for(i = 0, p = root->down; p && p != x; p = nextpage(p))
+       for(i = 0, p = root->down; p != nil && p != x; p = nextpage(p))
                i++;
        return (p == x) ? i : -1;
 }
@@ -1096,8 +1278,9 @@ char*
 pagemenugen(int i)
 {
        Page *p;
-       if(p = pageat(i))
-               return p->label;
+
+       if((p = pageat(i)) != nil)
+               return shortlabel(p->name);
        return nil;
 }
 
@@ -1109,8 +1292,13 @@ cmdmenugen(int i)
        return cmds[i].m;
 }
 
+/*
+ * spawn new proc to load a run of pages starting with p
+ * the display should *not* be locked as it gets called
+ * from recursive page load.
+ */
 void
-showpage(Page *p)
+showpage1(Page *p)
 {
        static int nproc;
        int oviewgen;
@@ -1118,40 +1306,44 @@ showpage(Page *p)
        if(p == nil)
                return;
        esetcursor(&reading);
+       writeaddr(p, "/dev/label");
        current = p;
        oviewgen = viewgen;
-       if(++nproc > NPROC)
-               if(waitpid() > 0)
-                       nproc--;
        switch(rfork(RFPROC|RFMEM)){
        case -1:
                sysfatal("rfork: %r");
        case 0:
-               loadpages(p, NAHEAD, oviewgen);
+               loadpages(p, oviewgen);
                exits(nil);
        }
+       if(++nproc >= NPROC)
+               if(waitpid() > 0)
+                       nproc--;
 }
 
+/* recursive display lock, called from main proc only */
 void
-shownext(void)
-{
-       Page *p;
-
-       for(p = nextpage(current); p; p = nextpage(p))
-               if(p->image || p->open)
-                       break;
-       showpage(p);
+drawlock(int dolock){
+       static int ref = 0;
+       if(dolock){
+               if(ref++ == 0)
+                       lockdisplay(display);
+       } else {
+               if(--ref == 0)
+                       unlockdisplay(display);
+       }
 }
 
+
 void
-showprev(void)
+showpage(Page *p)
 {
-       Page *p;
-
-       for(p = prevpage(current); p; p = prevpage(p))
-               if(p->image || p->open)
-                       break;
-       showpage(p);
+       if(p == nil)
+               return;
+       drawlock(0);
+       unloadpages(imemlimit);
+       showpage1(p);
+       drawlock(1);
 }
 
 void
@@ -1162,7 +1354,7 @@ zerox(Page *p)
 
        if(p == nil)
                return;
-       esetcursor(&reading);
+       drawlock(0);
        qlock(p);
        if((fd = openpage(p)) < 0)
                goto Out;
@@ -1178,7 +1370,50 @@ zerox(Page *p)
        close(fd);
 Out:
        qunlock(p);
-       esetcursor(nil);
+       drawlock(1);
+}
+
+void
+showext(Page *p)
+{
+       char label[64], *argv[4];
+       Point ps;
+       int fd;
+
+       if(p->ext == nil)
+               return;
+       snprint(label, sizeof(label), "%s %s", p->ext, p->name);
+       ps = Pt(0, 0);
+       if(p->image != nil)
+               ps = addpt(subpt(p->image->r.max, p->image->r.min), Pt(24, 24));
+       drawlock(0);
+       if((fd = p->fd) < 0){
+               if(p->open != popenfile)
+                       return;
+               fd = open((char*)p->data, OREAD);
+       } else {
+               fd = dup(fd, -1);
+               seek(fd, 0, 0);
+       }
+       if(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG|RFREND|RFNOWAIT) == 0){
+               if(newwindow(nil) != -1){
+                       dupfds(fd, open("/dev/cons", OWRITE), open("/dev/cons", OWRITE), -1);
+                       if((fd = open("/dev/label", OWRITE)) >= 0){
+                               write(fd, label, strlen(label));
+                               close(fd);
+                       }
+                       if(ps.x && ps.y)
+                               resizewin(ps);
+                       argv[0] = "rc";
+                       argv[1] = "-c";
+                       argv[2] = p->ext;
+                       argv[3] = nil;
+                       exec("/bin/rc", argv);
+               }
+               exits(0);
+       }
+       close(fd);
+       drawlock(1);
 }
 
 void
@@ -1186,16 +1421,16 @@ eresized(int new)
 {
        Page *p;
 
-       lockdisplay(display);
+       drawlock(1);
        if(new && getwindow(display, Refnone) == -1)
                sysfatal("getwindow: %r");
-       if(p = current){
+       if((p = current) != nil){
                if(canqlock(p)){
                        drawpage(p);
                        qunlock(p);
                }
        }
-       unlockdisplay(display);
+       drawlock(0);
 }
 
 int cohort = -1;
@@ -1216,11 +1451,11 @@ void drawerr(Display *, char *msg)
 void
 usage(void)
 {
-       fprint(2, "usage: %s [ -iRw ] [ -p ppi ] [ file ... ]\n", argv0);
+       fprint(2, "usage: %s [ -iRw ] [ -m mb ] [ -p ppi ] [ -j addr ] [ file ... ]\n", argv0);
        exits("usage");
 }
 
-int
+void
 docmd(int i, Mouse *m)
 {
        char buf[NPATH], *s;
@@ -1235,11 +1470,11 @@ docmd(int i, Mouse *m)
                rotate = 0;
        Unload:
                viewgen++;
-               unlockdisplay(display);
-               esetcursor(&reading);
+               drawlock(0);
                unloadpages(0);
-               showpage(current);
-               return 0;
+               showpage1(current);
+               drawlock(1);
+               break;
        case Cupsidedown:
                rotate += 90;
        case Crotate90:
@@ -1261,10 +1496,10 @@ docmd(int i, Mouse *m)
        case Czoomin:
        case Czoomout:
                if(current == nil || !canqlock(current))
-                       return 1;
+                       break;
                o = subpt(m->xy, screen->r.min);
                if(i == Czoomin){
-                       if(zoom < 0x40000000){
+                       if(zoom < 0x1000){
                                zoom *= 2;
                                pos =  addpt(mulpt(subpt(pos, o), 2), o);
                        }
@@ -1276,18 +1511,18 @@ docmd(int i, Mouse *m)
                }
                drawpage(current);
                qunlock(current);
-               return 1;
+               break;
        case Cwrite:
                if(current == nil || !canqlock(current))
-                       return 1;
-               if(current->image){
+                       break;
+               if(current->image != nil){
                        s = nil;
-                       if(current->up && current->up != root)
-                               s = current->up->label;
+                       if(current->up != nil && current->up != root)
+                               s = current->up->name;
                        snprint(buf, sizeof(buf), "%s%s%s.bit",
-                               s ? s : "",
-                               s ? "." : "",
-                               current->label);
+                               s != nil ? s : "",
+                               s != nil ? "." : "",
+                               current->name);
                        if(eenter("Write", buf, sizeof(buf), m) > 0){
                                if((fd = create(buf, OWRITE, 0666)) < 0){
                                        errstr(buf, sizeof(buf));
@@ -1301,23 +1536,77 @@ docmd(int i, Mouse *m)
                        }
                }
                qunlock(current);
-               return 1;
+               break;
+       case Cext:
+               if(current == nil || !canqlock(current))
+                       break;
+               showext(current);
+               qunlock(current);
+               break;
+       case Csnarf:
+               writeaddr(current, "/dev/snarf");
+               break;
        case Cnext:
-               unlockdisplay(display);
-               shownext();
-               return 0;
+               forward = 1;
+               showpage(nextpage(current));
+               break;
        case Cprev:
-               unlockdisplay(display);
-               showprev();
-               return 0;
+               forward = -1;
+               showpage(prevpage(current));
+               break;
        case Czerox:
-               unlockdisplay(display);
                zerox(current);
-               return 0;
+               break;
        case Cquit:
                exits(0);
        }
-       return 1;
+}
+
+void
+scroll(int y)
+{
+       Point z;
+       Page *p;
+
+       if(current == nil || !canqlock(current))
+               return;
+       if(y < 0){
+               if(pos.y >= 0){
+                       p = prevpage(current);
+                       if(p != nil){
+                               qunlock(current);
+                               z = ZP;
+                               if(canqlock(p)){
+                                       z = pagesize(p);
+                                       qunlock(p);
+                               }
+                               if(z.y == 0)
+                                       z.y = Dy(screen->r);
+                               if(pos.y+z.y > Dy(screen->r))
+                                       pos.y = Dy(screen->r) - z.y;
+                               forward = -1;
+                               showpage(p);
+                               return;
+                       }
+                       y = 0;
+               }
+       } else {
+               z = pagesize(current);
+               if(pos.y+z.y <= Dy(screen->r)){
+                       p = nextpage(current);
+                       if(p != nil){
+                               qunlock(current);
+                               if(pos.y < 0)
+                                       pos.y = 0;
+                               forward = 1;
+                               showpage(p);
+                               return;
+                       }
+                       y = 0;
+               }
+       }
+       translate(current, Pt(0, -y));
+       qunlock(current);
 }
 
 void
@@ -1332,6 +1621,8 @@ main(int argc, char *argv[])
        char *s;
        int i;
 
+       quotefmtinstall();
+
        ARGBEGIN {
        case 'a':
        case 'v':
@@ -1339,7 +1630,8 @@ main(int argc, char *argv[])
        case 'P':
                break;
        case 'R':
-               newwin = -1;
+               if(newwin == 0)
+                       newwin = -1;
                break;
        case 'w':
                newwin = 1;
@@ -1347,6 +1639,12 @@ main(int argc, char *argv[])
        case 'i':
                imode = 1;
                break;
+       case 'j':
+               trywalk(EARGF(usage()), nil);
+               break;
+       case 'm':
+               imemlimit = atol(EARGF(usage()))*MiB;
+               break;
        case 'p':
                ppi = atoi(EARGF(usage()));
                break;
@@ -1354,6 +1652,11 @@ main(int argc, char *argv[])
                usage();
        } ARGEND;
 
+       if(newwin > 0){
+               if(newwindow(nil) < 0)
+                       sysfatal("newwindow: %r");
+       }
+
        /*
         * so that we can stop all subprocesses with a note,
         * and to isolate rendezvous from other processes
@@ -1366,13 +1669,6 @@ main(int argc, char *argv[])
        }
        cohort = getpid();
        atexit(killcohort);
-
-       if(newwin > 0){
-               s = smprint("-pid %d", getpid());
-               if(newwindow(s) < 0)
-                       sysfatal("newwindow: %r");
-               free(s);
-       }
        if(initdraw(drawerr, nil, argv0) < 0)
                sysfatal("initdraw: %r");
        paper = display->white;
@@ -1380,23 +1676,30 @@ main(int argc, char *argv[])
        ground = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x777777FF);
        display->locking = 1;
        unlockdisplay(display);
+
        einit(Ekeyboard|Emouse);
        eplumb(Eplumb, "image");
        memset(&m, 0, sizeof(m));
        if((nullfd = open("/dev/null", ORDWR)) < 0)
                sysfatal("open: %r");
+       dup(nullfd, 1);
+       lru.lprev = &lru;
+       lru.lnext = &lru;
        current = root = addpage(nil, "", nil, nil, -1);
-
+       root->delim = "";
        if(*argv == nil && !imode)
                addpage(root, "stdin", popenfile, strdup("/fd/0"), -1);
        for(; *argv; argv++)
-               addpage(root, shortname(*argv), popenfile, strdup(*argv), -1);
+               addpage(root, *argv, popenfile, strdup(*argv), -1);
 
+       drawlock(1);
        for(;;){
+               drawlock(0);
                i=event(&e);
+               drawlock(1);
+
                switch(i){
                case Emouse:
-                       lockdisplay(display);
                        m = e.mouse;
                        if(m.buttons & 1){
                                if(current &&  canqlock(current)){
@@ -1413,8 +1716,7 @@ main(int argc, char *argv[])
                                o = m.xy;
                                i = emenuhit(2, &m, &cmdmenu);
                                m.xy = o;
-                               if(!docmd(i, &m))
-                                       continue;
+                               docmd(i, &m);
                        } else if(m.buttons & 4){
                                if(root->down){
                                        Page *x;
@@ -1423,44 +1725,28 @@ main(int argc, char *argv[])
                                        pagemenu.lasthit = pageindex(current);
                                        x = pageat(emenuhit(3, &m, &pagemenu));
                                        qunlock(&pagelock);
-                                       unlockdisplay(display);
+                                       forward = 0;
                                        showpage(x);
-                                       continue;
                                }
+                       } else if(m.buttons & 8){
+                               scroll(screen->r.min.y - m.xy.y);
+                       } else if(m.buttons & 16){
+                               scroll(m.xy.y - screen->r.min.y);
                        }
-                       unlockdisplay(display);
                        break;
                case Ekeyboard:
-                       lockdisplay(display);
                        switch(e.kbdc){
                        case Kup:
-                               if(current == nil || !canqlock(current))
-                                       break;
-                               if(pos.y < 0){
-                                       translate(current, Pt(0, Dy(screen->r)/2));
-                                       qunlock(current);
-                                       break;
-                               }
-                               if(prevpage(current))
-                                       pos.y = 0;
-                               qunlock(current);
-                               if(!docmd(Cprev, &m))
-                                       continue;
+                               scroll(-Dy(screen->r)/3);
+                               break;
+                       case Kpgup:
+                               scroll(-Dy(screen->r)/2);
                                break;
                        case Kdown:
-                               if(current == nil || !canqlock(current))
-                                       break;
-                               o = addpt(pos, pagesize(current));
-                               if(o.y > Dy(screen->r)){
-                                       translate(current, Pt(0, -Dy(screen->r)/2));
-                                       qunlock(current);
-                                       break;
-                               }
-                               if(nextpage(current))
-                                       pos.y = 0;
-                               qunlock(current);
-                               if(!docmd(Cnext, &m))
-                                       continue;
+                               scroll(Dy(screen->r)/3);
+                               break;
+                       case Kpgdown:
+                               scroll(Dy(screen->r)/2);
                                break;
                        default:
                                for(i = 0; i<nelem(cmds); i++)
@@ -1469,8 +1755,7 @@ main(int argc, char *argv[])
                                           (cmds[i].k3 == e.kbdc))
                                                break;
                                if(i < nelem(cmds)){
-                                       if(!docmd(i, &m))
-                                               continue;
+                                       docmd(i, &m);
                                        break;
                                }
                                if((e.kbdc < 0x20) || 
@@ -1478,18 +1763,16 @@ main(int argc, char *argv[])
                                   (e.kbdc & 0xFF00) == Spec)
                                        break;
                                snprint(buf, sizeof(buf), "%C", (Rune)e.kbdc);
-                               i = eenter("Go to", buf, sizeof(buf), &m);
-                               if(i > 0){
-                                       unlockdisplay(display);
+                               if(eenter("Go to", buf, sizeof(buf), &m) > 0){
+                                       forward = 0;
                                        showpage(findpage(buf));
-                                       continue;
                                }
                        }
-                       unlockdisplay(display);
                        break;
                case Eplumb:
                        pm = e.v;
                        if(pm && pm->ndata > 0){
+                               Page *j;
                                int fd;
 
                                fd = -1;
@@ -1514,7 +1797,15 @@ main(int argc, char *argv[])
                                        sprint(s, "%s/%s", pm->wdir, pm->data);
                                        cleanname(s);
                                }
-                               showpage(addpage(root, shortname(s), popenfile, s, fd));
+                               j = trywalk(s, plumblookup(pm->attr, "addr"));
+                               if(j == nil){
+                                       current = root;
+                                       drawlock(0);
+                                       j = addpage(root, s, popenfile, s, fd);
+                                       drawlock(1);
+                               }
+                               forward = 0;
+                               showpage(j);
                        }
                Plumbfree:
                        plumbfree(pm);