// ShortHelp returns a formatted CLI helptext string, generated for the given command func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error { cmd, err := root.Get(path) if err != nil { return err } // default cmd to root if there is no path if path == nil && cmd == nil { cmd = root } pathStr := rootName if len(path) > 0 { pathStr += " " + strings.Join(path, " ") } fields := helpFields{ Indent: indentStr, Path: pathStr, ArgUsage: usageText(cmd), Tagline: cmd.Helptext.Tagline, Synopsis: cmd.Helptext.Synopsis, Description: cmd.Helptext.ShortDescription, Usage: cmd.Helptext.Usage, MoreHelp: (cmd != root), } // trim the extra newlines (see TrimNewlines doc) fields.TrimNewlines() // indent all fields that have been set fields.IndentAll() return shortHelpTemplate.Execute(out, fields) }
// Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) { path, opts, stringVals, cmd, err := parseOpts(input, root) if err != nil { return nil, nil, path, err } optDefs, err := root.GetOptions(path) if err != nil { return nil, cmd, path, err } req, err := cmds.NewRequest(path, opts, nil, nil, cmd, optDefs) if err != nil { return nil, cmd, path, err } // if -r is provided, and it is associated with the package builtin // recursive path option, allow recursive file paths recursiveOpt := req.Option(cmds.RecShort) recursive := false if recursiveOpt != nil && recursiveOpt.Definition() == cmds.OptionRecursivePath { recursive, _, err = recursiveOpt.Bool() if err != nil { return req, nil, nil, u.ErrCast() } } // if '--hidden' is provided, enumerate hidden paths hiddenOpt := req.Option("hidden") hidden := false if hiddenOpt != nil { hidden, _, err = hiddenOpt.Bool() if err != nil { return req, nil, nil, u.ErrCast() } } stringArgs, fileArgs, err := parseArgs(stringVals, stdin, cmd.Arguments, recursive, hidden, root) if err != nil { return req, cmd, path, err } req.SetArguments(stringArgs) if len(fileArgs) > 0 { file := files.NewSliceFile("", "", fileArgs) req.SetFiles(file) } err = cmd.CheckArguments(req) if err != nil { return req, cmd, path, err } return req, cmd, path, nil }
// LongHelp writes a formatted CLI helptext string to a Writer for the given command func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error { cmd, err := root.Get(path) if err != nil { return err } pathStr := rootName if len(path) > 0 { pathStr += " " + strings.Join(path, " ") } fields := helpFields{ Indent: indentStr, Path: pathStr, ArgUsage: usageText(cmd), Tagline: cmd.Helptext.Tagline, Arguments: cmd.Helptext.Arguments, Options: cmd.Helptext.Options, Synopsis: cmd.Helptext.Synopsis, Subcommands: cmd.Helptext.Subcommands, Description: cmd.Helptext.ShortDescription, Usage: cmd.Helptext.Usage, MoreHelp: (cmd != root), } if len(cmd.Helptext.LongDescription) > 0 { fields.Description = cmd.Helptext.LongDescription } // autogen fields that are empty if len(fields.Arguments) == 0 { fields.Arguments = strings.Join(argumentText(cmd), "\n") } if len(fields.Options) == 0 { fields.Options = strings.Join(optionText(cmd), "\n") } if len(fields.Subcommands) == 0 { fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n") } if len(fields.Synopsis) == 0 { fields.Synopsis = generateSynopsis(cmd, pathStr) } // trim the extra newlines (see TrimNewlines doc) fields.TrimNewlines() // indent all fields that have been set fields.IndentAll() return longHelpTemplate.Execute(out, fields) }
// Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) { path, opts, stringVals, cmd, err := parseOpts(input, root) if err != nil { return nil, nil, path, err } optDefs, err := root.GetOptions(path) if err != nil { return nil, cmd, path, err } req, err := cmds.NewRequest(path, opts, nil, nil, cmd, optDefs) if err != nil { return nil, cmd, path, err } // This is an ugly hack to maintain our current CLI interface while fixing // other stdin usage bugs. Let this serve as a warning, be careful about the // choices you make, they will haunt you forever. if len(path) == 2 && path[0] == "bootstrap" { if (path[1] == "add" && opts["default"] == true) || (path[1] == "rm" && opts["all"] == true) { stdin = nil } } stringArgs, fileArgs, err := ParseArgs(req, stringVals, stdin, cmd.Arguments, root) if err != nil { return req, cmd, path, err } req.SetArguments(stringArgs) if len(fileArgs) > 0 { file := files.NewSliceFile("", "", fileArgs) req.SetFiles(file) } err = cmd.CheckArguments(req) if err != nil { return req, cmd, path, err } return req, cmd, path, nil }
func callCommand(ctx context.Context, req cmds.Request, root *cmds.Command, cmd *cmds.Command) (cmds.Response, error) { log.Info(config.EnvDir, " ", req.InvocContext().ConfigRoot) var res cmds.Response err := req.SetRootContext(ctx) if err != nil { return nil, err } details, err := commandDetails(req.Path(), root) if err != nil { return nil, err } client, err := commandShouldRunOnDaemon(*details, req, root) if err != nil { return nil, err } err = callPreCommandHooks(ctx, *details, req, root) if err != nil { return nil, err } if cmd.PreRun != nil { err = cmd.PreRun(req) if err != nil { return nil, err } } if client != nil && !cmd.External { log.Debug("Executing command via API") res, err = client.Send(req) if err != nil { if isConnRefused(err) { err = repo.ErrApiNotRunning } return nil, err } } else { log.Debug("Executing command locally") err := req.SetRootContext(ctx) if err != nil { return nil, err } // Okay!!!!! NOW we can call the command. res = root.Call(req) } if cmd.PostRun != nil { cmd.PostRun(req, res) } return res, nil }
// Parse a command line made up of sub-commands, short arguments, long arguments and positional arguments func parseOpts(args []string, root *cmds.Command) ( path []string, opts map[string]interface{}, stringVals []string, cmd *cmds.Command, err error, ) { path = make([]string, 0, len(args)) stringVals = make([]string, 0, len(args)) optDefs := map[string]cmds.Option{} opts = map[string]interface{}{} cmd = root // parseFlag checks that a flag is valid and saves it into opts // Returns true if the optional second argument is used parseFlag := func(name string, arg *string, mustUse bool) (bool, error) { if _, ok := opts[name]; ok { return false, fmt.Errorf("Duplicate values for option '%s'", name) } optDef, found := optDefs[name] if !found { err = fmt.Errorf("Unrecognized option '%s'", name) return false, err } if optDef.Type() == cmds.Bool { if mustUse { return false, fmt.Errorf("Option '%s' takes no arguments, but was passed '%s'", name, *arg) } opts[name] = "" return false, nil } else { if arg == nil { return true, fmt.Errorf("Missing argument for option '%s'", name) } opts[name] = *arg return true, nil } } optDefs, err = root.GetOptions(path) if err != nil { return } consumed := false for i, arg := range args { switch { case consumed: // arg was already consumed by the preceding flag consumed = false continue case arg == "--": // treat all remaining arguments as positional arguments stringVals = append(stringVals, args[i+1:]...) return case strings.HasPrefix(arg, "--"): // arg is a long flag, with an optional argument specified // using `=' or in args[i+1] var slurped bool var next *string split := strings.SplitN(arg, "=", 2) if len(split) == 2 { slurped = false arg = split[0] next = &split[1] } else { slurped = true if i+1 < len(args) { next = &args[i+1] } else { next = nil } } consumed, err = parseFlag(arg[2:], next, len(split) == 2) if err != nil { return } if !slurped { consumed = false } case strings.HasPrefix(arg, "-") && arg != "-": // args is one or more flags in short form, followed by an optional argument // all flags except the last one have type bool for arg = arg[1:]; len(arg) != 0; arg = arg[1:] { var rest *string var slurped bool mustUse := false if len(arg) > 1 { slurped = false str := arg[1:] if len(str) > 0 && str[0] == '=' { str = str[1:] mustUse = true } rest = &str } else { slurped = true if i+1 < len(args) { rest = &args[i+1] } else { rest = nil } } var end bool end, err = parseFlag(arg[0:1], rest, mustUse) if err != nil { return } if end { consumed = slurped break } } default: // arg is a sub-command or a positional argument sub := cmd.Subcommand(arg) if sub != nil { cmd = sub path = append(path, arg) optDefs, err = root.GetOptions(path) if err != nil { return } } else { stringVals = append(stringVals, arg) } } } return }
// Parse a command line made up of sub-commands, short arguments, long arguments and positional arguments func parseOpts(args []string, root *cmds.Command) ( path []string, opts map[string]interface{}, stringVals []string, cmd *cmds.Command, err error, ) { path = make([]string, 0, len(args)) stringVals = make([]string, 0, len(args)) optDefs := map[string]cmds.Option{} opts = map[string]interface{}{} cmd = root // parseFlag checks that a flag is valid and saves it into opts // Returns true if the optional second argument is used parseFlag := func(name string, arg *string, mustUse bool) (bool, error) { if _, ok := opts[name]; ok { return false, fmt.Errorf("Duplicate values for option '%s'", name) } optDef, found := optDefs[name] if !found { err = fmt.Errorf("Unrecognized option '%s'", name) return false, err } // mustUse implies that you must use the argument given after the '=' // eg. -r=true means you must take true into consideration // mustUse == true in the above case // eg. ipfs -r <file> means disregard <file> since there is no '=' // mustUse == false in the above situation //arg == nil implies the flag was specified without an argument if optDef.Type() == cmds.Bool { if arg == nil || !mustUse { opts[name] = true return false, nil } argVal := strings.ToLower(*arg) switch argVal { case "true": opts[name] = true return true, nil case "false": opts[name] = false return true, nil default: return true, fmt.Errorf("Option '%s' takes true/false arguments, but was passed '%s'", name, argVal) } } else { if arg == nil { return true, fmt.Errorf("Missing argument for option '%s'", name) } opts[name] = *arg return true, nil } } optDefs, err = root.GetOptions(path) if err != nil { return } consumed := false for i, arg := range args { switch { case consumed: // arg was already consumed by the preceding flag consumed = false continue case arg == "--": // treat all remaining arguments as positional arguments stringVals = append(stringVals, args[i+1:]...) return case strings.HasPrefix(arg, "--"): // arg is a long flag, with an optional argument specified // using `=' or in args[i+1] var slurped bool var next *string split := strings.SplitN(arg, "=", 2) if len(split) == 2 { slurped = false arg = split[0] next = &split[1] } else { slurped = true if i+1 < len(args) { next = &args[i+1] } else { next = nil } } consumed, err = parseFlag(arg[2:], next, len(split) == 2) if err != nil { return } if !slurped { consumed = false } case strings.HasPrefix(arg, "-") && arg != "-": // args is one or more flags in short form, followed by an optional argument // all flags except the last one have type bool for arg = arg[1:]; len(arg) != 0; arg = arg[1:] { var rest *string var slurped bool mustUse := false if len(arg) > 1 { slurped = false str := arg[1:] if len(str) > 0 && str[0] == '=' { str = str[1:] mustUse = true } rest = &str } else { slurped = true if i+1 < len(args) { rest = &args[i+1] } else { rest = nil } } var end bool end, err = parseFlag(arg[:1], rest, mustUse) if err != nil { return } if end { consumed = slurped break } } default: // arg is a sub-command or a positional argument sub := cmd.Subcommand(arg) if sub != nil { cmd = sub path = append(path, arg) optDefs, err = root.GetOptions(path) if err != nil { return } // If we've come across an external binary call, pass all the remaining // arguments on to it if cmd.External { stringVals = append(stringVals, args[i+1:]...) return } } else { stringVals = append(stringVals, arg) } } } return }
// Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) { path, opts, stringVals, cmd, err := parseOpts(input, root) if err != nil { return nil, nil, path, err } optDefs, err := root.GetOptions(path) if err != nil { return nil, cmd, path, err } req, err := cmds.NewRequest(path, opts, nil, nil, cmd, optDefs) if err != nil { return nil, cmd, path, err } // if -r is provided, and it is associated with the package builtin // recursive path option, allow recursive file paths recursiveOpt := req.Option(cmds.RecShort) recursive := false if recursiveOpt != nil && recursiveOpt.Definition() == cmds.OptionRecursivePath { recursive, _, err = recursiveOpt.Bool() if err != nil { return req, nil, nil, u.ErrCast() } } // if '--hidden' is provided, enumerate hidden paths hiddenOpt := req.Option("hidden") hidden := false if hiddenOpt != nil { hidden, _, err = hiddenOpt.Bool() if err != nil { return req, nil, nil, u.ErrCast() } } // This is an ugly hack to maintain our current CLI interface while fixing // other stdin usage bugs. Let this serve as a warning, be careful about the // choices you make, they will haunt you forever. if len(path) == 2 && path[0] == "bootstrap" { if (path[1] == "add" && opts["default"] == true) || (path[1] == "rm" && opts["all"] == true) { stdin = nil } } stringArgs, fileArgs, err := parseArgs(stringVals, stdin, cmd.Arguments, recursive, hidden, root) if err != nil { return req, cmd, path, err } req.SetArguments(stringArgs) if len(fileArgs) > 0 { file := files.NewSliceFile("", "", fileArgs) req.SetFiles(file) } err = cmd.CheckArguments(req) if err != nil { return req, cmd, path, err } return req, cmd, path, nil }
// Parse parses the data in a http.Request and returns a command Request object func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { if !strings.HasPrefix(r.URL.Path, ApiPath) { return nil, errors.New("Unexpected path prefix") } path := strings.Split(strings.TrimPrefix(r.URL.Path, ApiPath+"/"), "/") stringArgs := make([]string, 0) if err := apiVersionMatches(r); err != nil { if path[0] != "version" { // compatibility with previous version check return nil, err } } cmd, err := root.Get(path[:len(path)-1]) if err != nil { // 404 if there is no command at that path return nil, ErrNotFound } if sub := cmd.Subcommand(path[len(path)-1]); sub == nil { if len(path) <= 1 { return nil, ErrNotFound } // if the last string in the path isn't a subcommand, use it as an argument // e.g. /objects/Qabc12345 (we are passing "Qabc12345" to the "objects" command) stringArgs = append(stringArgs, path[len(path)-1]) path = path[:len(path)-1] } else { cmd = sub } opts, stringArgs2 := parseOptions(r) stringArgs = append(stringArgs, stringArgs2...) // count required argument definitions numRequired := 0 for _, argDef := range cmd.Arguments { if argDef.Required { numRequired++ } } // count the number of provided argument values valCount := len(stringArgs) args := make([]string, valCount) valIndex := 0 requiredFile := "" for _, argDef := range cmd.Arguments { // skip optional argument definitions if there aren't sufficient remaining values if valCount-valIndex <= numRequired && !argDef.Required { continue } else if argDef.Required { numRequired-- } if argDef.Type == cmds.ArgString { if argDef.Variadic { for _, s := range stringArgs { args[valIndex] = s valIndex++ } valCount -= len(stringArgs) } else if len(stringArgs) > 0 { args[valIndex] = stringArgs[0] stringArgs = stringArgs[1:] valIndex++ } else { break } } else if argDef.Type == cmds.ArgFile && argDef.Required && len(requiredFile) == 0 { requiredFile = argDef.Name } } optDefs, err := root.GetOptions(path) if err != nil { return nil, err } // create cmds.File from multipart/form-data contents contentType := r.Header.Get(contentTypeHeader) mediatype, _, _ := mime.ParseMediaType(contentType) var f *files.MultipartFile if mediatype == "multipart/form-data" { f = &files.MultipartFile{Mediatype: mediatype} f.Reader, err = r.MultipartReader() if err != nil { return nil, err } } // if there is a required filearg, error if no files were provided if len(requiredFile) > 0 && f == nil { return nil, fmt.Errorf("File argument '%s' is required", requiredFile) } req, err := cmds.NewRequest(path, opts, args, f, cmd, optDefs) if err != nil { return nil, err } err = cmd.CheckArguments(req) if err != nil { return nil, err } return req, nil }
func callCommand(ctx context.Context, req cmds.Request, root *cmds.Command, cmd *cmds.Command) (cmds.Response, error) { log.Info(config.EnvDir, " ", req.InvocContext().ConfigRoot) var res cmds.Response err := req.SetRootContext(ctx) if err != nil { return nil, err } details, err := commandDetails(req.Path(), root) if err != nil { return nil, err } log.Debug("looking for running daemon...") useDaemon, err := commandShouldRunOnDaemon(*details, req, root) if err != nil { return nil, err } err = callPreCommandHooks(ctx, *details, req, root) if err != nil { return nil, err } if cmd.PreRun != nil { err = cmd.PreRun(req) if err != nil { return nil, err } } if useDaemon { cfg, err := req.InvocContext().GetConfig() if err != nil { return nil, err } addr, err := ma.NewMultiaddr(cfg.Addresses.API) if err != nil { return nil, err } log.Infof("Executing command on daemon running at %s", addr) _, host, err := manet.DialArgs(addr) if err != nil { return nil, err } client := cmdsHttp.NewClient(host) res, err = client.Send(req) if err != nil { return nil, err } } else { log.Debug("Executing command locally") err := req.SetRootContext(ctx) if err != nil { return nil, err } // Okay!!!!! NOW we can call the command. res = root.Call(req) } if cmd.PostRun != nil { cmd.PostRun(req, res) } return res, nil }