github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/cmdflag/flag.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 cmdflag handles flag processing common to several go tools. 6 package cmdflag 7 8 import ( 9 "flag" 10 "fmt" 11 "os" 12 "strconv" 13 "strings" 14 15 "github.com/gagliardetto/golang-go/cmd/go/not-internal/base" 16 ) 17 18 // The flag handling part of go commands such as test is large and distracting. 19 // We can't use the standard flag package because some of the flags from 20 // our command line are for us, and some are for the binary we're running, 21 // and some are for both. 22 23 // Defn defines a flag we know about. 24 type Defn struct { 25 Name string // Name on command line. 26 BoolVar *bool // If it's a boolean flag, this points to it. 27 Value flag.Value // The flag.Value represented. 28 PassToTest bool // Pass to the test binary? Used only by go test. 29 Present bool // Flag has been seen. 30 } 31 32 // IsBool reports whether v is a bool flag. 33 func IsBool(v flag.Value) bool { 34 vv, ok := v.(interface { 35 IsBoolFlag() bool 36 }) 37 if ok { 38 return vv.IsBoolFlag() 39 } 40 return false 41 } 42 43 // SetBool sets the addressed boolean to the value. 44 func SetBool(cmd string, flag *bool, value string) { 45 x, err := strconv.ParseBool(value) 46 if err != nil { 47 SyntaxError(cmd, "illegal bool flag value "+value) 48 } 49 *flag = x 50 } 51 52 // SetInt sets the addressed integer to the value. 53 func SetInt(cmd string, flag *int, value string) { 54 x, err := strconv.Atoi(value) 55 if err != nil { 56 SyntaxError(cmd, "illegal int flag value "+value) 57 } 58 *flag = x 59 } 60 61 // SyntaxError reports an argument syntax error and exits the program. 62 func SyntaxError(cmd, msg string) { 63 fmt.Fprintf(os.Stderr, "go %s: %s\n", cmd, msg) 64 if cmd == "test" { 65 fmt.Fprintf(os.Stderr, `run "go help %s" or "go help testflag" for more information`+"\n", cmd) 66 } else { 67 fmt.Fprintf(os.Stderr, `run "go help %s" for more information`+"\n", cmd) 68 } 69 base.SetExitStatus(2) 70 base.Exit() 71 } 72 73 // AddKnownFlags registers the flags in defns with base.AddKnownFlag. 74 func AddKnownFlags(cmd string, defns []*Defn) { 75 for _, f := range defns { 76 base.AddKnownFlag(f.Name) 77 base.AddKnownFlag(cmd + "." + f.Name) 78 } 79 } 80 81 // Parse sees if argument i is present in the definitions and if so, 82 // returns its definition, value, and whether it consumed an extra word. 83 // If the flag begins (cmd.Name()+".") it is ignored for the purpose of this function. 84 func Parse(cmd string, usage func(), defns []*Defn, args []string, i int) (f *Defn, value string, extra bool) { 85 arg := args[i] 86 if strings.HasPrefix(arg, "--") { // reduce two minuses to one 87 arg = arg[1:] 88 } 89 switch arg { 90 case "-?", "-h", "-help": 91 usage() 92 } 93 if arg == "" || arg[0] != '-' { 94 return 95 } 96 name := arg[1:] 97 // If there's already a prefix such as "test.", drop it for now. 98 name = strings.TrimPrefix(name, cmd+".") 99 equals := strings.Index(name, "=") 100 if equals >= 0 { 101 value = name[equals+1:] 102 name = name[:equals] 103 } 104 for _, f = range defns { 105 if name == f.Name { 106 // Booleans are special because they have modes -x, -x=true, -x=false. 107 if f.BoolVar != nil || IsBool(f.Value) { 108 if equals < 0 { // Otherwise, it's been set and will be verified in SetBool. 109 value = "true" 110 } else { 111 // verify it parses 112 SetBool(cmd, new(bool), value) 113 } 114 } else { // Non-booleans must have a value. 115 extra = equals < 0 116 if extra { 117 if i+1 >= len(args) { 118 SyntaxError(cmd, "missing argument for flag "+f.Name) 119 } 120 value = args[i+1] 121 } 122 } 123 if f.Present { 124 SyntaxError(cmd, f.Name+" flag may be set only once") 125 } 126 f.Present = true 127 return 128 } 129 } 130 f = nil 131 return 132 } 133 134 // FindGOFLAGS extracts and returns the flags matching defns from GOFLAGS. 135 // Ideally the caller would mention that the flags were from GOFLAGS 136 // when reporting errors, but that's too hard for now. 137 func FindGOFLAGS(defns []*Defn) []string { 138 var flags []string 139 for _, flag := range base.GOFLAGS() { 140 // Flags returned by base.GOFLAGS are well-formed, one of: 141 // -x 142 // --x 143 // -x=value 144 // --x=value 145 if strings.HasPrefix(flag, "--") { 146 flag = flag[1:] 147 } 148 name := flag[1:] 149 if i := strings.Index(name, "="); i >= 0 { 150 name = name[:i] 151 } 152 for _, f := range defns { 153 if name == f.Name { 154 flags = append(flags, flag) 155 break 156 } 157 } 158 } 159 return flags 160 }