1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
5 // package cmd defines a RunFunc type, representing a process that can
6 // be invoked from a command line.
18 // A RunFunc runs a command with the given args, and returns an exit
20 type RunFunc func(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int
22 // Multi returns a RunFunc that looks up its first argument in m, and
23 // invokes the resulting RunFunc with the remaining args.
27 // os.Exit(Multi(map[string]RunFunc{
28 // "foobar": func(prog string, args []string) int {
29 // fmt.Println(args[0])
32 // })("/usr/bin/multi", []string{"foobar", "baz"}))
34 // ...prints "baz" and exits 2.
35 func Multi(m map[string]RunFunc) RunFunc {
36 return func(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
38 fmt.Fprintf(stderr, "usage: %s command [args]\n", prog)
42 if cmd, ok := m[args[0]]; !ok {
43 fmt.Fprintf(stderr, "unrecognized command %q\n", args[0])
47 return cmd(prog+" "+args[0], args[1:], stdin, stdout, stderr)
52 func multiUsage(stderr io.Writer, m map[string]RunFunc) {
53 var subcommands []string
55 if strings.HasPrefix(sc, "-") {
56 // Some subcommands have alternate versions
57 // like "--version" for compatibility. Don't
58 // clutter the subcommand summary with those.
61 subcommands = append(subcommands, sc)
63 sort.Strings(subcommands)
64 fmt.Fprintf(stderr, "\nAvailable commands:\n")
65 for _, sc := range subcommands {
66 fmt.Fprintf(stderr, " %s\n", sc)
70 // WithLateSubcommand wraps a RunFunc by skipping over some known
71 // flags to find a subcommand, and moving that subcommand to the front
72 // of the args before calling the wrapped RunFunc. For example:
74 // // Translate [ --format foo subcommand bar]
75 // // to [subcommand --format foo bar]
76 // WithLateSubcommand(fn, []string{"format"}, nil)
77 func WithLateSubcommand(run RunFunc, argFlags, boolFlags []string) RunFunc {
78 return func(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
79 flags := flag.NewFlagSet("prog", flag.ContinueOnError)
80 for _, arg := range argFlags {
81 flags.String(arg, "", "")
83 for _, arg := range boolFlags {
84 flags.Bool(arg, false, "")
86 // Ignore errors. We can't report a useful error
88 flags.SetOutput(ioutil.Discard)
89 flags.Usage = func() {}
92 // Move the first arg after the recognized
93 // flags up to the front.
94 flagargs := len(args) - flags.NArg()
95 newargs := make([]string, len(args))
96 newargs[0] = args[flagargs]
97 copy(newargs[1:flagargs+1], args[:flagargs])
98 copy(newargs[flagargs+1:], args[flagargs+1:])
101 return run(prog, args, stdin, stdout, stderr)