summaryrefslogtreecommitdiff
path: root/appl
diff options
context:
space:
mode:
authorforsyth <forsyth@vitanuova.com>2010-08-02 14:49:50 +0100
committerforsyth <forsyth@vitanuova.com>2010-08-02 14:49:50 +0100
commit66f5808b81b1df84bc57c4f7b9d487201bc162fb (patch)
treefe09448075dcf50ecca78a673e16ad84a666d3da /appl
parent7781741266783e4df3b35d42a55e8e504838898b (diff)
20100802-1449
Diffstat (limited to 'appl')
-rw-r--r--appl/NOTICE2
-rw-r--r--appl/acme/acme/bin/src/win.b14
-rw-r--r--appl/cmd/auth/countersigner.b11
-rw-r--r--appl/cmd/cat.b51
-rw-r--r--appl/cmd/crypt.b41
-rw-r--r--appl/cmd/fs/proto.b39
-rw-r--r--appl/cmd/sh/std.b2
-rw-r--r--appl/cmd/stackv.b116
-rw-r--r--appl/cmd/wmexport.b1
-rw-r--r--appl/lib/NOTICE2
-rw-r--r--appl/lib/names.b4
-rw-r--r--appl/wm/tetris.b10
12 files changed, 188 insertions, 105 deletions
diff --git a/appl/NOTICE b/appl/NOTICE
index 5e277521..04cbab52 100644
--- a/appl/NOTICE
+++ b/appl/NOTICE
@@ -8,7 +8,7 @@ file such as NOTICE, LICENCE or COPYING.
Copyright © 1995-1999 Lucent Technologies Inc.
Portions Copyright © 1997-2000 Vita Nuova Limited
-Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
+Portions Copyright © 2000-2010 Vita Nuova Holdings Limited
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/appl/acme/acme/bin/src/win.b b/appl/acme/acme/bin/src/win.b
index 857539cf..a470820a 100644
--- a/appl/acme/acme/bin/src/win.b
+++ b/appl/acme/acme/bin/src/win.b
@@ -625,25 +625,27 @@ addtype(c : int, p0 : int, b : array of byte, nb : int, nr : int)
sendtype(fd0 : ref FD, raw : int)
{
- i, n, nr : int;
-
while(ntypebreak){
brkc := 0;
- for(i=0; i<ntypeb; i++)
+ i := 0;
+ while(i<ntypeb){
if(typing[i]==byte '\n' || typing[i]==byte 16r04){
- n = i + (typing[i] == byte '\n');
+ n := i + (typing[i] == byte '\n');
i++;
if(write(fd0, typing, n) != n)
error("sending to program");
- nr = nrunes(typing, i);
+ nr := nrunes(typing, i);
if (!raw)
q.p += nr;
ntyper -= nr;
ntypeb -= i;
typing[0:] = typing[i:i+ntypeb];
+ i = 0;
ntypebreak--;
brkc = 1;
- }
+ }else
+ i++;
+ }
if (!brkc) {
fprint(stdout, "no breakchar\n");
ntypebreak = 0;
diff --git a/appl/cmd/auth/countersigner.b b/appl/cmd/auth/countersigner.b
index a444f807..57e4388d 100644
--- a/appl/cmd/auth/countersigner.b
+++ b/appl/cmd/auth/countersigner.b
@@ -9,6 +9,9 @@ include "draw.m";
include "keyring.m";
kr: Keyring;
+include "msgio.m";
+ msgio: Msgio;
+
include "security.m";
Countersigner: module
@@ -22,6 +25,8 @@ init(nil: ref Draw->Context, nil: list of string)
{
sys = load Sys Sys->PATH;
kr = load Keyring Keyring->PATH;
+ msgio = load Msgio Msgio->PATH;
+ msgio->init();
stdin = sys->fildes(0);
stdout = sys->fildes(1);
@@ -34,7 +39,7 @@ init(nil: ref Draw->Context, nil: list of string)
}
# get boxid
- buf := kr->getmsg(stdin);
+ buf := msgio->getmsg(stdin);
if(buf == nil){
sys->fprint(stderr, "countersigner: client hung up\n");
raise "fail:hungup";
@@ -48,12 +53,12 @@ init(nil: ref Draw->Context, nil: list of string)
sys->fprint(stderr, "countersigner: can't open %s: %r\n", file);
raise "fail:bad boxid";
}
- blind := kr->getmsg(fd);
+ blind := msgio->getmsg(fd);
if(blind == nil){
sys->fprint(stderr, "countersigner: can't read %s\n", file);
raise "fail:no blind";
}
# answer client
- kr->sendmsg(stdout, blind, len blind);
+ msgio->sendmsg(stdout, blind, len blind);
}
diff --git a/appl/cmd/cat.b b/appl/cmd/cat.b
index 24d62372..5b9e3b5b 100644
--- a/appl/cmd/cat.b
+++ b/appl/cmd/cat.b
@@ -1,6 +1,8 @@
implement Cat;
include "sys.m";
+ sys: Sys;
+
include "draw.m";
Cat: module
@@ -8,50 +10,39 @@ Cat: module
init: fn(ctxt: ref Draw->Context, argv: list of string);
};
-sys: Sys;
stdout: ref Sys->FD;
-init(nil: ref Draw->Context, argl: list of string)
+init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
-
stdout = sys->fildes(1);
-
- argl = tl argl;
- if(argl == nil)
- argl = "-" :: nil;
- while(argl != nil) {
- cat(hd argl);
- argl = tl argl;
+ args = tl args;
+ if(args == nil)
+ args = "-" :: nil;
+ for(; args != nil; args = tl args){
+ file := hd args;
+ if(file != "-"){
+ fd := sys->open(file, Sys->OREAD);
+ if(fd == nil){
+ sys->fprint(sys->fildes(2), "cat: cannot open %s: %r\n", file);
+ raise "fail:bad open";
+ }
+ cat(fd, file);
+ }else
+ cat(sys->fildes(0), "<stdin>");
}
}
-cat(file: string)
+cat(fd: ref Sys->FD, file: string)
{
- n: int;
- fd: ref Sys->FD;
- buf := array[8192] of byte;
-
- if(file == "-")
- fd = sys->fildes(0);
- else {
- fd = sys->open(file, sys->OREAD);
- if(fd == nil) {
- sys->fprint(sys->fildes(2), "cat: cannot open %s: %r\n", file);
- raise "fail:bad open";
- }
- }
- for(;;) {
- n = sys->read(fd, buf, len buf);
- if(n <= 0)
- break;
+ buf := array[Sys->ATOMICIO] of byte;
+ while((n := sys->read(fd, buf, len buf)) > 0)
if(sys->write(stdout, buf, n) < n) {
sys->fprint(sys->fildes(2), "cat: write error: %r\n");
raise "fail:write error";
}
- }
if(n < 0) {
- sys->fprint(sys->fildes(2), "cat: read error: %r\n");
+ sys->fprint(sys->fildes(2), "cat: error reading %s: %r\n", file);
raise "fail:read error";
}
}
diff --git a/appl/cmd/crypt.b b/appl/cmd/crypt.b
index eec73bc9..adc2a1a0 100644
--- a/appl/cmd/crypt.b
+++ b/appl/cmd/crypt.b
@@ -10,6 +10,9 @@ include "keyring.m";
keyring: Keyring;
include "security.m";
ssl: SSL;
+include "bufio.m";
+include "msgio.m";
+ msgio: Msgio;
include "arg.m";
Crypt: module {
@@ -47,6 +50,8 @@ init(nil: ref Draw->Context, argv: list of string)
keyring = load Keyring Keyring->PATH;
if (keyring == nil)
badmodule(SSL->PATH);
+ msgio = load Msgio Msgio->PATH;
+ msgio->init();
arg := load Arg Arg->PATH;
if (arg == nil)
@@ -84,16 +89,14 @@ init(nil: ref Draw->Context, argv: list of string)
argv = arg->argv();
if (argv != nil)
usage();
- if(secret == nil){
- sys->fprint(stderr, "crypt: no secret given\n");
- usage();
- }
+ if(secret == nil)
+ secret = array of byte readpassword();
sk := array[Keyring->SHA1dlen] of byte;
keyring->sha1(secret, len secret, sk, nil);
if (headers) {
# deal with header - the header encodes the algorithm along with the data.
if (decrypt) {
- msg := keyring->getmsg(sys->fildes(0));
+ msg := msgio->getmsg(sys->fildes(0));
if (msg != nil)
alg = string msg;
if (msg == nil || len alg < len ALGSTR || alg[0:len ALGSTR] != ALGSTR)
@@ -101,7 +104,7 @@ init(nil: ref Draw->Context, argv: list of string)
alg = alg[len ALGSTR:];
} else {
msg := array of byte ("alg " + alg);
- e := keyring->sendmsg(sys->fildes(1), msg, len msg);
+ e := msgio->sendmsg(sys->fildes(1), msg, len msg);
if (e == -1)
error("couldn't write algorithm string");
}
@@ -205,6 +208,32 @@ showalgs(fd: ref Sys->FD)
}
}
+readpassword(): string
+{
+ bufio := load Bufio Bufio->PATH;
+ Iobuf: import bufio;
+ stdin := bufio->open("/dev/cons", Sys->OREAD);
+
+ cfd := sys->open("/dev/consctl", Sys->OWRITE);
+ if (cfd == nil || sys->fprint(cfd, "rawon") <= 0)
+ sys->fprint(stderr, "crypt: warning: cannot hide typed password\n");
+ sys->fprint(stderr, "password: ");
+ s := "";
+ while ((c := stdin.getc()) >= 0 && c != '\n'){
+ case c {
+ '\b' =>
+ if (len s > 0)
+ s = s[0:len s - 1];
+ 8r25 => # ^U
+ s = nil;
+ * =>
+ s[len s] = c;
+ }
+ }
+ sys->fprint(stderr, "\n");
+ return s;
+}
+
stream(src, dst: ref Sys->FD, bufsize: int)
{
sys->stream(src, dst, bufsize);
diff --git a/appl/cmd/fs/proto.b b/appl/cmd/fs/proto.b
index bc836f44..859f34ce 100644
--- a/appl/cmd/fs/proto.b
+++ b/appl/cmd/fs/proto.b
@@ -33,7 +33,7 @@ Proto: adt {
iob: ref Iobuf;
};
-Star, Plus: con 1<<iota;
+Star, Plus, Empty: con 1<<iota;
types(): string
{
@@ -103,29 +103,30 @@ protowalk1(c: Fschan, flags: int, path: string, d: ref Sys->Dir,
Skip =>
return r;
}
- (a, n) := readdir->init(path, Readdir->NAME|Readdir->COMPACT);
- if(len a == 0){
- c <-= ((nil, nil), reply);
- if(<-reply == Quit)
- quit(errorc);
- return Next;
- }
- j := 0;
+ a: array of ref Sys->Dir;
+ n := 0;
+ if((flags&Empty)==0)
+ (a, n) = readdir->init(path, Readdir->NAME|Readdir->COMPACT);
+ i := j := 0;
prevsub: string;
- for(i := 0; i < n; i++){
+ while(i < n || j < len sub){
for(; j < len sub; j++){
s := sub[j].name;
if(s == prevsub){
report(errorc, sys->sprint("duplicate entry %s", pathconcat(path, s)));
continue; # eliminate duplicates in proto
}
- if(s >= a[i].name || sub[j].old != nil)
+ # if we're copying from an old file, and there's a matching
+ # entry in the directory, then skip it.
+ if(sub[j].old != nil && i < n && s == a[i].name)
+ i++;
+ if(sub[j].old != nil || i < n && s >= a[i].name)
break;
report(errorc, sys->sprint("%s not found", pathconcat(path, s)));
}
- foundsub := j < len sub && (sub[j].name == a[i].name || sub[j].old != nil);
- if(foundsub || flags&Plus ||
- (flags&Star && (a[i].mode & Sys->DMDIR)==0)){
+ foundsub := j < len sub && (sub[j].old != nil || sub[j].name == a[i].name);
+
+ if(foundsub || flags&(Plus|Star)){
f: ref File;
if(foundsub){
f = sub[j++];
@@ -143,7 +144,7 @@ protowalk1(c: Fschan, flags: int, path: string, d: ref Sys->Dir,
d = ref xd;
}else{
p = pathconcat(path, a[i].name);
- d = a[i];
+ d = a[i++];
}
d = file2dir(f, d);
@@ -152,12 +153,16 @@ protowalk1(c: Fschan, flags: int, path: string, d: ref Sys->Dir,
r = walkfile(c, p, d, errorc);
else if(flags & Plus)
r = protowalk1(c, Plus, p, d, nil, errorc);
+ else if((flags&Star) && !foundsub)
+ r = protowalk1(c, Empty, p, d, nil, errorc);
else
r = protowalk1(c, f.flags, p, d, f.sub, errorc);
if(r == Skip)
return Next;
- }
+ }else
+ i++;
}
+
c <-= ((nil, nil), reply);
if(<-reply == Quit)
quit(errorc);
@@ -272,7 +277,7 @@ readline(proto: ref Proto, indent: int): ref File
return nil;
}
proto.indent = spc;
- (n, toks) := sys->tokenize(s, " \t\n");
+ (nil, toks) := sys->tokenize(s, " \t\n");
f := ref File(nil, ~0, nil, nil, nil, 0, nil);
(f.name, toks) = (getname(hd toks, 0), tl toks);
if(toks == nil)
diff --git a/appl/cmd/sh/std.b b/appl/cmd/sh/std.b
index 6a944614..32a80a2e 100644
--- a/appl/cmd/sh/std.b
+++ b/appl/cmd/sh/std.b
@@ -512,7 +512,7 @@ builtin_rescue(ctxt: ref Context, args: list of ref Listnode, last: int) : strin
status := ctxt.run(handler, last);
ctxt.pop();
return status;
- } exception e2{
+ } exception {
"fail:*" =>
ctxt.pop();
raise e;
diff --git a/appl/cmd/stackv.b b/appl/cmd/stackv.b
index 4688f804..be842aa5 100644
--- a/appl/cmd/stackv.b
+++ b/appl/cmd/stackv.b
@@ -16,7 +16,8 @@ include "bufio.m";
stderr: ref Sys->FD;
stdout: ref Iobuf;
-hasht := array[97] of list of string;
+
+hasht := array[97] of (int, array of int);
Stackv: module {
init: fn(ctxt: ref Draw->Context, argv: list of string);
@@ -33,7 +34,7 @@ badmodule(p: string)
currp: ref Prog;
showtypes := 1;
showsource := 0;
-sep := "\t";
+showmodule := 0;
init(nil: ref Draw->Context, argv: list of string)
{
@@ -52,7 +53,7 @@ init(nil: ref Draw->Context, argv: list of string)
stdout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
arg->init(argv);
- arg->setusage("stackv [-Tl] [-i indent] [-r maxdepth] [-s dis sbl]... [pid[.sym...] ...]");
+ arg->setusage("stackv [-Tlm] [-r maxdepth] [-s dis sbl]... [pid[.sym]...] ...");
sblfile := "";
while((opt := arg->opt()) != 0){
case opt {
@@ -61,12 +62,12 @@ init(nil: ref Draw->Context, argv: list of string)
sblfile = arg->earg();
'l' =>
showsource = 1;
+ 'm' =>
+ showmodule = 1;
'r' =>
maxrecur = int arg->earg();
'T' =>
showtypes = 0;
- 'i' =>
- sep = arg->earg();
* =>
arg->usage();
}
@@ -137,7 +138,7 @@ pexp(stk: array of ref Exp, toks: list of string, depth: int)
exp := stackfindsym(stk, toks, depth);
if(exp == nil)
return;
- pname(exp, depth);
+ pname(exp, depth, nil);
stdout.putc('\n');
}
}
@@ -218,7 +219,7 @@ pfn(exp: ref Exp, depth: int)
if((e := getname(exps, "args")) != nil){
args := e.expand();
for(i := 0; i < len args; i++){
- pname(args[i], depth+1);
+ pname(args[i], depth+1, nil);
if(i != len args - 1)
stdout.puts(", ");
}
@@ -230,7 +231,15 @@ pfn(exp: ref Exp, depth: int)
locals := e.expand();
for(i := 0; i < len locals; i++){
indent(depth+1);
- pname(locals[i], depth+1);
+ pname(locals[i], depth+1, nil);
+ stdout.puts("\n");
+ }
+ }
+ if(showmodule && (e = getname(exps, "module")) != nil){
+ mvars := e.expand();
+ for(i := 0; i < len mvars; i++){
+ indent(depth+1);
+ pname(mvars[i], depth+1, "module.");
stdout.puts("\n");
}
}
@@ -256,8 +265,9 @@ strval(v: string): string
return v;
}
-pname(exp: ref Exp, depth: int)
+pname(exp: ref Exp, depth: int, prefix: string)
{
+ name := prefix+symname(exp);
(v, w) := exp.val();
if (!w && v == nil) {
stdout.puts(sys->sprint("%s: %s = novalue", symname(exp), exp.typename()));
@@ -267,18 +277,18 @@ pname(exp: ref Exp, depth: int)
Tfn =>
pfn(exp, depth);
Tint =>
- stdout.puts(sys->sprint("%s := %s", symname(exp), v));
+ stdout.puts(sys->sprint("%s := %s", name, v));
Tstring =>
- stdout.puts(sys->sprint("%s := %s", symname(exp), strval(v)));
+ stdout.puts(sys->sprint("%s := %s", name, strval(v)));
Tbyte or
Tbig or
Treal =>
- stdout.puts(sys->sprint("%s := %s %s", symname(exp), exp.typename(), v));
+ stdout.puts(sys->sprint("%s := %s %s", name, exp.typename(), v));
* =>
if(showtypes)
- stdout.puts(sys->sprint("%s: %s = ", symname(exp), exp.typename()));
+ stdout.puts(sys->sprint("%s: %s = ", name, exp.typename()));
else
- stdout.puts(sys->sprint("%s := ", symname(exp)));
+ stdout.puts(sys->sprint("%s := ", name));
pval(exp, v, w, depth);
}
}
@@ -390,7 +400,7 @@ pgenval(exp: ref Exp, v: string, w: int, depth: int)
}else{
for (i := 0; i < len exps; i++){
indent(depth+1);
- pname(exps[i], depth+1);
+ pname(exps[i], depth+1, nil);
stdout.puts("\n");
}
}
@@ -412,36 +422,80 @@ symname(exp: ref Exp): string
indent(n: int)
{
while(n-- > 0)
- stdout.puts(sep);
+ stdout.putc('\t');
+}
+
+ref2int(v: string): int
+{
+ if(v == nil)
+ error("bad empty value for ref");
+ i := 0;
+ n := len v;
+ if(v[0] == '@')
+ i = 1;
+ else{
+ # skip array bounds
+ if(v[0] == '['){
+ for(; i < n && v[i] != ']'; i++)
+ ;
+ if(i >= n - 2 || v[i+1] != ' ' || v[i+2] != '@')
+ error("bad value for ref: "+v);
+ i += 3;
+ }
+ }
+ if(n - i > 8)
+ error("64-bit pointers?");
+ p := 0;
+ for(; i < n; i++){
+ c := v[i];
+ case c {
+ '0' to '9' =>
+ p = (p << 4) + (c - '0');
+ 'a' to 'f' =>
+ p = (p << 4) + (c - 'a' + 10);
+ * =>
+ error("bad value for ref: "+v);
+ }
+ }
+ return p;
}
pref(v: string): int
{
- if(addref(v) == 0){
+ if(v == "nil"){
+ stdout.puts("nil");
+ return 0;
+ }
+ if(addref(ref2int(v)) == 0){
stdout.puts(v);
- if(v != "nil")
- stdout.puts("(qv)");
+ stdout.puts("(qv)");
return 0;
}
return 1;
}
-addref(v: string): int
+# hash table implementation that tries to be reasonably
+# parsimonious on memory usage.
+addref(v: int): int
{
- slot := hashfn(v, len hasht);
- for(l := hasht[slot]; l != nil; l = tl l)
- if((hd l) == v)
+ slot := (v & 16r7fffffff) % len hasht;
+ (n, a) := hasht[slot];
+ for(i := 0; i < n; i++)
+ if(a[i] == v)
return 0;
- hasht[slot] = v :: hasht[slot];
+ if(n == len a){
+ if(n == 0)
+ n = 3;
+ t := array[n*3/2] of int;
+ t[0:] = a;
+ hasht[slot].t1 = t;
+ }
+ a[hasht[slot].t0++] = v;
return 1;
}
-hashfn(s: string, n: int): int
+error(e: string)
{
- h := 0;
- m := len s;
- for(i:=0; i<m; i++){
- h = 65599*h+s[i];
- }
- return (h & 16r7fffffff) % n;
+ sys->fprint(sys->fildes(2), "stackv: error: %s\n", e);
+ raise "fail:error";
}
diff --git a/appl/cmd/wmexport.b b/appl/cmd/wmexport.b
index 204337cd..178b588d 100644
--- a/appl/cmd/wmexport.b
+++ b/appl/cmd/wmexport.b
@@ -18,7 +18,6 @@ include "styxservers.m";
styxservers: Styxservers;
Styxserver, Fid, Navigator, Navop: import styxservers;
Enotdir, Enotfound: import Styxservers;
- nametree: Nametree;
Wmexport: module {
init: fn(nil: ref Draw->Context, argv: list of string);
diff --git a/appl/lib/NOTICE b/appl/lib/NOTICE
index a9a7616f..6169ac2f 100644
--- a/appl/lib/NOTICE
+++ b/appl/lib/NOTICE
@@ -8,7 +8,7 @@ file such as NOTICE, LICENCE or COPYING.
Copyright © 1995-1999 Lucent Technologies Inc.
Portions Copyright © 1997-2000 Vita Nuova Limited
-Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
+Portions Copyright © 2000-2010 Vita Nuova Holdings Limited
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License (`LGPL') as published by
diff --git a/appl/lib/names.b b/appl/lib/names.b
index d3a2082e..08bc4875 100644
--- a/appl/lib/names.b
+++ b/appl/lib/names.b
@@ -120,7 +120,7 @@ isprefix(a: string, b: string): int
{
la := len a;
if(la == 0)
- return 0; # "" isnt' a pathname
+ return 0; # "" isn't a pathname
while(la > 1 && a[la-1] == '/')
a = a[0:--la];
lb := len b;
@@ -128,7 +128,7 @@ isprefix(a: string, b: string): int
return 0;
if(la == lb)
return a == b;
- return a == b[0:la] && (b[la] == '/' || a == "/");
+ return a == b[0:la] && (a == "/" || b[la] == '/');
}
elements(name: string): list of string
diff --git a/appl/wm/tetris.b b/appl/wm/tetris.b
index ddde1a17..0ef16b2b 100644
--- a/appl/wm/tetris.b
+++ b/appl/wm/tetris.b
@@ -438,10 +438,8 @@ Game.rotate(g: self ref Game, clockwise: int)
npiece.rot = (npiece.rot + inc + nrots) % nrots;
if (canmove(g, npiece, g.pos)) {
c := coords[npiece.rot];
- for (i := 0; i < len c; i++) {
- np := g.pos.add(c[i]);
+ for (i := 0; i < len c; i++)
g.bd.moveblock(g.pieceids[i], g.pos.add(c[i]));
- }
g.curr = npiece;
g.bd.update();
}
@@ -525,7 +523,7 @@ canmove(g: ref Game, piece: Piece, p: Point): int
newpiece(g: ref Game)
{
- piece := g.curr = g.next;
+ g.curr = g.next;
g.next = randompiece();
g.bd.setnextshape(shapes[g.next.shape].colour, shapes[g.next.shape].coords[g.next.rot]);
shape := shapes[g.curr.shape];
@@ -582,7 +580,7 @@ makescorewidget(top: ref Tk->Toplevel, w, title: string)
cmd(top, "frame " + w);
cmd(top, "label " + w + ".title -text " + tk->quote(title));
cmd(top, "label " + w +
- ".val -bd 3 -relief sunken -width 5w -text 0 -anchor e");
+ ".val -bd 2 -relief sunken -width 5w -text 0 -anchor e");
cmd(top, "pack " + w + ".title -side left -anchor w");
cmd(top, "pack " + w + ".val -side right -anchor e");
}
@@ -619,7 +617,7 @@ Board.landedblock(bd: self ref Board, b: string, p: Point)
rs := cmd(bd.win, bd.w + ".c coords " + b);
if (rs != nil && rs[0] == '!')
return;
- (n, toks) := sys->tokenize(rs, " ");
+ (nil, toks) := sys->tokenize(rs, " ");
if (len toks != 4) {
sys->fprint(stderr, "bad coords for block %s\n", b);
return;