]> Cypherpunks repositories - gostls13.git/commitdiff
cmd: move internal/str back to cmd/go
authorRuss Cox <rsc@golang.org>
Mon, 11 Oct 2021 15:57:24 +0000 (11:57 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 27 Oct 2021 16:39:27 +0000 (16:39 +0000)
cmd/go is not subject to all the same restrictions as most of cmd.
In particular it need not be buildable with the bootstrap toolchain.
So it is better to keep as little code shared between cmd/go and
cmd/compile, cmd/link, cmd/cgo as possible.

cmd/internal/str started as cmd/go/internal/str but was moved
to cmd/internal in order to make use of the quoted string code.
Move that code to cmd/internal/quoted and then move the rest of
cmd/internal/str back to cmd/go/internal/str.

Change-Id: I3a98f754d545cc3af7e9a32c2b77a5a035ea7b9a
Reviewed-on: https://go-review.googlesource.com/c/go/+/355010
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
35 files changed:
src/cmd/cgo/gcc.go
src/cmd/compile/internal/ssa/stmtlines_test.go
src/cmd/dist/buildtool.go
src/cmd/go/internal/base/base.go
src/cmd/go/internal/base/flag.go
src/cmd/go/internal/envcmd/env.go
src/cmd/go/internal/fix/fix.go
src/cmd/go/internal/generate/generate.go
src/cmd/go/internal/get/get.go
src/cmd/go/internal/list/list.go
src/cmd/go/internal/load/flag.go
src/cmd/go/internal/load/pkg.go
src/cmd/go/internal/load/test.go
src/cmd/go/internal/modcmd/vendor.go
src/cmd/go/internal/modfetch/codehost/codehost.go
src/cmd/go/internal/modfetch/codehost/vcs.go
src/cmd/go/internal/modget/query.go
src/cmd/go/internal/modload/load.go
src/cmd/go/internal/modload/query.go
src/cmd/go/internal/run/run.go
src/cmd/go/internal/str/path.go [moved from src/cmd/internal/str/path.go with 100% similarity]
src/cmd/go/internal/str/str.go [new file with mode: 0644]
src/cmd/go/internal/str/str_test.go [new file with mode: 0644]
src/cmd/go/internal/test/test.go
src/cmd/go/internal/vcs/vcs.go
src/cmd/go/internal/work/buildid.go
src/cmd/go/internal/work/exec.go
src/cmd/go/internal/work/gc.go
src/cmd/go/internal/work/gccgo.go
src/cmd/go/internal/work/init.go
src/cmd/internal/quoted/quoted.go [new file with mode: 0644]
src/cmd/internal/quoted/quoted_test.go [moved from src/cmd/internal/str/str_test.go with 79% similarity]
src/cmd/internal/str/str.go [deleted file]
src/cmd/link/dwarf_test.go
src/cmd/link/internal/ld/main.go

index c78197896c39a44abf7f3ca1b75d03ea0d723853..997a830994f09bdd6118299e31e20125cadff3ff 100644 (file)
@@ -29,7 +29,7 @@ import (
        "unicode"
        "unicode/utf8"
 
-       "cmd/internal/str"
+       "cmd/internal/quoted"
 )
 
 var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
@@ -1568,7 +1568,7 @@ func checkGCCBaseCmd() ([]string, error) {
        if value == "" {
                value = defaultCC(goos, goarch)
        }
-       args, err := str.SplitQuotedFields(value)
+       args, err := quoted.Split(value)
        if err != nil {
                return nil, err
        }
index 843db8c07ef409a3abdc485dfe9b4221f64fd4cc..90dd261c557fec7d5603cd7b3d3c5520f9382a19 100644 (file)
@@ -2,7 +2,7 @@ package ssa_test
 
 import (
        cmddwarf "cmd/internal/dwarf"
-       "cmd/internal/str"
+       "cmd/internal/quoted"
        "debug/dwarf"
        "debug/elf"
        "debug/macho"
@@ -58,7 +58,7 @@ func TestStmtLines(t *testing.T) {
                if extld == "" {
                        extld = "gcc"
                }
-               extldArgs, err := str.SplitQuotedFields(extld)
+               extldArgs, err := quoted.Split(extld)
                if err != nil {
                        t.Fatal(err)
                }
index 320c62f8505aa8bfaa68bb1d7bf81ac5c3d89207..75f04a975c92d492ef46591d10bef65ba2520b53 100644 (file)
@@ -46,8 +46,8 @@ var bootstrapDirs = []string{
        "cmd/internal/obj/...",
        "cmd/internal/objabi",
        "cmd/internal/pkgpath",
+       "cmd/internal/quoted",
        "cmd/internal/src",
-       "cmd/internal/str",
        "cmd/internal/sys",
        "cmd/link",
        "cmd/link/internal/...",
index 0144525e307b0565b002c25b103dd9bc8108e8c0..954ce47a9899325014b4e99b5b6a1875a4120f30 100644 (file)
@@ -17,7 +17,7 @@ import (
        "sync"
 
        "cmd/go/internal/cfg"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 )
 
 // A Command is an implementation of a go command
index 7e5121bffbe8e37c34ae472edb44ea153e4e149f..2c72c7e562b7333be43813553321469a555b44a0 100644 (file)
@@ -9,7 +9,7 @@ import (
 
        "cmd/go/internal/cfg"
        "cmd/go/internal/fsys"
-       "cmd/internal/str"
+       "cmd/internal/quoted"
 )
 
 // A StringsFlag is a command-line flag that interprets its argument
@@ -18,7 +18,7 @@ type StringsFlag []string
 
 func (v *StringsFlag) Set(s string) error {
        var err error
-       *v, err = str.SplitQuotedFields(s)
+       *v, err = quoted.Split(s)
        if *v == nil {
                *v = []string{}
        }
index 181d2a2ca1f49734a8c94f88983d6ade8da9f367..e56dd8223f0519e90e9c89cd8e576d5f227becc5 100644 (file)
@@ -26,7 +26,7 @@ import (
        "cmd/go/internal/load"
        "cmd/go/internal/modload"
        "cmd/go/internal/work"
-       "cmd/internal/str"
+       "cmd/internal/quoted"
 )
 
 var CmdEnv = &base.Command{
@@ -470,7 +470,7 @@ func checkEnvWrite(key, val string) error {
                if val == "" {
                        break
                }
-               args, err := str.SplitQuotedFields(val)
+               args, err := quoted.Split(val)
                if err != nil {
                        return fmt.Errorf("invalid %s: %v", key, err)
                }
index cc5940fccd8f27672a3c202b2b06a322a40b820d..988d45e71ccfe2ccf571e409962bdd3ade4e3430 100644 (file)
@@ -10,7 +10,7 @@ import (
        "cmd/go/internal/cfg"
        "cmd/go/internal/load"
        "cmd/go/internal/modload"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
        "context"
        "fmt"
        "os"
index 5981e5ecdbeb6c391dee10cded5ffaf01a3714d4..a3873d11387c8195f0de88a887f1c4ead93ce3ed 100644 (file)
@@ -26,7 +26,7 @@ import (
        "cmd/go/internal/load"
        "cmd/go/internal/modload"
        "cmd/go/internal/work"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 )
 
 var CmdGenerate = &base.Command{
index 0412506b9e87531fcac36adb8f84b60dc13abfbf..f46313dcff605434dfc4902e8fe91a59f1acf587 100644 (file)
@@ -20,7 +20,7 @@ import (
        "cmd/go/internal/vcs"
        "cmd/go/internal/web"
        "cmd/go/internal/work"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 
        "golang.org/x/mod/module"
 )
index 821e622abbed0eea16775cbbee0381a08c8b70e1..8c85ddcf21163e94b0dad71b61801f492195080c 100644 (file)
@@ -24,7 +24,7 @@ import (
        "cmd/go/internal/modinfo"
        "cmd/go/internal/modload"
        "cmd/go/internal/work"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 )
 
 var CmdList = &base.Command{
index d0d5716c3fc2dcd4a77a5dbc323e7710dfc38125..de079decdf2541c6f7ac312e002aec761427bfbc 100644 (file)
@@ -6,7 +6,7 @@ package load
 
 import (
        "cmd/go/internal/base"
-       "cmd/internal/str"
+       "cmd/internal/quoted"
        "fmt"
        "strings"
 )
@@ -63,7 +63,7 @@ func (f *PerPackageFlag) set(v, cwd string) error {
                match = MatchPackage(pattern, cwd)
                v = v[i+1:]
        }
-       flags, err := str.SplitQuotedFields(v)
+       flags, err := quoted.Split(v)
        if err != nil {
                return err
        }
index dfe7849516a48b26f12e205476285bfef9ba7eb3..c6c5fb00a8db54c288b7c80deb6f8d27e4b86ba1 100644 (file)
@@ -38,9 +38,9 @@ import (
        "cmd/go/internal/modload"
        "cmd/go/internal/par"
        "cmd/go/internal/search"
+       "cmd/go/internal/str"
        "cmd/go/internal/trace"
        "cmd/go/internal/vcs"
-       "cmd/internal/str"
        "cmd/internal/sys"
 
        "golang.org/x/mod/modfile"
index 4cefb62d51ac2f0ac6cf1fe02859d7064080b4b5..8a18dfbe931c9b2416ee142fbc8793859d9d9d1b 100644 (file)
@@ -23,7 +23,7 @@ import (
 
        "cmd/go/internal/fsys"
        "cmd/go/internal/trace"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 )
 
 var TestMainDeps = []string{
index 57189b4607fa69e183fee5ad6a668358044fec8b..484e095cc764c2d4955b02b625280733882e74d2 100644 (file)
@@ -24,7 +24,7 @@ import (
        "cmd/go/internal/imports"
        "cmd/go/internal/load"
        "cmd/go/internal/modload"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 
        "golang.org/x/mod/module"
        "golang.org/x/mod/semver"
index efb4b1516a2ef48874a19c4fc240119bc1a84ec1..378fbae34f9530378ced67a8298f797891af9e72 100644 (file)
@@ -21,7 +21,7 @@ import (
 
        "cmd/go/internal/cfg"
        "cmd/go/internal/lockedfile"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 )
 
 // Downloaded size limits.
index 5d810d2621c907614df4d02da47a015b0f937e6c..c2cca084e3077a8b3f9627e8b212491c1baf06dd 100644 (file)
@@ -20,7 +20,7 @@ import (
 
        "cmd/go/internal/lockedfile"
        "cmd/go/internal/par"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 )
 
 // A VCSError indicates an error using a version control system.
index d7341e7813dd6ea12aa7f96bfebaade626c7c8d5..887cb51b317f7f3c46db33192452197571467a09 100644 (file)
@@ -14,7 +14,7 @@ import (
        "cmd/go/internal/base"
        "cmd/go/internal/modload"
        "cmd/go/internal/search"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 
        "golang.org/x/mod/module"
 )
index 0f5b0150003b7027495c9f2dd4daad5402d220a3..845bf2f8a2323ac1beff39fc2adcb4acfb52e7fd 100644 (file)
@@ -119,7 +119,7 @@ import (
        "cmd/go/internal/mvs"
        "cmd/go/internal/par"
        "cmd/go/internal/search"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 
        "golang.org/x/mod/module"
        "golang.org/x/mod/semver"
index c9ed129dbfaa6cb2472fc0f35854cb842cdc813a..1eb484de9d0b8796e4bbe9a6ecf37340399b1dfd 100644 (file)
@@ -22,7 +22,7 @@ import (
        "cmd/go/internal/modfetch"
        "cmd/go/internal/search"
        "cmd/go/internal/trace"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 
        "golang.org/x/mod/module"
        "golang.org/x/mod/semver"
index 11e2c81b9aa53fec1c94c8b4981efe434630a20c..03895d27ebf2d68419ca0200c3ae79c139dcfa01 100644 (file)
@@ -19,7 +19,7 @@ import (
        "cmd/go/internal/load"
        "cmd/go/internal/modload"
        "cmd/go/internal/work"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 )
 
 var CmdRun = &base.Command{
diff --git a/src/cmd/go/internal/str/str.go b/src/cmd/go/internal/str/str.go
new file mode 100644 (file)
index 0000000..5bc521b
--- /dev/null
@@ -0,0 +1,111 @@
+// Copyright 2017 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 str provides string manipulation utilities.
+package str
+
+import (
+       "bytes"
+       "fmt"
+       "unicode"
+       "unicode/utf8"
+)
+
+// StringList flattens its arguments into a single []string.
+// Each argument in args must have type string or []string.
+func StringList(args ...interface{}) []string {
+       var x []string
+       for _, arg := range args {
+               switch arg := arg.(type) {
+               case []string:
+                       x = append(x, arg...)
+               case string:
+                       x = append(x, arg)
+               default:
+                       panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg))
+               }
+       }
+       return x
+}
+
+// ToFold returns a string with the property that
+//     strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
+// This lets us test a large set of strings for fold-equivalent
+// duplicates without making a quadratic number of calls
+// to EqualFold. Note that strings.ToUpper and strings.ToLower
+// do not have the desired property in some corner cases.
+func ToFold(s string) string {
+       // Fast path: all ASCII, no upper case.
+       // Most paths look like this already.
+       for i := 0; i < len(s); i++ {
+               c := s[i]
+               if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
+                       goto Slow
+               }
+       }
+       return s
+
+Slow:
+       var buf bytes.Buffer
+       for _, r := range s {
+               // SimpleFold(x) cycles to the next equivalent rune > x
+               // or wraps around to smaller values. Iterate until it wraps,
+               // and we've found the minimum value.
+               for {
+                       r0 := r
+                       r = unicode.SimpleFold(r0)
+                       if r <= r0 {
+                               break
+                       }
+               }
+               // Exception to allow fast path above: A-Z => a-z
+               if 'A' <= r && r <= 'Z' {
+                       r += 'a' - 'A'
+               }
+               buf.WriteRune(r)
+       }
+       return buf.String()
+}
+
+// FoldDup reports a pair of strings from the list that are
+// equal according to strings.EqualFold.
+// It returns "", "" if there are no such strings.
+func FoldDup(list []string) (string, string) {
+       clash := map[string]string{}
+       for _, s := range list {
+               fold := ToFold(s)
+               if t := clash[fold]; t != "" {
+                       if s > t {
+                               s, t = t, s
+                       }
+                       return s, t
+               }
+               clash[fold] = s
+       }
+       return "", ""
+}
+
+// Contains reports whether x contains s.
+func Contains(x []string, s string) bool {
+       for _, t := range x {
+               if t == s {
+                       return true
+               }
+       }
+       return false
+}
+
+// Uniq removes consecutive duplicate strings from ss.
+func Uniq(ss *[]string) {
+       if len(*ss) <= 1 {
+               return
+       }
+       uniq := (*ss)[:1]
+       for _, s := range *ss {
+               if s != uniq[len(uniq)-1] {
+                       uniq = append(uniq, s)
+               }
+       }
+       *ss = uniq
+}
diff --git a/src/cmd/go/internal/str/str_test.go b/src/cmd/go/internal/str/str_test.go
new file mode 100644 (file)
index 0000000..8ea758e
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2020 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 str
+
+import (
+       "testing"
+)
+
+var foldDupTests = []struct {
+       list   []string
+       f1, f2 string
+}{
+       {StringList("math/rand", "math/big"), "", ""},
+       {StringList("math", "strings"), "", ""},
+       {StringList("strings"), "", ""},
+       {StringList("strings", "strings"), "strings", "strings"},
+       {StringList("Rand", "rand", "math", "math/rand", "math/Rand"), "Rand", "rand"},
+}
+
+func TestFoldDup(t *testing.T) {
+       for _, tt := range foldDupTests {
+               f1, f2 := FoldDup(tt.list)
+               if f1 != tt.f1 || f2 != tt.f2 {
+                       t.Errorf("foldDup(%q) = %q, %q, want %q, %q", tt.list, f1, f2, tt.f1, tt.f2)
+               }
+       }
+}
index dc1bea505bda24ebd09183d9ae0fa879aecaf4e9..ea1d4ff20e9ff15d7edb5ed8c5c5a32e91cf463a 100644 (file)
@@ -33,7 +33,7 @@ import (
        "cmd/go/internal/search"
        "cmd/go/internal/trace"
        "cmd/go/internal/work"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
        "cmd/internal/test2json"
 )
 
index 941bd57147e244ecd3e84c31f28f057cc1a1b528..c4853d7ae31d1b40a8b0d1287b021b60045393b9 100644 (file)
@@ -27,7 +27,7 @@ import (
        "cmd/go/internal/cfg"
        "cmd/go/internal/search"
        "cmd/go/internal/web"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 
        "golang.org/x/mod/module"
 )
index 15f944d2af219e398a59b68ae1ab3f0a5edb6690..d4f2a716d7b98c76dabbc5ff0e592a2adf7f04ee 100644 (file)
@@ -16,7 +16,7 @@ import (
        "cmd/go/internal/cfg"
        "cmd/go/internal/fsys"
        "cmd/internal/buildid"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 )
 
 // Build IDs
index 62d814382881afbca28960b2818faae3fbdbd896..03f8866cf2cc210e9840d420fee0adeea926f1cc 100644 (file)
@@ -34,8 +34,9 @@ import (
        "cmd/go/internal/fsys"
        "cmd/go/internal/load"
        "cmd/go/internal/modload"
+       "cmd/go/internal/str"
        "cmd/go/internal/trace"
-       "cmd/internal/str"
+       "cmd/internal/quoted"
        "cmd/internal/sys"
 )
 
@@ -2666,7 +2667,7 @@ func envList(key, def string) []string {
        if v == "" {
                v = def
        }
-       args, err := str.SplitQuotedFields(v)
+       args, err := quoted.Split(v)
        if err != nil {
                panic(fmt.Sprintf("could not parse environment variable %s with value %q: %v", key, v, err))
        }
index 3eb9b35f40828f9010cfb63ca0f6aa3ab5079531..e3b4a817e782c0c7e30e7d0dd8713d920c1a5a0a 100644 (file)
@@ -20,8 +20,9 @@ import (
        "cmd/go/internal/cfg"
        "cmd/go/internal/fsys"
        "cmd/go/internal/load"
+       "cmd/go/internal/str"
        "cmd/internal/objabi"
-       "cmd/internal/str"
+       "cmd/internal/quoted"
        "cmd/internal/sys"
        "crypto/sha1"
 )
@@ -565,7 +566,7 @@ func setextld(ldflags []string, compiler []string) ([]string, error) {
                        return ldflags, nil
                }
        }
-       joined, err := str.JoinAndQuoteFields(compiler)
+       joined, err := quoted.Join(compiler)
        if err != nil {
                return nil, err
        }
index 3cb7b641833a48db0acf35f4a31697d336a9df16..60181b99e471d409b91408ccf26a7ff891cd89b2 100644 (file)
@@ -17,7 +17,7 @@ import (
        "cmd/go/internal/fsys"
        "cmd/go/internal/load"
        "cmd/internal/pkgpath"
-       "cmd/internal/str"
+       "cmd/go/internal/str"
 )
 
 // The Gccgo toolchain.
index 56e39f8c52a071510e3cb057ae1cbbb5d1f3f9c5..4dbbd2a13f2b4659e27ca880d55ff7c708fe3873 100644 (file)
@@ -11,7 +11,7 @@ import (
        "cmd/go/internal/cfg"
        "cmd/go/internal/fsys"
        "cmd/go/internal/modload"
-       "cmd/internal/str"
+       "cmd/internal/quoted"
        "cmd/internal/sys"
        "fmt"
        "os"
@@ -46,7 +46,7 @@ func BuildInit() {
        // Make sure CC, CXX, and FC are absolute paths.
        for _, key := range []string{"CC", "CXX", "FC"} {
                value := cfg.Getenv(key)
-               args, err := str.SplitQuotedFields(value)
+               args, err := quoted.Split(value)
                if err != nil {
                        base.Fatalf("go: %s environment variable could not be parsed: %v", key, err)
                }
diff --git a/src/cmd/internal/quoted/quoted.go b/src/cmd/internal/quoted/quoted.go
new file mode 100644 (file)
index 0000000..e7575df
--- /dev/null
@@ -0,0 +1,127 @@
+// Copyright 2017 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 quoted provides string manipulation utilities.
+package quoted
+
+import (
+       "flag"
+       "fmt"
+       "strings"
+       "unicode"
+)
+
+func isSpaceByte(c byte) bool {
+       return c == ' ' || c == '\t' || c == '\n' || c == '\r'
+}
+
+// Split splits s into a list of fields,
+// allowing single or double quotes around elements.
+// There is no unescaping or other processing within
+// quoted fields.
+func Split(s string) ([]string, error) {
+       // Split fields allowing '' or "" around elements.
+       // Quotes further inside the string do not count.
+       var f []string
+       for len(s) > 0 {
+               for len(s) > 0 && isSpaceByte(s[0]) {
+                       s = s[1:]
+               }
+               if len(s) == 0 {
+                       break
+               }
+               // Accepted quoted string. No unescaping inside.
+               if s[0] == '"' || s[0] == '\'' {
+                       quote := s[0]
+                       s = s[1:]
+                       i := 0
+                       for i < len(s) && s[i] != quote {
+                               i++
+                       }
+                       if i >= len(s) {
+                               return nil, fmt.Errorf("unterminated %c string", quote)
+                       }
+                       f = append(f, s[:i])
+                       s = s[i+1:]
+                       continue
+               }
+               i := 0
+               for i < len(s) && !isSpaceByte(s[i]) {
+                       i++
+               }
+               f = append(f, s[:i])
+               s = s[i:]
+       }
+       return f, nil
+}
+
+// Join joins a list of arguments into a string that can be parsed
+// with Split. Arguments are quoted only if necessary; arguments
+// without spaces or quotes are kept as-is. No argument may contain both
+// single and double quotes.
+func Join(args []string) (string, error) {
+       var buf []byte
+       for i, arg := range args {
+               if i > 0 {
+                       buf = append(buf, ' ')
+               }
+               var sawSpace, sawSingleQuote, sawDoubleQuote bool
+               for _, c := range arg {
+                       switch {
+                       case c > unicode.MaxASCII:
+                               continue
+                       case isSpaceByte(byte(c)):
+                               sawSpace = true
+                       case c == '\'':
+                               sawSingleQuote = true
+                       case c == '"':
+                               sawDoubleQuote = true
+                       }
+               }
+               switch {
+               case !sawSpace && !sawSingleQuote && !sawDoubleQuote:
+                       buf = append(buf, []byte(arg)...)
+
+               case !sawSingleQuote:
+                       buf = append(buf, '\'')
+                       buf = append(buf, []byte(arg)...)
+                       buf = append(buf, '\'')
+
+               case !sawDoubleQuote:
+                       buf = append(buf, '"')
+                       buf = append(buf, []byte(arg)...)
+                       buf = append(buf, '"')
+
+               default:
+                       return "", fmt.Errorf("argument %q contains both single and double quotes and cannot be quoted", arg)
+               }
+       }
+       return string(buf), nil
+}
+
+// A Flag parses a list of string arguments encoded with Join.
+// It is useful for flags like cmd/link's -extldflags.
+type Flag []string
+
+var _ flag.Value = (*Flag)(nil)
+
+func (f *Flag) Set(v string) error {
+       fs, err := Split(v)
+       if err != nil {
+               return err
+       }
+       *f = fs[:len(fs):len(fs)]
+       return nil
+}
+
+func (f *Flag) String() string {
+       if f == nil {
+               return ""
+       }
+       s, err := Join(*f)
+       if err != nil {
+               return strings.Join(*f, " ")
+       }
+       return s
+}
similarity index 79%
rename from src/cmd/internal/str/str_test.go
rename to src/cmd/internal/quoted/quoted_test.go
index 3609af6a06df48874ba647df51b62e4d85fda873..d76270c87b493e31907b155d3ff9a1695205e81e 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package str
+package quoted
 
 import (
        "reflect"
@@ -10,27 +10,7 @@ import (
        "testing"
 )
 
-var foldDupTests = []struct {
-       list   []string
-       f1, f2 string
-}{
-       {StringList("math/rand", "math/big"), "", ""},
-       {StringList("math", "strings"), "", ""},
-       {StringList("strings"), "", ""},
-       {StringList("strings", "strings"), "strings", "strings"},
-       {StringList("Rand", "rand", "math", "math/rand", "math/Rand"), "Rand", "rand"},
-}
-
-func TestFoldDup(t *testing.T) {
-       for _, tt := range foldDupTests {
-               f1, f2 := FoldDup(tt.list)
-               if f1 != tt.f1 || f2 != tt.f2 {
-                       t.Errorf("foldDup(%q) = %q, %q, want %q, %q", tt.list, f1, f2, tt.f1, tt.f2)
-               }
-       }
-}
-
-func TestSplitQuotedFields(t *testing.T) {
+func TestSplit(t *testing.T) {
        for _, test := range []struct {
                name    string
                value   string
@@ -54,7 +34,7 @@ func TestSplitQuotedFields(t *testing.T) {
                {name: "quote_unclosed", value: `'a`, wantErr: "unterminated ' string"},
        } {
                t.Run(test.name, func(t *testing.T) {
-                       got, err := SplitQuotedFields(test.value)
+                       got, err := Split(test.value)
                        if err != nil {
                                if test.wantErr == "" {
                                        t.Fatalf("unexpected error: %v", err)
@@ -73,7 +53,7 @@ func TestSplitQuotedFields(t *testing.T) {
        }
 }
 
-func TestJoinAndQuoteFields(t *testing.T) {
+func TestJoin(t *testing.T) {
        for _, test := range []struct {
                name          string
                args          []string
@@ -88,7 +68,7 @@ func TestJoinAndQuoteFields(t *testing.T) {
                {name: "unquoteable", args: []string{`'"`}, wantErr: "contains both single and double quotes and cannot be quoted"},
        } {
                t.Run(test.name, func(t *testing.T) {
-                       got, err := JoinAndQuoteFields(test.args)
+                       got, err := Join(test.args)
                        if err != nil {
                                if test.wantErr == "" {
                                        t.Fatalf("unexpected error: %v", err)
diff --git a/src/cmd/internal/str/str.go b/src/cmd/internal/str/str.go
deleted file mode 100644 (file)
index 409cf8f..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright 2017 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 str provides string manipulation utilities.
-package str
-
-import (
-       "bytes"
-       "flag"
-       "fmt"
-       "strings"
-       "unicode"
-       "unicode/utf8"
-)
-
-// StringList flattens its arguments into a single []string.
-// Each argument in args must have type string or []string.
-func StringList(args ...interface{}) []string {
-       var x []string
-       for _, arg := range args {
-               switch arg := arg.(type) {
-               case []string:
-                       x = append(x, arg...)
-               case string:
-                       x = append(x, arg)
-               default:
-                       panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg))
-               }
-       }
-       return x
-}
-
-// ToFold returns a string with the property that
-//     strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
-// This lets us test a large set of strings for fold-equivalent
-// duplicates without making a quadratic number of calls
-// to EqualFold. Note that strings.ToUpper and strings.ToLower
-// do not have the desired property in some corner cases.
-func ToFold(s string) string {
-       // Fast path: all ASCII, no upper case.
-       // Most paths look like this already.
-       for i := 0; i < len(s); i++ {
-               c := s[i]
-               if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
-                       goto Slow
-               }
-       }
-       return s
-
-Slow:
-       var buf bytes.Buffer
-       for _, r := range s {
-               // SimpleFold(x) cycles to the next equivalent rune > x
-               // or wraps around to smaller values. Iterate until it wraps,
-               // and we've found the minimum value.
-               for {
-                       r0 := r
-                       r = unicode.SimpleFold(r0)
-                       if r <= r0 {
-                               break
-                       }
-               }
-               // Exception to allow fast path above: A-Z => a-z
-               if 'A' <= r && r <= 'Z' {
-                       r += 'a' - 'A'
-               }
-               buf.WriteRune(r)
-       }
-       return buf.String()
-}
-
-// FoldDup reports a pair of strings from the list that are
-// equal according to strings.EqualFold.
-// It returns "", "" if there are no such strings.
-func FoldDup(list []string) (string, string) {
-       clash := map[string]string{}
-       for _, s := range list {
-               fold := ToFold(s)
-               if t := clash[fold]; t != "" {
-                       if s > t {
-                               s, t = t, s
-                       }
-                       return s, t
-               }
-               clash[fold] = s
-       }
-       return "", ""
-}
-
-// Contains reports whether x contains s.
-func Contains(x []string, s string) bool {
-       for _, t := range x {
-               if t == s {
-                       return true
-               }
-       }
-       return false
-}
-
-// Uniq removes consecutive duplicate strings from ss.
-func Uniq(ss *[]string) {
-       if len(*ss) <= 1 {
-               return
-       }
-       uniq := (*ss)[:1]
-       for _, s := range *ss {
-               if s != uniq[len(uniq)-1] {
-                       uniq = append(uniq, s)
-               }
-       }
-       *ss = uniq
-}
-
-func isSpaceByte(c byte) bool {
-       return c == ' ' || c == '\t' || c == '\n' || c == '\r'
-}
-
-// SplitQuotedFields splits s into a list of fields,
-// allowing single or double quotes around elements.
-// There is no unescaping or other processing within
-// quoted fields.
-func SplitQuotedFields(s string) ([]string, error) {
-       // Split fields allowing '' or "" around elements.
-       // Quotes further inside the string do not count.
-       var f []string
-       for len(s) > 0 {
-               for len(s) > 0 && isSpaceByte(s[0]) {
-                       s = s[1:]
-               }
-               if len(s) == 0 {
-                       break
-               }
-               // Accepted quoted string. No unescaping inside.
-               if s[0] == '"' || s[0] == '\'' {
-                       quote := s[0]
-                       s = s[1:]
-                       i := 0
-                       for i < len(s) && s[i] != quote {
-                               i++
-                       }
-                       if i >= len(s) {
-                               return nil, fmt.Errorf("unterminated %c string", quote)
-                       }
-                       f = append(f, s[:i])
-                       s = s[i+1:]
-                       continue
-               }
-               i := 0
-               for i < len(s) && !isSpaceByte(s[i]) {
-                       i++
-               }
-               f = append(f, s[:i])
-               s = s[i:]
-       }
-       return f, nil
-}
-
-// JoinAndQuoteFields joins a list of arguments into a string that can be parsed
-// with SplitQuotedFields. Arguments are quoted only if necessary; arguments
-// without spaces or quotes are kept as-is. No argument may contain both
-// single and double quotes.
-func JoinAndQuoteFields(args []string) (string, error) {
-       var buf []byte
-       for i, arg := range args {
-               if i > 0 {
-                       buf = append(buf, ' ')
-               }
-               var sawSpace, sawSingleQuote, sawDoubleQuote bool
-               for _, c := range arg {
-                       switch {
-                       case c > unicode.MaxASCII:
-                               continue
-                       case isSpaceByte(byte(c)):
-                               sawSpace = true
-                       case c == '\'':
-                               sawSingleQuote = true
-                       case c == '"':
-                               sawDoubleQuote = true
-                       }
-               }
-               switch {
-               case !sawSpace && !sawSingleQuote && !sawDoubleQuote:
-                       buf = append(buf, []byte(arg)...)
-
-               case !sawSingleQuote:
-                       buf = append(buf, '\'')
-                       buf = append(buf, []byte(arg)...)
-                       buf = append(buf, '\'')
-
-               case !sawDoubleQuote:
-                       buf = append(buf, '"')
-                       buf = append(buf, []byte(arg)...)
-                       buf = append(buf, '"')
-
-               default:
-                       return "", fmt.Errorf("argument %q contains both single and double quotes and cannot be quoted", arg)
-               }
-       }
-       return string(buf), nil
-}
-
-// A QuotedStringListFlag parses a list of string arguments encoded with
-// JoinAndQuoteFields. It is useful for flags like cmd/link's -extldflags.
-type QuotedStringListFlag []string
-
-var _ flag.Value = (*QuotedStringListFlag)(nil)
-
-func (f *QuotedStringListFlag) Set(v string) error {
-       fs, err := SplitQuotedFields(v)
-       if err != nil {
-               return err
-       }
-       *f = fs[:len(fs):len(fs)]
-       return nil
-}
-
-func (f *QuotedStringListFlag) String() string {
-       if f == nil {
-               return ""
-       }
-       s, err := JoinAndQuoteFields(*f)
-       if err != nil {
-               return strings.Join(*f, " ")
-       }
-       return s
-}
index f7bbb014d9e48e446c8c611516eddbc82472cb3e..78ef3cfe9713691cbb053a8e180cff02dcd66820 100644 (file)
@@ -8,7 +8,7 @@ import (
        "bytes"
        cmddwarf "cmd/internal/dwarf"
        "cmd/internal/objfile"
-       "cmd/internal/str"
+       "cmd/internal/quoted"
        "debug/dwarf"
        "internal/testenv"
        "os"
@@ -68,7 +68,7 @@ func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string)
                        if extld == "" {
                                extld = "gcc"
                        }
-                       extldArgs, err := str.SplitQuotedFields(extld)
+                       extldArgs, err := quoted.Split(extld)
                        if err != nil {
                                t.Fatal(err)
                        }
index a5a5a71250fe92b592b8d4beca01c5b932c85512..a1d86965e434bb99f6eb1e68522debd3fd07f0c1 100644 (file)
@@ -34,7 +34,7 @@ import (
        "bufio"
        "cmd/internal/goobj"
        "cmd/internal/objabi"
-       "cmd/internal/str"
+       "cmd/internal/quoted"
        "cmd/internal/sys"
        "cmd/link/internal/benchmark"
        "flag"
@@ -76,8 +76,8 @@ var (
        flagLibGCC     = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable")
        flagTmpdir     = flag.String("tmpdir", "", "use `directory` for temporary files")
 
-       flagExtld      str.QuotedStringListFlag
-       flagExtldflags str.QuotedStringListFlag
+       flagExtld      quoted.Flag
+       flagExtldflags quoted.Flag
        flagExtar      = flag.String("extar", "", "archive program for buildmode=c-archive")
 
        flagA             = flag.Bool("a", false, "no-op (deprecated)")