diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/fs | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/cmd/fs')
32 files changed, 4354 insertions, 0 deletions
diff --git a/appl/cmd/fs/and.b b/appl/cmd/fs/and.b new file mode 100644 index 00000000..ff867409 --- /dev/null +++ b/appl/cmd/fs/and.b @@ -0,0 +1,65 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "pppp*"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + c := chan of Gatequery; + spawn andgate(c, args); + return ref Value.P(c); +} + +andgate(c: Gatechan, args: list of ref Value) +{ + sub: list of Gatechan; + for(; args != nil; args = tl args) + sub = (hd args).p().i :: sub; + sub = rev(sub); + myreply := chan of int; + while(((d, reply) := <-c).t0.t0 != nil){ + for(l := sub; l != nil; l = tl l){ + (hd l) <-= (d, myreply); + if(<-myreply == 0) + break; + } + reply <-= l == nil; + } + for(; sub != nil; sub = tl sub) + hd sub <-= (Nilentry, nil); +} + +rev[T](x: list of T): list of T +{ + l: list of T; + for(; x != nil; x = tl x) + l = hd x :: l; + return l; +} diff --git a/appl/cmd/fs/bundle.b b/appl/cmd/fs/bundle.b new file mode 100644 index 00000000..a4b1cee5 --- /dev/null +++ b/appl/cmd/fs/bundle.b @@ -0,0 +1,195 @@ +implement Bundle; +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "readdir.m"; + readdir: Readdir; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, report, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; +include "bundle.m"; + +# XXX if we can't open a directory, is it ever worth passing its metadata +# through anyway? + +EOF: con "end of archive\n"; + +types(): string +{ + return "vx"; +} +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: bundle: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + badmod(Readdir->PATH); + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + badmod(Readdir->PATH); + bufio->fopen(nil, Sys->OREAD); # XXX no bufio->init! + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Readdir->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + return ref Value.V( + bundle( + report, + bufio->fopen(sys->fildes(1), Sys->OWRITE), + (hd args).x().i + ) + ); +} + +bundle(r: ref Report, iob: ref Iobuf, c: Fschan): chan of int +{ + sync := chan of int; + spawn bundleproc(c, sync, iob, r.start("bundle")); + return sync; +} + +bundleproc(c: Fschan, sync: chan of int, iob: ref Iobuf, errorc: chan of string) +{ + if(sync != nil && <-sync == 0){ + (<-c).t1 <-= Quit; + quit(errorc); + } + (d, reply) := <-c; + if(d.dir == nil){ + report(errorc, "no root directory"); + endarchive(iob, errorc); + } + if(puts(iob, dir2header(d.dir), errorc) == -1){ + reply <-= Quit; + quit(errorc); + } + reply <-= Down; + bundledir(d.dir.name, d, c, iob, errorc); + endarchive(iob, errorc); +} + +endarchive(iob: ref Iobuf, errorc: chan of string) +{ + if(puts(iob, EOF, errorc) != -1) + iob.flush(); + quit(errorc); + exit; +} + +bundledir(path: string, d: Fsdata, + c: Fschan, + iob: ref Iobuf, errorc: chan of string) +{ + if(d.dir.mode & Sys->DMDIR){ + path[len path] = '/'; + for(;;){ + (ent, reply) := <-c; + if(ent.dir == nil){ + reply <-= Skip; + break; + } + if(puts(iob, dir2header(ent.dir), errorc) == -1){ + reply <-= Quit; + quit(errorc); + } + reply <-= Down; + bundledir(path + ent.dir.name, ent, c, iob, errorc); + } + iob.putc('\n'); + }else{ + buf: array of byte; + reply: chan of int; + length := big d.dir.length; + n := big 0; + for(;;){ + ((nil, buf), reply) = <-c; + if(buf == nil){ + reply <-= Skip; + break; + } + if(write(iob, buf, len buf, errorc) != len buf){ + reply <-= Quit; + quit(errorc); + } + n += big len buf; + if(n > length){ # should never happen + report(errorc, sys->sprint("%q is longer than expected (fatal)", path)); + reply <-= Quit; + quit(errorc); + } + if(n == length){ + reply <-= Skip; + break; + } + reply <-= Next; + } + if(n < length){ + report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd); adding null bytes", path, n, length)); + buf = array[Sys->ATOMICIO] of {* => byte 0}; + while(n < length){ + nb := len buf; + if(length - n < big len buf) + nb = int (length - n); + if(write(iob, buf, nb, errorc) != nb){ + (<-c).t1 <-= Quit; + quit(errorc); + } + report(errorc, sys->sprint("added %d null bytes", nb)); + n += big nb; + } + } + } +} + +dir2header(d: ref Sys->Dir): string +{ + return sys->sprint("%q %uo %q %q %ud %bd\n", d.name, d.mode, d.uid, d.gid, d.mtime, d.length); +} + +puts(iob: ref Iobuf, s: string, errorc: chan of string): int +{ + { + if(iob.puts(s) == -1) + report(errorc, sys->sprint("write error: %r")); + return 0; + } exception { + "write on closed pipe" => + return -1; + } +} + +write(iob: ref Iobuf, buf: array of byte, n: int, errorc: chan of string): int +{ + { + nw := iob.write(buf, n); + if(nw < n){ + if(nw >= 0) + report(errorc, "short write"); + else{ + report(errorc, sys->sprint("write error: %r")); + } + } + return nw; + } exception { + "write on closed pipe" => + report(errorc, "write on closed pipe"); + return -1; + } +} diff --git a/appl/cmd/fs/chstat.b b/appl/cmd/fs/chstat.b new file mode 100644 index 00000000..e549527e --- /dev/null +++ b/appl/cmd/fs/chstat.b @@ -0,0 +1,185 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fsfilter: Fsfilter; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +Query: adt { + gate: Gatechan; + stat: Sys->Dir; + mask: int; + cflag: int; + reply: chan of int; + + query: fn(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int; +}; + +types(): string +{ + return "xx-pp-ms-us-gs-ts-as-c"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + fsfilter = load Fsfilter Fsfilter->PATH; + if(fsfilter == nil) + badmod(Fsfilter->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + ws := Sys->nulldir; + mask := 0; + gate: ref Value; + cflag := 0; + for(; opts != nil; opts = tl opts){ + o := (hd opts).args; + case (hd opts).opt { + 'p' => + gate.discard(); + gate = hd o; + 'm' => + ok: int; + m := (hd o).s().i; + (ok, mask, ws.mode) = parsemode(m); + mask &= ~Sys->DMDIR; + if(ok == 0){ + sys->fprint(sys->fildes(2), "fs: chstat: bad mode %#q\n", m); + gate.discard(); + return nil; + } + 'u' => + ws.uid = (hd o).s().i; + 'g' => + ws.gid = (hd o).s().i; + 't' => + ws.mtime = int (hd o).s().i; + 'a' => + ws.atime = int (hd o).s().i; + 'c' => + cflag++; + } + } + + dst := chan of (Fsdata, chan of int); + p: Gatechan; + if(gate != nil) + p = gate.p().i; + spawn chstatproc((hd args).x().i, dst, p, ws, mask, cflag); + return ref Value.X(dst); +} + +chstatproc(src, dst: Fschan, gate: Gatechan, stat: Sys->Dir, mask: int, cflag: int) +{ + fsfilter->filter(ref Query(gate, stat, mask, cflag, chan of int), src, dst); + if(gate != nil) + gate <-= ((nil, nil, 0), nil); +} + +Query.query(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int +{ + c := 1; + if(q.gate != nil){ + q.gate <-= ((d, name, depth), q.reply); + c = <-q.reply; + } + if(c){ + if(q.cflag){ + m := d.mode & 8r700; + d.mode = (d.mode & ~8r77)|(m>>3)|(m>>6); + } + stat := q.stat; + d.mode = (d.mode & ~q.mask) | (stat.mode & q.mask); + if(stat.uid != nil) + d.uid = stat.uid; + if(stat.gid != nil) + d.gid = stat.gid; + if(stat.mtime != ~0) + d.mtime = stat.mtime; + if(stat.atime != ~0) + d.atime = stat.atime; + } + return 1; +} + +# stolen from /appl/cmd/chmod.b +User: con 8r700; +Group: con 8r070; +Other: con 8r007; +All: con User | Group | Other; + +Read: con 8r444; +Write: con 8r222; +Exec: con 8r111; +parsemode(spec: string): (int, int, int) +{ + mask := Sys->DMAPPEND | Sys->DMEXCL | Sys->DMDIR | Sys->DMAUTH; +loop: + for(i := 0; i < len spec; i++){ + case spec[i] { + 'u' => + mask |= User; + 'g' => + mask |= Group; + 'o' => + mask |= Other; + 'a' => + mask |= All; + * => + break loop; + } + } + if(i == len spec) + return (0, 0, 0); + if(i == 0) + mask |= All; + + op := spec[i++]; + if(op != '+' && op != '-' && op != '=') + return (0, 0, 0); + + mode := 0; + for(; i < len spec; i++){ + case spec[i]{ + 'r' => + mode |= Read; + 'w' => + mode |= Write; + 'x' => + mode |= Exec; + 'a' => + mode |= Sys->DMAPPEND; + 'l' => + mode |= Sys->DMEXCL; + 'd' => + mode |= Sys->DMDIR; + 'A' => + mode |= Sys->DMAUTH; + * => + return (0, 0, 0); + } + } + if(op == '+' || op == '-') + mask &= mode; + if(op == '-') + mode = ~mode; + return (1, mask, mode); +} diff --git a/appl/cmd/fs/compose.b b/appl/cmd/fs/compose.b new file mode 100644 index 00000000..69187d6b --- /dev/null +++ b/appl/cmd/fs/compose.b @@ -0,0 +1,100 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Cmpchan, + Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +AinB: con 1<<3; +BinA: con 1<<2; +AoutB: con 1<<1; +BoutA: con 1<<0; + +A: con AinB|AoutB; +AoverB: con AinB|AoutB|BoutA; +AatopB: con AinB|BoutA; +AxorB: con AoutB|BoutA; + +B: con BinA|BoutA; +BoverA: con BinA|BoutA|AoutB; +BatopA: con BinA|AoutB; +BxorA: con BoutA|AoutB; + +ops := array[] of { + AinB => "AinB", + BinA => "BinA", + AoutB => "AoutB", + BoutA => "BoutA", + A => "A", + AoverB => "AoverB", + AatopB => "AatopB", + AxorB => "AxorB", + B => "B", + BoverA => "BoverA", + BatopA => "BatopA", +}; + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +types(): string +{ + return "ms-d"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + c := chan of (ref Sys->Dir, ref Sys->Dir, chan of int); + s := (hd args).s().i; + for(i := 0; i < len ops; i++) + if(ops[i] == s) + break; + if(i == len ops){ + sys->fprint(sys->fildes(2), "fs: join: bad op %q\n", s); + return nil; + } + spawn compose(c, i, opts != nil); + return ref Value.M(c); +} + +compose(c: Cmpchan, op: int, dflag: int) +{ + t := array[4] of {* => 0}; + if(op & AinB) + t[2r11] = 2r01; + if(op & BinA) + t[2r11] = 2r10; + if(op & AoutB) + t[2r01] = 2r01; + if(op & BoutA) + t[2r10] = 2r10; + if(dflag){ + while(((d0, d1, reply) := <-c).t2 != nil){ + x := (d1 != nil) << 1 | d0 != nil; + r := t[d0 != nil | (d1 != nil) << 1]; + if(r == 0 && x == 2r11 && (d0.mode & d1.mode & Sys->DMDIR)) + r = 2r11; + reply <-= r; + } + }else{ + while(((d0, d1, reply) := <-c).t2 != nil) + reply <-= t[(d1 != nil) << 1 | d0 != nil]; + } +} diff --git a/appl/cmd/fs/depth.b b/appl/cmd/fs/depth.b new file mode 100644 index 00000000..19c03b2d --- /dev/null +++ b/appl/cmd/fs/depth.b @@ -0,0 +1,49 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "ps"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + d := int (hd args).s().i; + if(d <= 0){ + sys->fprint(sys->fildes(2), "fs: depth: invalid depth\n"); + return nil; + } + c := chan of Gatequery; + spawn depthgate(c, d); + return ref Value.P(c); +} + +depthgate(c: Gatechan, d: int) +{ + while((((dir, nil, depth), reply) := <-c).t0.t0 != nil) + reply <-= depth <= d; +} diff --git a/appl/cmd/fs/entries.b b/appl/cmd/fs/entries.b new file mode 100644 index 00000000..56aac67f --- /dev/null +++ b/appl/cmd/fs/entries.b @@ -0,0 +1,86 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "tx"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + sc := Entrychan(chan of int, chan of Entry); + spawn entriesproc((hd args).x().i, sc); + return ref Value.T(sc); +} + +entriesproc(c: Fschan, sc: Entrychan) +{ + if(<-sc.sync == 0){ + (<-c).t1 <-= Quit; + exit; + } + indent := 0; + names: list of string; + name: string; +loop: + for(;;){ + (d, reply) := <-c; + if(d.dir != nil){ + p: string; + depth := indent; + if(d.dir.mode & Sys->DMDIR){ + names = name :: names; + if(indent == 0) + name = d.dir.name; + else{ + if(name[len name - 1] != '/') + name[len name] = '/'; + name += d.dir.name; + } + indent++; + reply <-= Down; + p = name; + }else{ + p = name; + if(p[len p - 1] != '/') + p[len p] = '/'; + p += d.dir.name; + reply <-= Next; + } + if(p != nil) + sc.c <-= (d.dir, p, depth); + }else{ + reply <-= Next; + if(d.dir == nil && d.data == nil){ + if(--indent == 0) + break loop; + (name, names) = (hd names, tl names); + } + } + } + sc.c <-= Nilentry; +} diff --git a/appl/cmd/fs/eval.b b/appl/cmd/fs/eval.b new file mode 100644 index 00000000..5eaf9291 --- /dev/null +++ b/appl/cmd/fs/eval.b @@ -0,0 +1,648 @@ +implement Eval; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "readdir.m"; +#include "env.m"; +# env: Env; +#include "string.m"; +# str: String; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Quit: import Fslib; + +# more general: +# eval: fn[V, M](ctxt: ref Context, r: ref Report, expr: string, args:...) with { +# V => +# typec: fn(t: self V): int; +# cvt: fn(t: self V, tc: int): V; +# cvt2s: fn(t: self V): (int, string); +# cvt2v: fn(t: self V): chan of int; +# mkstring: fn(s: string): V; +# mkcmd: fn(c: ref Sh->Cmd): V; +# discard: fn(t: self V); +# type2s: fn(c: int): string; +# loadmod: fn(cmd: string): M; +# M => +# types: fn(): string; +# init: fn(); +# run: fn(ctxt: ref Draw->Context, r: ref Report, cmd: string, +# opts: list of (int, list of V), args: list of V): V; +# } +# how to call eval? +# (eval with [V=>ref Value, M=>Fsmodule])( +# +# sort out error reporting; stderr is not good. + + +# possible things to do: +# pipe [-1pP] [-t command] command fs -> void +# pipe all files in fs through command. +# extract [-r root] gate fs -> fs +# extract the first entry within fs which +# passes through the gate. +# if -r is specified, the entry is placed +# within the given root, and may be a file, +# otherwise files are not allowed. +# apply string fs +# for each file in fs, evaluates string as an fs expression +# (which should yield fs), and replace the file in the +# original hierarchy with the result. +# e.g. +# fs apply '{unbundle $file}' {filter {or {mode +d} *.bundle} .} +# a bit fanciful this... +# merge could take an optional boolean operator +# +# venti? +# +# Cmpgate: chan of Cmpgatequery; +# Cmpgatequery: type (Entry, Entry, chan of int); +# returns 00, 01, 10 or 11 +# used by merge to decide what to do when merging +# used by write to decide what to do when writing +# +# cmpdate [-u] '>' +# cmpquery command + +Eval: module { + types: fn(): string; + init: fn(); + run: fn(ctxt: ref Draw->Context, r: ref Fslib->Report, + opts: list of Fslib->Option, args: list of ref Fslib->Value): ref Fslib->Value; + eval: fn(ctxt: ref Draw->Context, r: ref Fslib->Report, + expr: string, args: list of ref Fslib->Value, ret: int): ref Fslib->Value; +}; + +WORD, SHCMD, VAR: con iota; + +Evalstate: adt { + s: string; + spos: int; + drawctxt: ref Draw->Context; + report: ref Report; + args: array of ref Value; + verbose: int; + + expr: fn(p: self ref Evalstate): ref Value; + getc: fn(p: self ref Evalstate): int; + ungetc: fn(p: self ref Evalstate); + gettok: fn(p: self ref Evalstate): (int, string); +}; + +ops: list of (string, Fsmodule); +lock: chan of int; + +# to do: +# - change value letters to more appropriate (e.g. fs->f, entries->e, gate->g). +# - allow shell $variable expansions + +types(): string +{ + return "as-v"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: eval: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + fslib->init(); +# env = load Env Env->PATH; +# if(env == nil) +# badmod(Env->PATH); +# str = load String String->PATH; +# if(str == nil) +# badmod(String->PATH); + lock = chan[1] of int; +} + +run(ctxt: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + return (ref Evalstate((hd args).s().i, 0, ctxt, report, nil, opts != nil)).expr(); +} + +eval(ctxt: ref Draw->Context, report: ref Report, + expr: string, args: list of ref Value, rtype: int): ref Value +{ + a := array[len args] of ref Value; + for(i := 0; args != nil; args = tl args) + a[i++] = hd args; + e := ref Evalstate(expr, 0, ctxt, report, a, 0); + v := e.expr(); + vl: list of ref Value; + for(i = 0; i < len a; i++) + if(a[i] != nil) + vl = a[i] :: vl; + nv := cvt(e, v, rtype); + if(nv == nil){ + vl = v :: vl; + sys->fprint(stderr(), "fs: eval fn: %s cannot be converted to %s\n", + type2s(v.typec()), type2s(rtype)); + } + if(vl != nil) + spawn discard(nil, vl); + return nv; +} + +tok2s(t: int, s: string): string +{ + case t { + WORD => + return s; + SHCMD => + return "@"; + VAR => + return "$" + s; + } + return sys->sprint("%c", t); +} + +# expr: WORD exprs +# exprs: +# | exprs '{' expr '}' +# | exprs WORD +# | exprs SHCMD +# | exprs VAR +Evalstate.expr(p: self ref Evalstate): ref Value +{ + args: list of ref Value; + t: int; + s: string; + { + (t, s) = p.gettok(); + } exception e { + "parse error" => + return nil; + } + if(t != WORD){ + sys->fprint(stderr(), "fs: eval: syntax error (char %d), expected word, found %#q\n", + p.spos, tok2s(t, s)); + return nil; + } + cmd := s; +loop: + for(;;){ + { + (t, s) = p.gettok(); + } exception e { + "parse error" => + spawn discard(nil, args); + return nil; + } + case t { + '{' => + v := p.expr(); + if(v == nil){ + spawn discard(nil, args); + return nil; + } + args = v :: args; + '}' => + break loop; + WORD => + args = ref Value.S(s) :: args; + VAR => + n := int s; + if(n < 0 || n >= len p.args){ + sys->fprint(stderr(), "fs: eval: invalid arg reference $%s\n", s); + spawn discard(nil, args); + return nil; + } + if(p.args[n] == nil){ + sys->fprint(stderr(), "fs: eval: cannot use $%d twice\n", n); + spawn discard(nil, args); + return nil; + } + args = p.args[n] :: args; + p.args[n] = nil; + SHCMD => + if(sh == nil && (sh = load Sh Sh->PATH) == nil){ + sys->fprint(stderr(), "fs: eval: cannot load %s: %r\n", Sh->PATH); + spawn discard(nil, args); + return nil; + } + (c, err) := sh->parse(s); + if(c == nil){ + sys->fprint(stderr(), "fs: eval: cannot parse shell command @%s: %s\n", s, err); + spawn discard(nil, args); + return nil; + } + args = ref Value.C(c) :: args; + -1 => + break loop; + * => + spawn discard(nil, args); + sys->fprint(stderr(), "fs: eval: syntax error; unexpected token %d before char %d\n", t, p.spos); + return nil; + } + } + return runcmd(p, cmd, rev(args)); +} + +runcmd(p: ref Evalstate, cmd: string, args: list of ref Value): ref Value +{ + m := loadmodule(cmd); + if(m == nil){ + spawn discard(nil, args); + return nil; + } + otype := m->types(); + ok: int; + opts: list of Option; + (ok, opts, args) = cvtargs(p, args, cmd, otype); + if(ok == -1){ + sys->fprint(stderr(), "fs: eval: usage: %s\n", fslib->cmdusage(cmd, otype)); + spawn discard(opts, args); + return nil; + } + r := m->run(p.drawctxt, p.report, opts, args); + if(r == nil) + spawn discard(opts, args); + return r; +} + +cvtargs(e: ref Evalstate, args: list of ref Value, cmd, otype: string): (int, list of Option, list of ref Value) +{ + ok: int; + opts: list of Option; + (nil, at, t) := fslib->splittype(otype); + (ok, opts, args) = cvtopts(e, t, cmd, args); + if(ok == -1) + return (-1, opts, args); + if(len at < 1 || at[0] == '*'){ + sys->fprint(stderr(), "fs: eval: invalid type descriptor %#q for %#q\n", at, cmd); + return (-1, opts, args); + } + n := len args; + if(at[len at - 1] == '*'){ + tc := at[len at - 2]; + at = at[0:len at - 2]; + for(i := len at; i < n; i++) + at[i] = tc; + } + if(n != len at){ + sys->fprint(stderr(), "fs: eval: wrong number of arguments to %#q\n", cmd); + return (-1, opts, args); + } + d: list of ref Value; + (ok, args, d) = cvtvalues(e, at, cmd, args); + if(ok == -1) + args = join(args, d); + return (ok, opts, args); +} + +cvtvalues(e: ref Evalstate, t: string, cmd: string, args: list of ref Value): (int, list of ref Value, list of ref Value) +{ + cargs: list of ref Value; + for(i := 0; i < len t; i++){ + tc := t[i]; + if(args == nil){ + sys->fprint(stderr(), "fs: eval: %q missing argument of type %s\n", cmd, type2s(tc)); + return (-1, cargs, args); + } + v := cvt(e, hd args, tc); + if(v == nil){ + sys->fprint(stderr(), "fs: eval: %q: %s cannot be converted to %s\n", + cmd, type2s((hd args).typec()), type2s(tc)); + return (-1, cargs, args); + } + cargs = v :: cargs; + args = tl args; + } + return (0, rev(cargs), args); +} + +cvtopts(e: ref Evalstate, opttype: string, cmd: string, args: list of ref Value): (int, list of Option, list of ref Value) +{ + if(opttype == nil) + return (0, nil, args); + opts: list of Option; +getopts: + while(args != nil){ + s := ""; + pick v := hd args { + S => + s = v.i; + if(s == nil || s[0] != '-' || len s == 1) + s = nil; + else if(s == "--"){ + args = tl args; + s = nil; + } + } + if(s == nil) + return (0, opts, args); + s = s[1:]; + while(len s > 0){ + opt := s[0]; + if(((ok, t) := fslib->opttypes(opt, opttype)).t0 == -1){ + sys->fprint(stderr(), "fs: eval: %s: unknown option -%c\n", cmd, opt); + return (-1, opts, args); + } + if(t == nil){ + s = s[1:]; + opts = (opt, nil) :: opts; + }else{ + if(len s > 1) + args = ref Value.S(s[1:]) :: tl args; + else + args = tl args; + vl: list of ref Value; + (ok, vl, args) = cvtvalues(e, t, cmd, args); + if(ok == -1) + return (-1, opts, join(vl, args)); + opts = (opt, vl) :: opts; + continue getopts; + } + } + args = tl args; + } + return (0, opts, args); +} + +discard(ol: list of (int, list of ref Value), vl: list of ref Value) +{ + for(; ol != nil; ol = tl ol) + for(ovl := (hd ol).t1; ovl != nil; ovl = tl ovl) + vl = (hd ovl) :: vl; + for(; vl != nil; vl = tl vl) + (hd vl).discard(); +} + +loadmodule(cmd: string): Fsmodule +{ + lock <-= 0; + for(ol := ops; ol != nil; ol = tl ol) + if((hd ol).t0 == cmd) + break; + if(ol != nil){ + <-lock; + return (hd ol).t1; + } + p := cmd + ".dis"; + if(p[0] != '/' && !(p[0] == '.' && p[1] == '/')) + p = "/dis/fs/" + p; + m := load Fsmodule p; + if(m == nil){ + sys->fprint(stderr(), "fs: eval: cannot load %s: %r\n", p); + sys->fprint(stderr(), "fs: eval: unknown verb %#q\n", cmd); + sys->werrstr(sys->sprint("cannot load module %q", cmd)); + <-lock; + return nil; + } + { + m->init(); + } exception e { + "fail:*" => + <-lock; + sys->werrstr(sys->sprint("module init failed: %s", e[5:])); + return nil; + } + ops = (cmd, m) :: ops; + <-lock; + return m; +} + +runexternal(p: ref Evalstate, cmd: string, t: string, opts: list of Option, args: list of ref Value): ref Value +{ + m := loadmodule(cmd); + if(m == nil) + return nil; + if(!fslib->typecompat(t, m->types())){ + sys->fprint(stderr(), "fs: eval: %s has incompatible type\n", cmd); + sys->fprint(stderr(), "fs: eval: expected usage: %s\n", fslib->cmdusage(cmd, t)); + sys->fprint(stderr(), "fs: eval: actually usage: %s\n", fslib->cmdusage(cmd, m->types())); + return nil; + } + return m->run(p.drawctxt, p.report, opts, args); +} + +cvt(e: ref Evalstate, v: ref Value, t: int): ref Value +{ + { + return cvt1(e, v, t); + } exception { + "type conversion" => + return nil; + } +} + +cvt1(e: ref Evalstate, v: ref Value, t: int): ref Value +{ + if(v.typec() == t) + return v; + r: ref Value; + case t { + 't' => + r = runexternal(e, "entries", "tx", nil, cvt1(e, v, 'x') :: nil); + 'x' => + r = runexternal(e, "walk", "xs", nil, cvt1(e, v, 's') :: nil); + 'p' => + r = runexternal(e, "match", "ps", nil, cvt1(e, v, 's') :: nil); + 's' => + r = runexternal(e, "run", "sc", nil, cvt1(e, v, 'c') :: nil); + 'v' => + r = runexternal(e, "print", "vt", nil, cvt1(e, v, 't') :: nil); + } + if(r == nil) + raise "type conversion"; + return r; +} + +Evalstate.getc(p: self ref Evalstate): int +{ + c := -1; + if(p.spos < len p.s) + c = p.s[p.spos]; + p.spos++; + return c; +} + +Evalstate.ungetc(p: self ref Evalstate) +{ + p.spos--; +} + +# XXX backslash escapes newline? +Evalstate.gettok(p: self ref Evalstate): (int, string) +{ + while ((c := p.getc()) == ' ' || c == '\t') + ; + t: int; + s: string; + + case c { + -1 => + t = -1; + '\n' => + t = '\n'; + '{' => + t = '{'; + '}' => + t = '}'; + '@' => # embedded shell command + while((nc := p.getc()) == ' ' || nc == '\t') + ; + if(nc != '{'){ + sys->fprint(stderr(), "fs: eval: expected '{' after '@'\n"); + raise "parse error"; + } + s = "{"; + d := 1; + getcmd: + while((nc = p.getc()) != -1){ + s[len s] = nc; + case nc { + '{' => + d++; + '}' => + if(--d == 0) + break getcmd; + '\'' => + s += getqword(p, 1); + } + } + if(nc == -1){ + sys->fprint(stderr(), "fs: eval: unbalanced '{' in shell command\n"); + raise "parse error"; + } + t = SHCMD; + '$' => + t = VAR; + s = getvar(p); + '\'' => + s = getqword(p, 0); + t = WORD; + * => + do { + s[len s] = c; + c = p.getc(); + if (in(c, " \t{}\n")){ + p.ungetc(); + break; + } + } while (c >= 0); + t = WORD; + } + return (t, s); +} + +getvar(p: ref Evalstate): string +{ + c := p.getc(); + if(c == -1){ + sys->fprint(stderr(), "fs: eval: unexpected eof after '$'\n"); + raise "parse error"; + } + v: string; + while(in(c, " \t\n@{}'") == 0){ + v[len v] = c; + c = p.getc(); + } + p.ungetc(); + for(i := 0; i < len v; i++) + if(v[i] < '0' || v[i] > '9') + break; + if(i < len v || v == nil){ + sys->fprint(stderr(), "fs: eval: invalid $ reference $%q\n", v); + raise "parse error"; + } + return v; +} +# v: string; +# if(c == '\''){ +# v = getqword(p, 0); +# c = p.getc(); +# } else{ +# v[0] = c; +# while((c = p.getc()) != -1){ +# if(in(c, "a-zA-Z0-9*_") == 0) # heuristic stolen from rc +# break; +# v[len v] = c; +# } +# } +# vl := str->unquoted(env->getenv(v)); +# if(vl == nil){ +# sys->fprint(stderr(), "fs: eval: shell variable $%q has %d elements\n", v, len vl); +# raise "parse error"; +# } +# val := hd vl; +# if(c == -1 || in(c, " \t@{}\n")){ +# p.ungetc(); +# return (WORD, val); +# } +# (t, s) = p.gettok(); +# if(t != WORD){ +# sys->fprint(stderr(), "fs: eval: expected word after $%q\n", v); +# raise "parse error"; +# } +# s = val + s; +#} + +in(c: int, s: string): int +{ + for(i := 0; i < len s; i++) + if(s[i] == c) + return 1; + return 0; +} + +# get a quoted word; the starting quote has already been seen +getqword(p: ref Evalstate, keepq: int): string +{ + s := ""; + for(;;) { + while ((nc := p.getc()) != '\'' && nc >= 0) + s[len s] = nc; + if (nc == -1){ + sys->fprint(stderr(), "fs: eval: unterminated quote\n"); + raise "parse error"; + } + if (p.getc() != '\'') { + p.ungetc(); + if(keepq) + s[len s] = '\''; + return s; + } + s[len s] = '\''; # 'xxx''yyy' becomes WORD(xxx'yyy) + if(keepq) + s[len s] = '\''; + } +} + +rev[T](x: list of T): list of T +{ + l: list of T; + for(; x != nil; x = tl x) + l = hd x :: l; + return l; +} + +# join x to y, leaving result in arbitrary order. +join[T](x, y: list of T): list of T +{ + if(len x > len y) + (x, y) = (y, x); + for(; x != nil; x = tl x) + y = hd x :: y; + return y; +} + +stderr(): ref Sys->FD +{ + return sys->fildes(2); +} diff --git a/appl/cmd/fs/exec.b b/appl/cmd/fs/exec.b new file mode 100644 index 00000000..60beb74e --- /dev/null +++ b/appl/cmd/fs/exec.b @@ -0,0 +1,162 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "fslib.m"; + fslib: Fslib; + Option, Value, Entrychan, Report: import fslib; + +# usage: exec [-n nfiles] [-t endcmd] [-pP] command entries +types(): string +{ + return "vct-ns-tc-p-P"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + sh = load Sh Sh->PATH; + if(sh == nil) + badmod(Sh->PATH); + sh->initialise(); +} + +run(drawctxt: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + n := 1; + pflag := 0; + tcmd: ref Sh->Cmd; + for(; opts != nil; opts = tl opts){ + o := hd opts; + case o.opt { + 'n' => + if((n = int (hd o.args).s().i) <= 0){ + sys->fprint(sys->fildes(2), "fs: exec: invalid argument to -n\n"); + return nil; + } + 't' => + tcmd = (hd o.args).c().i; + 'p' => + pflag = 1; + 'P' => + pflag = 2; + } + } + if(pflag && n > 1){ + sys->fprint(sys->fildes(2), "fs: exec: cannot specify -p with -n %d\n", n); + return nil; + } + cmd := (hd args).c().i; + c := (hd tl args).t().i; + sync := chan of int; + spawn execproc(drawctxt, sync, n, pflag, c, cmd, tcmd, report.start("exec")); + sync <-= 1; + return ref Value.V(sync); +} + +execproc(drawctxt: ref Draw->Context, sync: chan of int, n, pflag: int, + c: Entrychan, cmd, tcmd: ref Sh->Cmd, errorc: chan of string) +{ + sys->pctl(Sys->NEWFD, 0::1::2::nil); + ctxt := Context.new(drawctxt); + <-sync; + if(<-sync == 0){ + c.sync <-= 0; + errorc <-= nil; + exit; + } + c.sync <-= 1; + argv := ref Sh->Listnode(cmd, nil) :: nil; + + fl: list of ref Sh->Listnode; + nf := 0; + while(((d, p, nil) := <-c.c).t0 != nil){ + fl = ref Sh->Listnode(nil, p) :: fl; + if(++nf >= n){ + ctxt.set("file", rev(fl)); + if(pflag) + setstatenv(ctxt, d, pflag); + fl = nil; + nf = 0; + {ctxt.run(argv, 0);} exception {"fail:*" =>;} + } + } + if(nf > 0){ + ctxt.set("file", rev(fl)); + {ctxt.run(argv, 0);} exception {"fail:*" =>;} + } + if(tcmd != nil){ + ctxt.set("file", nil); + {ctxt.run(ref Sh->Listnode(tcmd, nil) :: nil, 0);} exception {"fail:*" =>;} + } + errorc <-= nil; +} + +setenv(ctxt: ref Context, var: string, val: list of string) +{ + ctxt.set(var, sh->stringlist2list(val)); +} + +setstatenv(ctxt: ref Context, dir: ref Sys->Dir, pflag: int) +{ + setenv(ctxt, "mode", modes(dir.mode) :: nil); + setenv(ctxt, "uid", dir.uid :: nil); + setenv(ctxt, "mtime", string dir.mtime :: nil); + setenv(ctxt, "length", string dir.length :: nil); + + if(pflag > 1){ + setenv(ctxt, "name", dir.name :: nil); + setenv(ctxt, "gid", dir.gid :: nil); + setenv(ctxt, "muid", dir.muid :: nil); + setenv(ctxt, "qid", sys->sprint("16r%ubx", dir.qid.path) :: string dir.qid.vers :: nil); + setenv(ctxt, "atime", string dir.atime :: nil); + setenv(ctxt, "dtype", sys->sprint("%c", dir.dtype) :: nil); + setenv(ctxt, "dev", string dir.dev :: nil); + } +} + +mtab := array[] of { + "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx" +}; + +modes(mode: int): string +{ + s: string; + + if(mode & Sys->DMDIR) + s = "d"; + else if(mode & Sys->DMAPPEND) + s = "a"; + else if(mode & Sys->DMAUTH) + s = "A"; + else + s = "-"; + if(mode & Sys->DMEXCL) + s += "l"; + else + s += "-"; + s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; + return s; +} + +rev[T](x: list of T): list of T +{ + l: list of T; + for(; x != nil; x = tl x) + l = hd x :: l; + return l; +} diff --git a/appl/cmd/fs/filter.b b/appl/cmd/fs/filter.b new file mode 100644 index 00000000..9275cc7f --- /dev/null +++ b/appl/cmd/fs/filter.b @@ -0,0 +1,64 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fsfilter: Fsfilter; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + + +Query: adt { + gate: Gatechan; + dflag: int; + reply: chan of int; + query: fn(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int; +}; + +types(): string +{ + return "xpx-d"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + fsfilter = load Fsfilter Fsfilter->PATH; + if(fsfilter == nil) + badmod(Fsfilter->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + dst := chan of (Fsdata, chan of int); + spawn filterproc((hd tl args).x().i, dst, (hd args).p().i, opts != nil); + return ref Value.X(dst); +} + +filterproc(src, dst: Fschan, gate: Gatechan, dflag: int) +{ + fsfilter->filter(ref Query(gate, dflag, chan of int), src, dst); + gate <-= ((nil, nil, 0), nil); +} + +Query.query(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int +{ + if(depth == 0 || (q.dflag && (d.mode & Sys->DMDIR))) + return 1; + q.gate <-= ((d, name, depth), q.reply); + return <-q.reply; +} diff --git a/appl/cmd/fs/ls.b b/appl/cmd/fs/ls.b new file mode 100644 index 00000000..70beae48 --- /dev/null +++ b/appl/cmd/fs/ls.b @@ -0,0 +1,97 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "daytime.m"; + daytime: Daytime; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Option, Value, Entrychan, Report: import fslib; + +types(): string +{ + return "vt-u-m"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: ls: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + daytime = load Daytime Daytime->PATH; + if(daytime == nil) + badmod(Daytime->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + sync := chan of int; + spawn lsproc(sync, opts, (hd args).t().i, daytime, report.start("ls")); + return ref Value.V(sync); +} + +lsproc(sync: chan of int, opts: list of Option, c: Entrychan, daytime: Daytime, errorc: chan of string) +{ + now := daytime->now(); + mflag := uflag := 0; + if(<-sync == 0){ + c.sync <-= 0; + errorc <-= nil; + } + c.sync <-= 1; + for(; opts != nil; opts = tl opts){ + case (hd opts).opt { + 'm' => + mflag = 1; + 'u' => + uflag = 1; + } + } + while(((dir, p, nil) := <-c.c).t0 != nil){ + t := dir.mtime; + if(uflag) + t = dir.atime; + s := sys->sprint("%s %c %d %s %s %bud %s %s\n", + modes(dir.mode), dir.dtype, dir.dev, + dir.uid, dir.gid, dir.length, + daytime->filet(now, dir.mtime), p); + if(mflag) + s = "[" + dir.muid + "] " + s; + sys->print("%s", s); + } + errorc <-= nil; +} + +mtab := array[] of { + "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx" +}; + +modes(mode: int): string +{ + s: string; + + if(mode & Sys->DMDIR) + s = "d"; + else if(mode & Sys->DMAPPEND) + s = "a"; + else if(mode & Sys->DMAUTH) + s = "A"; + else + s = "-"; + if(mode & Sys->DMEXCL) + s += "l"; + else + s += "-"; + s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; + return s; +} diff --git a/appl/cmd/fs/match.b b/appl/cmd/fs/match.b new file mode 100644 index 00000000..331867a9 --- /dev/null +++ b/appl/cmd/fs/match.b @@ -0,0 +1,79 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "filepat.m"; + filepat: Filepat; +include "regex.m"; + regex: Regex; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "ps-a-r"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + regex = load Regex Regex->PATH; + if(regex == nil) + badmod(Regex->PATH); + filepat = load Filepat Filepat->PATH; + if(filepat == nil) + badmod(Filepat->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + pat := (hd args).s().i; + aflag := rflag := 0; + for(; opts != nil; opts = tl opts){ + case (hd opts).opt { + 'a' => + aflag = 1; + 'r' => + rflag = 1; + } + } + v := ref Value.P(chan of Gatequery); + re: Regex->Re; + if(rflag){ + err: string; + (re, err) = regex->compile(pat, 0); + if(re == nil){ + sys->fprint(sys->fildes(2), "fs: match: regex error on %#q: %s\n", pat, err); + return nil; + } + } + spawn matchproc(v.i, aflag, pat, re); + return v; +} + +matchproc(c: Gatechan, all: int, pat: string, re: Regex->Re) +{ + while((((d, name, nil), reply) := <-c).t0.t0 != nil){ + if(all == 0) + name = d.name; + if(re != nil) + reply <-= regex->execute(re, name) != nil; # XXX should anchor it? + else + reply <-= filepat->match(pat, name); + } +} diff --git a/appl/cmd/fs/merge.b b/appl/cmd/fs/merge.b new file mode 100644 index 00000000..977102b2 --- /dev/null +++ b/appl/cmd/fs/merge.b @@ -0,0 +1,187 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s: import fslib; + Fschan, Fsdata, Entrychan, Cmpchan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +# e.g.... +# fs select {mode -d} {merge -c {compose -d AoutB} {filter {not {path /chan /dev /usr/rog /n/local /net}} /} {merge {proto FreeBSD} {proto Hp} {proto Irix} {proto Linux} {proto MacOSX} {proto Nt} {proto Nt.ti} {proto Nt.ti925} {proto Plan9} {proto Plan9.ti} {proto Plan9.ti925} {proto Solaris} {proto authsrv} {proto dl} {proto dlsrc} {proto ep7} {proto inferno} {proto inferno.ti} {proto ipaqfs} {proto minitel} {proto os} {proto scheduler.client} {proto scheduler.server} {proto sds} {proto src} {proto src.ti} {proto sword} {proto ti925.ti} {proto ti925bin} {proto tipaq} {proto umec} {proto utils} {proto utils.ti}}} >[2] /dev/null + +types(): string +{ + return "xxxx*-1-cm"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil){ + sys->fprint(sys->fildes(2), "fs: cannot load %s: %r\n", Fslib->PATH); + raise "fail:bad module"; + } +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + recurse := 1; + cmp: Cmpchan; + for(; opts != nil; opts = tl opts){ + case (hd opts).opt { + '1' => + recurse = 0; + 'c' => + cmp = (hd (hd opts).args).m().i; + } + } + dst := chan of (Fsdata, chan of int); + spawn mergeproc((hd args).x().i, (hd tl args).x().i, dst, recurse, cmp, tl tl args == nil); + for(args = tl tl args; args != nil; args = tl args){ + dst1 := chan of (Fsdata, chan of int); + spawn mergeproc(dst, (hd args).x().i, dst1, recurse, cmp, tl args == nil); + dst = dst1; + } + return ref Value.X(dst); +} + +# merge two trees; assume directories are alphabetically sorted. +mergeproc(c0, c1, dst: Fschan, recurse: int, cmp: Cmpchan, killcmp: int) +{ + myreply := chan of int; + ((d0, nil), reply0) := <-c0; + ((d1, nil), reply1) := <-c1; + + if(compare(cmp, d0, d1) == 2r10) + dst <-= ((d1, nil), myreply); + else + dst <-= ((d0, nil), myreply); + r := <-myreply; + reply0 <-= r; + reply1 <-= r; + if(r == Down){ + { + mergedir(c0, c1, dst, recurse, cmp); + } exception {"exit" =>;} + } + if(cmp != nil && killcmp) + cmp <-= (nil, nil, nil); +} + +mergedir(c0, c1, dst: Fschan, recurse: int, cmp: Cmpchan) +{ + myreply := chan of int; + reply0, reply1: chan of int; + d0, d1: ref Sys->Dir; + eof0 := eof1 := 0; + for(;;){ + if(!eof0 && d0 == nil){ + ((d0, nil), reply0) = <-c0; + if(d0 == nil){ + reply0 <-= Next; + eof0 = 1; + } + } + if(!eof1 && d1 == nil){ + ((d1, nil), reply1) = <-c1; + if(d1 == nil){ + reply1 <-= Next; + eof1 = 1; + } + } + if(eof0 && eof1) + break; + + (wd0, wd1) := (d0, d1); + if(d0 != nil && d1 != nil && d0.name != d1.name){ + if(d0.name < d1.name) + wd1 = nil; + else + wd0 = nil; + } + + wc0, wc1: Fschan; + wreply0, wreply1: chan of int; + weof0, weof1: int; + + c := compare(cmp, wd0, wd1); + if(wd0 != nil && wd1 != nil){ + if(c != 0 && recurse && (wd0.mode & wd1.mode & Sys->DMDIR) != 0){ + dst <-= ((wd0, nil), myreply); + r := <-myreply; + reply0 <-= r; + reply1 <-= r; + d0 = d1 = nil; + case r { + Quit => + raise "exit"; + Skip => + return; + Down => + mergedir(c0, c1, dst, 1, cmp); + } + continue; + } + # when we can't merge and there's a clash, choose c0 over c1, unless cmp says otherwise + if(c == 2r10){ + reply0 <-= Next; + d0 = nil; + }else{ + reply1 <-= Next; + d1 = nil; + } + } + if(c & 2r01){ + (wd0, wc0, wreply0, weof0) = (d0, c0, reply0, eof0); + (wd1, wc1, wreply1, weof1) = (d1, c1, reply1, eof1); + d0 = nil; + }else if(c & 2r10){ + (wd0, wc0, wreply0, weof0) = (d1, c1, reply1, eof1); + (wd1, wc1, wreply1, weof1) = (d0, c0, reply0, eof0); + d1 = nil; + }else{ + if(wd0 == nil){ + reply1 <-= Next; + d1 = nil; + }else{ + reply0 <-= Next; + d0 = nil; + } + continue; + } + dst <-= ((wd0, nil), myreply); + r := <-myreply; + wreply0 <-= r; + if(r == Down) + r = fslib->copy(wc0, dst); # XXX hmm, maybe this should be a mergedir() + case r { + Quit or + Skip => + if(wd1 == nil && !weof1) + (nil, wreply1) = <-wc1; + wreply1 <-= r; + if(r == Quit) + raise "exit"; + return; + } + } + dst <-= ((nil, nil), myreply); + if(<-myreply == Quit) + raise "exit"; +} + +compare(cmp: Cmpchan, d0, d1: ref Sys->Dir): int +{ + mask := (d0 != nil) | (d1 != nil) << 1; + if(cmp == nil) + return mask; + reply := chan of int; + cmp <-= (d0, d1, reply); + return <-reply & mask; +} diff --git a/appl/cmd/fs/mergewrite.b b/appl/cmd/fs/mergewrite.b new file mode 100644 index 00000000..3ff1b1f1 --- /dev/null +++ b/appl/cmd/fs/mergewrite.b @@ -0,0 +1,186 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "readdir.m"; + readdir: Readdir; +include "fslib.m"; + fslib: Fslib; + Report, Value, quit, report: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Cmpchan, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "vmsx"; # XXX bad argument ordering... +} + +init() +{ + sys = load Sys Sys->PATH; + readdir = load Readdir Readdir->PATH; + if(readdir == nil){ + sys->fprint(sys->fildes(2), "fs: mergewrite: cannot load %s: %r\n", Readdir->PATH); + raise "fail:bad module"; + } + readdir->init(nil, 0); + + fslib = load Fslib Fslib->PATH; + if(fslib == nil){ + sys->fprint(sys->fildes(2), "fs: mergewrite: cannot load %s: %r\n", Fslib->PATH); + raise "fail:bad module"; + } +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + sync := chan of int; + spawn fswriteproc(sync, (hd args).m().i, (hd tl args).s().i, (hd tl tl args).x().i, report.start("mergewrite")); + <-sync; + return ref Value.V(sync); +} + +fswriteproc(sync: chan of int, cmp: Cmpchan, root: string, c: Fschan, errorc: chan of string) +{ + sys->pctl(Sys->FORKNS, nil); + sync <-= 1; + if(<-sync == 0){ + (<-c).t1 <-= Quit; + quit(errorc); + } + + ((d, nil), reply) := <-c; + if(root != nil){ + d = ref *d; + d.name = root; + } + fswritedir(d.name, cmp, d, reply, c, errorc); + quit(errorc); +} + +fswritedir(path: string, cmp: Cmpchan, dir: ref Sys->Dir, dreply: chan of int, c: Fschan, errorc: chan of string) +{ + fd: ref Sys->FD; + if(dir.mode & Sys->DMDIR){ + fd = sys->create(dir.name, Sys->OREAD, dir.mode|8r300); + made := fd != nil; + if(fd == nil && (fd = sys->open(dir.name, Sys->OREAD)) == nil){ + dreply <-= Next; + report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, dir.mode|8r300)); + return; + } + # XXX if we haven't just made it, we should chmod the old entry u+w to enable writing. + if(sys->chdir(dir.name) == -1){ # XXX beware of names starting with '#' + dreply <-= Next; + report(errorc, sys->sprint("cannot cd to %q: %r", path)); + fd = nil; + sys->remove(dir.name); + return; + } + dreply <-= Down; + entries: array of ref Sys->Dir; + if(made == 0) + entries = readdir->readall(fd, Readdir->NAME|Readdir->COMPACT).t0; + i := 0; + eod := 0; + d0, d1: ref Sys->Dir; + reply: chan of int; + path[len path] = '/'; + for(;;){ + if(!eod && d0 == nil){ + ((d0, nil), reply) = <-c; + if(d0 == nil){ + reply <-= Next; + eod = 1; + } + } + if(d1 == nil && i < len entries) + d1 = entries[i++]; + if(d0 == nil && d1 == nil) + break; + + (wd0, wd1) := (d0, d1); + if(d0 != nil && d1 != nil && d0.name != d1.name){ + if(d0.name < d1.name) + wd1 = nil; + else + wd0 = nil; + } + r := compare(cmp, wd0, wd1); + if(wd1 != nil && (r & 2r10) == 0){ + if(wd1.mode & Sys->DMDIR) + rmdir(wd1.name); + else + remove(wd1.name); + d1 = nil; + } + if(wd0 != nil){ + if((r & 2r01) == 0) + reply <-= Next; + else + fswritedir(path + wd0.name, cmp, d0, reply, c, errorc); + d0 = nil; + } + } + sys->chdir(".."); + if((dir.mode & 8r300) != 8r300){ + ws := Sys->nulldir; + ws.mode = dir.mode; + if(sys->fwstat(fd, ws) == -1) + report(errorc, sys->sprint("cannot wstat %q: %r", path)); + } + }else{ + fd = sys->create(dir.name, Sys->OWRITE, dir.mode); + if(fd == nil){ + dreply <-= Next; + report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, dir.mode|8r300)); + return; + } + dreply <-= Down; + while((((nil, buf), reply) := <-c).t0.data != nil){ + nw := sys->write(fd, buf, len buf); + if(nw < len buf){ + if(nw == -1) + errorc <-= sys->sprint("error writing %q: %r", path); + else + errorc <-= sys->sprint("short write"); + reply <-= Skip; + break; + } + reply <-= Next; + } + reply <-= Next; + } +} + +rmdir(name: string) +{ + (d, n) := readdir->init(name, Readdir->NONE|Readdir->COMPACT); + for(i := 0; i < n; i++){ + path := name+"/"+d[i].name; + if(d[i].mode & Sys->DMDIR) + rmdir(path); + else + remove(path); + } + remove(name); +} + +remove(name: string) +{ + if(sys->remove(name) < 0) + sys->fprint(sys->fildes(2), "mergewrite: cannot remove %q: %r\n", name); +} + +compare(cmp: Cmpchan, d0, d1: ref Sys->Dir): int +{ + mask := (d0 != nil) | (d1 != nil) << 1; + if(cmp == nil) + return mask; + reply := chan of int; + cmp <-= (d0, d1, reply); + return <-reply & mask; +} diff --git a/appl/cmd/fs/mkfile b/appl/cmd/fs/mkfile new file mode 100644 index 00000000..37fb5e12 --- /dev/null +++ b/appl/cmd/fs/mkfile @@ -0,0 +1,60 @@ +<../../../mkconfig +# fs write /n/local/n/fossil/usr/inferno {filter {and {not {or *.dis *.sbl}} {path /appl/cmd/fs /module/fslib.m /appl/lib/fslib.b /appl/cmd/fs.b /man/1/fs}} /} +TARG=\ + and.dis\ + bundle.dis\ + chstat.dis\ + compose.dis\ + depth.dis\ + entries.dis\ + eval.dis\ + exec.dis\ + filter.dis\ + ls.dis\ + match.dis\ + merge.dis\ + mergewrite.dis\ + mode.dis\ + not.dis\ + or.dis\ + path.dis\ + pipe.dis\ + print.dis\ + proto.dis\ + query.dis\ + run.dis\ + select.dis\ + setroot.dis\ + size.dis\ + unbundle.dis\ + walk.dis\ + write.dis\ + void.dis\ + + +INS= ${TARG:%=$ROOT/dis/fs/%} + +SYSMODULES=\ + bufio.m\ + draw.m\ + sh.m\ + sys.m\ + bundle.m\ + fslib.m\ + +DISBIN=$ROOT/dis/fs + +<$ROOT/mkfiles/mkdis + +all:V: $TARG + +install:V: $INS + +nuke:V: clean + rm -f $INS + +clean:V: + rm -f *.dis *.sbl + +uninstall:V: + rm -f $INS diff --git a/appl/cmd/fs/mode.b b/appl/cmd/fs/mode.b new file mode 100644 index 00000000..83d385d7 --- /dev/null +++ b/appl/cmd/fs/mode.b @@ -0,0 +1,120 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +# XXX implement octal modes. + +User: con 8r700; +Group: con 8r070; +Other: con 8r007; +All: con User | Group | Other; + +Read: con 8r444; +Write: con 8r222; +Exec: con 8r111; + +types(): string +{ + return "ps"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + spec := (hd args).s().i; + (ok, mask, mode) := parsemode(spec); + if(ok == 0){ + sys->fprint(sys->fildes(2), "fs: mode: bad mode %#q\n", spec); + return nil; + } + c := chan of Gatequery; + spawn modegate(c, mask, mode); + return ref Value.P(c); +} + +modegate(c: Gatechan, mask, mode: int) +{ + m := mode & mask; + while((((d, nil, nil), reply) := <-c).t0.t0 != nil) + reply <-= ((d.mode & mask) ^ m) == 0; +} + +# stolen from /appl/cmd/chmod.b +parsemode(spec: string): (int, int, int) +{ + mask := Sys->DMAPPEND | Sys->DMEXCL | Sys->DMDIR | Sys->DMAUTH; +loop: + for(i := 0; i < len spec; i++){ + case spec[i] { + 'u' => + mask |= User; + 'g' => + mask |= Group; + 'o' => + mask |= Other; + 'a' => + mask |= All; + * => + break loop; + } + } + if(i == len spec) + return (0, 0, 0); + if(i == 0) + mask |= All; + + op := spec[i++]; + if(op != '+' && op != '-' && op != '=') + return (0, 0, 0); + + mode := 0; + for(; i < len spec; i++){ + case spec[i]{ + 'r' => + mode |= Read; + 'w' => + mode |= Write; + 'x' => + mode |= Exec; + 'a' => + mode |= Sys->DMAPPEND; + 'l' => + mode |= Sys->DMEXCL; + 'd' => + mode |= Sys->DMDIR; + 'A' => + mode |= Sys->DMAUTH; + * => + return (0, 0, 0); + } + } + if(op == '+' || op == '-') + mask &= mode; + if(op == '-') + mode = ~mode; + return (1, mask, mode); +} + + diff --git a/appl/cmd/fs/not.b b/appl/cmd/fs/not.b new file mode 100644 index 00000000..e318f855 --- /dev/null +++ b/appl/cmd/fs/not.b @@ -0,0 +1,48 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "pp"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + c := chan of Gatequery; + spawn notgate(c, (hd args).p().i); + return ref Value.P(c); +} + +notgate(c, sub: Gatechan) +{ + myreply := chan of int; + while(((d, reply) := <-c).t0.t0 != nil){ + sub <-= (d, myreply); + reply <-= !<-myreply; + } + sub <-= (Nilentry, nil); +} diff --git a/appl/cmd/fs/or.b b/appl/cmd/fs/or.b new file mode 100644 index 00000000..ca6668d1 --- /dev/null +++ b/appl/cmd/fs/or.b @@ -0,0 +1,65 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "pppp*"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + c := chan of Gatequery; + spawn orgate(c, args); + return ref Value.P(c); +} + +orgate(c: Gatechan, args: list of ref Value) +{ + sub: list of Gatechan; + for(; args != nil; args = tl args) + sub = (hd args).p().i :: sub; + sub = rev(sub); + myreply := chan of int; + while(((d, reply) := <-c).t0.t0 != nil){ + for(l := sub; l != nil; l = tl l){ + (hd l) <-= (d, myreply); + if(<-myreply) + break; + } + reply <-= l != nil; + } + for(; sub != nil; sub = tl sub) + hd sub <-= (Nilentry, nil); +} + +rev[T](x: list of T): list of T +{ + l: list of T; + for(; x != nil; x = tl x) + l = hd x :: l; + return l; +} diff --git a/appl/cmd/fs/path.b b/appl/cmd/fs/path.b new file mode 100644 index 00000000..2b8c98a0 --- /dev/null +++ b/appl/cmd/fs/path.b @@ -0,0 +1,77 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "pss*-x"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + # XXX cleanname all paths? + c := chan of Gatequery; + p: list of string; + for(; args != nil; args = tl args) + p = (hd args).s().i :: p; + spawn pathgate(c, opts != nil, p); + return ref Value.P(c); +} + +pathgate(c: Gatechan, xflag: int, paths: list of string) +{ + if(xflag){ + while((((d, path, nil), reply) := <-c).t0.t0 != nil){ + for(q := paths; q != nil; q = tl q){ + r := 1; + p := hd q; + if(len path > len p) + r = path[len p] != '/' || path[0:len p] != p; + else if(len path == len p) + r = path != p; + if(r == 0) + break; + } + reply <-= q == nil; + } + }else{ + while((((d, path, nil), reply) := <-c).t0.t0 != nil){ + for(q := paths; q != nil; q = tl q){ + r := 0; + p := hd q; + if(len path > len p) + r = path[len p] == '/' && path[0:len p] == p; + else if(len path == len p) + r = path == p; + else + r = p[len path] == '/' && p[0:len path] == path; + if(r) + break; + } + reply <-= q != nil; + } + } +} diff --git a/appl/cmd/fs/pipe.b b/appl/cmd/fs/pipe.b new file mode 100644 index 00000000..665abdeb --- /dev/null +++ b/appl/cmd/fs/pipe.b @@ -0,0 +1,223 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "fslib.m"; + fslib: Fslib; + Option, Value, Fschan, Report, quit: import fslib; + Skip, Next, Down, Quit: import fslib; + + +# pipe the contents of the files in a filesystem through +# a command. -1 causes one command only to be executed. +# -p and -P (exclusive to -1) cause stat modes to be set in the shell environment. +types(): string +{ + return "vcx-1-p-P"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + sh = load Sh Sh->PATH; + if(sh == nil) + badmod(Sh->PATH); + sh->initialise(); +} + +run(drawctxt: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + n := 1; + oneflag := pflag := 0; + for(; opts != nil; opts = tl opts){ + o := hd opts; + case o.opt { + '1' => + oneflag = 1; + 'p' => + pflag = 1; + 'P' => + pflag = 2; + } + } + if(pflag && oneflag){ + sys->fprint(sys->fildes(2), "fs: exec: cannot specify -p with -1\n"); + return nil; + } + cmd := (hd args).c().i; + c := (hd tl args).x().i; + sync := chan of int; + spawn execproc(drawctxt, sync, oneflag, pflag, c, cmd, report.start("exec")); + sync <-= 1; + return ref Value.V(sync); +} + +execproc(drawctxt: ref Draw->Context, sync: chan of int, oneflag, pflag: int, + c: Fschan, cmd: ref Sh->Cmd, errorc: chan of string) +{ + sys->pctl(Sys->NEWFD, 0::1::2::nil); + ctxt := Context.new(drawctxt); + <-sync; + if(<-sync == 0){ + (<-c).t1 <-= Quit; + quit(errorc); + } + argv := ref Sh->Listnode(cmd, nil) :: nil; + fd: ref Sys->FD; + result := chan of string; + if(oneflag){ + fd = popen(ctxt, argv, result); + if(fd == nil){ + (<-c).t1 <-= Quit; + quit(errorc); + } + } + + names: list of string; + name: string; + indent := 0; + for(;;){ + (d, reply) := <-c; + if(d.dir == nil){ + reply <-= Next; + if(--indent == 0){ + break; + } + (name, names) = (hd names, tl names); + continue; + } + if((d.dir.mode & Sys->DMDIR) != 0){ + reply <-= Down; + names = name :: names; + if(indent > 0 && name != nil && name[len name - 1] != '/') + name[len name] = '/'; + name += d.dir.name; + indent++; + continue; + } + if(!oneflag){ + p := name; + if(p != nil && p[len p - 1] != '/') + p[len p] = '/'; + setenv(ctxt, "file", p + d.dir.name :: nil); + if(pflag) + setstatenv(ctxt, d.dir, pflag); + fd = popen(ctxt, argv, result); + } + if(fd == nil){ + reply <-= Next; + continue; + } + reply <-= Down; + for(;;){ + data: array of byte; + ((nil, data), reply) = <-c; + reply <-= Next; + if(data == nil) + break; + n := -1; + {n = sys->write(fd, data, len data);}exception {"write on closed pipe" => ;} + if(n != len data){ + if(oneflag){ + (<-c).t1 <-= Quit; + quit(errorc); + } + (<-c).t1 <-= Skip; + break; + } + } + if(!oneflag){ + fd = nil; + <-result; + } + } + fd = nil; + if(oneflag) + <-result; + quit(errorc); +} + +popen(ctxt: ref Context, argv: list of ref Sh->Listnode, result: chan of string): ref Sys->FD +{ + sync := chan of int; + fds := array[2] of ref Sys->FD; + sys->pipe(fds); + spawn runcmd(ctxt, argv, fds[0], sync, result); + <-sync; + return fds[1]; +} + +runcmd(ctxt: ref Context, argv: list of ref Sh->Listnode, stdin: ref Sys->FD, sync: chan of int, result: chan of string) +{ + sys->pctl(Sys->FORKFD, nil); + sys->dup(stdin.fd, 0); + stdin = nil; + sys->pctl(Sys->NEWFD, 0::1::2::nil); + ctxt = ctxt.copy(0); + sync <-= 0; + r := ctxt.run(argv, 0); + ctxt = nil; + sys->pctl(Sys->NEWFD, nil); + result <-=r; +} + +setenv(ctxt: ref Context, var: string, val: list of string) +{ + ctxt.set(var, sh->stringlist2list(val)); +} + +setstatenv(ctxt: ref Context, dir: ref Sys->Dir, pflag: int) +{ + setenv(ctxt, "mode", modes(dir.mode) :: nil); + setenv(ctxt, "uid", dir.uid :: nil); + setenv(ctxt, "mtime", string dir.mtime :: nil); + setenv(ctxt, "length", string dir.length :: nil); + + if(pflag > 1){ + setenv(ctxt, "name", dir.name :: nil); + setenv(ctxt, "gid", dir.gid :: nil); + setenv(ctxt, "muid", dir.muid :: nil); + setenv(ctxt, "qid", sys->sprint("16r%ubx", dir.qid.path) :: string dir.qid.vers :: nil); + setenv(ctxt, "atime", string dir.atime :: nil); + setenv(ctxt, "dtype", sys->sprint("%c", dir.dtype) :: nil); + setenv(ctxt, "dev", string dir.dev :: nil); + } +} + +mtab := array[] of { + "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx" +}; + +modes(mode: int): string +{ + s: string; + + if(mode & Sys->DMDIR) + s = "d"; + else if(mode & Sys->DMAPPEND) + s = "a"; + else if(mode & Sys->DMAUTH) + s = "A"; + else + s = "-"; + if(mode & Sys->DMEXCL) + s += "l"; + else + s += "-"; + s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; + return s; +} diff --git a/appl/cmd/fs/print.b b/appl/cmd/fs/print.b new file mode 100644 index 00000000..21761e0e --- /dev/null +++ b/appl/cmd/fs/print.b @@ -0,0 +1,51 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "vt"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + sync := chan of int; + spawn printproc(sync, (hd args).t().i, report.start("print")); + return ref Value.V(sync); +} + +printproc(sync: chan of int, c: Entrychan, errorc: chan of string) +{ + if(<-sync == 0){ + c.sync <-= 0; + quit(errorc); + exit; + } + c.sync <-= 1; + while(((d, p, nil) := <-c.c).t0 != nil) + sys->print("%s\n", p); + quit(errorc); +} diff --git a/appl/cmd/fs/proto.b b/appl/cmd/fs/proto.b new file mode 100644 index 00000000..bc836f44 --- /dev/null +++ b/appl/cmd/fs/proto.b @@ -0,0 +1,388 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "readdir.m"; + readdir: Readdir; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "string.m"; + str: String; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, report, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +File: adt { + name: string; + mode: int; + owner: string; + group: string; + old: string; + flags: int; + sub: cyclic array of ref File; +}; + +Proto: adt { + indent: int; + lastline: string; + iob: ref Iobuf; +}; + +Star, Plus: con 1<<iota; + +types(): string +{ + return "xs-rs"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: proto: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + badmod(Readdir->PATH); + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + badmod(Bufio->PATH); + str = load String String->PATH; + if(str == nil) + badmod(String->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + protofile := (hd args).s().i; + rootpath: string; + if(opts != nil) + rootpath = (hd (hd opts).args).s().i; + if(rootpath == nil) + rootpath = "/"; + + proto := ref Proto(0, nil, nil); + if((proto.iob = bufio->open(protofile, Sys->OREAD)) == nil){ + sys->fprint(sys->fildes(2), "fs: proto: cannot open %q: %r\n", protofile); + return nil; + } + root := ref File(rootpath, ~0, nil, nil, nil, 0, nil); + (root.flags, root.sub) = readproto(proto, -1); + c := chan of (Fsdata, chan of int); + spawn protowalk(c, root, report.start("proto")); + return ref Value.X(c); +} + +protowalk(c: Fschan, root: ref File, errorc: chan of string) +{ + protowalk1(c, root.flags, root.name, file2dir(root, nil), root.sub, errorc); + quit(errorc); +} + +protowalk1(c: Fschan, flags: int, path: string, d: ref Sys->Dir, + sub: array of ref File, errorc: chan of string): int +{ + reply := chan of int; + c <-= ((d, nil), reply); + case r := <-reply { + Quit => + quit(errorc); + Next or + 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; + prevsub: string; + for(i := 0; i < n; i++){ + 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) + 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)){ + f: ref File; + if(foundsub){ + f = sub[j++]; + prevsub = f.name; + } + p: string; + d: ref Sys->Dir; + if(foundsub && f.old != nil){ + p = f.old; + (ok, xd) := sys->stat(p); + if(ok == -1){ + report(errorc, sys->sprint("cannot stat %q: %r", p)); + continue; + } + d = ref xd; + }else{ + p = pathconcat(path, a[i].name); + d = a[i]; + } + + d = file2dir(f, d); + r: int; + if((d.mode & Sys->DMDIR) == 0) + r = walkfile(c, p, d, errorc); + else if(flags & Plus) + r = protowalk1(c, Plus, p, d, nil, errorc); + else + r = protowalk1(c, f.flags, p, d, f.sub, errorc); + if(r == Skip) + return Next; + } + } + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; +} + +pathconcat(p, name: string): string +{ + if(p != nil && p[len p - 1] != '/') + p[len p] = '/'; + p += name; + return p; +} + +# from(ish) walk.b +walkfile(c: Fschan, path: string, d: ref Sys->Dir, errorc: chan of string): int +{ + reply := chan of int; + fd := sys->open(path, Sys->OREAD); + if(fd == nil){ + report(errorc, sys->sprint("cannot open %q: %r", path)); + return Next; + } + c <-= ((d, nil), reply); + case r := <-reply { + Quit => + quit(errorc); + Next or + Skip => + return r; + } + length := d.length; + for(n := big 0; n < length; ){ + nr := Sys->ATOMICIO; + if(n + big Sys->ATOMICIO > length) + nr = int (length - n); + buf := array[nr] of byte; + nr = sys->read(fd, buf, nr); + if(nr <= 0){ + if(nr < 0) + report(errorc, sys->sprint("error reading %q: %r", path)); + else + report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd)", + path, n, length)); + break; + }else if(nr < len buf) + buf = buf[0:nr]; + c <-= ((nil, buf), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + return Next; + } + n += big nr; + } + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; +} + +readproto(proto: ref Proto, indent: int): (int, array of ref File) +{ + a := array[10] of ref File; + n := 0; + flags := 0; + while((f := readline(proto, indent)) != nil){ + if(f.name == "*") + flags |= Star; + else if(f.name == "+") + flags |= Plus; + else{ + (f.flags, f.sub) = readproto(proto, proto.indent); + if(n == len a) + a = (array[n * 2] of ref File)[0:] = a; + a[n++] = f; + } + } + if(n < len a) + a = (array[n] of ref File)[0:] = a[0:n]; + mergesort(a, array[n] of ref File); + return (flags, a); +} + +readline(proto: ref Proto, indent: int): ref File +{ + s: string; + if(proto.lastline != nil){ + s = proto.lastline; + proto.lastline = nil; + }else if(proto.indent == -1) + return nil; + else if((s = proto.iob.gets('\n')) == nil){ + proto.indent = -1; + return nil; + } + spc := 0; + for(i := 0; i < len s; i++){ + c := s[i]; + if(c == ' ') + spc++; + else if(c == '\t') + spc += 8; + else + break; + } + if(i == len s || s[i] == '#' || s[i] == '\n') + return readline(proto, indent); # XXX sort out tail recursion! + if(spc <= indent){ + proto.lastline = s; + return nil; + } + proto.indent = spc; + (n, 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) + return f; + (f.mode, toks) = (getmode(hd toks), tl toks); + if(toks == nil) + return f; + (f.owner, toks) = (getname(hd toks, 1), tl toks); + if(toks == nil) + return f; + (f.group, toks) = (getname(hd toks, 1), tl toks); + if(toks == nil) + return f; + (f.old, toks) = (hd toks, tl toks); + return f; +} + +mergesort(a, b: array of ref File) +{ + r := len a; + if (r > 1) { + m := (r-1)/2 + 1; + mergesort(a[0:m], b[0:m]); + mergesort(a[m:], b[m:]); + b[0:] = a; + for ((i, j, k) := (0, m, 0); i < m && j < r; k++) { + if(b[i].name > b[j].name) + a[k] = b[j++]; + else + a[k] = b[i++]; + } + if (i < m) + a[k:] = b[i:m]; + else if (j < r) + a[k:] = b[j:r]; + } +} + +getname(s: string, allowminus: int): string +{ + if(s == nil) + return nil; + if(allowminus && s == "-") + return nil; + if(s[0] == '$') + return getenv(s[1:]); + return s; +} + +getenv(s: string): string +{ + # XXX implement env variables + return nil; +} + +getmode(s: string): int +{ + s = getname(s, 1); + if(s == nil) + return ~0; + m := 0; + i := 0; + if(s[i] == 'd'){ + m |= Sys->DMDIR; + i++; + } + if(i < len s && s[i] == 'a'){ + m |= Sys->DMAPPEND; + i++; + } + if(i < len s && s[i] == 'l'){ + m |= Sys->DMEXCL; + i++; + } + (xmode, t) := str->toint(s, 8); + if(t != nil){ + # report(aux.errorc, "bad mode specification %q", s); + return ~0; + } + return xmode | m; +} + +file2dir(f: ref File, old: ref Sys->Dir): ref Sys->Dir +{ + d := ref Sys->nulldir; + if(old != nil){ + if(old.dtype != 'M'){ + d.uid = "sys"; + d.gid = "sys"; + xmode := (old.mode >> 6) & 7; + d.mode = old.mode | xmode | (xmode << 3); + }else{ + d.uid = old.uid; + d.gid = old.gid; + d.mode = old.mode; + } + d.length = old.length; + d.mtime = old.mtime; + d.atime = old.atime; + d.muid = old.muid; + d.name = old.name; + } + if(f != nil){ + d.name = f.name; + if(f.owner != nil) + d.uid = f.owner; + if(f.group != nil) + d.gid = f.group; + if(f.mode != ~0) + d.mode = f.mode; + } + return d; +} diff --git a/appl/cmd/fs/query.b b/appl/cmd/fs/query.b new file mode 100644 index 00000000..421be1d9 --- /dev/null +++ b/appl/cmd/fs/query.b @@ -0,0 +1,130 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "fslib.m"; + fslib: Fslib; + Option, Value, Gatechan, Gatequery, Report, Nilentry: import fslib; + +types(): string +{ + return "pc-p-P"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: query: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + sh = load Sh Sh->PATH; + if(sh == nil) + badmod(Sh->PATH); +} + +run(drawctxt: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + pflag := 0; + for(; opts != nil; opts = tl opts){ + o := hd opts; + case o.opt { + 'p' => + pflag = 1; + 'P' => + pflag = 2; + } + } + + v := ref Value.P(chan of Gatequery); + spawn querygate(drawctxt, v.i, (hd args).c().i, pflag); + v.i <-= (Nilentry, nil); + return v; +} + +querygate(drawctxt: ref Draw->Context, c: Gatechan, cmd: ref Sh->Cmd, pflag: int) +{ + sys->pctl(Sys->NEWFD, 0::1::2::nil); + ctxt := Context.new(drawctxt); + <-c; + argv := ref Sh->Listnode(cmd, nil) :: nil; + while((((d, p, nil), reply) := <-c).t0.t0 != nil){ + ctxt.set("file", ref Sh->Listnode(nil, p) :: nil); + if(pflag) + setstatenv(ctxt, d, pflag); + err := ""; + { + err = ctxt.run(argv, 0); + } exception e { + "fail:*" => + err = e; + } + reply <-= (err == nil); + } +} + +# XXX shouldn't duplicate this... + +setenv(ctxt: ref Context, var: string, val: list of string) +{ + ctxt.set(var, sh->stringlist2list(val)); +} + +setstatenv(ctxt: ref Context, dir: ref Sys->Dir, pflag: int) +{ + setenv(ctxt, "mode", modes(dir.mode) :: nil); + setenv(ctxt, "uid", dir.uid :: nil); + setenv(ctxt, "mtime", string dir.mtime :: nil); + setenv(ctxt, "length", string dir.length :: nil); + + if(pflag > 1){ + setenv(ctxt, "name", dir.name :: nil); + setenv(ctxt, "gid", dir.gid :: nil); + setenv(ctxt, "muid", dir.muid :: nil); + setenv(ctxt, "qid", sys->sprint("16r%ubx", dir.qid.path) :: string dir.qid.vers :: nil); + setenv(ctxt, "atime", string dir.atime :: nil); + setenv(ctxt, "dtype", sys->sprint("%c", dir.dtype) :: nil); + setenv(ctxt, "dev", string dir.dev :: nil); + } +} + +start(startc: chan of (string, chan of string), name: string): chan of string +{ + c := chan of string; + startc <-= (name, c); + return c; +} + +mtab := array[] of { + "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx" +}; + +modes(mode: int): string +{ + s: string; + + if(mode & Sys->DMDIR) + s = "d"; + else if(mode & Sys->DMAPPEND) + s = "a"; + else if(mode & Sys->DMAUTH) + s = "A"; + else + s = "-"; + if(mode & Sys->DMEXCL) + s += "l"; + else + s += "-"; + s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; + return s; +} diff --git a/appl/cmd/fs/readfile.b b/appl/cmd/fs/readfile.b new file mode 100644 index 00000000..4a52ae08 --- /dev/null +++ b/appl/cmd/fs/readfile.b @@ -0,0 +1,144 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, report, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +this is a bad idea, i think +i think walk + filter + setroot is good enough. + +types(): string +{ + # usage: readfile [-f file] name + return "xs-fs"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: readfile: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + path: string; + f := (hd args).s().i; + fd: ref Sys->FD; + seekable: int; + if(f == "-"){ + if(opts == nil){ + sys->fprint(sys->fildes(2), "fs: readfile: must specify a path when reading stdin\n"); + return nil; + } + fd = sys->fildes(0); + seekable = 0; + }else{ + fd = sys->open(f, Sys->OREAD); + seekable = isseekable(fd); + } + if(fd == nil){ + sys->fprint(sys->fildes(2), "fs: readfile: cannot open %s: %r\n", f); + return nil; + } + if(opts != nil) + path = (hd (hd opts).args).s().i; + else + path = f; + + (root, file) := pathsplit(path); + if(file == nil || file == "." || file == ".."){ + sys->fprint(sys->fildes(2), "fs: readfile: invalid filename %q\n", fname); + return nil; + } + d.name = file; + v := ref Value.X(chan of (Fsdata, chan of int)); + spawn readproc(v.i, fd, root, ref d, seekable, report.start("read")); + return v; +} + +readproc(c: Fschan, fd: ref Sys->FD, root: string, d: ref Sys->Dir, seekable: int, errorc: chan of string) +{ + reply := chan of int; + rd := ref Sys->nulldir; + rd.name = root; + c <-= ((rd, nil), reply); + if(<-reply != Down) + quit(errorc); + + c <-= ((d, nil), reply); + case <-reply { + Down => + sendfile(c, fd, errorc); + Skip or + Quit => + quit(errorc); + } + c <-= ((nil, nil), reply); + <-reply; + quit(errorc); +} + +sendfile(c: Fschan, data: list of array of byte, length: big, errorc: chan of string) +{ + reply := chan of int; + for(;;){ + buf: array of byte; + if(fd != nil){ + buf := array[Sys->ATOMICIO] of byte; + if((n := sys->read(fd, buf, len buf)) <= 0){ + if(n < 0) + report(errorc, sys->sprint("read error: %r")); + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return; + } + c <-= ((nil, buf), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + return; + } + } +} + +pathsplit(p: string): (string, string) +{ + for (i := len p - 1; i >= 0; i--) + if (p[i] != '/') + break; + if (i < 0) + return (p, nil); + p = p[0:i+1]; + for (i = len p - 1; i >=0; i--) + if (p[i] == '/') + break; + if (i < 0) + return (".", p); + return (p[0:i+1], p[i+1:]); +} + +# dodgy heuristic... avoid, or using the stat-length of pipes and net connections +isseekable(fd: ref Sys->FD): int +{ + (ok, stat) := sys->stat(iob.fd); + if(ok != -1 && stat.dtype == '|' || stat.dtype == 'I') + return 0; + return 1; +} diff --git a/appl/cmd/fs/run.b b/appl/cmd/fs/run.b new file mode 100644 index 00000000..a5734d7c --- /dev/null +++ b/appl/cmd/fs/run.b @@ -0,0 +1,60 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; + sh: Sh; + Context: import sh; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "sc"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + sh = load Sh Sh->PATH; + if(sh == nil) + badmod(Sh->PATH); + sh->initialise(); +} + +run(drawctxt: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + c := (hd args).c().i; + ctxt := Context.new(drawctxt); + ctxt.setlocal("s", nil); + { + ctxt.run(ref Sh->Listnode(c, nil)::nil, 0); + } exception e { + "fail:*" => + sys->fprint(sys->fildes(2), "fs: run: exception %q raised in %s\n", e[5:], sh->cmd2string(c)); + return nil; + } + sl := ctxt.get("s"); + if(sl == nil || tl sl != nil){ + sys->fprint(sys->fildes(2), "fs: run: $s has %d members; exactly one is required\n", len sl); + return nil; + } + s := (hd sl).word; + if(s == nil && (hd sl).cmd != nil) + s = sh->cmd2string((hd sl).cmd); + return ref Value.S(s); +} diff --git a/appl/cmd/fs/select.b b/appl/cmd/fs/select.b new file mode 100644 index 00000000..9fe5e4b0 --- /dev/null +++ b/appl/cmd/fs/select.b @@ -0,0 +1,56 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "tpt"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + dst := Entrychan(chan of int, chan of Entry); + spawn selectproc((hd tl args).t().i, dst, (hd args).p().i); + return ref Value.T(dst); +} + +selectproc(src, dst: Entrychan, query: Gatechan) +{ + if(<-dst.sync == 0){ + query <-= (Nilentry, nil); + src.sync <-= 0; + exit; + } + src.sync <-= 1; + reply := chan of int; + while((d := <-src.c).t0 != nil){ + query <-= (d, reply); + if(<-reply) + dst.c <-= d; + } + dst.c <-= Nilentry; + query <-= (Nilentry, nil); +} diff --git a/appl/cmd/fs/setroot.b b/appl/cmd/fs/setroot.b new file mode 100644 index 00000000..d39a4cf4 --- /dev/null +++ b/appl/cmd/fs/setroot.b @@ -0,0 +1,104 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +# set the root +types(): string +{ + return "xsx-c"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ + root := (hd args).s().i; + if(root == nil && opts == nil){ + sys->fprint(sys->fildes(2), "fs: setroot: empty path\n"); + return nil; + } + v := ref Value.X(chan of (Fsdata, chan of int)); + spawn setroot((hd tl args).x().i, v.i, root, opts != nil); + return v; +} + +setroot(src, dst: Fschan, root: string, cflag: int) +{ + ((d, nil), reply) := <-src; + if(cflag){ + createroot(src, dst, root, d, reply); + }else{ + myreply := chan of int; + rd := ref *d; + rd.name = root; + dst <-= ((rd, nil), myreply); + if(<-myreply == Down){ + reply <-= Down; + fslib->copy(src, dst); + } + } +} + +createroot(src, dst: Fschan, root: string, d: ref Sys->Dir, reply: chan of int) +{ + if(root == nil) + root = d.name; + (n, elems) := sys->tokenize(root, "/"); # XXX should really do a cleanname first + if(root[0] == '/'){ + elems = "/" :: elems; + n++; + } + myreply := chan of int; + lev := 0; + r := -1; + for(; elems != nil; elems = tl elems){ + rd := ref *d; + rd.name = hd elems; + dst <-= ((rd, nil), myreply); + case r = <-myreply { + Quit => + (<-src).t1 <-= Quit; + exit; + Skip => + break; + Next => + lev++; + break; + } + lev++; + } + if(r == Down){ + reply <-= Down; + if(fslib->copy(src, dst) == Quit) + exit; + }else + reply <-= Quit; + while(lev-- > 1){ + dst <-= ((nil, nil), myreply); + if(<-myreply == Quit){ + (<-src).t1 <-= Quit; + exit; + } + } +} diff --git a/appl/cmd/fs/size.b b/appl/cmd/fs/size.b new file mode 100644 index 00000000..d72edd99 --- /dev/null +++ b/appl/cmd/fs/size.b @@ -0,0 +1,54 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "vt"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + sync := chan of int; + spawn sizeproc(sync, (hd args).t().i, report.start("size")); + return ref Value.V(sync); +} + +sizeproc(sync: chan of int, c: Entrychan, errorc: chan of string) +{ + if(<-sync == 0){ + c.sync <-= 0; + quit(errorc); + exit; + } + c.sync <-= 1; + + size := big 0; + while(((d, nil, nil) := <-c.c).t0 != nil) + size += d.length; + sys->print("%bd\n", size); + quit(errorc); +} diff --git a/appl/cmd/fs/template.b b/appl/cmd/fs/template.b new file mode 100644 index 00000000..9b08f589 --- /dev/null +++ b/appl/cmd/fs/template.b @@ -0,0 +1,35 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "nil"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + opts: list of Option, args: list of ref Value): ref Value +{ +} diff --git a/appl/cmd/fs/unbundle.b b/appl/cmd/fs/unbundle.b new file mode 100644 index 00000000..ad500e8e --- /dev/null +++ b/appl/cmd/fs/unbundle.b @@ -0,0 +1,259 @@ +implement Unbundle; +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "string.m"; + str: String; +include "bundle.m"; + bundle: Bundle; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value,quit, report: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Quit, Next, Skip, Down, + Option: import Fslib; +include "unbundle.m"; + +types(): string +{ + return "xs"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + badmod(Bufio->PATH); + str = load String String->PATH; + if(str == nil) + badmod(String->PATH); + bundle = load Bundle Bundle->PATH; + if(bundle == nil) + badmod(Bundle->PATH); + bundle->init(); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + p := (hd args).s().i; + iob: ref Bufio->Iobuf; + if(p == "-") + iob = bufio->fopen(sys->fildes(0), Sys->OREAD); + else + iob = bufio->open(p, Sys->OREAD); + if(iob == nil){ + sys->fprint(sys->fildes(2), "fs: unbundle: cannot open %q: %r\n", p); + return nil; + } + seekable := p != "-"; + if(seekable) + seekable = isseekable(iob.fd); + return ref Value.X(unbundle(report, iob, seekable, Sys->ATOMICIO)); +} + +# dodgy heuristic... avoid, or using the stat-length of pipes and net connections +isseekable(fd: ref Sys->FD): int +{ + (ok, stat) := sys->fstat(fd); + if(ok != -1 && stat.dtype == '|' || stat.dtype == 'I') + return 0; + return 1; +} + +unbundle(r: ref Report, iob: ref Iobuf, seekable, blocksize: int): Fschan +{ + c := chan of (Fsdata, chan of int); + spawn unbundleproc(iob, c, seekable, blocksize, r.start("bundle")); + return c; +} + +EOF: con "end of archive\n"; + +unbundleproc(iob: ref Iobuf, c: Fschan, seekable, blocksize: int, errorc: chan of string) +{ + reply := chan of int; + p := iob.gets('\n'); + # XXX overall header? + if(p == nil || p == EOF){ + fslib->sendnulldir(c); + quit(errorc); + } + d := header2dir(p); + if(d == nil){ + fslib->sendnulldir(c); + report(errorc, "invalid first header"); + quit(errorc); + } + if((d.mode & Sys->DMDIR) == 0){ + fslib->sendnulldir(c); + report(errorc, "first entry is not a directory"); + quit(errorc); + } + c <-= ((d, nil), reply); + case r := <-reply { + Down => + unbundledir(iob, c, 0, seekable, blocksize, errorc); + c <-= ((nil, nil), reply); + <-reply; + Skip or + Next => + unbundledir(iob, c, 1, seekable, blocksize, errorc); + Quit => + break; + } + quit(errorc); +} + +unbundledir(iob: ref Iobuf, c: Fschan, + skipping, seekable, blocksize: int, errorc: chan of string): int +{ + reply := chan of int; + while((p := iob.gets('\n')) != nil){ + if(p == EOF) + break; + if(p[0] == '\n') + break; + d := header2dir(p); + if(d == nil){ + report(errorc, sys->sprint("invalid bundle header %q", p[0:len p - 1])); + return -1; + } + if(d.mode & Sys->DMDIR){ + if(skipping) + continue; + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Down => + r := unbundledir(iob, c, 0, seekable, blocksize, errorc); + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + if(r == -1) + return -1; + Skip => + if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1) + return -1; + skipping = 1; + Next => + if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1) + return -1; + } + }else{ + if(skipping){ + if(skipdata(iob, d.length, seekable) == -1) + return -1; + }else{ + case unbundlefile(iob, d, c, errorc, seekable, blocksize) { + -1 => + return -1; + Skip => + skipping = 1; + } + } + } + } + if(p == nil) + report(errorc, "unexpected eof"); + return 0; +} + +skipdata(iob: ref Iobuf, length: big, seekable: int): int +{ + if(seekable){ + iob.seek(big length, Sys->SEEKRELA); + return 0; + } + buf := array[Sys->ATOMICIO] of byte; + for(n := big 0; n < length; ){ + nb := Sys->ATOMICIO; + if(length - n < big Sys->ATOMICIO) + nb = int (length - n); + nb = iob.read(buf, nb); + if(nb <= 0) + return -1; + n += big nb; + } + return 0; +} + +unbundlefile(iob: ref Iobuf, d: ref Sys->Dir, + c: Fschan, errorc: chan of string, seekable, blocksize: int): int +{ + reply := chan of int; + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + if(skipdata(iob, d.length, seekable) == -1) + return -1; + return Skip; + Next => + if(skipdata(iob, d.length, seekable) == -1) + return -1; + return Next; + } + length := d.length; + for(n := big 0; n < length; ){ + nr := blocksize; + if(n + big blocksize > length) + nr = int (length - n); + buf := array[nr] of byte; + nr = iob.read(buf, nr); + if(nr <= 0){ + if(nr < 0) + report(errorc, sys->sprint("read error: %r")); + else + report(errorc, sys->sprint("premature eof")); + return -1; + }else if(nr < len buf) + buf = buf[0:nr]; + c <-= ((nil, buf), reply); + n += big nr; + case <-reply { + Quit => + quit(errorc); + Skip => + if(skipdata(iob, length - n, seekable) == -1) + return -1; + return Next; + } + } + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; +} + +header2dir(s: string): ref Sys->Dir +{ + toks := str->unquoted(s); + nf := len toks; + if(nf != 6) + return nil; + d := ref Sys->nulldir; + (d.name, toks) = (hd toks, tl toks); + (d.mode, toks) = (str->toint(hd toks, 8).t0, tl toks); + (d.uid, toks) = (hd toks, tl toks); + (d.gid, toks) = (hd toks, tl toks); + (d.mtime, toks) = (int hd toks, tl toks); + (d.length, toks) = (big hd toks, tl toks); + return d; +} diff --git a/appl/cmd/fs/void.b b/appl/cmd/fs/void.b new file mode 100644 index 00000000..0f8c72fa --- /dev/null +++ b/appl/cmd/fs/void.b @@ -0,0 +1,33 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, Option: import fslib; + +types(): string +{ + return "vv"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: void: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); +} + +run(nil: ref Draw->Context, nil: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + return (hd args).v(); +} diff --git a/appl/cmd/fs/walk.b b/appl/cmd/fs/walk.b new file mode 100644 index 00000000..1c5016bd --- /dev/null +++ b/appl/cmd/fs/walk.b @@ -0,0 +1,233 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "readdir.m"; + readdir: Readdir; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, type2s, report, quit: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fslib; + +Loopcheck: adt { + a: array of list of ref Sys->Dir; + + new: fn(): ref Loopcheck; + enter: fn(l: self ref Loopcheck, d: ref Sys->Dir): int; + leave: fn(l: self ref Loopcheck, d: ref Sys->Dir); +}; + +types(): string +{ + return "xs-bs"; +} + +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: walk: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil) + badmod(Fslib->PATH); + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + badmod(Readdir->PATH); +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + path := (hd args).s().i; + (ok, d) := sys->stat(path); + if(ok== -1){ + sys->fprint(sys->fildes(2), "fs: walk: cannot stat %q: %r\n", path); + return nil; + } + if((d.mode & Sys->DMDIR) == 0){ + # XXX could produce an fs containing just the single file. + # would have to split the path though. + sys->fprint(sys->fildes(2), "fs: walk: %q is not a directory\n", path); + return nil; + } + sync := chan of int; + c := chan of (Fsdata, chan of int); + spawn fswalkproc(sync, path, c, Sys->ATOMICIO, report.start("walk")); + <-sync; + return ref Value.X(c); +} + +# XXX need to avoid loops in the filesystem... +fswalkproc(sync: chan of int, path: string, c: Fschan, blocksize: int, errorc: chan of string) +{ + sys->pctl(Sys->FORKNS, nil); + sync <-= 1; + # XXX could allow a single root file? + if(sys->chdir(path) == -1){ + report(errorc, sys->sprint("cannot cd to %q: %r", path)); + fslib->sendnulldir(c); + quit(errorc); + } + (ok, d) := sys->stat("."); + if(ok == -1){ + report(errorc, sys->sprint("cannot stat %q: %r", path)); + fslib->sendnulldir(c); + quit(errorc); + } + d.name = path; + reply := chan of int; + c <-= ((ref d, nil), reply); + if(<-reply == Down){ + loopcheck := Loopcheck.new(); + loopcheck.enter(ref d); + if(path[len path - 1] != '/') + path[len path] = '/'; + fswalkdir(path, c, blocksize, loopcheck, errorc); + c <-= ((nil, nil), reply); + <-reply; + } + quit(errorc); +} + +fswalkdir(path: string, c: Fschan, blocksize: int, loopcheck: ref Loopcheck, errorc: chan of string) +{ + reply := chan of int; + (a, n) := readdir->init(".", Readdir->NAME|Readdir->COMPACT); + if(n == -1){ + report(errorc, sys->sprint("cannot readdir %q: %r", path)); + return; + } + for(i := 0; i < n; i++) + if(a[i].mode & Sys->DMDIR) + if(loopcheck.enter(a[i]) == 0) + a[i].dtype = ~0; +directory: + for(i = 0; i < n; i++){ + if(a[i].mode & Sys->DMDIR){ + d := a[i]; + if(d.dtype == ~0){ + report(errorc, sys->sprint("filesystem loop at %#q", path + d.name)); + continue; + } + if(sys->chdir("./" + d.name) == -1){ + report(errorc, sys->sprint("cannot cd to %#q: %r", path + a[i].name)); + continue; + } + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Down => + fswalkdir(path + a[i].name + "/", c, blocksize, loopcheck, errorc); + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + Skip => + sys->chdir(".."); + i++; + break directory; + Next => + break; + } + if(sys->chdir("..") == -1) # XXX what should we do if this fails? + report(errorc, sys->sprint("failed to cd .. from %#q: %r\n", path + a[i].name)); + + } else { + if(fswalkfile(path, a[i], c, blocksize, errorc) == Skip) + break directory; + } + } + for(i = n - 1; i >= 0; i--) + if(a[i].mode & Sys->DMDIR && a[i].dtype != ~0) + loopcheck.leave(a[i]); +} + +fswalkfile(path: string, d: ref Sys->Dir, c: Fschan, blocksize: int, errorc: chan of string): int +{ + reply := chan of int; + fd := sys->open(d.name, Sys->OREAD); + if(fd == nil){ + report(errorc, sys->sprint("cannot open %q: %r", path+d.name)); + return Next; + } + c <-= ((d, nil), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + return Skip; + Next => + return Next; + Down => + break; + } + length := d.length; + for(n := big 0; n < length; ){ + nr := blocksize; + if(n + big blocksize > length) + nr = int (length - n); + buf := array[nr] of byte; + nr = sys->read(fd, buf, nr); + if(nr <= 0){ + if(nr < 0) + report(errorc, sys->sprint("error reading %q: %r", path + d.name)); + else + report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd)", + path + d.name, n, length)); + break; + }else if(nr < len buf) + buf = buf[0:nr]; + c <-= ((nil, buf), reply); + case <-reply { + Quit => + quit(errorc); + Skip => + return Next; + } + n += big nr; + } + c <-= ((nil, nil), reply); + if(<-reply == Quit) + quit(errorc); + return Next; +} + +HASHSIZE: con 32; + +issamedir(d0, d1: ref Sys->Dir): int +{ + (q0, q1) := (d0.qid, d1.qid); + return q0.path == q1.path && + q0.qtype == q1.qtype && + d0.dtype == d1.dtype && + d0.dev == d1.dev; +} + +Loopcheck.new(): ref Loopcheck +{ + return ref Loopcheck(array[HASHSIZE] of list of ref Sys->Dir); +} + +# XXX we're assuming no-one modifies the values in d behind our back... +Loopcheck.enter(l: self ref Loopcheck, d: ref Sys->Dir): int +{ + slot := int d.qid.path & (HASHSIZE-1); + for(ll := l.a[slot]; ll != nil; ll = tl ll) + if(issamedir(d, hd ll)) + return 0; + l.a[slot] = d :: l.a[slot]; + return 1; +} + +Loopcheck.leave(l: self ref Loopcheck, d: ref Sys->Dir) +{ + slot := int d.qid.path & (HASHSIZE-1); + l.a[slot] = tl l.a[slot]; +} diff --git a/appl/cmd/fs/write.b b/appl/cmd/fs/write.b new file mode 100644 index 00000000..934d4d67 --- /dev/null +++ b/appl/cmd/fs/write.b @@ -0,0 +1,111 @@ +implement Fsmodule; +include "sys.m"; + sys: Sys; +include "draw.m"; +include "sh.m"; +include "fslib.m"; + fslib: Fslib; + Report, Value, quit, report: import fslib; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Option, + Next, Down, Skip, Quit: import Fslib; + +types(): string +{ + return "vsx"; +} + +init() +{ + sys = load Sys Sys->PATH; + fslib = load Fslib Fslib->PATH; + if(fslib == nil){ + sys->fprint(sys->fildes(2), "fs: write: cannot load %s: %r\n", Fslib->PATH); + raise "fail:bad module"; + } +} + +run(nil: ref Draw->Context, report: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + sync := chan of int; + spawn fswriteproc(sync, (hd args).s().i, (hd tl args).x().i, report.start("fswrite")); + <-sync; + return ref Value.V(sync); +} + +fswriteproc(sync: chan of int, root: string, c: Fschan, errorc: chan of string) +{ + sys->pctl(Sys->FORKNS, nil); + sync <-= 1; + if(<-sync == 0){ + (<-c).t1 <-= Quit; + quit(errorc); + } + + (d, reply) := <-c; + if(root != nil){ + d.dir = ref *d.dir; + d.dir.name = root; + } + fswritedir(d.dir.name, d, reply, c, errorc); + quit(errorc); +} + +fswritedir(path: string, d: Fsdata, dreply: chan of int, c: Fschan, errorc: chan of string) +{ + fd: ref Sys->FD; + if(d.dir.mode & Sys->DMDIR){ + fd = sys->create(d.dir.name, Sys->OREAD, d.dir.mode|8r300); + if(fd == nil && (fd = sys->open(d.dir.name, Sys->OREAD)) == nil){ + dreply <-= Next; + report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, d.dir.mode|8r300)); + return; + } + if(sys->chdir(d.dir.name) == -1){ # XXX beware of names starting with '#' + dreply <-= Next; + report(errorc, sys->sprint("cannot cd to %q: %r", path)); + fd = nil; + sys->remove(d.dir.name); + return; + } + dreply <-= Down; + path[len path] = '/'; + for(;;){ + (ent, reply) := <-c; + if(ent.dir == nil){ + reply <-= Next; + break; + } + fswritedir(path + ent.dir.name, ent, reply, c, errorc); + } + sys->chdir(".."); + if((d.dir.mode & 8r300) != 8r300){ + ws := Sys->nulldir; + ws.mode = d.dir.mode; + if(sys->fwstat(fd, ws) == -1) + report(errorc, sys->sprint("cannot wstat %q: %r", path)); + } + }else{ + fd = sys->create(d.dir.name, Sys->OWRITE, d.dir.mode); + if(fd == nil){ + dreply <-= Next; + report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, d.dir.mode|8r300)); + return; + } + dreply <-= Down; + while((((nil, buf), reply) := <-c).t0.data != nil){ + nw := sys->write(fd, buf, len buf); + if(nw < len buf){ + if(nw == -1) + errorc <-= sys->sprint("error writing %q: %r", path); + else + errorc <-= sys->sprint("short write"); + reply <-= Skip; + break; + } + reply <-= Next; + } + reply <-= Next; + } +} |
