19 "git.omarpolo.com/go-p9p"
20 "github.com/chzyer/readline"
26 flag.StringVar(&addr, "addr", ":5640", "addr of 9p service")
31 log.Println(http.ListenAndServe("localhost:6060", nil))
34 ctx := context.Background()
39 if strings.HasPrefix(addr, "unix:") {
44 log.Println("dialing", addr)
45 conn, err := net.Dial(proto, addr)
50 csession, err := p9p.NewSession(ctx, conn)
55 commander := &fsCommander{
56 ctx: context.Background(),
63 completer := readline.NewPrefixCompleter(
64 readline.PcItem("ls"),
65 // readline.PcItem("find"),
66 readline.PcItem("stat"),
67 readline.PcItem("cat"),
68 readline.PcItem("cd"),
69 readline.PcItem("pwd"),
72 rl, err := readline.NewEx(&readline.Config{
73 HistoryFile: ".history",
74 AutoComplete: completer,
79 commander.readline = rl
81 msize, version := commander.session.Version()
85 log.Println("9p version", version, msize)
89 if _, err := commander.session.Attach(commander.ctx, commander.nextfid, p9p.NOFID, "anyone", "/"); err != nil {
92 commander.rootfid = commander.nextfid
95 // clone the pwd fid so we can clunk it
96 if _, err := commander.session.Walk(commander.ctx, commander.rootfid, commander.nextfid); err != nil {
99 commander.pwdfid = commander.nextfid
103 commander.readline.SetPrompt(fmt.Sprintf("%s 🐳 > ", commander.pwd))
105 line, err := rl.Readline()
107 log.Fatalln("error: ", err)
114 args := strings.Fields(line)
117 var cmd func(ctx context.Context, args ...string) error
121 cmd = commander.cmdls
123 cmd = commander.cmdcd
125 cmd = commander.cmdpwd
127 cmd = commander.cmdcat
129 cmd = commander.cmdstat
131 cmd = func(ctx context.Context, args ...string) error {
132 return fmt.Errorf("command not implemented")
136 ctx, _ = context.WithTimeout(commander.ctx, 5*time.Second)
137 if err := cmd(ctx, args[1:]...); err != nil {
138 if err == p9p.ErrClosed {
139 log.Println("connection closed, shutting down")
143 log.Printf("👹 %s: %v", name, err)
148 type fsCommander struct {
157 readline *readline.Instance
162 func (c *fsCommander) cmdls(ctx context.Context, args ...string) error {
163 ps := []string{c.pwd}
168 wr := tabwriter.NewWriter(c.stdout, 0, 8, 8, ' ', 0)
170 for _, p := range ps {
171 // create a header if have more than one path.
173 fmt.Fprintln(wr, p+":")
177 p = path.Join(c.pwd, p)
180 targetfid := c.nextfid
182 components := strings.Split(strings.Trim(p, "/"), "/")
183 if _, err := c.session.Walk(ctx, c.rootfid, targetfid, components...); err != nil {
186 defer c.session.Clunk(ctx, targetfid)
188 _, iounit, err := c.session.Open(ctx, targetfid, p9p.OREAD)
194 msize, _ := c.session.Version()
195 iounit = uint32(msize - 24) // size of message max minus fcall io header (Rread)
198 p := make([]byte, iounit)
200 n, err := c.session.Read(ctx, targetfid, p, 0)
205 rd := bytes.NewReader(p[:n])
206 codec := p9p.NewCodec() // TODO(stevvooe): Need way to resolve codec based on session.
209 if err := p9p.DecodeDir(codec, rd, &d); err != nil {
217 fmt.Fprintf(wr, "%v\t%v\t%v\t%s\n", os.FileMode(d.Mode), d.Length, d.ModTime, d.Name)
225 // all output is dumped only after success.
229 func (c *fsCommander) cmdcd(ctx context.Context, args ...string) error {
237 return fmt.Errorf("cd: invalid args: %v", args)
241 p = path.Join(c.pwd, p)
244 targetfid := c.nextfid
246 components := strings.Split(strings.TrimSpace(strings.Trim(p, "/")), "/")
247 if _, err := c.session.Walk(c.ctx, c.rootfid, targetfid, components...); err != nil {
250 defer c.session.Clunk(c.ctx, c.pwdfid)
252 log.Println("cd", p, targetfid)
259 func (c *fsCommander) cmdstat(ctx context.Context, args ...string) error {
260 ps := []string{c.pwd}
265 wr := tabwriter.NewWriter(c.stdout, 0, 8, 8, ' ', 0)
267 for _, p := range ps {
268 targetfid := c.nextfid
270 components := strings.Split(strings.Trim(p, "/"), "/")
271 if _, err := c.session.Walk(ctx, c.rootfid, targetfid, components...); err != nil {
274 defer c.session.Clunk(ctx, targetfid)
276 d, err := c.session.Stat(ctx, targetfid)
281 fmt.Fprintf(wr, "%v\t%v\t%v\t%s\n", os.FileMode(d.Mode), d.Length, d.ModTime, d.Name)
287 func (c *fsCommander) cmdpwd(ctx context.Context, args ...string) error {
289 return fmt.Errorf("pwd takes no arguments")
296 func (c *fsCommander) cmdcat(ctx context.Context, args ...string) error {
304 return fmt.Errorf("cd: invalid args: %v", args)
308 p = path.Join(c.pwd, p)
311 targetfid := c.nextfid
313 components := strings.Split(strings.TrimSpace(strings.Trim(p, "/")), "/")
314 if _, err := c.session.Walk(ctx, c.rootfid, targetfid, components...); err != nil {
317 defer c.session.Clunk(ctx, c.pwdfid)
319 _, iounit, err := c.session.Open(ctx, targetfid, p9p.OREAD)
325 msize, _ := c.session.Version()
326 iounit = uint32(msize - 24) // size of message max minus fcall io header (Rread)
329 b := make([]byte, iounit)
331 n, err := c.session.Read(ctx, targetfid, b, 0)
336 if _, err := os.Stdout.Write(b[:n]); err != nil {
340 os.Stdout.Write([]byte("\n"))