1package cli
2
3import (
4	"fmt"
5	"io"
6	"os"
7	"strings"
8	"text/tabwriter"
9	"text/template"
10)
11
12// AppHelpTemplate is the text template for the Default help topic.
13// cli.go uses text/template to render templates. You can
14// render custom help text by setting this variable.
15var AppHelpTemplate = `NAME:
16   {{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
17
18USAGE:
19   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
20
21VERSION:
22   {{.Version}}{{end}}{{end}}{{if .Description}}
23
24DESCRIPTION:
25   {{.Description}}{{end}}{{if len .Authors}}
26
27AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
28   {{range $index, $author := .Authors}}{{if $index}}
29   {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
30
31COMMANDS:{{range .VisibleCategories}}{{if .Name}}
32   {{.Name}}:{{end}}{{range .VisibleCommands}}
33     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
34
35GLOBAL OPTIONS:
36   {{range $index, $option := .VisibleFlags}}{{if $index}}
37   {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}}
38
39COPYRIGHT:
40   {{.Copyright}}{{end}}
41`
42
43// CommandHelpTemplate is the text template for the command help topic.
44// cli.go uses text/template to render templates. You can
45// render custom help text by setting this variable.
46var CommandHelpTemplate = `NAME:
47   {{.HelpName}} - {{.Usage}}
48
49USAGE:
50   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}
51
52CATEGORY:
53   {{.Category}}{{end}}{{if .Description}}
54
55DESCRIPTION:
56   {{.Description}}{{end}}{{if .VisibleFlags}}
57
58OPTIONS:
59   {{range .VisibleFlags}}{{.}}
60   {{end}}{{end}}
61`
62
63// SubcommandHelpTemplate is the text template for the subcommand help topic.
64// cli.go uses text/template to render templates. You can
65// render custom help text by setting this variable.
66var SubcommandHelpTemplate = `NAME:
67   {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}}
68
69USAGE:
70   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
71
72COMMANDS:{{range .VisibleCategories}}{{if .Name}}
73   {{.Name}}:{{end}}{{range .VisibleCommands}}
74     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
75{{end}}{{if .VisibleFlags}}
76OPTIONS:
77   {{range .VisibleFlags}}{{.}}
78   {{end}}{{end}}
79`
80
81var helpCommand = Command{
82	Name:      "help",
83	Aliases:   []string{"h"},
84	Usage:     "Shows a list of commands or help for one command",
85	ArgsUsage: "[command]",
86	Action: func(c *Context) error {
87		args := c.Args()
88		if args.Present() {
89			return ShowCommandHelp(c, args.First())
90		}
91
92		ShowAppHelp(c)
93		return nil
94	},
95}
96
97var helpSubcommand = Command{
98	Name:      "help",
99	Aliases:   []string{"h"},
100	Usage:     "Shows a list of commands or help for one command",
101	ArgsUsage: "[command]",
102	Action: func(c *Context) error {
103		args := c.Args()
104		if args.Present() {
105			return ShowCommandHelp(c, args.First())
106		}
107
108		return ShowSubcommandHelp(c)
109	},
110}
111
112// Prints help for the App or Command
113type helpPrinter func(w io.Writer, templ string, data interface{})
114
115// Prints help for the App or Command with custom template function.
116type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{})
117
118// HelpPrinter is a function that writes the help output. If not set a default
119// is used. The function signature is:
120// func(w io.Writer, templ string, data interface{})
121var HelpPrinter helpPrinter = printHelp
122
123// HelpPrinterCustom is same as HelpPrinter but
124// takes a custom function for template function map.
125var HelpPrinterCustom helpPrinterCustom = printHelpCustom
126
127// VersionPrinter prints the version for the App
128var VersionPrinter = printVersion
129
130// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
131func ShowAppHelpAndExit(c *Context, exitCode int) {
132	ShowAppHelp(c)
133	os.Exit(exitCode)
134}
135
136// ShowAppHelp is an action that displays the help.
137func ShowAppHelp(c *Context) (err error) {
138	if c.App.CustomAppHelpTemplate == "" {
139		HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
140		return
141	}
142	customAppData := func() map[string]interface{} {
143		if c.App.ExtraInfo == nil {
144			return nil
145		}
146		return map[string]interface{}{
147			"ExtraInfo": c.App.ExtraInfo,
148		}
149	}
150	HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData())
151	return nil
152}
153
154// DefaultAppComplete prints the list of subcommands as the default app completion method
155func DefaultAppComplete(c *Context) {
156	for _, command := range c.App.Commands {
157		if command.Hidden {
158			continue
159		}
160		for _, name := range command.Names() {
161			fmt.Fprintln(c.App.Writer, name)
162		}
163	}
164}
165
166// ShowCommandHelpAndExit - exits with code after showing help
167func ShowCommandHelpAndExit(c *Context, command string, code int) {
168	ShowCommandHelp(c, command)
169	os.Exit(code)
170}
171
172// ShowCommandHelp prints help for the given command
173func ShowCommandHelp(ctx *Context, command string) error {
174	// show the subcommand help for a command with subcommands
175	if command == "" {
176		HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
177		return nil
178	}
179
180	for _, c := range ctx.App.Commands {
181		if c.HasName(command) {
182			if c.CustomHelpTemplate != "" {
183				HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil)
184			} else {
185				HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
186			}
187			return nil
188		}
189	}
190
191	if ctx.App.CommandNotFound == nil {
192		return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3)
193	}
194
195	ctx.App.CommandNotFound(ctx, command)
196	return nil
197}
198
199// ShowSubcommandHelp prints help for the given subcommand
200func ShowSubcommandHelp(c *Context) error {
201	return ShowCommandHelp(c, c.Command.Name)
202}
203
204// ShowVersion prints the version number of the App
205func ShowVersion(c *Context) {
206	VersionPrinter(c)
207}
208
209func printVersion(c *Context) {
210	fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
211}
212
213// ShowCompletions prints the lists of commands within a given context
214func ShowCompletions(c *Context) {
215	a := c.App
216	if a != nil && a.BashComplete != nil {
217		a.BashComplete(c)
218	}
219}
220
221// ShowCommandCompletions prints the custom completions for a given command
222func ShowCommandCompletions(ctx *Context, command string) {
223	c := ctx.App.Command(command)
224	if c != nil && c.BashComplete != nil {
225		c.BashComplete(ctx)
226	}
227}
228
229func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) {
230	funcMap := template.FuncMap{
231		"join": strings.Join,
232	}
233	if customFunc != nil {
234		for key, value := range customFunc {
235			funcMap[key] = value
236		}
237	}
238
239	w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
240	t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
241	err := t.Execute(w, data)
242	if err != nil {
243		// If the writer is closed, t.Execute will fail, and there's nothing
244		// we can do to recover.
245		if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" {
246			fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
247		}
248		return
249	}
250	w.Flush()
251}
252
253func printHelp(out io.Writer, templ string, data interface{}) {
254	printHelpCustom(out, templ, data, nil)
255}
256
257func checkVersion(c *Context) bool {
258	found := false
259	if VersionFlag.GetName() != "" {
260		eachName(VersionFlag.GetName(), func(name string) {
261			if c.GlobalBool(name) || c.Bool(name) {
262				found = true
263			}
264		})
265	}
266	return found
267}
268
269func checkHelp(c *Context) bool {
270	found := false
271	if HelpFlag.GetName() != "" {
272		eachName(HelpFlag.GetName(), func(name string) {
273			if c.GlobalBool(name) || c.Bool(name) {
274				found = true
275			}
276		})
277	}
278	return found
279}
280
281func checkCommandHelp(c *Context, name string) bool {
282	if c.Bool("h") || c.Bool("help") {
283		ShowCommandHelp(c, name)
284		return true
285	}
286
287	return false
288}
289
290func checkSubcommandHelp(c *Context) bool {
291	if c.Bool("h") || c.Bool("help") {
292		ShowSubcommandHelp(c)
293		return true
294	}
295
296	return false
297}
298
299func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
300	if !a.EnableBashCompletion {
301		return false, arguments
302	}
303
304	pos := len(arguments) - 1
305	lastArg := arguments[pos]
306
307	if lastArg != "--"+BashCompletionFlag.GetName() {
308		return false, arguments
309	}
310
311	return true, arguments[:pos]
312}
313
314func checkCompletions(c *Context) bool {
315	if !c.shellComplete {
316		return false
317	}
318
319	if args := c.Args(); args.Present() {
320		name := args.First()
321		if cmd := c.App.Command(name); cmd != nil {
322			// let the command handle the completion
323			return false
324		}
325	}
326
327	ShowCompletions(c)
328	return true
329}
330
331func checkCommandCompletions(c *Context, name string) bool {
332	if !c.shellComplete {
333		return false
334	}
335
336	ShowCommandCompletions(c, name)
337	return true
338}
339