github.com/imannamdari/v2ray-core/v5@v5.0.5/main/commands/base/help.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package base 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "io" 12 "os" 13 "strings" 14 "text/template" 15 "unicode" 16 "unicode/utf8" 17 ) 18 19 // Help implements the 'help' command. 20 func Help(w io.Writer, args []string) { 21 cmd := RootCommand 22 Args: 23 for i, arg := range args { 24 for _, sub := range cmd.Commands { 25 if sub.Name() == arg { 26 cmd = sub 27 continue Args 28 } 29 } 30 31 // helpSuccess is the help command using as many args as possible that would succeed. 32 helpSuccess := CommandEnv.Exec + " help" 33 if i > 0 { 34 helpSuccess += " " + strings.Join(args[:i], " ") 35 } 36 fmt.Fprintf(os.Stderr, "%s help %s: unknown help topic. Run '%s'.\n", CommandEnv.Exec, strings.Join(args, " "), helpSuccess) 37 SetExitStatus(2) // failed at 'v2ray help cmd' 38 Exit() 39 } 40 41 if len(cmd.Commands) > 0 { 42 PrintUsage(os.Stdout, cmd) 43 } else { 44 buildCommandText(cmd) 45 tmpl(os.Stdout, helpTemplate, makeTmplData(cmd)) 46 } 47 } 48 49 var usageTemplate = `{{.Long | trim}} 50 51 Usage: 52 53 {{.UsageLine}} <command> [arguments] 54 55 The commands are: 56 {{range .Commands}}{{if and (ne .Short "") (or (.Runnable) .Commands)}} 57 {{.Name | width $.CommandsWidth}} {{.Short}}{{end}}{{end}} 58 59 Use "{{.Exec}} help{{with .LongName}} {{.}}{{end}} <command>" for more information about a command. 60 {{if eq (.UsageLine) (.Exec)}} 61 Additional help topics: 62 {{range .Commands}}{{if and (not .Runnable) (not .Commands)}} 63 {{.Name | width $.CommandsWidth}} {{.Short}}{{end}}{{end}} 64 65 Use "{{.Exec}} help{{with .LongName}} {{.}}{{end}} <topic>" for more information about that topic. 66 {{end}} 67 ` 68 69 var helpTemplate = `{{if .Runnable}}usage: {{.UsageLine}} 70 71 {{end}}{{.Long | trim}} 72 ` 73 74 // An errWriter wraps a writer, recording whether a write error occurred. 75 type errWriter struct { 76 w io.Writer 77 err error 78 } 79 80 func (w *errWriter) Write(b []byte) (int, error) { 81 n, err := w.w.Write(b) 82 if err != nil { 83 w.err = err 84 } 85 return n, err 86 } 87 88 // tmpl executes the given template text on data, writing the result to w. 89 func tmpl(w io.Writer, text string, data interface{}) { 90 t := template.New("top") 91 t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize, "width": width}) 92 template.Must(t.Parse(text)) 93 ew := &errWriter{w: w} 94 err := t.Execute(ew, data) 95 if ew.err != nil { 96 // I/O error writing. Ignore write on closed pipe. 97 if strings.Contains(ew.err.Error(), "pipe") { 98 SetExitStatus(1) 99 Exit() 100 } 101 Fatalf("writing output: %v", ew.err) 102 } 103 if err != nil { 104 panic(err) 105 } 106 } 107 108 func capitalize(s string) string { 109 if s == "" { 110 return s 111 } 112 r, n := utf8.DecodeRuneInString(s) 113 return string(unicode.ToTitle(r)) + s[n:] 114 } 115 116 func width(width int, value string) string { 117 format := fmt.Sprintf("%%-%ds", width) 118 return fmt.Sprintf(format, value) 119 } 120 121 // PrintUsage prints usage of cmd to w 122 func PrintUsage(w io.Writer, cmd *Command) { 123 buildCommandText(cmd) 124 bw := bufio.NewWriter(w) 125 tmpl(bw, usageTemplate, makeTmplData(cmd)) 126 bw.Flush() 127 } 128 129 // buildCommandText build command text as template 130 func buildCommandText(cmd *Command) { 131 data := makeTmplData(cmd) 132 cmd.UsageLine = buildText(cmd.UsageLine, data) 133 // DO NOT SUPPORT ".Short": 134 // - It's not necessary 135 // - Or, we have to build text for all sub commands of current command, like "v2ray help api" 136 // cmd.Short = buildText(cmd.Short, data) 137 cmd.Long = buildText(cmd.Long, data) 138 } 139 140 func buildText(text string, data interface{}) string { 141 buf := bytes.NewBuffer([]byte{}) 142 text = strings.ReplaceAll(text, "\t", " ") 143 tmpl(buf, text, data) 144 return buf.String() 145 } 146 147 type tmplData struct { 148 *Command 149 *CommandEnvHolder 150 } 151 152 func makeTmplData(cmd *Command) tmplData { 153 // Minimum width of the command column 154 width := 12 155 for _, c := range cmd.Commands { 156 l := len(c.Name()) 157 if width < l { 158 width = l 159 } 160 } 161 CommandEnv.CommandsWidth = width 162 return tmplData{ 163 Command: cmd, 164 CommandEnvHolder: &CommandEnv, 165 } 166 }