man.go 4.7 KB

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