blob: 0cb114e7482ff015ec84bd8b2551a4e5005eedb3 [file] [log] [blame]
Scott Bakerbdb962b2020-04-03 10:53:36 -07001package 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}