]> 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 0926b84868150c0032598754d43f1a45136d4ed7..49478d3a41770cc136cd1b39c060308847691ea4 100644 (file)
@@ -8,7 +8,8 @@
 
 typedef struct Page Page;
 struct Page {
-       char    *label;
+       char    *name;
+       char    *delim;
 
        QLock;
        char    *ext;
@@ -17,12 +18,14 @@ struct Page {
 
        Image   *image;
        int     fd;
-       int     gen;
 
        Page    *up;
        Page    *next;
        Page    *down;
        Page    *tail;
+
+       Page    *lnext;
+       Page    *lprev;
 };
 
 int zoom = 1;
@@ -31,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;
 
@@ -43,7 +55,6 @@ char pagespool[] = "/tmp/pagespool.";
 
 enum {
        NPROC = 4,
-       NAHEAD = 2,
        NBUF = 8*1024,
        NPATH = 1024,
 };
@@ -59,6 +70,7 @@ enum {
        Cdummy1,
        Cnext,
        Cprev,
+       Csnarf,
        Czerox,
        Cwrite,
        Cext,
@@ -82,6 +94,7 @@ 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,
@@ -116,28 +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)
@@ -149,8 +159,11 @@ addpage(Page *up, char *label, int (*popen)(Page *), void *pdata, int fd)
        }
        qunlock(&pagelock);
 
-       if(up && current == up)
+       if(up && current == up){
+               if(!pagewalk1(p))
+                       return p;
                showpage1(p);
+       }
        return p;
 }
 
@@ -163,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;
@@ -262,19 +275,50 @@ pipeline(int fd, char *fmt, ...)
        close(pfd[0]);
 }
 
-char*
-shortname(char *s)
+static char*
+shortlabel(char *s)
 {
-       char *x;
-
-       while(strlen(s) > 20){
-               if((x = strchr(s, '/')) == nil)
-                       break;
-               if(x[1] == 0)
-                       break;
-               s = x+1;
+       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++;
        }
-       return s;
+       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*
+pageaddr(Page *p, char *buf, int nbuf)
+{
+       buf[0] = 0;
+       pageaddr1(p, buf, buf+nbuf);
+       return buf;
 }
 
 int
@@ -296,10 +340,9 @@ popenimg(Page *p)
        if(p->data){
                p->ext = p->data;
                if(strcmp(p->ext, "ico") == 0)
-                       snprint(nam, sizeof(nam), "%s -c", p->ext);
+                       pipeline(fd, "exec %s -c", p->ext);
                else
-                       snprint(nam, sizeof(nam), "%s -t9", p->ext);
-               pipeline(fd, "%s", nam);
+                       pipeline(fd, "exec %s -t9", p->ext);
        }
 
        /*
@@ -322,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;
@@ -335,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", (char*)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);
@@ -399,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;
@@ -437,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;
@@ -475,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;
@@ -680,6 +724,7 @@ popenfile(Page *p)
        "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",
@@ -696,6 +741,7 @@ popenfile(Page *p)
        p->ext = nil;
        file = p->data;
        p->data = nil;
+       p->open = nil;
        if(fd < 0){
                if((fd = open(file, OREAD)) < 0){
                Err0:
@@ -721,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);
@@ -775,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;
        }
@@ -790,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;
        }
@@ -808,92 +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){
+       qlock(&lru);
+       llinkhead(p);
+       qunlock(&lru);
+
+       if(p->open != nil && p->image == nil){
                fd = openpage(p);
                if(fd >= 0){
-                       pagegen++;
                        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);
                }
        }
 }
@@ -1012,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;
@@ -1026,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
@@ -1043,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);
 }
@@ -1063,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*
@@ -1099,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;
 }
@@ -1110,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;
 }
@@ -1119,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;
 }
 
@@ -1146,18 +1306,19 @@ showpage1(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 */
@@ -1177,33 +1338,14 @@ drawlock(int dolock){
 void
 showpage(Page *p)
 {
+       if(p == nil)
+               return;
        drawlock(0);
+       unloadpages(imemlimit);
        showpage1(p);
        drawlock(1);
 }
 
-void
-shownext(void)
-{
-       Page *p;
-
-       for(p = nextpage(current); p; p = nextpage(p))
-               if(p->image || p->open)
-                       break;
-       showpage(p);
-}
-
-void
-showprev(void)
-{
-       Page *p;
-
-       for(p = prevpage(current); p; p = prevpage(p))
-               if(p->image || p->open)
-                       break;
-       showpage(p);
-}
-
 void
 zerox(Page *p)
 {
@@ -1234,15 +1376,15 @@ Out:
 void
 showext(Page *p)
 {
-       char buf[128], label[64], *argv[4];
+       char label[64], *argv[4];
        Point ps;
        int fd;
 
        if(p->ext == nil)
                return;
-       snprint(label, sizeof(label), "%s %s", p->ext, p->label);
+       snprint(label, sizeof(label), "%s %s", p->ext, p->name);
        ps = Pt(0, 0);
-       if(p->image)
+       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){
@@ -1253,10 +1395,9 @@ showext(Page *p)
                fd = dup(fd, -1);
                seek(fd, 0, 0);
        }
-       if(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG|RFNAMEG|RFNOWAIT) == 0){
-               snprint(buf, sizeof(buf), "-pid %d", getpid());
-               if(newwindow(buf) != -1){
-                       dupfds(fd, 1, 2, -1);
+       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);
@@ -1275,7 +1416,6 @@ showext(Page *p)
        drawlock(1);
 }
 
-
 void
 eresized(int new)
 {
@@ -1284,7 +1424,7 @@ eresized(int new)
        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);
@@ -1311,7 +1451,7 @@ 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");
 }
 
@@ -1375,14 +1515,14 @@ docmd(int i, Mouse *m)
        case Cwrite:
                if(current == nil || !canqlock(current))
                        break;
-               if(current->image){
+               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));
@@ -1403,11 +1543,16 @@ docmd(int i, Mouse *m)
                showext(current);
                qunlock(current);
                break;
