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