1package flags
2
3import (
4	"fmt"
5	"io"
6	"runtime"
7	"strings"
8	"time"
9)
10
11func manQuote(s string) string {
12	return strings.Replace(s, "\\", "\\\\", -1)
13}
14
15func formatForMan(wr io.Writer, s string) {
16	for {
17		idx := strings.IndexRune(s, '`')
18
19		if idx < 0 {
20			fmt.Fprintf(wr, "%s", manQuote(s))
21			break
22		}
23
24		fmt.Fprintf(wr, "%s", manQuote(s[:idx]))
25
26		s = s[idx+1:]
27		idx = strings.IndexRune(s, '\'')
28
29		if idx < 0 {
30			fmt.Fprintf(wr, "%s", manQuote(s))
31			break
32		}
33
34		fmt.Fprintf(wr, "\\fB%s\\fP", manQuote(s[:idx]))
35		s = s[idx+1:]
36	}
37}
38
39func writeManPageOptions(wr io.Writer, grp *Group) {
40	grp.eachGroup(func(group *Group) {
41		if group.Hidden || len(group.options) == 0 {
42			return
43		}
44
45		// If the parent (grp) has any subgroups, display their descriptions as
46		// subsection headers similar to the output of --help.
47		if group.ShortDescription != "" && len(grp.groups) > 0 {
48			fmt.Fprintf(wr, ".SS %s\n", group.ShortDescription)
49
50			if group.LongDescription != "" {
51				formatForMan(wr, group.LongDescription)
52				fmt.Fprintln(wr, "")
53			}
54		}
55
56		for _, opt := range group.options {
57			if !opt.canCli() || opt.Hidden {
58				continue
59			}
60
61			fmt.Fprintln(wr, ".TP")
62			fmt.Fprintf(wr, "\\fB")
63
64			if opt.ShortName != 0 {
65				fmt.Fprintf(wr, "\\fB\\-%c\\fR", opt.ShortName)
66			}
67
68			if len(opt.LongName) != 0 {
69				if opt.ShortName != 0 {
70					fmt.Fprintf(wr, ", ")
71				}
72
73				fmt.Fprintf(wr, "\\fB\\-\\-%s\\fR", manQuote(opt.LongNameWithNamespace()))
74			}
75
76			if len(opt.ValueName) != 0 || opt.OptionalArgument {
77				if opt.OptionalArgument {
78					fmt.Fprintf(wr, " [\\fI%s=%s\\fR]", manQuote(opt.ValueName), manQuote(strings.Join(quoteV(opt.OptionalValue), ", ")))
79				} else {
80					fmt.Fprintf(wr, " \\fI%s\\fR", manQuote(opt.ValueName))
81				}
82			}
83
84			if len(opt.Default) != 0 {
85				fmt.Fprintf(wr, " <default: \\fI%s\\fR>", manQuote(strings.Join(quoteV(opt.Default), ", ")))
86			} else if len(opt.EnvDefaultKey) != 0 {
87				if runtime.GOOS == "windows" {
88					fmt.Fprintf(wr, " <default: \\fI%%%s%%\\fR>", manQuote(opt.EnvDefaultKey))
89				} else {
90					fmt.Fprintf(wr, " <default: \\fI$%s\\fR>", manQuote(opt.EnvDefaultKey))
91				}
92			}
93
94			if opt.Required {
95				fmt.Fprintf(wr, " (\\fIrequired\\fR)")
96			}
97
98			fmt.Fprintln(wr, "\\fP")
99
100			if len(opt.Description) != 0 {
101				formatForMan(wr, opt.Description)
102				fmt.Fprintln(wr, "")
103			}
104		}
105	})
106}
107
108func writeManPageSubcommands(wr io.Writer, name string, root *Command) {
109	commands := root.sortedVisibleCommands()
110
111	for _, c := range commands {
112		var nn string
113
114		if c.Hidden {
115			continue
116		}
117
118		if len(name) != 0 {
119			nn = name + " " + c.Name
120		} else {
121			nn = c.Name
122		}
123
124		writeManPageCommand(wr, nn, root, c)
125	}
126}
127
128func writeManPageCommand(wr io.Writer, name string, root *Command, command *Command) {
129	fmt.Fprintf(wr, ".SS %s\n", name)
130	fmt.Fprintln(wr, command.ShortDescription)
131
132	if len(command.LongDescription) > 0 {
133		fmt.Fprintln(wr, "")
134
135		cmdstart := fmt.Sprintf("The %s command", manQuote(command.Name))
136
137		if strings.HasPrefix(command.LongDescription, cmdstart) {
138			fmt.Fprintf(wr, "The \\fI%s\\fP command", manQuote(command.Name))
139
140			formatForMan(wr, command.LongDescription[len(cmdstart):])
141			fmt.Fprintln(wr, "")
142		} else {
143			formatForMan(wr, command.LongDescription)
144			fmt.Fprintln(wr, "")
145		}
146	}
147
148	var usage string
149	if us, ok := command.data.(Usage); ok {
150		usage = us.Usage()
151	} else if command.hasCliOptions() {
152		usage = fmt.Sprintf("[%s-OPTIONS]", command.Name)
153	}
154
155	var pre string
156	if root.hasCliOptions() {
157		pre = fmt.Sprintf("%s [OPTIONS] %s", root.Name, command.Name)
158	} else {
159		pre = fmt.Sprintf("%s %s", root.Name, command.Name)
160	}
161
162	if len(usage) > 0 {
163		fmt.Fprintf(wr, "\n\\fBUsage\\fP: %s %s\n.TP\n", manQuote(pre), manQuote(usage))
164	}
165
166	if len(command.Aliases) > 0 {
167		fmt.Fprintf(wr, "\n\\fBAliases\\fP: %s\n\n", manQuote(strings.Join(command.Aliases, ", ")))
168	}
169
170	writeManPageOptions(wr, command.Group)
171	writeManPageSubcommands(wr, name, command)
172}
173
174// WriteManPage writes a basic man page in groff format to the specified
175// writer.
176func (p *Parser) WriteManPage(wr io.Writer) {
177	t := time.Now()
178
179	fmt.Fprintf(wr, ".TH %s 1 \"%s\"\n", manQuote(p.Name), t.Format("2 January 2006"))
180	fmt.Fprintln(wr, ".SH NAME")
181	fmt.Fprintf(wr, "%s \\- %s\n", manQuote(p.Name), manQuote(p.ShortDescription))
182	fmt.Fprintln(wr, ".SH SYNOPSIS")
183
184	usage := p.Usage
185
186	if len(usage) == 0 {
187		usage = "[OPTIONS]"
188	}
189
190	fmt.Fprintf(wr, "\\fB%s\\fP %s\n", manQuote(p.Name), manQuote(usage))
191	fmt.Fprintln(wr, ".SH DESCRIPTION")
192
193	formatForMan(wr, p.LongDescription)
194	fmt.Fprintln(wr, "")
195
196	fmt.Fprintln(wr, ".SH OPTIONS")
197
198	writeManPageOptions(wr, p.Command.Group)
199
200	if len(p.visibleCommands()) > 0 {
201		fmt.Fprintln(wr, ".SH COMMANDS")
202
203		writeManPageSubcommands(wr, "", p.Command)
204	}
205}
206