github.com/ethereum/go-ethereum@v1.14.3/internal/flags/flags.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package flags
    18  
    19  import (
    20  	"encoding"
    21  	"errors"
    22  	"flag"
    23  	"fmt"
    24  	"math/big"
    25  	"os"
    26  	"os/user"
    27  	"path/filepath"
    28  	"strings"
    29  	"syscall"
    30  
    31  	"github.com/ethereum/go-ethereum/common/math"
    32  	"github.com/urfave/cli/v2"
    33  )
    34  
    35  // DirectoryString is custom type which is registered in the flags library which cli uses for
    36  // argument parsing. This allows us to expand Value to an absolute path when
    37  // the argument is parsed
    38  type DirectoryString string
    39  
    40  func (s *DirectoryString) String() string {
    41  	return string(*s)
    42  }
    43  
    44  func (s *DirectoryString) Set(value string) error {
    45  	*s = DirectoryString(expandPath(value))
    46  	return nil
    47  }
    48  
    49  var (
    50  	_ cli.Flag              = (*DirectoryFlag)(nil)
    51  	_ cli.RequiredFlag      = (*DirectoryFlag)(nil)
    52  	_ cli.VisibleFlag       = (*DirectoryFlag)(nil)
    53  	_ cli.DocGenerationFlag = (*DirectoryFlag)(nil)
    54  	_ cli.CategorizableFlag = (*DirectoryFlag)(nil)
    55  )
    56  
    57  // DirectoryFlag is custom cli.Flag type which expand the received string to an absolute path.
    58  // e.g. ~/.ethereum -> /home/username/.ethereum
    59  type DirectoryFlag struct {
    60  	Name string
    61  
    62  	Category    string
    63  	DefaultText string
    64  	Usage       string
    65  
    66  	Required   bool
    67  	Hidden     bool
    68  	HasBeenSet bool
    69  
    70  	Value DirectoryString
    71  
    72  	Aliases []string
    73  	EnvVars []string
    74  }
    75  
    76  // For cli.Flag:
    77  
    78  func (f *DirectoryFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
    79  func (f *DirectoryFlag) IsSet() bool     { return f.HasBeenSet }
    80  func (f *DirectoryFlag) String() string  { return cli.FlagStringer(f) }
    81  
    82  // Apply called by cli library, grabs variable from environment (if in env)
    83  // and adds variable to flag set for parsing.
    84  func (f *DirectoryFlag) Apply(set *flag.FlagSet) error {
    85  	for _, envVar := range f.EnvVars {
    86  		envVar = strings.TrimSpace(envVar)
    87  		if value, found := syscall.Getenv(envVar); found {
    88  			f.Value.Set(value)
    89  			f.HasBeenSet = true
    90  			break
    91  		}
    92  	}
    93  	eachName(f, func(name string) {
    94  		set.Var(&f.Value, f.Name, f.Usage)
    95  	})
    96  	return nil
    97  }
    98  
    99  // For cli.RequiredFlag:
   100  
   101  func (f *DirectoryFlag) IsRequired() bool { return f.Required }
   102  
   103  // For cli.VisibleFlag:
   104  
   105  func (f *DirectoryFlag) IsVisible() bool { return !f.Hidden }
   106  
   107  // For cli.CategorizableFlag:
   108  
   109  func (f *DirectoryFlag) GetCategory() string { return f.Category }
   110  
   111  // For cli.DocGenerationFlag:
   112  
   113  func (f *DirectoryFlag) TakesValue() bool     { return true }
   114  func (f *DirectoryFlag) GetUsage() string     { return f.Usage }
   115  func (f *DirectoryFlag) GetValue() string     { return f.Value.String() }
   116  func (f *DirectoryFlag) GetEnvVars() []string { return f.EnvVars }
   117  
   118  func (f *DirectoryFlag) GetDefaultText() string {
   119  	if f.DefaultText != "" {
   120  		return f.DefaultText
   121  	}
   122  	return f.GetValue()
   123  }
   124  
   125  type TextMarshaler interface {
   126  	encoding.TextMarshaler
   127  	encoding.TextUnmarshaler
   128  }
   129  
   130  // textMarshalerVal turns a TextMarshaler into a flag.Value
   131  type textMarshalerVal struct {
   132  	v TextMarshaler
   133  }
   134  
   135  func (v textMarshalerVal) String() string {
   136  	if v.v == nil {
   137  		return ""
   138  	}
   139  	text, _ := v.v.MarshalText()
   140  	return string(text)
   141  }
   142  
   143  func (v textMarshalerVal) Set(s string) error {
   144  	return v.v.UnmarshalText([]byte(s))
   145  }
   146  
   147  var (
   148  	_ cli.Flag              = (*TextMarshalerFlag)(nil)
   149  	_ cli.RequiredFlag      = (*TextMarshalerFlag)(nil)
   150  	_ cli.VisibleFlag       = (*TextMarshalerFlag)(nil)
   151  	_ cli.DocGenerationFlag = (*TextMarshalerFlag)(nil)
   152  	_ cli.CategorizableFlag = (*TextMarshalerFlag)(nil)
   153  )
   154  
   155  // TextMarshalerFlag wraps a TextMarshaler value.
   156  type TextMarshalerFlag struct {
   157  	Name string
   158  
   159  	Category    string
   160  	DefaultText string
   161  	Usage       string
   162  
   163  	Required   bool
   164  	Hidden     bool
   165  	HasBeenSet bool
   166  
   167  	Value TextMarshaler
   168  
   169  	Aliases []string
   170  	EnvVars []string
   171  }
   172  
   173  // For cli.Flag:
   174  
   175  func (f *TextMarshalerFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
   176  func (f *TextMarshalerFlag) IsSet() bool     { return f.HasBeenSet }
   177  func (f *TextMarshalerFlag) String() string  { return cli.FlagStringer(f) }
   178  
   179  func (f *TextMarshalerFlag) Apply(set *flag.FlagSet) error {
   180  	for _, envVar := range f.EnvVars {
   181  		envVar = strings.TrimSpace(envVar)
   182  		if value, found := syscall.Getenv(envVar); found {
   183  			if err := f.Value.UnmarshalText([]byte(value)); err != nil {
   184  				return fmt.Errorf("could not parse %q from environment variable %q for flag %s: %s", value, envVar, f.Name, err)
   185  			}
   186  			f.HasBeenSet = true
   187  			break
   188  		}
   189  	}
   190  	eachName(f, func(name string) {
   191  		set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage)
   192  	})
   193  	return nil
   194  }
   195  
   196  // For cli.RequiredFlag:
   197  
   198  func (f *TextMarshalerFlag) IsRequired() bool { return f.Required }
   199  
   200  // For cli.VisibleFlag:
   201  
   202  func (f *TextMarshalerFlag) IsVisible() bool { return !f.Hidden }
   203  
   204  // For cli.CategorizableFlag:
   205  
   206  func (f *TextMarshalerFlag) GetCategory() string { return f.Category }
   207  
   208  // For cli.DocGenerationFlag:
   209  
   210  func (f *TextMarshalerFlag) TakesValue() bool     { return true }
   211  func (f *TextMarshalerFlag) GetUsage() string     { return f.Usage }
   212  func (f *TextMarshalerFlag) GetEnvVars() []string { return f.EnvVars }
   213  
   214  func (f *TextMarshalerFlag) GetValue() string {
   215  	t, err := f.Value.MarshalText()
   216  	if err != nil {
   217  		return "(ERR: " + err.Error() + ")"
   218  	}
   219  	return string(t)
   220  }
   221  
   222  func (f *TextMarshalerFlag) GetDefaultText() string {
   223  	if f.DefaultText != "" {
   224  		return f.DefaultText
   225  	}
   226  	return f.GetValue()
   227  }
   228  
   229  // GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set.
   230  func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler {
   231  	val := ctx.Generic(name)
   232  	if val == nil {
   233  		return nil
   234  	}
   235  	return val.(textMarshalerVal).v
   236  }
   237  
   238  var (
   239  	_ cli.Flag              = (*BigFlag)(nil)
   240  	_ cli.RequiredFlag      = (*BigFlag)(nil)
   241  	_ cli.VisibleFlag       = (*BigFlag)(nil)
   242  	_ cli.DocGenerationFlag = (*BigFlag)(nil)
   243  	_ cli.CategorizableFlag = (*BigFlag)(nil)
   244  )
   245  
   246  // BigFlag is a command line flag that accepts 256 bit big integers in decimal or
   247  // hexadecimal syntax.
   248  type BigFlag struct {
   249  	Name string
   250  
   251  	Category    string
   252  	DefaultText string
   253  	Usage       string
   254  
   255  	Required   bool
   256  	Hidden     bool
   257  	HasBeenSet bool
   258  
   259  	Value        *big.Int
   260  	defaultValue *big.Int
   261  
   262  	Aliases []string
   263  	EnvVars []string
   264  }
   265  
   266  // For cli.Flag:
   267  
   268  func (f *BigFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
   269  func (f *BigFlag) IsSet() bool     { return f.HasBeenSet }
   270  func (f *BigFlag) String() string  { return cli.FlagStringer(f) }
   271  
   272  func (f *BigFlag) Apply(set *flag.FlagSet) error {
   273  	// Set default value so that environment wont be able to overwrite it
   274  	if f.Value != nil {
   275  		f.defaultValue = new(big.Int).Set(f.Value)
   276  	}
   277  	for _, envVar := range f.EnvVars {
   278  		envVar = strings.TrimSpace(envVar)
   279  		if value, found := syscall.Getenv(envVar); found {
   280  			if _, ok := f.Value.SetString(value, 10); !ok {
   281  				return fmt.Errorf("could not parse %q from environment variable %q for flag %s", value, envVar, f.Name)
   282  			}
   283  			f.HasBeenSet = true
   284  			break
   285  		}
   286  	}
   287  	eachName(f, func(name string) {
   288  		f.Value = new(big.Int)
   289  		set.Var((*bigValue)(f.Value), f.Name, f.Usage)
   290  	})
   291  	return nil
   292  }
   293  
   294  // For cli.RequiredFlag:
   295  
   296  func (f *BigFlag) IsRequired() bool { return f.Required }
   297  
   298  // For cli.VisibleFlag:
   299  
   300  func (f *BigFlag) IsVisible() bool { return !f.Hidden }
   301  
   302  // For cli.CategorizableFlag:
   303  
   304  func (f *BigFlag) GetCategory() string { return f.Category }
   305  
   306  // For cli.DocGenerationFlag:
   307  
   308  func (f *BigFlag) TakesValue() bool     { return true }
   309  func (f *BigFlag) GetUsage() string     { return f.Usage }
   310  func (f *BigFlag) GetValue() string     { return f.Value.String() }
   311  func (f *BigFlag) GetEnvVars() []string { return f.EnvVars }
   312  
   313  func (f *BigFlag) GetDefaultText() string {
   314  	if f.DefaultText != "" {
   315  		return f.DefaultText
   316  	}
   317  	return f.defaultValue.String()
   318  }
   319  
   320  // bigValue turns *big.Int into a flag.Value
   321  type bigValue big.Int
   322  
   323  func (b *bigValue) String() string {
   324  	if b == nil {
   325  		return ""
   326  	}
   327  	return (*big.Int)(b).String()
   328  }
   329  
   330  func (b *bigValue) Set(s string) error {
   331  	intVal, ok := math.ParseBig256(s)
   332  	if !ok {
   333  		return errors.New("invalid integer syntax")
   334  	}
   335  	*b = (bigValue)(*intVal)
   336  	return nil
   337  }
   338  
   339  // GlobalBig returns the value of a BigFlag from the global flag set.
   340  func GlobalBig(ctx *cli.Context, name string) *big.Int {
   341  	val := ctx.Generic(name)
   342  	if val == nil {
   343  		return nil
   344  	}
   345  	return (*big.Int)(val.(*bigValue))
   346  }
   347  
   348  // Expands a file path
   349  // 1. replace tilde with users home dir
   350  // 2. expands embedded environment variables
   351  // 3. cleans the path, e.g. /a/b/../c -> /a/c
   352  // Note, it has limitations, e.g. ~someuser/tmp will not be expanded
   353  func expandPath(p string) string {
   354  	// Named pipes are not file paths on windows, ignore
   355  	if strings.HasPrefix(p, `\\.\pipe`) {
   356  		return p
   357  	}
   358  	if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
   359  		if home := HomeDir(); home != "" {
   360  			p = home + p[1:]
   361  		}
   362  	}
   363  	return filepath.Clean(os.ExpandEnv(p))
   364  }
   365  
   366  func HomeDir() string {
   367  	if home := os.Getenv("HOME"); home != "" {
   368  		return home
   369  	}
   370  	if usr, err := user.Current(); err == nil {
   371  		return usr.HomeDir
   372  	}
   373  	return ""
   374  }
   375  
   376  func eachName(f cli.Flag, fn func(string)) {
   377  	for _, name := range f.Names() {
   378  		name = strings.Trim(name, " ")
   379  		fn(name)
   380  	}
   381  }