<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/cinap_lenrek/paint.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/cinap_lenrek/paint.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
#include &lt;u.h&gt;
#include &lt;libc.h&gt;
#include &lt;draw.h&gt;
#include &lt;event.h&gt;
#include &lt;keyboard.h&gt;

char *filename;
int zoom = 1;
int brush = 1;
Point spos;             /* position on screen */
Point cpos;             /* position on canvas */
Image *canvas;
Image *ink;
Image *back;
Image *pal[16];         /* palette */
Rectangle palr;         /* palette rect on screen */
Rectangle penr;         /* pen size rect on screen */
enum {
       NBRUSH = 10+1,
};

int nundo = 0;
Image *undo[1024];

int c64[] = {           /* c64 color palette */
       0x000000,
       0xFFFFFF,
       0x68372B,
       0x70A4B2,
       0x6F3D86,
       0x588D43,
       0x352879,
       0xB8C76F,
       0x6F4F25,
       0x433900,
       0x9A6759,
       0x444444,
       0x6C6C6C,
       0x9AD284,
       0x6C5EB5,
       0x959595,
};

/*
* A draw operation that touches only the area contained in bot but not in top.
* mp and sp get aligned with bot.min.
*/
static void
gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
       Image *src, Point sp, Image *mask, Point mp, int op)
{
       Rectangle r;
       Point origin;
       Point delta;

       if(Dx(bot)*Dy(bot) == 0)
               return;

       /* no points in bot - top */
       if(rectinrect(bot, top))
               return;

       /* bot - top ≡ bot */
       if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
               gendrawop(dst, bot, src, sp, mask, mp, op);
               return;
       }

       origin = bot.min;
       /* split bot into rectangles that don't intersect top */
       /* left side */
       if(bot.min.x &lt; top.min.x){
               r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
               delta = subpt(r.min, origin);
               gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
               bot.min.x = top.min.x;
       }

       /* right side */
       if(bot.max.x &gt; top.max.x){
               r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
               delta = subpt(r.min, origin);
               gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
               bot.max.x = top.max.x;
       }

       /* top */
       if(bot.min.y &lt; top.min.y){
               r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
               delta = subpt(r.min, origin);
               gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
               bot.min.y = top.min.y;
       }

       /* bottom */
       if(bot.max.y &gt; top.max.y){
               r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
               delta = subpt(r.min, origin);
               gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
               bot.max.y = top.max.y;
       }
}

int
alphachan(ulong chan)
{
       for(; chan; chan &gt;&gt;= 8)
               if(TYPE(chan) == CAlpha)
                       return 1;
       return 0;
}

void
zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int f)
{
       Rectangle dr;
       Image *t;
       Point a;
       int w;

       a = ZP;
       if(r.min.x &lt; d-&gt;r.min.x){
               sp.x += (d-&gt;r.min.x - r.min.x)/f;
               a.x = (d-&gt;r.min.x - r.min.x)%f;
               r.min.x = d-&gt;r.min.x;
       }
       if(r.min.y &lt; d-&gt;r.min.y){
               sp.y += (d-&gt;r.min.y - r.min.y)/f;
               a.y = (d-&gt;r.min.y - r.min.y)%f;
               r.min.y = d-&gt;r.min.y;
       }
       rectclip(&amp;r, d-&gt;r);
       w = s-&gt;r.max.x - sp.x;
       if(w &gt; Dx(r))
               w = Dx(r);
       dr = r;
       dr.max.x = dr.min.x+w;
       if(!alphachan(s-&gt;chan))
               b = nil;
       if(f &lt;= 1){
               if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
               gendrawdiff(d, dr, top, s, sp, nil, ZP, SoverD);
               return;
       }
       if((t = allocimage(display, dr, s-&gt;chan, 0, 0)) == nil)
               return;
       for(; dr.min.y &lt; r.max.y; dr.min.y++){
               dr.max.y = dr.min.y+1;
               draw(t, dr, s, nil, sp);
               if(++a.y == f){
                       a.y = 0;
                       sp.y++;
               }
       }
       dr = r;
       for(sp=dr.min; dr.min.x &lt; r.max.x; sp.x++){
               dr.max.x = dr.min.x+1;
               if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
               gendrawdiff(d, dr, top, t, sp, nil, ZP, SoverD);
               for(dr.min.x++; ++a.x &lt; f &amp;&amp; dr.min.x &lt; r.max.x; dr.min.x++){
                       dr.max.x = dr.min.x+1;
                       gendrawdiff(d, dr, top, d, Pt(dr.min.x-1, dr.min.y), nil, ZP, SoverD);
               }
               a.x = 0;
       }
       freeimage(t);
}

