github.com/ethereum/go-ethereum@v1.16.1/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  	"errors"
    21  	"flag"
    22  	"fmt"
    23  	"math/big"
    24  	"os"
    25  	"os/user"
    26  	"path/filepath"
    27  	"strings"
    28  	"syscall"
    29  
    30  	"github.com/ethereum/go-ethereum/common/math"
    31  	"github.com/urfave/cli/v2"
    32  )
    33  
    34  // DirectoryString is custom type which is registered in the flags library which cli uses for
    35  // argument parsing. This allows us to expand Value to an absolute path when
    36  // the argument is parsed
    37  type DirectoryString string
    38  
    39  func (s *DirectoryString) String() string {
    40  	return string(*s)
    41  }
    42  
    43  func (s *DirectoryString) Set(value string) error {
    44  	*s = DirectoryString(expandPath(value))
    45  	return nil
    46  }
    47  
    48  var (
    49  	_ cli.Flag              = (*DirectoryFlag)(nil)
    50  	_ cli.RequiredFlag      = (*DirectoryFlag)(nil)
    51  	_ cli.VisibleFlag       = (*DirectoryFlag)(nil)
    52  	_ cli.DocGenerationFlag = (*DirectoryFlag)(nil)
    53  	_ cli.CategorizableFlag = (*DirectoryFlag)(nil)
    54  )
    55  
    56  // DirectoryFlag is custom cli.Flag type which expand the received string to an absolute path.
    57  // e.g. ~/.ethereum -> /home/username/.ethereum
    58  type DirectoryFlag struct {
    59  	Name string
    60  
    61  	Category    string
    62  	DefaultText string
    63  	Usage       string
    64  
    65  	Required   bool
    66  	Hidden     bool
    67  	HasBeenSet bool
    68  
    69  	Value DirectoryString
    70  
    71  	Aliases []string
    72  	EnvVars []string
    73  }
    74  
    75  // For cli.Flag:
    76  
    77  func (f *DirectoryFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
    78  func (f *DirectoryFlag) IsSet() bool     { return f.HasBeenSet }
    79  func (f *DirectoryFlag) String() string  { return cli.FlagStringer(f) }
    80  
    81  // Apply called by cli library, grabs variable from environment (if in env)
    82  // and adds variable to flag set for parsing.
    83  func (f *DirectoryFlag) Apply(set *flag.FlagSet) error {
    84  	for _, envVar := range f.EnvVars {
    85  		envVar = strings.TrimSpace(envVar)
    86  		if value, found := syscall.Getenv(envVar); found {
    87  			f.Value.Set(value)
    88  			f.HasBeenSet = true
    89  			break
    90  		}
    91  	}
    92  	eachName(f, func(name string) {
    93  		set.Var(&f.Value, name, f.Usage)
    94  	})
    95  	return nil
    96  }
    97  
    98  // For cli.RequiredFlag:
    99  
   100  func (f *DirectoryFlag) IsRequired() bool { return f.Required }
   101  
   102  // For cli.VisibleFlag:
   103  
   104  func (f *DirectoryFlag) IsVisible() bool { return !f.Hidden }
   105  
   106  // For cli.CategorizableFlag:
   107  
   108  func (f *DirectoryFlag) GetCategory() string { return f.Category }
   109  
   110  // For cli.DocGenerationFlag:
   111  
   112  func (f *DirectoryFlag) TakesValue() bool     { return true }
   113  func (f *DirectoryFlag) GetUsage() string     { return f.Usage }
   114  func (f *DirectoryFlag) GetValue() string     { return f.Value.String() }
   115  func (f *DirectoryFlag) GetEnvVars() []string { return f.EnvVars }
   116  
   117  func (f *DirectoryFlag) GetDefaultText() string {
   118  	if f.DefaultText != "" {
   119  		return f.DefaultText
   120  	}
   121  	return f.GetValue()
   122  }
   123  
   124  var (
   125  	_ cli.Flag              = (*BigFlag)(nil)
   126  	_ cli.RequiredFlag      = (*BigFlag)(nil)
   127  	_ cli.VisibleFlag       = (*BigFlag)(nil)
   128  	_ cli.DocGenerationFlag = (*BigFlag)(nil)
   129  	_ cli.CategorizableFlag = (*BigFlag)(nil)
   130  )
   131  
   132  // BigFlag is a command line flag that accepts 256 bit big integers in decimal or
   133  // hexadecimal syntax.
   134  type BigFlag struct {
   135  	Name string
   136  
   137  	Category    string
   138  	DefaultText string
   139  	Usage       string
   140  
   141  	Required   bool
   142  	Hidden     bool
   143  	HasBeenSet bool
   144  
   145  	Value        *big.Int
   146  	defaultValue *big.Int
   147  
   148  	Aliases []string
   149  	EnvVars []string
   150  }
   151  
   152  // For cli.Flag:
   153  
   154  func (f *BigFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
   155  func (f *BigFlag) IsSet() bool     { return f.HasBeenSet }
   156  func (f *BigFlag) String() string  { return cli.FlagStringer(f) }
   157  
   158  func (f *BigFlag) Apply(set *flag.FlagSet) error {
   159  	// Set default value so that environment wont be able to overwrite it
   160  	if f.Value != nil {
   161  		f.defaultValue = new(big.Int).Set(f.Value)
   162  	}
   163  	for _, envVar := range f.EnvVars {
   164  		envVar = strings.TrimSpace(envVar)
   165  		if value, found := syscall.Getenv(envVar); found {
   166  			if _, ok := f.Value.SetString(value, 10); !ok {
   167  				return fmt.Errorf("could not parse %q from environment variable %q for flag %s", value, envVar, f.Name)
   168  			}
   169  			f.HasBeenSet = true
   170  			break
   171  		}
   172  	}
   173  	eachName(f, func(name string) {
   174  		f.Value = new(big.Int)
   175  		set.Var((*bigValue)(f.Value), name, f.Usage)
   176  	})
   177  	return nil
   178  }
   179  
   180  // For cli.RequiredFlag:
   181  
   182  func (f *BigFlag) IsRequired() bool { return f.Required }
   183  
   184  // For cli.VisibleFlag:
   185  
   186  func (f *BigFlag) IsVisible() bool { return !f.Hidden }
   187  
   188  // For cli.CategorizableFlag:
   189  
   190  func (f *BigFlag) GetCategory() string { return f.Category }
   191  
   192  // For cli.DocGenerationFlag:
   193  
   194  func (f *BigFlag) TakesValue() bool     { return true }
   195  func (f *BigFlag) GetUsage() string     { return f.Usage }
   196  func (f *BigFlag) GetValue() string     { return f.Value.String() }
   197  func (f *BigFlag) GetEnvVars() []string { return f.EnvVars }
   198  
   199  func (f *BigFlag) GetDefaultText() string {
   200  	if f.DefaultText != "" {
   201  		return f.DefaultText
   202  	}
   203  	return f.defaultValue.String()
   204  }
   205  
   206  // bigValue turns *big.Int into a flag.Value
   207  type bigValue big.Int
   208  
   209  func (b *bigValue) String() string {
   210  	if b == nil {
   211  		return ""
   212  	}
   213  	return (*big.Int)(b).String()
   214  }
   215  
   216  func (b *bigValue) Set(s string) error {
   217  	intVal, ok := math.ParseBig256(s)
   218  	if !ok {
   219  		return errors.New("invalid integer syntax")
   220  	}
   221  	*b = (bigValue)(*intVal)
   222  	return nil
   223  }
   224  
   225  // GlobalBig returns the value of a BigFlag from the global flag set.
   226  func GlobalBig(ctx *cli.Context, name string) *big.Int {
   227  	val := ctx.Generic(name)
   228  	if val == nil {
   229  		return nil
   230  	}
   231  	return (*big.Int)(val.(*bigValue))
   232  }
   233  
   234  // Expands a file path
   235  // 1. replace tilde with users home dir
   236  // 2. expands embedded environment variables
   237  // 3. cleans the path, e.g. /a/b/../c -> /a/c
   238  // Note, it has limitations, e.g. ~someuser/tmp will not be expanded
   239  func expandPath(p string) string {
   240  	// Named pipes are not file paths on windows, ignore
   241  	if strings.HasPrefix(p, `\\.\pipe`) {
   242  		return p
   243  	}
   244  	if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
   245  		if home := HomeDir(); home != "" {
   246  			p = home + p[1:]
   247  		}
   248  	}
   249  	return filepath.Clean(os.ExpandEnv(p))
   250  }
   251  
   252  func HomeDir() string {
   253  	if home := os.Getenv("HOME"); home != "" {
   254  		return home
   255  	}
   256  	if usr, err := user.Current(); err == nil {
   257  		return usr.HomeDir
   258  	}
   259  	return ""
   260  }
   261  
   262  func eachName(f cli.Flag, fn func(string)) {
   263  	for _, name := range f.Names() {
   264  		name = strings.Trim(name, " ")
   265  		fn(name)
   266  	}
   267  }