package shell import ( "bytes" "context" "fmt" "log" "os" "strings" "j3s.sh/zoa/color" "j3s.sh/zoa/utils" maybeio "github.com/google/renameio/maybe" "mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/syntax" ) func Run(r *interp.Runner, script string) { color.ZoaSay("zoa runs :3") f, err := parseFile(script) if err != nil { log.Fatal(err) } ctx := context.TODO() fmt.Printf("f stmts: %+v\n", f.Stmts) for _, stmt := range f.Stmts { // 14:8 fmt.Printf("stmt: %T %+v\n", stmt.Cmd, stmt.Cmd) switch stmt.Cmd.(type) { case *syntax.CallExpr: var thing *syntax.CallExpr thing = stmt.Cmd.(*syntax.CallExpr) for _, a := range thing.Args { fmt.Printf("thing: %s\n", a) } } pos := stmt.Pos() ctx = context.WithValue(ctx, "position", pos) err = r.Run(ctx, stmt) if err != nil { // we yell but keep plowing forward // twirling, twirling color.ZoaYell(fmt.Sprintf("%s:%s: %s", script, pos, err.Error())) } } } func CallHandler(ctx context.Context, args []string) ([]string, error) { command := args[0] fmt.Println() // shell tips go here if command == "echo" { color.ZoaSay(`prefer printf over echo! ex: printf '%s\n' 'hello world!'`) } fmt.Printf("$ %s\n", strings.Join(args, " ")) return args, nil } func parseFile(filename string) (*syntax.File, error) { var result = &syntax.File{} f, err := os.Open(filename) if err != nil { return result, err } defer f.Close() result, err = syntax.NewParser().Parse(f, "") return result, err } func ZoaFmt(script string) { f, err := os.Open(script) if err != nil { log.Fatal(err) } defer f.Close() p := syntax.NewParser() syntax.KeepComments(true)(p) syntax.Variant(syntax.LangPOSIX)(p) node, err := p.Parse(f, "") if err != nil { log.Fatal(err) } var writeBuf bytes.Buffer syntax.NewPrinter().Print(&writeBuf, node) res := writeBuf.Bytes() info, err := os.Lstat(script) if err != nil { log.Fatal(err) } perm := info.Mode().Perm() // TODO: remove maybeio when golang implements atomic writing if err := maybeio.WriteFile(script, res, perm); err != nil { log.Fatal(err) } } // ZoaCopy copies a file from src to dst, optionally modifying its mode. // zoaCopy defaults to 0666 for permissions (before umask) func ZoaCopy(src string, dst string, mode os.FileMode) { srcChk, err := utils.ChecksumFile(src) if err != nil { // source file should always exist, return error fmt.Println(err) os.Exit(1) } dstChk, err := utils.ChecksumFile(dst) if err != nil { // dstfile may not exist for a million // reasons, set checksum to blank // to force a mismatch // TODO: why did i do this? revise errors. dstChk = "" } // TODO: templating engine? // - expand vars // - loop support? if srcChk != dstChk { err = utils.Copy(src, dst) if err != nil { fmt.Println(err) os.Exit(1) } } fi, err := os.Lstat(dst) if err != nil { fmt.Println(err) os.Exit(1) } if fi.Mode().Perm() != mode { err = os.Chmod(dst, mode) if err != nil { fmt.Println(err) os.Exit(1) } } }