Point
s2c(Point p){
       p = subpt(p, spos);
       if(p.x &lt; 0) p.x -= zoom-1;
       if(p.y &lt; 0) p.y -= zoom-1;
       return addpt(divpt(p, zoom), cpos);
}

Point
c2s(Point p){
       return addpt(mulpt(subpt(p, cpos), zoom), spos);
}

Rectangle
c2sr(Rectangle r){
       return Rpt(c2s(r.min), c2s(r.max));
}

void
update(Rectangle *rp){
       if(canvas==nil)
               draw(screen, screen-&gt;r, back, nil, ZP);
       else {
               if(rp == nil)
                       rp = &amp;canvas-&gt;r;
               gendrawdiff(screen, screen-&gt;r, c2sr(canvas-&gt;r), back, ZP, nil, ZP, SoverD);
               zoomdraw(screen, c2sr(*rp), ZR, back, canvas, rp-&gt;min, zoom);
       }
       flushimage(display, 1);
}

void
expand(Rectangle r)
{
       Rectangle nr;
       Image *tmp;

       if(canvas==nil){
               if((canvas = allocimage(display, r, screen-&gt;chan, 0, DNofill)) == nil)
                       sysfatal("allocimage: %r");
               draw(canvas, canvas-&gt;r, back, nil, ZP);
               return;
       }
       nr = canvas-&gt;r;
       combinerect(&amp;nr, r);
       if(eqrect(nr, canvas-&gt;r))
               return;
       if((tmp = allocimage(display, nr, canvas-&gt;chan, 0, DNofill)) == nil)
               return;
       draw(tmp, canvas-&gt;r, canvas, nil, canvas-&gt;r.min);
       gendrawdiff(tmp, tmp-&gt;r, canvas-&gt;r, back, ZP, nil, ZP, SoverD);
       freeimage(canvas);
       canvas = tmp;
}

void
save(Rectangle r, int mark)
{
       Image *tmp;
       int x;

       if(mark){
               x = nundo++ % nelem(undo);
               if(undo[x])
                       freeimage(undo[x]);
               undo[x] = nil;
       }
       if(canvas==nil || nundo&lt;0)
               return;
       if(!rectclip(&amp;r, canvas-&gt;r))
               return;
       if((tmp = allocimage(display, r, canvas-&gt;chan, 0, DNofill)) == nil)
               return;
       draw(tmp, r, canvas, nil, r.min);
       x = nundo++ % nelem(undo);
       if(undo[x])
               freeimage(undo[x]);
       undo[x] = tmp;
}

void
restore(int n)
{
       Image *tmp;
       int x;

       while(nundo &gt; 0){
               if(n-- == 0)
                       return;
               x = --nundo % nelem(undo);
               if((tmp = undo[x]) == nil)
                       return;
               undo[x] = nil;
               expand(tmp-&gt;r);
               draw(canvas, tmp-&gt;r, tmp, nil, tmp-&gt;r.min);
               update(&amp;tmp-&gt;r);
               freeimage(tmp);
       }
}

