package cmd import ( "bytes" "clive/u" "clive/zx" "errors" "fmt" fpath "path" "strings" "unicode/utf8") func Stat(path string) (zx.Dir, error) { upath := path path = AbsPath(path) rc := NS().Stat(path) d := <-rc if d != nil { d["Upath"] = upath d["Rpath"] = "/" } return d, cerror(rc)} func Get(path string, off, count int64) <-chan []byte { path = AbsPath(path) return NS().Get(path, off, count)} func GetAll(path string) ([]byte, error) { path = AbsPath(path) return zx.GetAll(NS(), path)} // Unlike zx.GetDir(), this updates the paths in dirs to reflect user paths,// like Dirs() and Files() do.func GetDir(path string) ([]zx.Dir, error) { apath := AbsPath(path) ds, err := zx.GetDir(NS(), apath) if err != nil { return nil, err } for _, d := range ds { d["Rpath"] = d["path"] d["Upath"] = fpath.Join(d["path"], d["name"]) d["path"] = fpath.Join(apath, d["name"]) } return ds, nil} func Put(path string, ud zx.Dir, off int64, dc <-chan []byte) <-chan zx.Dir { upath := path apath := AbsPath(path) rc := make(chan zx.Dir) go func() { pc := NS().Put(apath, ud, off, dc) d := <-pc if d != nil { d["Rpath"] = "/" d["Upath"] = upath rc <- d } close(rc, cerror(pc)) }() return rc} func PutAll(path string, data []byte, mode ...string) error { path = AbsPath(path) return zx.PutAll(NS(), path, data, mode...)} func Wstat(path string, ud zx.Dir) (zx.Dir, error) { upath := path apath := AbsPath(path) rc := NS().Wstat(apath, ud) d := <-rc if d != nil { d["Rpath"] = "/" d["Upath"] = upath } return d, cerror(rc)} func Remove(path string) error { path = AbsPath(path) return <-NS().Remove(path)} func RemoveAll(path string) error { path = AbsPath(path) return <-NS().RemoveAll(path)} func Move(from, to string) error { from = AbsPath(from) to = AbsPath(to) return <-NS().Move(from, to)} // Clean a name according to conventions so that it has both a path and// a predicate and return both things.// In the predicate, both '&' and ',' can be used as the and operator.// (which is & in clive/zx/pred)// The name is returned as given by the user, it's not an absolute path.// A missing name is taken as ".".func CleanName(name string) (string, string) { toks := strings.SplitN(name, ",", 2) if toks[0] == "" { toks[0] = "." } if len(toks) == 1 { toks = append(toks, "0") } else { toks[1] = strings.Replace(toks[1], ",", "&", -1) } toks[0] = fpath.Clean(toks[0]) return toks[0], toks[1]} // Issue a find for these names ("filename,predicate")// In the predicate, both '&' and ',' can be used as the and operator.// (which is & in clive/zx/pred)// If no predicate is given, then "depth<1" is used.// eg. /a -> just /a; /a, -> subtree at /a// Errors are reported by sending an error.// The chan error status is not nil if there's an error.// The Upath attribute in the dir entries returned mimics the paths given// in the names.// The Rpath attribute in the dir entries provide a path relative to the one// specified by the user.// If one argument is "|...", it names an IO chan and a dir entry is sent for it, type "c",// With the path (and Upath/Rpath) set to the namefunc Dirs(names ...string) chan face{} { ns := NS() rc := make(chan face{}) go func() { var err error for _, name := range names { name = strings.TrimSpace(name) if len(name) > 0 && name[0] == '|' { d := zx.Dir{"path": name, "name": name, "Upath": name, "Rpath": name, "type": "c"} rc <- d continue } tok0, tok1 := CleanName(name) name = AbsPath(tok0) Dprintf("getdirs: find %s %s\n", name, tok1) dc := ns.Find(name, tok1, "/", "/", 0) for d := range dc { if d == nil { break } d["Upath"] = d["path"] if tok0 != name && zx.HasPrefix(d["path"], name) { u := fpath.Join(tok0, zx.Suffix(d["path"], name)) d["Upath"] = u } d["Rpath"] = zx.Suffix(d["path"], name) if d["err"] != "" { if d["err"] != "pruned" { err = errors.New(d["err"]) rc <- err } continue } if ok := rc <- d; !ok { close(dc, cerror(rc)) return } } if derr := cerror(dc); derr != nil { err = derr if ok := rc <- err; !ok { return } } else { close(dc) // in case a null was sent but no error } } close(rc, err) }() return rc} // Like Dirs(), but return a single dir.func Dir(name string) (zx.Dir, error) { tok0, tok1 := CleanName(name) if tok1 == "0" { return Stat(tok0) } rc := Dirs(name) for x := range rc { switch x := x.(type) { case zx.Dir: close(rc) if err := x["err"]; err != "" { return nil, errors.New(err) } return x, nil default: Dprintf("Dir: ignored %T\n", x) } } if err := cerror(rc); err != nil { return nil, err } return nil, fmt.Errorf("%s: %s", name, zx.ErrNotExist)} // Like Dirs(), but sends also file contents// If one argument is "|...", it names an IO chan and a dir entry is sent for it, type "c",// With the path (and Upath/Rpath) set to the name, then its contents.func Files(names ...string) chan face{} { ns := NS() rc := make(chan face{}) go func() { var err error for _, name := range names { name = strings.TrimSpace(name) if len(name) > 2 && name[0] == '|' { d := zx.Dir{"path": name, "name": name, "Upath": name, "type": "c"} rc <- d cc := In(name[2:]) if cc != nil { for m := range cc { if ok := rc <- m; !ok { close(cc, cerror(rc)) return } } err = cerror(cc) } else { err = fmt.Errorf("no I/O chan %s", name[1:]) } if err != nil { rc <- err } continue } tok0, tok1 := CleanName(name) name = AbsPath(tok0) Dprintf("getfiles: findget %s %s\n", name, tok1) dc := ns.FindGet(name, tok1, "/", "/", 0) for m := range dc { d, ok := m.(zx.Dir) if !ok { if ok := rc <- m; !ok { close(dc, cerror(rc)) return } continue } d["Upath"] = d["path"] if tok0 != name && zx.HasPrefix(d["path"], name) { u := fpath.Join(tok0, zx.Suffix(d["path"], name)) d["Upath"] = u } d["Rpath"] = zx.Suffix(d["path"], name) if d["err"] != "" { if d["err"] != "pruned" { err = errors.New(d["err"]) rc <- err } continue } if ok := rc <- d; !ok { close(dc, cerror(rc)) return } } if derr := cerror(dc); derr != nil { err = derr if ok := rc <- err; !ok { return } } else { close(dc) // in case a null was sent but no error } } close(rc, err) }() return rc} // Process a stream of input []byte data and send one line at a timefunc ByteLines(c <-chan []byte) <-chan []byte { sep := '\n' rc := make(chan []byte) go func() { var buf bytes.Buffer saved := []byte{} for d := range c { if len(saved) > 0 { nb := []byte{} nb = append(nb, saved...) nb = append(nb, d...) d = nb saved = nil } for len(d) > 0 && utf8.FullRune(d) { r, n := utf8.DecodeRune(d) d = d[n:] buf.WriteRune(r) if r == sep { nb := make([]byte, buf.Len()) copy(nb, buf.Bytes()) if ok := rc <- nb; !ok { close(c, cerror(rc)) return } buf.Reset() } } saved = d } if len(saved) > 0 { buf.Write(saved) } if buf.Len() > 0 { rc <- buf.Bytes() } close(rc, cerror(c)) }() return rc} // Process a stream of input file data and send one line at a time.func Lines(c <-chan face{}) <-chan face{} { sep := '\n' rc := make(chan face{}) go func() { var buf bytes.Buffer saved := []byte{} for m := range c { d, ok := m.([]byte) if !ok { if len(saved) > 0 { rc <- saved saved = nil } if ok := rc <- m; !ok { close(c, cerror(rc)) return } continue } if len(saved) > 0 { nb := []byte{} nb = append(nb, saved...) nb = append(nb, d...) d = nb saved = nil } for len(d) > 0 && utf8.FullRune(d) { r, n := utf8.DecodeRune(d) d = d[n:] buf.WriteRune(r) if r == sep { nb := make([]byte, buf.Len()) copy(nb, buf.Bytes()) if ok := rc <- nb; !ok { close(c, cerror(rc)) return } buf.Reset() } } saved = d } if len(saved) > 0 { buf.Write(saved) } if buf.Len() > 0 { rc <- buf.Bytes() } close(rc, cerror(c)) }() return rc} // pipe an input chan and make sure the output// issues one message per file in the input containing all data.// non []byte messages forwarded as-is.func FullFiles(c <-chan face{}) <-chan face{} { rc := make(chan face{}) go func() { var b *bytes.Buffer for m := range c { switch d := m.(type) { case []byte: if b == nil { b = &bytes.Buffer{} } b.Write(d) default: if b != nil { if ok := rc <- b.Bytes(); !ok { close(c, cerror(rc)) break } b = nil } if ok := rc <- m; !ok { close(c, cerror(rc)) break } } } if b != nil { if ok := rc <- b.Bytes(); !ok { close(c, cerror(rc)) } } close(rc, cerror(c)) }() return rc} // Return the content of a `dotfile' for the given name.// if $name is defined, the data is its value.// Otherwise, if $home/lib/name or $home/.name exists,// The empty string is returned if there's no configuration for name.func DotFile(name string) string { s := GetEnv(name) if s != "" { return s } dat, err := GetAll(fpath.Join(u.Home, "lib", name)) if err == nil { s := string(dat) return s } dat, err = GetAll(fpath.Join(u.Home, "."+name)) if err == nil { s := string(dat) return s } return ""}
package cmd
import (
"bytes"
"clive/u"
"clive/zx"
"errors"
"fmt"
fpath "path"
"strings"
"unicode/utf8"
)
func Stat(path string) (zx.Dir, error) {
upath := path
path = AbsPath(path)
rc := NS().Stat(path)
d := <-rc
if d != nil {
d["Upath"] = upath
d["Rpath"] = "/"
}
return d, cerror(rc)
}
func Get(path string, off, count int64) <-chan []byte {
path = AbsPath(path)
return NS().Get(path, off, count)
}
func GetAll(path string) ([]byte, error) {
path = AbsPath(path)
return zx.GetAll(NS(), path)
}
// Unlike zx.GetDir(), this updates the paths in dirs to reflect user paths,
// like Dirs() and Files() do.
func GetDir(path string) ([]zx.Dir, error) {
apath := AbsPath(path)
ds, err := zx.GetDir(NS(), apath)
if err != nil {
return nil, err
}
for _, d := range ds {
d["Rpath"] = d["path"]
d["Upath"] = fpath.Join(d["path"], d["name"])
d["path"] = fpath.Join(apath, d["name"])
}
return ds, nil
}
func Put(path string, ud zx.Dir, off int64, dc <-chan []byte) <-chan zx.Dir {
upath := path
apath := AbsPath(path)
rc := make(chan zx.Dir)
go func() {
pc := NS().Put(apath, ud, off, dc)
d := <-pc
if d != nil {
d["Rpath"] = "/"
d["Upath"] = upath
rc <- d
}
close(rc, cerror(pc))
}()
return rc
}
func PutAll(path string, data []byte, mode ...string) error {
path = AbsPath(path)
return zx.PutAll(NS(), path, data, mode...)
}
func Wstat(path string, ud zx.Dir) (zx.Dir, error) {
upath := path
apath := AbsPath(path)
rc := NS().Wstat(apath, ud)
d := <-rc
if d != nil {
d["Rpath"] = "/"
d["Upath"] = upath
}
return d, cerror(rc)
}
func Remove(path string) error {
path = AbsPath(path)
return <-NS().Remove(path)
}
func RemoveAll(path string) error {
path = AbsPath(path)
return <-NS().RemoveAll(path)
}
func Move(from, to string) error {
from = AbsPath(from)
to = AbsPath(to)
return <-NS().Move(from, to)
}
// Clean a name according to conventions so that it has both a path and
// a predicate and return both things.
// In the predicate, both '&' and ',' can be used as the and operator.
// (which is & in clive/zx/pred)
// The name is returned as given by the user, it's not an absolute path.
// A missing name is taken as ".".
func CleanName(name string) (string, string) {
toks := strings.SplitN(name, ",", 2)
if toks[0] == "" {
toks[0] = "."
}
if len(toks) == 1 {
toks = append(toks, "0")
} else {
toks[1] = strings.Replace(toks[1], ",", "&", -1)
}
toks[0] = fpath.Clean(toks[0])
return toks[0], toks[1]
}
// Issue a find for these names ("filename,predicate")
// In the predicate, both '&' and ',' can be used as the and operator.
// (which is & in clive/zx/pred)
// If no predicate is given, then "depth<1" is used.
// eg. /a -> just /a; /a, -> subtree at /a
// Errors are reported by sending an error.
// The chan error status is not nil if there's an error.
// The Upath attribute in the dir entries returned mimics the paths given
// in the names.
// The Rpath attribute in the dir entries provide a path relative to the one
// specified by the user.
// If one argument is "|...", it names an IO chan and a dir entry is sent for it, type "c",
// With the path (and Upath/Rpath) set to the name
func Dirs(names ...string) chan face{} {
ns := NS()
rc := make(chan face{})
go func() {
var err error
for _, name := range names {
name = strings.TrimSpace(name)
if len(name) > 0 && name[0] == '|' {
d := zx.Dir{"path": name, "name": name,
"Upath": name, "Rpath": name, "type": "c"}
rc <- d
continue
}
tok0, tok1 := CleanName(name)
name = AbsPath(tok0)
Dprintf("getdirs: find %s %s\n", name, tok1)
dc := ns.Find(name, tok1, "/", "/", 0)
for d := range dc {
if d == nil {
break
}
d["Upath"] = d["path"]
if tok0 != name && zx.HasPrefix(d["path"], name) {
u := fpath.Join(tok0, zx.Suffix(d["path"], name))
d["Upath"] = u
}
d["Rpath"] = zx.Suffix(d["path"], name)
if d["err"] != "" {
if d["err"] != "pruned" {
err = errors.New(d["err"])
rc <- err
}
continue
}
if ok := rc <- d; !ok {
close(dc, cerror(rc))
return
}
}
if derr := cerror(dc); derr != nil {
err = derr
if ok := rc <- err; !ok {
return
}
} else {
close(dc) // in case a null was sent but no error
}
}
close(rc, err)
}()
return rc
}
// Like Dirs(), but return a single dir.
func Dir(name string) (zx.Dir, error) {
tok0, tok1 := CleanName(name)
if tok1 == "0" {
return Stat(tok0)
}
rc := Dirs(name)
for x := range rc {
switch x := x.(type) {
case zx.Dir:
close(rc)
if err := x["err"]; err != "" {
return nil, errors.New(err)
}
return x, nil
default:
Dprintf("Dir: ignored %T\n", x)
}
}
if err := cerror(rc); err != nil {
return nil, err
}
return nil, fmt.Errorf("%s: %s", name, zx.ErrNotExist)
}
// Like Dirs(), but sends also file contents
// If one argument is "|...", it names an IO chan and a dir entry is sent for it, type "c",
// With the path (and Upath/Rpath) set to the name, then its contents.
func Files(names ...string) chan face{} {
ns := NS()
rc := make(chan face{})
go func() {
var err error
for _, name := range names {
name = strings.TrimSpace(name)
if len(name) > 2 && name[0] == '|' {
d := zx.Dir{"path": name, "name": name,
"Upath": name, "type": "c"}
rc <- d
cc := In(name[2:])
if cc != nil {
for m := range cc {
if ok := rc <- m; !ok {
close(cc, cerror(rc))
return
}
}
err = cerror(cc)
} else {
err = fmt.Errorf("no I/O chan %s", name[1:])
}
if err != nil {
rc <- err
}
continue
}
tok0, tok1 := CleanName(name)
name = AbsPath(tok0)
Dprintf("getfiles: findget %s %s\n", name, tok1)
dc := ns.FindGet(name, tok1, "/", "/", 0)
for m := range dc {
d, ok := m.(zx.Dir)
if !ok {
if ok := rc <- m; !ok {
close(dc, cerror(rc))
return
}
continue
}
d["Upath"] = d["path"]
if tok0 != name && zx.HasPrefix(d["path"], name) {
u := fpath.Join(tok0, zx.Suffix(d["path"], name))
d["Upath"] = u
}
d["Rpath"] = zx.Suffix(d["path"], name)
if d["err"] != "" {
if d["err"] != "pruned" {
err = errors.New(d["err"])
rc <- err
}
continue
}
if ok := rc <- d; !ok {
close(dc, cerror(rc))
return
}
}
if derr := cerror(dc); derr != nil {
err = derr
if ok := rc <- err; !ok {
return
}
} else {
close(dc) // in case a null was sent but no error
}
}
close(rc, err)
}()
return rc
}
// Process a stream of input []byte data and send one line at a time
func ByteLines(c <-chan []byte) <-chan []byte {
sep := '\n'
rc := make(chan []byte)
go func() {
var buf bytes.Buffer
saved := []byte{}
for d := range c {
if len(saved) > 0 {
nb := []byte{}
nb = append(nb, saved...)
nb = append(nb, d...)
d = nb
saved = nil
}
for len(d) > 0 && utf8.FullRune(d) {
r, n := utf8.DecodeRune(d)
d = d[n:]
buf.WriteRune(r)
if r == sep {
nb := make([]byte, buf.Len())
copy(nb, buf.Bytes())
if ok := rc <- nb; !ok {
close(c, cerror(rc))
return
}
buf.Reset()
}
}
saved = d
}
if len(saved) > 0 {
buf.Write(saved)
}
if buf.Len() > 0 {
rc <- buf.Bytes()
}
close(rc, cerror(c))
}()
return rc
}
// Process a stream of input file data and send one line at a time.
func Lines(c <-chan face{}) <-chan face{} {
sep := '\n'
rc := make(chan face{})
go func() {
var buf bytes.Buffer
saved := []byte{}
for m := range c {
d, ok := m.([]byte)
if !ok {
if len(saved) > 0 {
rc <- saved
saved = nil
}
if ok := rc <- m; !ok {
close(c, cerror(rc))
return
}
continue
}
if len(saved) > 0 {
nb := []byte{}
nb = append(nb, saved...)
nb = append(nb, d...)
d = nb
saved = nil
}
for len(d) > 0 && utf8.FullRune(d) {
r, n := utf8.DecodeRune(d)
d = d[n:]
buf.WriteRune(r)
if r == sep {
nb := make([]byte, buf.Len())
copy(nb, buf.Bytes())
if ok := rc <- nb; !ok {
close(c, cerror(rc))
return
}
buf.Reset()
}
}
saved = d
}
if len(saved) > 0 {
buf.Write(saved)
}
if buf.Len() > 0 {
rc <- buf.Bytes()
}
close(rc, cerror(c))
}()
return rc
}
// pipe an input chan and make sure the output
// issues one message per file in the input containing all data.
// non []byte messages forwarded as-is.
func FullFiles(c <-chan face{}) <-chan face{} {
rc := make(chan face{})
go func() {
var b *bytes.Buffer
for m := range c {
switch d := m.(type) {
case []byte:
if b == nil {
b = &bytes.Buffer{}
}
b.Write(d)
default:
if b != nil {
if ok := rc <- b.Bytes(); !ok {
close(c, cerror(rc))
break
}
b = nil
}
if ok := rc <- m; !ok {
close(c, cerror(rc))
break
}
}
}
if b != nil {
if ok := rc <- b.Bytes(); !ok {
close(c, cerror(rc))
}
}
close(rc, cerror(c))
}()
return rc
}
// Return the content of a `dotfile' for the given name.
// if $name is defined, the data is its value.
// Otherwise, if $home/lib/name or $home/.name exists,
// The empty string is returned if there's no configuration for name.
func DotFile(name string) string {
s := GetEnv(name)
if s != "" {
return s
}
dat, err := GetAll(fpath.Join(u.Home, "lib", name))
if err == nil {
s := string(dat)
return s
}
dat, err = GetAll(fpath.Join(u.Home, "."+name))
if err == nil {
s := string(dat)
return s
}
return ""
}