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