summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/src/9/pc/sdata.c1
-rw-r--r--sys/src/cmd/cwfs/con.c11
-rwxr-xr-xsys/src/cmd/pkg/create21
-rwxr-xr-xsys/src/cmd/pkg/install12
-rwxr-xr-xsys/src/cmd/pkg/list3
-rw-r--r--sys/src/cmd/pkg/mkfile20
-rwxr-xr-xsys/src/cmd/pkg/remove18
-rw-r--r--sys/src/cmd/pkg/unpkg.c100
-rw-r--r--sys/src/cmd/ramfs.c17
-rw-r--r--sys/src/games/glendy.c530
-rw-r--r--sys/src/games/mkfile1
11 files changed, 732 insertions, 2 deletions
diff --git a/sys/src/9/pc/sdata.c b/sys/src/9/pc/sdata.c
index 438ee90ba..8f7ad76d3 100644
--- a/sys/src/9/pc/sdata.c
+++ b/sys/src/9/pc/sdata.c
@@ -2037,6 +2037,7 @@ atapnp(void)
case (0x24CB<<16)|0x8086: /* 82801DB (ICH4, High-End) */
case (0x24DB<<16)|0x8086: /* 82801EB (ICH5) */
case (0x25A3<<16)|0x8086: /* 6300ESB (E7210) */
+ case (0x2653<<16)|0x8086: /* 82801FBM SATA */
case (0x266F<<16)|0x8086: /* 82801FB (ICH6) */
case (0x27DF<<16)|0x8086: /* 82801G SATA (ICH7) */
case (0x27C0<<16)|0x8086: /* 82801GB SATA AHCI (ICH7) */
diff --git a/sys/src/cmd/cwfs/con.c b/sys/src/cmd/cwfs/con.c
index 0a7aa2134..41fe43142 100644
--- a/sys/src/cmd/cwfs/con.c
+++ b/sys/src/cmd/cwfs/con.c
@@ -740,11 +740,22 @@ cmd_files(int, char *[])
print("%ld out of %ld files used\n", n, conf.nfile);
}
+void
+cmd_chatty(int argc, char *argv[])
+{
+ if(argc < 2) {
+ print("cmd_chatty: usage: chatty n\n");
+ return;
+ }
+ chatty = atoi(argv[1]);
+}
+
static void
installcmds(void)
{
cmd_install("allow", "-- disable permission checking", cmd_allow);
cmd_install("cfs", "[file] -- set current filesystem", cmd_cfs);
+ cmd_install("chatty", "n -- set chattiness", cmd_chatty);
cmd_install("clean", "file [bno [addr]] -- block print/fix", cmd_clean);
cmd_install("check", "[options]", cmd_check);
cmd_install("clri", "[file ...] -- purge files/dirs", cmd_clri);
diff --git a/sys/src/cmd/pkg/create b/sys/src/cmd/pkg/create
new file mode 100755
index 000000000..ec350e731
--- /dev/null
+++ b/sys/src/cmd/pkg/create
@@ -0,0 +1,21 @@
+#!/bin/rc -e
+
+i=`{basename $1}
+d=$1
+echo Creating $i
+C=`{pwd}
+@{
+rfork en
+cd $d
+mkdir /tmp/$i
+mk
+divergefs -p /tmp/$i /
+mk install clean
+unmount /
+}
+cd /tmp/$i/files
+rm -r env
+tar cv * | bzip2 -9 > $C/$i.tbz
+cd /tmp
+rm -r $i
+echo Created $C/$i.tbz
diff --git a/sys/src/cmd/pkg/install b/sys/src/cmd/pkg/install
new file mode 100755
index 000000000..7bc228c8f
--- /dev/null
+++ b/sys/src/cmd/pkg/install
@@ -0,0 +1,12 @@
+#!/bin/rc -e
+
+cd /
+mkdir -p /sys/lib/pkg
+if (test -s /sys/lib/pkg/$1) {
+ echo $i already installed
+ exit
+}
+echo Installing $1
+hget http://pkg.violetti.org/$cputype/$1.tbz | bunzip2 | pkg/unpkg>[2]/sys/lib/pkg/$1
+echo Done
+
diff --git a/sys/src/cmd/pkg/list b/sys/src/cmd/pkg/list
new file mode 100755
index 000000000..419e47a52
--- /dev/null
+++ b/sys/src/cmd/pkg/list
@@ -0,0 +1,3 @@
+#!/bin/rc
+
+hget http://pkg.violetti.org/$cputype | htmlfmt | grep '\.tbz' | sed -e 's/\.tbz$//'
diff --git a/sys/src/cmd/pkg/mkfile b/sys/src/cmd/pkg/mkfile
new file mode 100644
index 000000000..eb206806e
--- /dev/null
+++ b/sys/src/cmd/pkg/mkfile
@@ -0,0 +1,20 @@
+</$objtype/mkfile
+
+all: $O.unpkg
+ echo
+
+$O.unpkg: unpkg.c
+ $CC unpkg.c
+ $LD -o $O.unpkg unpkg.$O
+
+install:V: $O.unpkg
+ mkdir -p /$objtype/bin/pkg
+ cp $O.unpkg /$objtype/bin/pkg/unpkg
+ cp create install list remove /$objtype/bin/pkg
+
+clean:
+ rm -f $O.unpkg *.$O
+
+nuke: clean
+ rm -f /$objtype/bin/pkg/*
+
diff --git a/sys/src/cmd/pkg/remove b/sys/src/cmd/pkg/remove
new file mode 100755
index 000000000..349d18410
--- /dev/null
+++ b/sys/src/cmd/pkg/remove
@@ -0,0 +1,18 @@
+#!/bin/rc -e
+
+cd /
+if(test -s /sys/lib/pkg/$1) {
+ fs=(`{cat /sys/lib/pkg/$1 | awk '{print $1}'})
+ ss=(`{cat /sys/lib/pkg/$1 | awk '{print $2}'})
+ for(i in `{seq $#fs}) {
+ s=`{sha1sum $fs($i) | awk '{print $1}' | tr a-z A-Z}
+ if(test $s '=' $ss($i)) {
+ echo D $fs($i)
+ rm $fs($i)
+ }
+ if not {
+ echo M $fs($i) NOT DELETING
+ }
+ }
+ rm /sys/lib/pkg/$1
+}
diff --git a/sys/src/cmd/pkg/unpkg.c b/sys/src/cmd/pkg/unpkg.c
new file mode 100644
index 000000000..2c749e3b3
--- /dev/null
+++ b/sys/src/cmd/pkg/unpkg.c
@@ -0,0 +1,100 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+
+struct th {
+ char *name;
+ ulong perm;
+ ulong size;
+ char type;
+ char *user, *group;
+};
+
+static char *sndup(char* s, ulong n) {
+ char *d, *p;
+ p = memchr(s, 0, n);
+ if(p)
+ n = p-s;
+ d = malloc(n+1);
+ memcpy(d,s,n);
+ d[n] = 0;
+ return d;
+}
+
+
+int readheader(int fd, struct th* th) {
+ int i;
+ char b[512];
+ if(readn(fd, b, 512) != 512) return -1;
+
+ // Check for end of archive
+ for(i=0; i<512; i++) {
+ if(b[i]!=0) goto rhok;
+ }
+ if(readn(fd, b, 512) != 512) return -1;
+ for(i=0; i<512; i++) {
+ if(b[i]!=0) return -1;
+ }
+ return 0;
+
+ rhok:
+ th->name = sndup(b, 100);
+ th->perm = strtoul(b+100, nil, 8);
+ th->size = strtoul(b+124, nil, 8);
+ th->type = b[156];
+ th->user = sndup(b+265, 32);
+ th->group= sndup(b+297, 32);
+ return 1;
+}
+
+int main(void) {
+ while(1) {
+ struct th th;
+ ulong off;
+ uchar b[512];
+ DigestState *s;
+ int wfd;
+ int r = readheader(0, &th);
+ if(r <= 0) return r;
+
+ switch(th.type) {
+ case '5':
+ create(th.name, OREAD, DMDIR|th.perm);
+ break;
+ case '0': case 0:
+ print("A %s\n", th.name);
+ r = access(th.name, 0);
+ if(r == 0) {
+ print("File already exists: %s\n", th.name);
+ return -1;
+ }
+ if((wfd = create(th.name, OWRITE, th.perm)) < 0) {
+ print("Create failed: %s\n", th.name);
+ return -1;
+ }
+ s = nil;
+ for(off=0; off<th.size; off+=512) {
+ int n = th.size-off;
+ n = n<512 ? n : 512;
+ if(readn(0, b, 512) != 512) return -1;
+ if(write(wfd, b, n) != n) return -1;
+ s = sha1(b, n, nil, s);
+ }
+
+ uchar digest[20], hdigest[41];
+ sha1(nil, 0, digest, s);
+ enc16((char*)hdigest, 41, digest, 20);
+ fprint(2, "%s\t%s\n", th.name, hdigest);
+ close(wfd);
+ break;
+ default:
+ print("Unknown file type '%c'\n", th.type);
+ return -1;
+ }
+
+ free(th.name);
+ free(th.user);
+ free(th.group);
+ }
+}
diff --git a/sys/src/cmd/ramfs.c b/sys/src/cmd/ramfs.c
index 835a94bf9..534edc640 100644
--- a/sys/src/cmd/ramfs.c
+++ b/sys/src/cmd/ramfs.c
@@ -157,9 +157,11 @@ main(int argc, char *argv[])
int p[2];
int fd;
int stdio = 0;
+ int mountflags;
service = "ramfs";
defmnt = "/tmp";
+ mountflags = 0;
ARGBEGIN{
case 'i':
defmnt = 0;
@@ -186,9 +188,20 @@ main(int argc, char *argv[])
defmnt = 0;
service = EARGF(usage());
break;
+ case 'b':
+ mountflags |= MBEFORE;
+ break;
+ case 'c':
+ mountflags |= MCREATE;
+ break;
+ case 'a':
+ mountflags |= MAFTER;
+ break;
default:
usage();
}ARGEND
+ if(mountflags == 0)
+ mountflags = MREPL | MCREATE;
if(pipe(p) < 0)
error("pipe failed");
@@ -239,7 +252,7 @@ main(int argc, char *argv[])
break;
default:
close(p[0]); /* don't deadlock if child fails */
- if(defmnt && mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0)
+ if(defmnt && mount(p[1], -1, defmnt, mountflags, "") < 0)
error("mount failed");
}
exits(0);
@@ -902,6 +915,6 @@ estrdup(char *q)
void
usage(void)
{
- fprint(2, "usage: %s [-Dipsu] [-m mountpoint] [-S srvname]\n", argv0);
+ fprint(2, "usage: %s [-Dipsubac] [-m mountpoint] [-S srvname]\n", argv0);
exits("usage");
}
diff --git a/sys/src/games/glendy.c b/sys/src/games/glendy.c
new file mode 100644
index 000000000..753bd0a55
--- /dev/null
+++ b/sys/src/games/glendy.c
@@ -0,0 +1,530 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+
+enum{
+ /* difficulty levels (how many circles are initially occupied) */
+ DEasy, /* 10≤x<15 */
+ DMed, /* 5≤x<10 */
+ DHard, /* 0≤x<5 */
+
+ /* dynamic? original game has a fixed grid size, but we don't need to abide by it */
+ SzX = 11,
+ SzY = 11,
+
+ Border = 10,
+ /* movement directions */
+ NE,
+ E,
+ SE,
+ SW,
+ W,
+ NW,
+
+ Won = 1, /* game-ending states */
+ Lost = 2,
+};
+
+Font *font;
+
+int difficulty = DMed;
+int finished;
+
+int grid[SzX][SzY];
+int ogrid[SzX][SzY]; /* so we can restart levels */
+
+Image *gl; /* glenda */
+Image *glm; /* glenda's mask */
+Image *cc; /* clicked */
+Image *ec; /* empty; not clicked */
+Image *bg;
+Image *lost;
+Image *won;
+
+
+char *mbuttons[] =
+{
+ "easy",
+ "medium",
+ "hard",
+ 0
+};
+
+char *rbuttons[] =
+{
+ "new",
+ "reset",
+ "exit",
+ 0
+};
+
+Menu mmenu =
+{
+ mbuttons,
+};
+
+Menu rmenu =
+{
+ rbuttons,
+};
+
+Image *
+eallocimage(Rectangle r, int repl, uint color)
+{
+ Image *tmp;
+
+ tmp = allocimage(display, r, screen->chan, repl, color);
+ if(tmp == nil)
+ sysfatal("cannot allocate buffer image: %r");
+
+ return tmp;
+}
+
+Image *
+eloadfile(char *path)
+{
+ Image *img;
+ int fd;
+
+ fd = open(path, OREAD);
+ if(fd < 0) {
+ fprint(2, "cannot open image file %s: %r\n", path);
+ exits("image");
+ }
+ img = readimage(display, fd, 0);
+ if(img == nil)
+ sysfatal("cannot load image: %r");
+ close(fd);
+
+ return img;
+}
+
+
+void
+allocimages(void)
+{
+ Rectangle one = Rect(0, 0, 1, 1);
+
+ cc = eallocimage(one, 1, 0x777777FF);
+ ec = eallocimage(one, 1, DPalegreen);
+ bg = eallocimage(one, 1, DPurpleblue);
+ lost = eallocimage(one, 1, DRed);
+ won = eallocimage(one, 1, DGreen);
+ gl = eloadfile("/lib/face/48x48x4/g/glenda.1");
+
+ glm = allocimage(display, Rect(0, 0, 48, 48), gl->chan, 1, DCyan);
+ if(glm == nil)
+ sysfatal("cannot allocate mask: %r");
+
+ draw(glm, glm->r, display->white, nil, ZP);
+ gendraw(glm, glm->r, display->black, ZP, gl, gl->r.min);
+ freeimage(gl);
+ gl = display->black;
+
+
+}
+
+/* unnecessary calculations here, but it's fine */
+Point
+board2pix(int x, int y)
+{
+ float d, rx, ry, yh;
+ int nx, ny;
+
+ d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20 : Dx(screen->r) -20;
+ rx = d/(float)SzX;
+ rx = rx/2.0;
+ ry = d/(float)SzY;
+ ry = ry/2.0;
+
+ yh = ry/3.73205082;
+
+ nx = (int)((float)x*rx*2.0+rx +(y%2?rx:0.0)); /* nx = x*(2rx) + rx + rx (conditional) */
+ ny = (int)((float)y*(ry*2.0-(y>0?yh:0.0)) + ry); /* ny = y*(2ry-yh) +ry */
+ return Pt(nx, ny);
+}
+
+Point
+pix2board(int x, int y)
+{
+ float d, rx, ry, yh;
+ int ny, nx;
+
+ /* XXX: float→int causes small rounding errors */
+
+ d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r)-20;
+ rx = d/(float)SzX;
+ rx = rx/2.0;
+ ry =d/(float)SzY;
+ ry = ry/2.0;
+
+ yh = ry/3.73205082;
+
+ /* reverse board2pix() */
+ ny = (int)(((float)y - ry)/(2*ry - ((y>2*ry)?yh:0.0)) + 0.5); /* ny = (y - ry)/(2ry-yh) */
+ nx = (int)(((float)x - rx - (ny%2?rx:0.0))/(rx*2.0) + 0.5); /* nx = (x - rx - rx)/2rx */
+
+ if (nx >= SzX)
+ nx = SzX-1;
+ if (ny >=SzY)
+ ny = SzY-1;
+
+ return Pt(nx, ny);
+}
+
+void
+initlevel(void)
+{
+ int i, cnt = 10, x, y;
+
+ for(x = 0; x < SzX; x++)
+ for(y = 0; y < SzY; y++)
+ ogrid[x][y] = 100;
+
+ switch(difficulty){
+ case DEasy:
+ cnt = 10 + nrand(5);
+ break;
+ case DMed:
+ cnt = 5 + nrand(5);
+ break;
+ case DHard:
+ cnt = nrand(5);
+ break;
+ }
+ for(i = 0; i < cnt; i++) {
+ do {
+ x = nrand(SzX);
+ y = nrand(SzY);
+ } while(ogrid[x][y] != 100);
+ ogrid[x][y] = 999;
+ }
+
+ ogrid[SzX/2][SzY/2] = 1000;
+
+ memcpy(grid, ogrid, sizeof grid);
+
+ finished = 0;
+
+}
+
+void
+drawlevel(void)
+{
+ Point p;
+ int x, y, rx, ry, d;
+ char *s = nil;
+
+ if(finished)
+ draw(screen, screen->r, finished==Won?won:lost, nil, ZP);
+ else
+ draw(screen, screen->r, bg, nil, ZP);
+
+ d = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r) -20;
+ rx = (int)ceil((float)(d-2*Border)/(float)SzX)/2;
+ ry = (int)ceil((float)(d-2*Border)/(float)SzY)/2;
+
+ for(x = 0; x < SzX; x++) {
+ for(y = 0; y < SzY; y++) {
+ p = board2pix(x, y);
+ switch(grid[x][y]){
+ case 999:
+ fillellipse(screen, addpt(screen->r.min, p), rx, ry, cc, ZP);
+ break;
+ case 1000:
+ p = addpt(screen->r.min, p);
+ fillellipse(screen, p, rx, ry, ec, ZP);
+ p = subpt(p, Pt(24, 24));
+ draw(screen, Rpt(p, addpt(p, Pt(48, 48))), gl, glm, ZP);
+ break;
+ default:
+ fillellipse(screen, addpt(screen->r.min, p), rx, ry, ec, ZP);
+ USED(s);
+ /* uncomment the following to see game state and field scores */
+ /*s = smprint("%d", grid[x][y]);
+ string(screen, addpt(screen->r.min, p), display->black, ZP, font, s);
+ free(s);
+ */
+ break;
+ }
+ }
+ }
+ flushimage(display, 1);
+}
+
+void
+domove(int dir, int x, int y)
+{
+ if(x == 0 || x == SzX-1 || y == 0 || y == SzY-1)
+ goto done;
+
+ switch(dir){
+ case NE:
+ if(y%2)
+ grid[x+1][y-1] = 1000;
+ else
+ grid[x][y-1] = 1000;
+ break;
+ case E:
+ grid[x+1][y] = 1000;
+ break;
+ case SE:
+ if(y%2)
+ grid[x+1][y+1] = 1000;
+ else
+ grid[x][y+1] = 1000;
+ break;
+ case SW:
+ if(y%2)
+ grid[x][y+1] = 1000;
+ else
+ grid[x-1][y+1] = 1000;
+ break;
+ case W:
+ grid[x-1][y] = 1000;
+ break;
+ case NW:
+ if(y%2)
+ grid[x][y-1] = 1000;
+ else
+ grid[x-1][y-1] = 1000;
+ break;
+ }
+done:
+ grid[x][y] = 100;
+}
+
+Point
+findglenda(void)
+{
+ int x, y;
+ for(x = 0; x < SzX; x++)
+ for(y = 0; y < SzY; y++)
+ if(grid[x][y] == 1000)
+ return Pt(x, y);
+ return Pt(-1, -1);
+}
+
+int
+checknext(int dir, int x, int y)
+{
+ switch(dir){
+ case NE:
+ return grid[x+(y%2?1:0)][y-1];
+ case E:
+ return grid[x+1][y];
+ case SE:
+ return grid[x+(y%2?1:0)][y+1];
+ case SW:
+ return grid[x+(y%2?0:-1)][y+1];
+ case W:
+ return grid[x-1][y];
+ case NW:
+ return grid[x+(y%2?0:-1)][y-1];
+ default:
+ sysfatal("andrey messed up big time");
+ }
+ return 1000;
+}
+/* the following two routines constitute the "game AI"
+* they score the field based on the number of moves
+* required to reach the edge from a particular point
+* scores > 100 are "dead spots" (this assumes the field
+* is not larger than ~100*2
+*
+* routines need to run at least twice to ensure a field is properly
+* scored: there are errors that creep up due to the nature of
+* traversing the board
+*/
+int
+score1(int x, int y) {
+ int dir, min = 999, next;
+
+ if(x == 0 || x == SzX-1 || y == 0 || y == SzY-1)
+ return 1; /* we can always escape from the edges */
+
+ for(dir = NE; dir <= NW; dir++) {
+ next = checknext(dir, x, y);
+ if(next < min)
+ min = next;
+ }
+ return 1+min;
+}
+
+void
+calc(void)
+{
+ int i, x, y;
+ for(i = 0; i < SzX; i++) /* assumes SzX = SzY */
+ for(x = i; x < SzX-i; x++)
+ for(y = i; y < SzY-i; y++)
+ if(grid[x][y] != 999)
+ grid[x][y] = score1(x, y);
+}
+
+void
+nextglenda(void)
+{
+ int min =1000, next, dir, nextdir = 0, count = 0;
+ Point p = findglenda();
+
+ calc();
+ calc();
+ calc();
+
+ grid[p.x][p.y] = 1000;
+
+ for(dir = NE; dir <= NW; dir++) {
+ next = checknext(dir, p.x, p.y);
+ if(next < min) {
+ min = next;
+ nextdir = dir;
+ ++count;
+ } else if(next == min) {
+ nextdir = (nrand(++count) == 0)?dir:nextdir;
+ }
+ }
+ if(min < 100)
+ domove(nextdir, p.x, p.y);
+ else
+ finished = Won;
+
+ if(eqpt(findglenda(), Pt(-1, -1)))
+ finished = Lost;
+}
+
+int
+checkfinished(void)
+{
+ int i, j;
+ for(i = 0; i < SzX; i++)
+ for(j = 0; j < SzY; j++)
+ if(grid[i][j] == 'E')
+ return 0;
+ return 1;
+}
+
+void
+move(Point m)
+{
+ Point p, nm;
+ int x, y;
+
+ nm = subpt(m, screen->r.min);
+
+ /* figure out where the click falls */
+ p = pix2board(nm.x, nm.y);
+
+ if(grid[p.x][p.y] >= 999)
+ return;
+
+ /* reset the board scores */
+ grid[p.x][p.y] = 999;
+ for(x = 0; x < SzX; x++)
+ for(y = 0; y < SzY; y++)
+ if(grid[x][y] != 999 && grid[x][y] != 1000)
+ grid[x][y] = 100;
+
+ nextglenda();
+}
+
+void
+resize(void)
+{
+ int fd, size = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) + 20 : Dx(screen->r)+20;
+
+ fd = open("/dev/wctl", OWRITE);
+ if(fd >= 0){
+ fprint(fd, "resize -dx %d -dy %d", size, size);
+ close(fd);
+ }
+
+}
+
+
+void
+eresized(int new)
+{
+ if(new && getwindow(display, Refnone) < 0)
+ sysfatal("can't reattach to window");
+
+ drawlevel();
+}
+
+void
+main(int argc, char **argv)
+{
+ Mouse m;
+ Event ev;
+ int e, mousedown=0;
+ char *fontname;
+
+ USED(argv, argc);
+
+ if(initdraw(nil, nil, "glendy") < 0)
+ sysfatal("initdraw failed: %r");
+ einit(Emouse);
+
+ resize();
+
+ srand(time(0));
+
+ allocimages();
+ initlevel(); /* must happen before "eresized" */
+ eresized(0);
+
+ fontname = "/lib/font/bit/lucidasans/unicode.8.font";
+ if((font = openfont(display, fontname)) == nil)
+ sysfatal("font '%s' not found", fontname);
+
+ for(;;) {
+ e = event(&ev);
+ switch(e) {
+ case Emouse:
+ m = ev.mouse;
+ if(m.buttons == 0) {
+ if(mousedown && !finished) {
+ mousedown = 0;
+ move(m.xy);
+ drawlevel();
+ }
+ }
+ if(m.buttons&1) {
+ mousedown = 1;
+ }
+ if(m.buttons&2) {
+ switch(emenuhit(2, &m, &mmenu)) {
+ case 0:
+ difficulty = DEasy;
+ initlevel();
+ break;
+ case 1:
+ difficulty = DMed;
+ initlevel();
+ break;
+ case 2:
+ difficulty = DHard;
+ initlevel();
+ break;
+ }
+ drawlevel();
+ }
+ if(m.buttons&4) {
+ switch(emenuhit(3, &m, &rmenu)) {
+ case 0:
+ initlevel();
+ break;
+ case 1:
+ memcpy(grid, ogrid, sizeof grid);
+ finished = 0;
+ break;
+ case 2:
+ exits(nil);
+ }
+ drawlevel();
+ }
+ break;
+ }
+ }
+}
diff --git a/sys/src/games/mkfile b/sys/src/games/mkfile
index 73467cc32..e59b1ad7f 100644
--- a/sys/src/games/mkfile
+++ b/sys/src/games/mkfile
@@ -9,6 +9,7 @@ TARG=4s\
life\
memo\
mole\
+ glendy\
OFILES=
HFILES=