github.com/btwiuse/jiri@v0.0.0-20191125065820-53353bcfef54/cmdline/env.go (about) 1 // Copyright 2015 The Vanadium 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 cmdline 6 7 import ( 8 "fmt" 9 "io" 10 "os" 11 "strconv" 12 13 "github.com/btwiuse/jiri/envvar" 14 "github.com/btwiuse/jiri/lookpath" 15 "github.com/btwiuse/jiri/textutil" 16 "github.com/btwiuse/jiri/timing" 17 ) 18 19 // EnvFromOS returns a new environment based on the operating system. 20 func EnvFromOS() *Env { 21 return &Env{ 22 Stdin: os.Stdin, 23 Stdout: os.Stdout, 24 Stderr: os.Stderr, 25 Vars: envvar.SliceToMap(os.Environ()), 26 Timer: timing.NewTimer("root"), 27 } 28 } 29 30 // Env represents the environment for command parsing and running. Typically 31 // EnvFromOS is used to produce a default environment. The environment may be 32 // explicitly set for finer control; e.g. in tests. 33 type Env struct { 34 Stdin io.Reader 35 Stdout io.Writer 36 Stderr io.Writer 37 Vars map[string]string // Environment variables 38 Timer *timing.Timer 39 40 // Usage is a function that prints usage information to w. Typically set by 41 // calls to Main or Parse to print usage of the leaf command. 42 Usage func(env *Env, w io.Writer) 43 44 // Comamnd name 45 CommandName string 46 // command flags 47 CommandFlags map[string]string 48 } 49 50 func (e *Env) clone() *Env { 51 return &Env{ 52 Stdin: e.Stdin, 53 Stdout: e.Stdout, 54 Stderr: e.Stderr, 55 Vars: envvar.CopyMap(e.Vars), 56 Usage: e.Usage, 57 Timer: e.Timer, // use the same timer for all operations 58 } 59 } 60 61 // UsageErrorf prints the error message represented by the printf-style format 62 // and args, followed by the output of the Usage function. Returns ErrUsage to 63 // make it easy to use from within the Runner.Run function. 64 func (e *Env) UsageErrorf(format string, args ...interface{}) error { 65 return usageErrorf(e, e.Usage, format, args...) 66 } 67 68 // TimerPush calls e.Timer.Push(name), only if the Timer is non-nil. 69 func (e *Env) TimerPush(name string) { 70 if e.Timer != nil { 71 e.Timer.Push(name) 72 } 73 } 74 75 // TimerPop calls e.Timer.Pop(), only if the Timer is non-nil. 76 func (e *Env) TimerPop() { 77 if e.Timer != nil { 78 e.Timer.Pop() 79 } 80 } 81 82 // LookPath returns the absolute path of the executable with the given name, 83 // based on the directories in PATH. Calls lookpath.Look. 84 func (e *Env) LookPath(name string) (string, error) { 85 e.TimerPush("lookpath " + name) 86 defer e.TimerPop() 87 return lookpath.Look(e.Vars, name) 88 } 89 90 // LookPathPrefix returns the absolute paths of all executables with the given 91 // name prefix, based on the directories in PATH. Calls lookpath.LookPrefix. 92 func (e *Env) LookPathPrefix(prefix string, names map[string]bool) ([]string, error) { 93 e.TimerPush("lookpathprefix " + prefix) 94 defer e.TimerPop() 95 return lookpath.LookPrefix(e.Vars, prefix, names) 96 } 97 98 func usageErrorf(env *Env, usage func(*Env, io.Writer), format string, args ...interface{}) error { 99 fmt.Fprint(env.Stderr, "ERROR: ") 100 fmt.Fprintf(env.Stderr, format, args...) 101 fmt.Fprint(env.Stderr, "\n\n") 102 if usage != nil { 103 usage(env, env.Stderr) 104 } else { 105 fmt.Fprint(env.Stderr, "usage error\n") 106 } 107 return ErrUsage 108 } 109 110 // defaultWidth is a reasonable default for the output width in runes. 111 const defaultWidth = 80 112 113 func (e *Env) width() int { 114 if width, err := strconv.Atoi(e.Vars["CMDLINE_WIDTH"]); err == nil && width != 0 { 115 return width 116 } 117 if _, width, err := textutil.TerminalSize(); err == nil && width != 0 { 118 return width 119 } 120 return defaultWidth 121 } 122 123 func (e *Env) style() style { 124 style := styleCompact 125 style.Set(e.Vars["CMDLINE_STYLE"]) 126 return style 127 } 128 129 func (e *Env) prefix() string { 130 return e.Vars["CMDLINE_PREFIX"] 131 } 132 133 func (e *Env) firstCall() bool { 134 return e.Vars["CMDLINE_FIRST_CALL"] == "" 135 } 136 137 // style describes the formatting style for usage descriptions. 138 type style int 139 140 const ( 141 styleCompact style = iota // Default style, good for compact cmdline output. 142 styleFull // Similar to compact but shows all global flags. 143 styleGoDoc // Good for godoc processing. 144 styleShortOnly // Only output short description. 145 ) 146 147 func (s *style) String() string { 148 switch *s { 149 case styleCompact: 150 return "compact" 151 case styleFull: 152 return "full" 153 case styleGoDoc: 154 return "godoc" 155 case styleShortOnly: 156 return "shortonly" 157 default: 158 panic(fmt.Errorf("unhandled style %d", *s)) 159 } 160 } 161 162 // Set implements the flag.Value interface method. 163 func (s *style) Set(value string) error { 164 switch value { 165 case "compact": 166 *s = styleCompact 167 case "full": 168 *s = styleFull 169 case "godoc": 170 *s = styleGoDoc 171 case "shortonly": 172 *s = styleShortOnly 173 default: 174 return fmt.Errorf("unknown style %q", value) 175 } 176 return nil 177 }