typedef struct {
       Rectangle       r;
       Rectangle       r0;
       Image*          dst;

       int             yscan;  /* current scanline */
       int             wscan;  /* bscan width in bytes */
       Image*          iscan;  /* scanline image */
       uchar*          bscan;  /* scanline buffer */

       int             nmask;  /* size of bmask in bytes */
       int             wmask;  /* width of bmask in bytes */
       Image*          imask;  /* mask image */
       uchar*          bmask;  /* mask buffer */

       int             ncmp;
       uchar           bcmp[4];
} Filldata;

void
fillscan(Filldata *f, Point p0)
{
       int x, y;
       uchar *b;

       x = p0.x;
       y = p0.y;
       b = f-&gt;bmask + y*f-&gt;wmask;
       if(b[x/8] &amp; 0x80&gt;&gt;(x%8))
               return;

       if(f-&gt;yscan != y){
               draw(f-&gt;iscan, f-&gt;iscan-&gt;r, f-&gt;dst, nil, Pt(f-&gt;r.min.x, f-&gt;r.min.y+y));
               if(unloadimage(f-&gt;iscan, f-&gt;iscan-&gt;r, f-&gt;bscan, f-&gt;wscan) &lt; 0)
                       return;
               f-&gt;yscan = y;
       }

       for(x = p0.x; x &gt;= 0; x--){
               if(memcmp(f-&gt;bscan + x*f-&gt;ncmp, f-&gt;bcmp, f-&gt;ncmp))
                       break;
               b[x/8] |= 0x80&gt;&gt;(x%8);
       }
       for(x = p0.x+1; x &lt; f-&gt;r0.max.x; x++){
               if(memcmp(f-&gt;bscan + x*f-&gt;ncmp, f-&gt;bcmp, f-&gt;ncmp))
                       break;
               b[x/8] |= 0x80&gt;&gt;(x%8);
       }

       y = p0.y-1;
       if(y &gt;= 0){
               for(x = p0.x; x &gt;= 0; x--){
                       if((b[x/8] &amp; 0x80&gt;&gt;(x%8)) == 0)
                               break;
                       fillscan(f, Pt(x, y));
               }
               for(x = p0.x+1; x &lt; f-&gt;r0.max.x; x++){
                       if((b[x/8] &amp; 0x80&gt;&gt;(x%8)) == 0)
                               break;
                       fillscan(f, Pt(x, y));
               }
       }

       y = p0.y+1;
       if(y &lt; f-&gt;r0.max.y){
               for(x = p0.x; x &gt;= 0; x--){
                       if((b[x/8] &amp; 0x80&gt;&gt;(x%8)) == 0)
                               break;
                       fillscan(f, Pt(x, y));
               }
               for(x = p0.x+1; x &lt; f-&gt;r0.max.x; x++){
                       if((b[x/8] &amp; 0x80&gt;&gt;(x%8)) == 0)
                               break;
                       fillscan(f, Pt(x, y));
               }
       }
}

void
floodfill(Image *dst, Rectangle r, Point p, Image *src)
{
       Filldata f;

       if(!rectclip(&amp;r, dst-&gt;r))
               return;
       if(!ptinrect(p, r))
               return;
       memset(&amp;f, 0, sizeof(f));
       f.dst = dst;
       f.r = r;
       f.r0 = rectsubpt(r, r.min);
       f.wmask = bytesperline(f.r0, 1);
       f.nmask = f.wmask*f.r0.max.y;
       if((f.bmask = mallocz(f.nmask, 1)) == nil)
               goto out;
       if((f.imask = allocimage(display, f.r0, GREY1, 0, DNofill)) == nil)
               goto out;

       r = f.r0;
       r.max.y = 1;
       if((f.iscan = allocimage(display, r, RGB24, 0, DNofill)) == nil)
               goto out;
       f.yscan = -1;
       f.wscan = bytesperline(f.iscan-&gt;r, f.iscan-&gt;depth);
       if((f.bscan = mallocz(f.wscan, 0)) == nil)
               goto out;

       r = Rect(0,0,1,1);
       f.ncmp = (f.iscan-&gt;depth+7) / 8;
       draw(f.iscan, r, dst, nil, p);
       if(unloadimage(f.iscan, r, f.bcmp, sizeof(f.bcmp)) &lt; 0)
               goto out;

       fillscan(&amp;f, subpt(p, f.r.min));

       loadimage(f.imask, f.imask-&gt;r, f.bmask, f.nmask);
       draw(f.dst, f.r, src, f.imask, f.imask-&gt;r.min);
out:
       free(f.bmask);
       free(f.bscan);
       if(f.iscan)
               freeimage(f.iscan);
       if(f.imask)
               freeimage(f.imask);
}

