summaryrefslogtreecommitdiffstats
path: root/src/cmd/internal/objabi/flag.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/cmd/internal/objabi/flag.go201
1 files changed, 201 insertions, 0 deletions
diff --git a/src/cmd/internal/objabi/flag.go b/src/cmd/internal/objabi/flag.go
new file mode 100644
index 0000000..3fd73f3
--- /dev/null
+++ b/src/cmd/internal/objabi/flag.go
@@ -0,0 +1,201 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package objabi
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "strconv"
+ "strings"
+)
+
+func Flagcount(name, usage string, val *int) {
+ flag.Var((*count)(val), name, usage)
+}
+
+func Flagfn1(name, usage string, f func(string)) {
+ flag.Var(fn1(f), name, usage)
+}
+
+func Flagprint(w io.Writer) {
+ flag.CommandLine.SetOutput(w)
+ flag.PrintDefaults()
+}
+
+func Flagparse(usage func()) {
+ flag.Usage = usage
+ os.Args = expandArgs(os.Args)
+ flag.Parse()
+}
+
+// expandArgs expands "response files" arguments in the provided slice.
+//
+// A "response file" argument starts with '@' and the rest of that
+// argument is a filename with CR-or-CRLF-separated arguments. Each
+// argument in the named files can also contain response file
+// arguments. See Issue 18468.
+//
+// The returned slice 'out' aliases 'in' iff the input did not contain
+// any response file arguments.
+//
+// TODO: handle relative paths of recursive expansions in different directories?
+// Is there a spec for this? Are relative paths allowed?
+func expandArgs(in []string) (out []string) {
+ // out is nil until we see a "@" argument.
+ for i, s := range in {
+ if strings.HasPrefix(s, "@") {
+ if out == nil {
+ out = make([]string, 0, len(in)*2)
+ out = append(out, in[:i]...)
+ }
+ slurp, err := ioutil.ReadFile(s[1:])
+ if err != nil {
+ log.Fatal(err)
+ }
+ args := strings.Split(strings.TrimSpace(strings.Replace(string(slurp), "\r", "", -1)), "\n")
+ for i, arg := range args {
+ args[i] = DecodeArg(arg)
+ }
+ out = append(out, expandArgs(args)...)
+ } else if out != nil {
+ out = append(out, s)
+ }
+ }
+ if out == nil {
+ return in
+ }
+ return
+}
+
+func AddVersionFlag() {
+ flag.Var(versionFlag{}, "V", "print version and exit")
+}
+
+var buildID string // filled in by linker
+
+type versionFlag struct{}
+
+func (versionFlag) IsBoolFlag() bool { return true }
+func (versionFlag) Get() interface{} { return nil }
+func (versionFlag) String() string { return "" }
+func (versionFlag) Set(s string) error {
+ name := os.Args[0]
+ name = name[strings.LastIndex(name, `/`)+1:]
+ name = name[strings.LastIndex(name, `\`)+1:]
+ name = strings.TrimSuffix(name, ".exe")
+
+ // If there's an active experiment, include that,
+ // to distinguish go1.10.2 with an experiment
+ // from go1.10.2 without an experiment.
+ p := Expstring()
+ if p == DefaultExpstring() {
+ p = ""
+ }
+ sep := ""
+ if p != "" {
+ sep = " "
+ }
+
+ // The go command invokes -V=full to get a unique identifier
+ // for this tool. It is assumed that the release version is sufficient
+ // for releases, but during development we include the full
+ // build ID of the binary, so that if the compiler is changed and
+ // rebuilt, we notice and rebuild all packages.
+ if s == "full" {
+ if strings.HasPrefix(Version, "devel") {
+ p += " buildID=" + buildID
+ }
+ }
+
+ fmt.Printf("%s version %s%s%s\n", name, Version, sep, p)
+ os.Exit(0)
+ return nil
+}
+
+// count is a flag.Value that is like a flag.Bool and a flag.Int.
+// If used as -name, it increments the count, but -name=x sets the count.
+// Used for verbose flag -v.
+type count int
+
+func (c *count) String() string {
+ return fmt.Sprint(int(*c))
+}
+
+func (c *count) Set(s string) error {
+ switch s {
+ case "true":
+ *c++
+ case "false":
+ *c = 0
+ default:
+ n, err := strconv.Atoi(s)
+ if err != nil {
+ return fmt.Errorf("invalid count %q", s)
+ }
+ *c = count(n)
+ }
+ return nil
+}
+
+func (c *count) Get() interface{} {
+ return int(*c)
+}
+
+func (c *count) IsBoolFlag() bool {
+ return true
+}
+
+func (c *count) IsCountFlag() bool {
+ return true
+}
+
+type fn1 func(string)
+
+func (f fn1) Set(s string) error {
+ f(s)
+ return nil
+}
+
+func (f fn1) String() string { return "" }
+
+// DecodeArg decodes an argument.
+//
+// This function is public for testing with the parallel encoder.
+func DecodeArg(arg string) string {
+ // If no encoding, fastpath out.
+ if !strings.ContainsAny(arg, "\\\n") {
+ return arg
+ }
+
+ // We can't use strings.Builder as this must work at bootstrap.
+ var b bytes.Buffer
+ var wasBS bool
+ for _, r := range arg {
+ if wasBS {
+ switch r {
+ case '\\':
+ b.WriteByte('\\')
+ case 'n':
+ b.WriteByte('\n')
+ default:
+ // This shouldn't happen. The only backslashes that reach here
+ // should encode '\n' and '\\' exclusively.
+ panic("badly formatted input")
+ }
+ } else if r == '\\' {
+ wasBS = true
+ continue
+ } else {
+ b.WriteRune(r)
+ }
+ wasBS = false
+ }
+ return b.String()
+}