9sh

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 65f3ba42356d9800e3836fac79d544560f0cb784
parent 12589fc26cadadc12e0553515826675575dd6142
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Wed, 15 Nov 2023 08:32:36 +0900

add parser

Diffstat:
Mmain.go | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 147 insertions(+), 7 deletions(-)

diff --git a/main.go b/main.go @@ -1,15 +1,60 @@ package main import ( + "context" + "errors" "fmt" + "io" "io/fs" - "net" "log" + "net" + "os" + "strings" "git.mtkn.jp/lib9p" "git.mtkn.jp/lib9p/client" ) +type stat struct { + cwd string // current working directory. + f fs.File + fsys fs.FS +} + +type token struct { + cmd string + err error +} + +type cmd struct { + args []string + err error +} + +func (c *cmd) run(s *stat) error { + if len(c.args) < 1 { + return errors.New("no command specified") + } + switch c.args[0] { + case "cd": + if len(c.args) != 2 { + return fmt.Errorf("usage: cd <dir>") + } + f, err := s.fsys.Open(c.args[1]) + if err != nil { + return err + } + s.f = f + s.cwd = c.args[1] + return nil + case "pwd": + fmt.Println(s.cwd) + return nil + default: + return fmt.Errorf("unknown command %v", c.args[0]) + } +} + func main() { conn, err := net.Dial("tcp", "127.0.0.1:5640") if err != nil { @@ -20,12 +65,107 @@ func main() { if err != nil { log.Fatalf("mount: %v", err) } - walk := func(path string, d fs.DirEntry, err error) error { - fmt.Println(path) - if err != nil { - return err + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tc := runTokenizer(ctx, os.Stdin) + cc := runParser(ctx, tc) + s := &stat{ + fsys: lib9p.ExportFS{fsys}, + } + for { + c := <-cc + if c.err == io.EOF { + break + } else if c.err != nil { + log.Println(c.err) + continue + } + if err := c.run(s); err != nil { + log.Println(err) } - return nil } - fs.WalkDir(lib9p.ExportFS{fsys}, ".", walk) +} + +func read(r io.Reader) (string, error) { + b := make([]byte, 128) + n, err := r.Read(b) + if err != nil { + return "", err + } + return string(b[:n]), nil +} + +func runTokenizer(ctx context.Context, r io.Reader) <-chan *token { + tc := make(chan *token) + go func() { + defer close(tc) +L: + for { + select { + case <-ctx.Done(): + break L + default: + } + str, err := read(r) + if err != nil { + select { + case tc <-&token{cmd: "", err: err}: + continue L + case <-ctx.Done(): + break L + } + } + ts := strings.Split(str, " ") + for _, s := range ts { + t := &token{cmd: s, err: nil} + select { + case tc <- t: + case <-ctx.Done(): + break L + } + } + } + }() + return tc +} + +func runParser(ctx context.Context, tc <-chan *token) <-chan *cmd { + cc := make(chan *cmd) + go func() { + defer close(cc) +L: + for { + c := new(cmd) +M: + for { + select { + case <-ctx.Done(): + break L + case t := <-tc: + if t.err != nil { + c.err = t.err + break M + } + end := false + if len(t.cmd) == 0 { + continue M + } + if t.cmd[len(t.cmd)-1] == '\n' { + t.cmd = t.cmd[:len(t.cmd)-1] + end = true + } + c.args = append(c.args, t.cmd) + if end { + break M + } + } + } + select { + case <-ctx.Done(): + break L + case cc<- c: + } + } + }() + return cc } \ No newline at end of file