void
translate(Point d)
{
       Rectangle r, nr;

       if(canvas==nil || d.x==0 &amp;&amp; d.y==0)
               return;
       r = c2sr(canvas-&gt;r);
       nr = rectaddpt(r, d);
       rectclip(&amp;r, screen-&gt;clipr);
       draw(screen, rectaddpt(r, d), screen, nil, r.min);
       zoomdraw(screen, nr, rectaddpt(r, d), back, canvas, canvas-&gt;r.min, zoom);
       gendrawdiff(screen, screen-&gt;r, nr, back, ZP, nil, ZP, SoverD);
       spos = addpt(spos, d);
       flushimage(display, 1);
}

void
setzoom(Point o, int z)
{
       if(z &lt; 1)
               return;
       cpos = s2c(o);
       spos = o;
       zoom = z;
       update(nil);
}

void
center(void)
{
       cpos = ZP;
       if(canvas)
               cpos = addpt(canvas-&gt;r.min,
                       divpt(subpt(canvas-&gt;r.max, canvas-&gt;r.min), 2));
       spos = addpt(screen-&gt;r.min,
               divpt(subpt(screen-&gt;r.max, screen-&gt;r.min), 2));
       update(nil);
}

void
drawpal(void)
{
       Rectangle r, rr;
       int i;

       r = screen-&gt;r;
       r.min.y = r.max.y - 20;
       replclipr(screen, 0, r);

       penr = r;
       penr.min.x = r.max.x - NBRUSH*Dy(r);

       palr = r;
       palr.max.x = penr.min.x;

       r = penr;
       draw(screen, r, back, nil, ZP);
       for(i=0; i&lt;10; i++){
               r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
               rr = r;
               if(i == brush)
                       rr.min.y += Dy(r)/3;
               fillellipse(screen, addpt(rr.min, divpt(subpt(rr.max, rr.min), 2)), i, i, ink, ZP);
               r.min.x = r.max.x;
       }
       r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
       rr = r;
       if(i == brush)
               rr.min.y += Dy(r)/3;
       draw(screen, rr, ink, nil, ZP);

       r = palr;
       for(i=1; i&lt;=nelem(pal); i++){
               r.max.x = palr.min.x + i*Dx(palr) / nelem(pal);
               rr = r;
               if(ink == pal[i-1])
                       rr.min.y += Dy(r)/3;
               draw(screen, rr, pal[i-1], nil, ZP);
               gendrawdiff(screen, r, rr, back, ZP, nil, ZP, SoverD);
               r.min.x = r.max.x;
       }

       r = screen-&gt;r;
       r.max.y -= Dy(palr);
       replclipr(screen, 0, r);
}

int
hitpal(Mouse m)
{
       if(ptinrect(m.xy, penr)){
               if(m.buttons &amp; 7){
                       brush = ((m.xy.x - penr.min.x) * NBRUSH) / Dx(penr);
                       drawpal();
               }
               return 1;
       }
       if(ptinrect(m.xy, palr)){
               Image *col;

               col = pal[(m.xy.x - palr.min.x) * nelem(pal) / Dx(palr)];
               switch(m.buttons &amp; 7){
               case 1:
                       ink = col;
                       drawpal();
                       break;
               case 2:
                       back = col;
                       drawpal();
                       update(nil);
                       break;
               }
               return 1;
       }
       return 0;
}

void
catch(void *, char *msg)
{
       if(strstr(msg, "closed pipe"))
               noted(NCONT);
       noted(NDFLT);
}