+       case Csnarf:
+               writeaddr(current, "/dev/snarf");
+               break;
        case Cnext:
-               shownext();
+               forward = 1;
+               showpage(nextpage(current));
                break;
        case Cprev:
-               showprev();
+               forward = -1;
+               showpage(prevpage(current));
                break;
        case Czerox:
                zerox(current);
@@ -1417,6 +1562,53 @@ docmd(int i, Mouse *m)
        }
 }
 
+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
 main(int argc, char *argv[])
 {
@@ -1429,6 +1621,8 @@ main(int argc, char *argv[])
        char *s;
        int i;
 
+       quotefmtinstall();
+
        ARGBEGIN {
        case 'a':
        case 'v':
@@ -1436,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;
@@ -1444,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;
@@ -1451,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
@@ -1463,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;
@@ -1477,19 +1676,23 @@ main(int argc, char *argv[])
        ground = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x777777FF);
        display->locking = 1;
        unlockdisplay(display);
-       drawlock(1);
 
        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);
@@ -1522,38 +1725,28 @@ main(int argc, char *argv[])
                                        pagemenu.lasthit = pageindex(current);
                                        x = pageat(emenuhit(3, &m, &pagemenu));
                                        qunlock(&pagelock);
+                                       forward = 0;
                                        showpage(x);
                                }
+                       } 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);
                        }
                        break;
                case Ekeyboard:
                        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);
-                               docmd(Cprev, &m);
+                               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);
-                               docmd(Cnext, &m);
+                               scroll(Dy(screen->r)/3);
+                               break;
+                       case Kpgdown:
+                               scroll(Dy(screen->r)/2);
                                break;
                        default:
                                for(i = 0; i<nelem(cmds); i++)
@@ -1570,13 +1763,16 @@ main(int argc, char *argv[])
                                   (e.kbdc & 0xFF00) == Spec)
                                        break;
                                snprint(buf, sizeof(buf), "%C", (Rune)e.kbdc);
-                               if(eenter("Go to", buf, sizeof(buf), &m) > 0)
+                               if(eenter("Go to", buf, sizeof(buf), &m) > 0){
+                                       forward = 0;
                                        showpage(findpage(buf));
+                               }
                        }
                        break;
                case Eplumb:
                        pm = e.v;
                        if(pm && pm->ndata > 0){
+                               Page *j;
                                int fd;
 
                                fd = -1;
@@ -1601,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);