github.com/klaytn/klaytn@v1.10.2/cmd/utils/customflags.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of go-ethereum. 4 // 5 // go-ethereum is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // go-ethereum is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from cmd/utils/customflags.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package utils 22 23 import ( 24 "encoding" 25 "errors" 26 "flag" 27 "fmt" 28 "math/big" 29 "os" 30 "os/user" 31 "path" 32 "strings" 33 "syscall" 34 35 "github.com/klaytn/klaytn/common/math" 36 "github.com/klaytn/klaytn/datasync/downloader" 37 "gopkg.in/urfave/cli.v1" 38 "gopkg.in/urfave/cli.v1/altsrc" 39 ) 40 41 // Custom type which is registered in the flags library which cli uses for 42 // argument parsing. This allows us to expand Value to an absolute path when 43 // the argument is parsed 44 type DirectoryString struct { 45 Value string 46 } 47 48 func (self *DirectoryString) String() string { 49 return self.Value 50 } 51 52 func (self *DirectoryString) Set(value string) error { 53 self.Value = expandPath(value) 54 return nil 55 } 56 57 type WrappedDirectoryFlag struct { 58 DirectoryFlag 59 set *flag.FlagSet 60 } 61 62 func NewWrappedDirectoryFlag(fl DirectoryFlag) *WrappedDirectoryFlag { 63 return &WrappedDirectoryFlag{DirectoryFlag: fl, set: nil} 64 } 65 66 func (f *WrappedDirectoryFlag) Apply(set *flag.FlagSet) { 67 f.set = set 68 f.DirectoryFlag.Apply(set) 69 } 70 71 func (f *WrappedDirectoryFlag) ApplyInputSourceValue(context *cli.Context, isc altsrc.InputSourceContext) error { 72 if f.set != nil { 73 if !isEnvVarSet(f.EnvVar) { 74 value, err := isc.String(f.DirectoryFlag.Name) 75 if err != nil { 76 return err 77 } 78 if value != "" { 79 eachName(f.Name, func(name string) { 80 f.set.Set(f.Name, value) 81 }) 82 } 83 } 84 } 85 return nil 86 } 87 88 func isEnvVarSet(envVars string) bool { 89 for _, envVar := range strings.Split(envVars, ",") { 90 envVar = strings.TrimSpace(envVar) 91 if env, ok := syscall.Getenv(envVar); ok { 92 // TODO: Can't use this for bools as 93 // set means that it was true or false based on 94 // Bool flag type, should work for other types 95 logger.Info("env", "env", env) 96 return true 97 } 98 } 99 return false 100 } 101 102 // Custom cli.Flag type which expand the received string to an absolute path. 103 // e.g. ~/.ethereum -> /home/username/.ethereum 104 type DirectoryFlag struct { 105 Name string 106 Value DirectoryString 107 Usage string 108 EnvVar string 109 } 110 111 func (self DirectoryFlag) String() string { 112 fmtString := "%s %v\t%v" 113 if len(self.Value.Value) > 0 { 114 fmtString = "%s \"%v\"\t%v" 115 } 116 return fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage) 117 } 118 119 func eachName(longName string, fn func(string)) { 120 parts := strings.Split(longName, ",") 121 for _, name := range parts { 122 name = strings.Trim(name, " ") 123 fn(name) 124 } 125 } 126 127 // called by cli library, grabs variable from environment (if in env) 128 // and adds variable to flag set for parsing. 129 func (self DirectoryFlag) Apply(set *flag.FlagSet) { 130 if self.EnvVar != "" { 131 if envVal, ok := syscall.Getenv(self.EnvVar); ok { 132 self.Value.Value = envVal 133 } 134 } 135 eachName(self.Name, func(name string) { 136 set.Var(&self.Value, self.Name, self.Usage) 137 }) 138 } 139 140 type TextMarshaler interface { 141 encoding.TextMarshaler 142 encoding.TextUnmarshaler 143 } 144 145 // textMarshalerVal turns a TextMarshaler into a flag.Value 146 type textMarshalerVal struct { 147 v TextMarshaler 148 } 149 150 func (v textMarshalerVal) String() string { 151 if v.v == nil { 152 return "" 153 } 154 text, _ := v.v.MarshalText() 155 return string(text) 156 } 157 158 func (v textMarshalerVal) Set(s string) error { 159 return v.v.UnmarshalText([]byte(s)) 160 } 161 162 // TextMarshalerFlag wraps a TextMarshaler value. 163 type TextMarshalerFlag struct { 164 Name string 165 Value TextMarshaler 166 Usage string 167 EnvVar string 168 } 169 170 func (f TextMarshalerFlag) GetName() string { 171 return f.Name 172 } 173 174 func (f TextMarshalerFlag) String() string { 175 return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage) 176 } 177 178 func (f TextMarshalerFlag) Apply(set *flag.FlagSet) { 179 if f.EnvVar != "" && f.Value != nil { 180 if envVal, ok := syscall.Getenv(f.EnvVar); ok { 181 var mode downloader.SyncMode 182 switch envVal { 183 case "full": 184 mode = downloader.FullSync 185 case "fast": 186 mode = downloader.FastSync 187 case "snap": 188 mode = downloader.SnapSync 189 case "light": 190 mode = downloader.LightSync 191 } 192 f.Value = &mode 193 } 194 } 195 eachName(f.Name, func(name string) { 196 set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) 197 }) 198 } 199 200 type WrappedTextMarshalerFlag struct { 201 TextMarshalerFlag 202 set *flag.FlagSet 203 } 204 205 func NewWrappedTextMarshalerFlag(fl TextMarshalerFlag) *WrappedTextMarshalerFlag { 206 return &WrappedTextMarshalerFlag{TextMarshalerFlag: fl, set: nil} 207 } 208 209 func (f *WrappedTextMarshalerFlag) Apply(set *flag.FlagSet) { 210 f.set = set 211 f.TextMarshalerFlag.Apply(set) 212 } 213 214 func (f *WrappedTextMarshalerFlag) ApplyInputSourceValue(context *cli.Context, isc altsrc.InputSourceContext) error { 215 if f.set != nil { 216 if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { 217 value, err := isc.String(f.TextMarshalerFlag.Name) 218 if err != nil { 219 return err 220 } 221 if value != "" { 222 eachName(f.Name, func(name string) { 223 f.set.Set(f.Name, value) 224 }) 225 } 226 } 227 } 228 return nil 229 } 230 231 // GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set. 232 func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { 233 val := ctx.GlobalGeneric(name) 234 if val == nil { 235 return nil 236 } 237 return val.(textMarshalerVal).v 238 } 239 240 // BigFlag is a command line flag that accepts 256 bit big integers in decimal or 241 // hexadecimal syntax. 242 type BigFlag struct { 243 Name string 244 Value *big.Int 245 Usage string 246 } 247 248 // bigValue turns *big.Int into a flag.Value 249 type bigValue big.Int 250 251 func (b *bigValue) String() string { 252 if b == nil { 253 return "" 254 } 255 return (*big.Int)(b).String() 256 } 257 258 func (b *bigValue) Set(s string) error { 259 int, ok := math.ParseBig256(s) 260 if !ok { 261 return errors.New("invalid integer syntax") 262 } 263 *b = (bigValue)(*int) 264 return nil 265 } 266 267 func (f BigFlag) GetName() string { 268 return f.Name 269 } 270 271 func (f BigFlag) String() string { 272 fmtString := "%s %v\t%v" 273 if f.Value != nil { 274 fmtString = "%s \"%v\"\t%v" 275 } 276 return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage) 277 } 278 279 func (f BigFlag) Apply(set *flag.FlagSet) { 280 eachName(f.Name, func(name string) { 281 set.Var((*bigValue)(f.Value), f.Name, f.Usage) 282 }) 283 } 284 285 // GlobalBig returns the value of a BigFlag from the global flag set. 286 func GlobalBig(ctx *cli.Context, name string) *big.Int { 287 val := ctx.GlobalGeneric(name) 288 if val == nil { 289 return nil 290 } 291 return (*big.Int)(val.(*bigValue)) 292 } 293 294 func prefixFor(name string) (prefix string) { 295 if len(name) == 1 { 296 prefix = "-" 297 } else { 298 prefix = "--" 299 } 300 301 return 302 } 303 304 func prefixedNames(fullName string) (prefixed string) { 305 parts := strings.Split(fullName, ",") 306 for i, name := range parts { 307 name = strings.Trim(name, " ") 308 prefixed += prefixFor(name) + name 309 if i < len(parts)-1 { 310 prefixed += ", " 311 } 312 } 313 return 314 } 315 316 func (self DirectoryFlag) GetName() string { 317 return self.Name 318 } 319 320 func (self *DirectoryFlag) Set(value string) { 321 self.Value.Value = value 322 } 323 324 // Expands a file path 325 // 1. replace tilde with users home dir 326 // 2. expands embedded environment variables 327 // 3. cleans the path, e.g. /a/b/../c -> /a/c 328 // Note, it has limitations, e.g. ~someuser/tmp will not be expanded 329 func expandPath(p string) string { 330 if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { 331 if home := homeDir(); home != "" { 332 p = home + p[1:] 333 } 334 } 335 return path.Clean(os.ExpandEnv(p)) 336 } 337 338 func homeDir() string { 339 if home := os.Getenv("HOME"); home != "" { 340 return home 341 } 342 if usr, err := user.Current(); err == nil { 343 return usr.HomeDir 344 } 345 return "" 346 }