int
pipeline(char *fmt, ...)
{
       char buf[1024];
       va_list a;
       int p[2];

       va_start(a, fmt);
       vsnprint(buf, sizeof(buf), fmt, a);
       va_end(a);
       if(pipe(p) &lt; 0)
               return -1;
       switch(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG)){
       case -1:
               close(p[0]);
               close(p[1]);
               return -1;
       case 0:
               close(p[1]);
               dup(p[0], 0);
               dup(p[0], 1);
               close(p[0]);
               execl("/bin/rc", "rc", "-c", buf, nil);
               exits("exec");
       }
       close(p[0]);
       return p[1];
}

void
usage(void)
{
       fprint(2, "usage: %s [ file ]\n", argv0);
       exits("usage");
}

void
main(int argc, char *argv[])
{
       char *s, buf[1024];
       Rectangle r;
       Image *img;
       int i, fd;
       Event e;
       Mouse m;
       Point p, d;

       ARGBEGIN {
       default:
               usage();
       } ARGEND;

       if(argc == 1)
               filename = strdup(argv[0]);
       else if(argc != 0)
               usage();

       if(initdraw(0, 0, "paint") &lt; 0)
               sysfatal("initdraw: %r");

       if(filename){
               if((fd = open(filename, OREAD)) &lt; 0)
                       sysfatal("open: %r");
               if((canvas = readimage(display, fd, 0)) == nil)
                       sysfatal("readimage: %r");
               close(fd);
       }

       /* palette initialization */
       for(i=0; i&lt;nelem(pal); i++){
               pal[i] = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1,
                       c64[i % nelem(c64)]&lt;&lt;8 | 0xFF);
               if(pal[i] == nil)
                       sysfatal("allocimage: %r");
       }
       ink = pal[0];
       back = pal[1];
       drawpal();
       center();

       einit(Emouse | Ekeyboard);

       notify(catch);
       for(;;) {
               switch(event(&amp;e)){
               case Emouse:
                       if(hitpal(e.mouse))
                               continue;

                       img = ink;
                       switch(e.mouse.buttons &amp; 7){
                       case 2:
                               img = back;
                               /* no break */
                       case 1:
                               p = s2c(e.mouse.xy);
                               if(brush &gt;= 10){
                                       /* flood fill brush */
                                       if(canvas == nil || !ptinrect(p, canvas-&gt;r)){
                                               back = img;
                                               drawpal();
                                               update(nil);
                                               break;
                                       }
                                       r = canvas-&gt;r;
                                       save(r, 1);
                                       floodfill(canvas, r, p, img);
                                       update(&amp;r);

                                       /* wait for mouse release */
                                       while(event(&amp;e) == Emouse &amp;&amp; (e.mouse.buttons &amp; 7) != 0)
                                               ;
                                       break;
                               }
                               r = Rect(p.x-brush, p.y-brush, p.x+brush+1, p.y+brush+1);
                               expand(r);
                               save(r, 1);
                               fillellipse(canvas, p, brush, brush, img, ZP);
                               update(&amp;r);
                               for(;;){
                                       m = e.mouse;
                                       if(event(&amp;e) != Emouse)
                                               break;
                                       if((e.mouse.buttons ^ m.buttons) &amp; 7)
                                               break;
                                       d = s2c(e.mouse.xy);
                                       if(eqpt(d, p))
                                               continue;
                                       r = canonrect(Rpt(p, d));
                                       r.min.x -= brush;
                                       r.min.y -= brush;
                                       r.max.x += brush+1;
                                       r.max.y += brush+1;
                                       expand(r);
                                       save(r, 0);
                                       line(canvas, p, d, Enddisc, Enddisc, brush, img, ZP);
                                       update(&amp;r);
                                       p = d;
                               }
                               break;
                       case 4:
                               for(;;){
                                       m = e.mouse;
                                       if(event(&amp;e) != Emouse)
                                               break;
                                       if((e.mouse.buttons &amp; 7) != 4)
                                               break;
                                       translate(subpt(e.mouse.xy, m.xy));
                               }
                               break;
                       }
                       break;
               case Ekeyboard:
                       switch(e.kbdc){
                       case Kesc:
                               zoom = 1;
                               center();
                               break;
                       case '+':
                               setzoom(e.mouse.xy, zoom*2);
                               break;
                       case '-':
                               setzoom(e.mouse.xy, zoom/2);
                               break;
                       case 'c':
                               if(canvas == nil)
                                       break;
                               save(canvas-&gt;r, 1);
                               freeimage(canvas);
                               canvas = nil;
                               update(nil);
                               break;
                       case 'u':
                               restore(16);
                               break;
                       case 'f':
                               brush = 10;
                               drawpal();
                               break;
                       case '0': case '1': case '2': case '3': case '4':
                       case '5': case '6': case '7': case '8': case '9':
                               brush = e.kbdc - '0';
                               drawpal();
                               break;
                       default:
                               if(e.kbdc == Kdel)
                                       e.kbdc = 'q';
                               buf[0] = 0;
                               if(filename &amp;&amp; (e.kbdc == 'r' || e.kbdc == 'w'))
                                       snprint(buf, sizeof(buf), "%C %s", e.kbdc, filename);
                               else if(e.kbdc &gt; 0x20 &amp;&amp; e.kbdc &lt; 0x7f)
                                       snprint(buf, sizeof(buf), "%C", e.kbdc);
                               if(eenter("Cmd", buf, sizeof(buf), &amp;e.mouse) &lt;= 0)
                                       break;
                               if(strcmp(buf, "q") == 0)
                                       exits(nil);
                               s = buf+1;
                               while(*s == ' ' || *s == '\t')
                                       s++;
                               if(*s == 0)
                                       break;
                               switch(buf[0]){
                               case 'r':
                                       if((fd = open(s, OREAD)) &lt; 0){
                                       Error:
                                               snprint(buf, sizeof(buf), "%r");
                                               eenter(buf, nil, 0, &amp;e.mouse);
                                               break;
                                       }
                                       free(filename);
                                       filename = strdup(s);
                               Readimage:
                                       unlockdisplay(display);
                                       img = readimage(display, fd, 1);
                                       close(fd);
                                       lockdisplay(display);
                                       if(img == nil){
                                               werrstr("readimage: %r");
                                               goto Error;
                                       }
                                       if(canvas){
                                               save(canvas-&gt;r, 1);
                                               freeimage(canvas);
                                       }
                                       canvas = img;
                                       center();
                                       break;
                               case 'w':
                                       if((fd = create(s, OWRITE, 0660)) &lt; 0)
                                               goto Error;
                                       free(filename);
                                       filename = strdup(s);
                               Writeimage:
                                       if(canvas)
                                       if(writeimage(fd, canvas, 0) &lt; 0){
                                               close(fd);
                                               werrstr("writeimage: %r");
                                               goto Error;
                                       }
                                       close(fd);
                                       break;
                               case '&lt;':
                                       if((fd = pipeline("%s", s)) &lt; 0)
                                               goto Error;
                                       goto Readimage;
                               case '&gt;':
                                       if((fd = pipeline("%s", s)) &lt; 0)
                                               goto Error;
                                       goto Writeimage;
                               case '|':
                                       if(canvas == nil)
                                               break;
                                       if((fd = pipeline("%s", s)) &lt; 0)
                                               goto Error;
                                       switch(rfork(RFMEM|RFPROC|RFFDG)){
                                       case -1:
                                               close(fd);
                                               werrstr("rfork: %r");
                                               goto Error;
                                       case 0:
                                               writeimage(fd, canvas, 1);
                                               exits(nil);
                                       }
                                       goto Readimage;
                               }
                               break;
                       }
                       break;
               }
       }
}

void
eresized(int)
{
       if(getwindow(display, Refnone) &lt; 0)
               sysfatal("resize failed");
       drawpal();
       update(nil);
}
<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>