1package 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}
224