Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/internal/objabi/flag.go

Documentation: cmd/internal/objabi

     1  // Copyright 2015 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 objabi
     6  
     7  import (
     8  	"bytes"
     9  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"log"
    14  	"os"
    15  	"strconv"
    16  	"strings"
    17  )
    18  
    19  func Flagcount(name, usage string, val *int) {
    20  	flag.Var((*count)(val), name, usage)
    21  }
    22  
    23  func Flagfn1(name, usage string, f func(string)) {
    24  	flag.Var(fn1(f), name, usage)
    25  }
    26  
    27  func Flagprint(w io.Writer) {
    28  	flag.CommandLine.SetOutput(w)
    29  	flag.PrintDefaults()
    30  }
    31  
    32  func Flagparse(usage func()) {
    33  	flag.Usage = usage
    34  	os.Args = expandArgs(os.Args)
    35  	flag.Parse()
    36  }
    37  
    38  // expandArgs expands "response files" arguments in the provided slice.
    39  //
    40  // A "response file" argument starts with '@' and the rest of that
    41  // argument is a filename with CR-or-CRLF-separated arguments. Each
    42  // argument in the named files can also contain response file
    43  // arguments. See Issue 18468.
    44  //
    45  // The returned slice 'out' aliases 'in' iff the input did not contain
    46  // any response file arguments.
    47  //
    48  // TODO: handle relative paths of recursive expansions in different directories?
    49  // Is there a spec for this? Are relative paths allowed?
    50  func expandArgs(in []string) (out []string) {
    51  	// out is nil until we see a "@" argument.
    52  	for i, s := range in {
    53  		if strings.HasPrefix(s, "@") {
    54  			if out == nil {
    55  				out = make([]string, 0, len(in)*2)
    56  				out = append(out, in[:i]...)
    57  			}
    58  			slurp, err := ioutil.ReadFile(s[1:])
    59  			if err != nil {
    60  				log.Fatal(err)
    61  			}
    62  			args := strings.Split(strings.TrimSpace(strings.Replace(string(slurp), "\r", "", -1)), "\n")
    63  			for i, arg := range args {
    64  				args[i] = DecodeArg(arg)
    65  			}
    66  			out = append(out, expandArgs(args)...)
    67  		} else if out != nil {
    68  			out = append(out, s)
    69  		}
    70  	}
    71  	if out == nil {
    72  		return in
    73  	}
    74  	return
    75  }
    76  
    77  func AddVersionFlag() {
    78  	flag.Var(versionFlag{}, "V", "print version and exit")
    79  }
    80  
    81  var buildID string // filled in by linker
    82  
    83  type versionFlag struct{}
    84  
    85  func (versionFlag) IsBoolFlag() bool { return true }
    86  func (versionFlag) Get() interface{} { return nil }
    87  func (versionFlag) String() string   { return "" }
    88  func (versionFlag) Set(s string) error {
    89  	name := os.Args[0]
    90  	name = name[strings.LastIndex(name, `/`)+1:]
    91  	name = name[strings.LastIndex(name, `\`)+1:]
    92  	name = strings.TrimSuffix(name, ".exe")
    93  
    94  	// If there's an active experiment, include that,
    95  	// to distinguish go1.10.2 with an experiment
    96  	// from go1.10.2 without an experiment.
    97  	p := Expstring()
    98  	if p == DefaultExpstring() {
    99  		p = ""
   100  	}
   101  	sep := ""
   102  	if p != "" {
   103  		sep = " "
   104  	}
   105  
   106  	// The go command invokes -V=full to get a unique identifier
   107  	// for this tool. It is assumed that the release version is sufficient
   108  	// for releases, but during development we include the full
   109  	// build ID of the binary, so that if the compiler is changed and
   110  	// rebuilt, we notice and rebuild all packages.
   111  	if s == "full" {
   112  		if strings.HasPrefix(Version, "devel") {
   113  			p += " buildID=" + buildID
   114  		}
   115  	}
   116  
   117  	fmt.Printf("%s version %s%s%s\n", name, Version, sep, p)
   118  	os.Exit(0)
   119  	return nil
   120  }
   121  
   122  // count is a flag.Value that is like a flag.Bool and a flag.Int.
   123  // If used as -name, it increments the count, but -name=x sets the count.
   124  // Used for verbose flag -v.
   125  type count int
   126  
   127  func (c *count) String() string {
   128  	return fmt.Sprint(int(*c))
   129  }
   130  
   131  func (c *count) Set(s string) error {
   132  	switch s {
   133  	case "true":
   134  		*c++
   135  	case "false":
   136  		*c = 0
   137  	default:
   138  		n, err := strconv.Atoi(s)
   139  		if err != nil {
   140  			return fmt.Errorf("invalid count %q", s)
   141  		}
   142  		*c = count(n)
   143  	}
   144  	return nil
   145  }
   146  
   147  func (c *count) Get() interface{} {
   148  	return int(*c)
   149  }
   150  
   151  func (c *count) IsBoolFlag() bool {
   152  	return true
   153  }
   154  
   155  func (c *count) IsCountFlag() bool {
   156  	return true
   157  }
   158  
   159  type fn1 func(string)
   160  
   161  func (f fn1) Set(s string) error {
   162  	f(s)
   163  	return nil
   164  }
   165  
   166  func (f fn1) String() string { return "" }
   167  
   168  // DecodeArg decodes an argument.
   169  //
   170  // This function is public for testing with the parallel encoder.
   171  func DecodeArg(arg string) string {
   172  	// If no encoding, fastpath out.
   173  	if !strings.ContainsAny(arg, "\\\n") {
   174  		return arg
   175  	}
   176  
   177  	// We can't use strings.Builder as this must work at bootstrap.
   178  	var b bytes.Buffer
   179  	var wasBS bool
   180  	for _, r := range arg {
   181  		if wasBS {
   182  			switch r {
   183  			case '\\':
   184  				b.WriteByte('\\')
   185  			case 'n':
   186  				b.WriteByte('\n')
   187  			default:
   188  				// This shouldn't happen. The only backslashes that reach here
   189  				// should encode '\n' and '\\' exclusively.
   190  				panic("badly formatted input")
   191  			}
   192  		} else if r == '\\' {
   193  			wasBS = true
   194  			continue
   195  		} else {
   196  			b.WriteRune(r)
   197  		}
   198  		wasBS = false
   199  	}
   200  	return b.String()
   201  }
   202  

View as plain text