1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
5 // package cmd helps define reusable functions that can be exposed as
6 // [subcommands of] command line programs.
18 type Handler interface {
19 RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int
22 type HandlerFunc func(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int
24 func (f HandlerFunc) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
25 return f(prog, args, stdin, stdout, stderr)
28 // Multi is a Handler that looks up its first argument in a map, and
29 // invokes the resulting Handler with the remaining args.
33 // os.Exit(Multi(map[string]Handler{
34 // "foobar": HandlerFunc(func(prog string, args []string) int {
35 // fmt.Println(args[0])
38 // })("/usr/bin/multi", []string{"foobar", "baz"}))
40 // ...prints "baz" and exits 2.
41 type Multi map[string]Handler
43 func (m Multi) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
45 fmt.Fprintf(stderr, "usage: %s command [args]\n", prog)
49 if cmd, ok := m[args[0]]; !ok {
50 fmt.Fprintf(stderr, "unrecognized command %q\n", args[0])
54 return cmd.RunCommand(prog+" "+args[0], args[1:], stdin, stdout, stderr)
58 func (m Multi) Usage(stderr io.Writer) {
59 var subcommands []string
61 if strings.HasPrefix(sc, "-") {
62 // Some subcommands have alternate versions
63 // like "--version" for compatibility. Don't
64 // clutter the subcommand summary with those.
67 subcommands = append(subcommands, sc)
69 sort.Strings(subcommands)
70 fmt.Fprintf(stderr, "\nAvailable commands:\n")
71 for _, sc := range subcommands {
72 fmt.Fprintf(stderr, " %s\n", sc)
76 type FlagSet interface {
77 Init(string, flag.ErrorHandling)
85 // SubcommandToFront silently parses args using flagset, and returns a
86 // copy of args with the first non-flag argument moved to the
87 // front. If parsing fails or consumes all of args, args is returned
90 // SubcommandToFront invokes methods on flagset that have side
91 // effects, including Parse. In typical usage, flagset will not used
92 // for anything else after being passed to SubcommandToFront.
93 func SubcommandToFront(args []string, flagset FlagSet) []string {
94 flagset.Init("", flag.ContinueOnError)
95 flagset.SetOutput(ioutil.Discard)
96 if err := flagset.Parse(args); err != nil || flagset.NArg() == 0 {
97 // No subcommand found.
100 // Move subcommand to the front.
101 flagargs := len(args) - flagset.NArg()
102 newargs := make([]string, len(args))
103 newargs[0] = args[flagargs]
104 copy(newargs[1:flagargs+1], args[:flagargs])
105 copy(newargs[flagargs+1:], args[flagargs+1:])