diff --git a/args.go b/args.go index 43a1047..580f810 100644 --- a/args.go +++ b/args.go @@ -17,26 +17,33 @@ var keepOrder = flag.BoolP("keep-order", "k", false, "print output in the order var dryRun = flag.Bool("dry-run", false, "print the commands that would be run instead of running them") // no shorthand to match GNU Parallel // parse flags and commandline args -func parseArgs() { +func parseArgs() (exitEarly bool, err error) { flag.SetInterspersed(false) // don't confuse flags to the command with our own flag.Parse() if *printVersion { fmt.Println(version) - os.Exit(0) + return true, nil } if *dryRun { - for _, c := range getCmdStrings() { + cmds, err := getCmdStrings() + if err != nil { + return false, err + } + + for _, c := range cmds { fmt.Println(c) } - os.Exit(0) + return true, nil } + + return false, nil } // return any command arguments given on the command line, if any // TODO pull out this parsing into a separate file and simplify it -func getArgSets() [][]string { +func getArgSets() ([][]string, error) { var cmdSets [][]string inArgList, inFileList, setStartIdx := false, false, 0 @@ -53,16 +60,19 @@ func getArgSets() [][]string { arg, symType = "::::", "files" } - fmt.Fprintln(os.Stderr, arg+" must be followed by "+symType) - os.Exit(2) + return nil, fmt.Errorf("%v must be followed by %v", arg, symType) } else if inFileList { // store the file set we were building for _, file := range flag.Args()[setStartIdx:i] { if file == "-" && stdinRead { - fmt.Fprintln(os.Stderr, "standard input cannot be used as an argument source multiple times") - os.Exit(2) + return nil, fmt.Errorf("standard input cannot be used as an argument source multiple times") } - cmdSets = append(cmdSets, readCmdsFromFile(file)) + cmds, err := readCmdsFromFile(file) + if err != nil { + return nil, err + } + + cmdSets = append(cmdSets, cmds) stdinRead = true } inFileList = false @@ -83,19 +93,22 @@ func getArgSets() [][]string { arg, symType = "::::", "files" } - fmt.Fprintln(os.Stderr, arg+" must be followed by "+symType) - os.Exit(2) + return nil, fmt.Errorf("%v must be followed by %v", arg, symType) } else if inArgList { // store the arg set we were building cmdSets = append(cmdSets, flag.Args()[setStartIdx:i]) inArgList = false } else if inFileList { // store the previous file set we were building for _, file := range flag.Args()[setStartIdx:i] { if file == "-" && stdinRead { - fmt.Fprintln(os.Stderr, "standard input cannot be used as an argument source multiple times") - os.Exit(2) + return nil, fmt.Errorf("standard input cannot be used as an argument source multiple times") + } + + cmds, err := readCmdsFromFile(file) + if err != nil { + return nil, err } - cmdSets = append(cmdSets, readCmdsFromFile(file)) + cmdSets = append(cmdSets, cmds) stdinRead = true } } @@ -115,23 +128,26 @@ func getArgSets() [][]string { arg, symType = "::::", "files" } - fmt.Fprintln(os.Stderr, arg+" must be followed by "+symType) - os.Exit(2) + return nil, fmt.Errorf("%v must be followed by %v", arg, symType) } else if inArgList { // otherwise we just need to store the final set cmdSets = append(cmdSets, flag.Args()[setStartIdx:len(flag.Args())]) } else if inFileList { for _, file := range flag.Args()[setStartIdx:len(flag.Args())] { if file == "-" && stdinRead { - fmt.Fprintln(os.Stderr, "standard input cannot be used as an argument source multiple times") - os.Exit(2) + return nil, fmt.Errorf("standard input cannot be used as an argument source multiple times") } - cmdSets = append(cmdSets, readCmdsFromFile(file)) + cmds, err := readCmdsFromFile(file) + if err != nil { + return nil, err + } + + cmdSets = append(cmdSets, cmds) stdinRead = true } } - return cmdSets + return cmdSets, nil } // return the command prefix given on the commandline, if any @@ -186,14 +202,14 @@ func permuteArgSets(sets [][]string) []string { } // reads in commands from a file with "-" meaning std in -func readCmdsFromFile(name string) []string { +func readCmdsFromFile(name string) ([]string, error) { var file io.Reader if name == "-" { file = os.Stdin } else { f, err := os.Open(name) if err != nil { - panic(err) + return nil, err } file = f } @@ -208,22 +224,26 @@ func readCmdsFromFile(name string) []string { cmds = append(cmds, scanner.Text()) } - return cmds + return cmds, nil } const placeHolder string = "{}" // read in commands to run -func getCmdStrings() []string { - prefix := getCmdPrefix() // any command given as arguments on the command line - cmdLineArgSets := getArgSets() // looks for ::: arguments - var cmds []string +func getCmdStrings() ([]string, error) { + prefix := getCmdPrefix() // any command given as arguments on the command line + cmdLineArgSets, err := getArgSets() // looks for ::: arguments + if err != nil { + return nil, err + } - // get commands + var cmds []string if len(cmdLineArgSets) == 0 { // get args from stdin - cmds = readCmdsFromFile("-") + if cmds, err = readCmdsFromFile("-"); err != nil { + return nil, err + } - } else { + } else { // get them from command line and / or files cmds = permuteArgSets(cmdLineArgSets) } @@ -237,5 +257,5 @@ func getCmdStrings() []string { } } - return cmds + return cmds, nil } diff --git a/args_test.go b/args_test.go index 1431196..2951837 100644 --- a/args_test.go +++ b/args_test.go @@ -115,7 +115,13 @@ func TestGetArgSets(t *testing.T) { // TODO refactor so this isn't called directly and we pass arguments instead os.Args = tC.args parseArgs() - if as := getArgSets(); !reflect.DeepEqual(as, tC.exp) { + + as, err := getArgSets() + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(as, tC.exp) { t.Fatalf("expected %v but got %v", tC.exp, as) } }) @@ -220,7 +226,12 @@ func TestReadCmdsFromFile(t *testing.T) { t.Fatal(err) } - if cmds := readCmdsFromFile(tmpfile.Name()); !reflect.DeepEqual(cmds, exp) { + cmds, err := readCmdsFromFile(tmpfile.Name()) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(cmds, exp) { t.Fatalf("expected %v but got %v", exp, cmds) } @@ -251,7 +262,13 @@ func TestGetCmdStrings(t *testing.T) { // TODO refactor so this isn't called directly and we pass arguments instead os.Args = tC.args parseArgs() - if c := getCmdStrings(); !reflect.DeepEqual(c, tC.exp) { + + c, err := getCmdStrings() + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(c, tC.exp) { t.Fatalf("expected %v but got %v", tC.exp, c) } }) diff --git a/job/job_windows.go b/job/job_windows.go index 2084893..bd6c44b 100644 --- a/job/job_windows.go +++ b/job/job_windows.go @@ -30,12 +30,12 @@ func (job *Job) Stop() { func sendCtrlBreak(pid int) { dll, err := syscall.LoadDLL("kernel32.dll") if err != nil { - panic(err) + panic(err) // should never happen } proc, err := dll.FindProc("GenerateConsoleCtrlEvent") if err != nil { - panic(err) + panic(err) // should never happen } res, _, err := proc.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid)) diff --git a/main.go b/main.go index fa6b420..9bda446 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os" "github.com/pwr22/zoom/run" @@ -9,6 +10,17 @@ import ( const version = "v0.1.2" func main() { - parseArgs() - os.Exit(run.Cmds(getCmdStrings(), *parallelism, *keepOrder)) + if exitEarly, err := parseArgs(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } else if exitEarly { + os.Exit(0) + } + + cmds, err := getCmdStrings() + if err != nil { + os.Exit(2) + } + + run.Cmds(cmds, *parallelism, *keepOrder) } diff --git a/run/run.go b/run/run.go index 26c24dc..73055e9 100644 --- a/run/run.go +++ b/run/run.go @@ -122,7 +122,7 @@ func (state *runState) handleStopEarlySignal() { } // Cmds executes the commands its given in parallel -func Cmds(cmdStrs []string, numOfRunners int, keepOrder bool) int { +func Cmds(cmdStrs []string, numOfRunners int, keepOrder bool) (exitStatus int) { if numOfRunners == 0 { // means run everything at once numOfRunners = len(cmdStrs) } else if numOfRunners > len(cmdStrs) { // or if there are more runners than commands then drop the excess