main.go (3968B)
1 package main 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 "io/fs" 9 "log" 10 "net" 11 "os" 12 "path" 13 "strings" 14 15 "git.mtkn.jp/lib9p" 16 "git.mtkn.jp/lib9p/client" 17 ) 18 19 20 type stat struct { 21 cwd string // current working directory. absolute path. 22 f lib9p.File 23 fsys *unionFS 24 } 25 26 type token struct { 27 cmd string 28 err error 29 } 30 31 type cmd struct { 32 args []string 33 err error 34 } 35 36 func (c *cmd) run(s *stat) error { 37 defer func() { 38 p := recover() 39 if p != nil { 40 log.Printf("s.cwd: %q", s.cwd) 41 panic(p) 42 } 43 }() 44 if len(c.args) < 1 { 45 return errors.New("no command specified") 46 } 47 switch c.args[0] { 48 case "cd": 49 var p string 50 if len(c.args) == 1 { 51 p = "." 52 } else if len(c.args) == 2 { 53 p = absPath(s.cwd, c.args[1]) 54 } else { 55 return fmt.Errorf("usage: cd <dir>") 56 } 57 if s.f != nil { 58 s.f.Close() 59 } 60 fi, err := fs.Stat(lib9p.ExportFS{s.fsys}, p) 61 if err != nil { 62 return err 63 } 64 if !fi.IsDir() { 65 return fmt.Errorf("not a directory") 66 } 67 f, err := s.fsys.OpenFile(p, lib9p.OREAD) 68 if err != nil { 69 return err 70 } 71 s.f = f 72 s.cwd = p 73 return nil 74 case "pwd": 75 fmt.Println(s.cwd) 76 return nil 77 case "ls": 78 p := s.cwd 79 if len(c.args) > 1 { 80 p = absPath(p, c.args[1]) 81 } 82 files, err := fs.Glob(lib9p.ExportFS{s.fsys}, path.Join(p, "*")) 83 if err != nil { 84 return err 85 } 86 for _, f := range files { 87 fmt.Println(f) 88 } 89 return nil 90 case "mount": 91 if len(c.args) != 3 { 92 return fmt.Errorf("usage: mount servename mtpt") 93 } 94 servname, mtpt := c.args[1], c.args[2] 95 conn, err := net.Dial("tcp", servname) 96 if err != nil { 97 return err 98 } 99 var p string 100 if len(mtpt) != 0 && mtpt[0] == '/' { 101 p = absPath("", mtpt) 102 } else { 103 p = absPath(s.cwd, mtpt) 104 } 105 cfsys, err := client.Mount(context.TODO(), conn, conn, "kenji", "") 106 if err != nil { 107 return err 108 } 109 s.fsys.Mount(cfsys, p) 110 return nil 111 case "fstab" : 112 fmt.Println(s.fsys.fstab) 113 return nil 114 default: 115 return fmt.Errorf("unknown command %v", c.args[0]) 116 } 117 } 118 119 func main() { 120 conn, err := net.Dial("tcp", "127.0.0.1:5640") 121 if err != nil { 122 log.Fatalf("dial: %v", err) 123 } 124 defer conn.Close() 125 ctx, cancel := context.WithCancel(context.Background()) 126 fsys, err := client.Mount(ctx, conn, conn, "kenji", "") 127 if err != nil { 128 log.Fatalf("mount: %v", err) 129 } 130 defer cancel() 131 tc := runTokenizer(ctx, os.Stdin) 132 cc := runParser(ctx, tc) 133 s := &stat{ 134 cwd: ".", 135 fsys: &unionFS{fstab: map[string]lib9p.FS{".": fsys}}, 136 } 137 for { 138 fmt.Printf("9%% ") 139 c := <-cc 140 if c.err == io.EOF { 141 break 142 } else if c.err != nil { 143 log.Println(c.err) 144 continue 145 } 146 if err := c.run(s); err != nil { 147 log.Println(err) 148 } 149 } 150 } 151 152 func read(r io.Reader) (string, error) { 153 b := make([]byte, 128) 154 n, err := r.Read(b) 155 if err != nil { 156 return "", err 157 } 158 return string(b[:n]), nil 159 } 160 161 func runTokenizer(ctx context.Context, r io.Reader) <-chan *token { 162 tc := make(chan *token) 163 go func() { 164 defer close(tc) 165 L: 166 for { 167 select { 168 case <-ctx.Done(): 169 break L 170 default: 171 } 172 str, err := read(r) 173 if err != nil { 174 select { 175 case tc <-&token{cmd: "", err: err}: 176 continue L 177 case <-ctx.Done(): 178 break L 179 } 180 } 181 ts := strings.Split(str, " ") 182 for _, s := range ts { 183 t := &token{cmd: s, err: nil} 184 select { 185 case tc <- t: 186 case <-ctx.Done(): 187 break L 188 } 189 } 190 } 191 }() 192 return tc 193 } 194 195 func runParser(ctx context.Context, tc <-chan *token) <-chan *cmd { 196 cc := make(chan *cmd) 197 go func() { 198 defer close(cc) 199 L: 200 for { 201 c := new(cmd) 202 M: 203 for { 204 select { 205 case <-ctx.Done(): 206 break L 207 case t := <-tc: 208 if t.err != nil { 209 c.err = t.err 210 break M 211 } 212 end := false 213 if len(t.cmd) == 0 { 214 continue M 215 } 216 if t.cmd[len(t.cmd)-1] == '\n' { 217 t.cmd = t.cmd[:len(t.cmd)-1] 218 end = true 219 } 220 c.args = append(c.args, t.cmd) 221 if end { 222 break M 223 } 224 } 225 } 226 select { 227 case <-ctx.Done(): 228 break L 229 case cc<- c: 230 } 231 } 232 }() 233 return cc 234 }