aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/internal
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2021-10-11 11:57:24 -0400
committerRuss Cox <rsc@golang.org>2021-10-27 16:39:27 +0000
commit4f73fd05a91a9b8ceced6b7f89d35f363c414ec8 (patch)
treed9a6436610d2521cce7ac703b470b076ae26ced2 /src/cmd/internal
parentcfb532158fc5b5cd6b9c35bbc2ff7c203fe5f09b (diff)
downloadgo-4f73fd05a91a9b8ceced6b7f89d35f363c414ec8.tar.gz
go-4f73fd05a91a9b8ceced6b7f89d35f363c414ec8.zip
cmd: move internal/str back to cmd/go
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>
Diffstat (limited to 'src/cmd/internal')
-rw-r--r--src/cmd/internal/quoted/quoted.go127
-rw-r--r--src/cmd/internal/quoted/quoted_test.go (renamed from src/cmd/internal/str/str_test.go)30
-rw-r--r--src/cmd/internal/str/path.go65
-rw-r--r--src/cmd/internal/str/str.go227
4 files changed, 132 insertions, 317 deletions
diff --git a/src/cmd/internal/quoted/quoted.go b/src/cmd/internal/quoted/quoted.go
new file mode 100644
index 0000000000..e7575dfc66
--- /dev/null
+++ b/src/cmd/internal/quoted/quoted.go
@@ -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
+}
diff --git a/src/cmd/internal/str/str_test.go b/src/cmd/internal/quoted/quoted_test.go
index 3609af6a06..d76270c87b 100644
--- a/src/cmd/internal/str/str_test.go
+++ b/src/cmd/internal/quoted/quoted_test.go
@@ -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/path.go b/src/cmd/internal/str/path.go
deleted file mode 100644
index 0c8aaeaca1..0000000000
--- a/src/cmd/internal/str/path.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2018 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 (
- "path/filepath"
- "strings"
-)
-
-// HasPathPrefix reports whether the slash-separated path s
-// begins with the elements in prefix.
-func HasPathPrefix(s, prefix string) bool {
- if len(s) == len(prefix) {
- return s == prefix
- }
- if prefix == "" {
- return true
- }
- if len(s) > len(prefix) {
- if prefix[len(prefix)-1] == '/' || s[len(prefix)] == '/' {
- return s[:len(prefix)] == prefix
- }
- }
- return false
-}
-
-// HasFilePathPrefix reports whether the filesystem path s
-// begins with the elements in prefix.
-func HasFilePathPrefix(s, prefix string) bool {
- sv := strings.ToUpper(filepath.VolumeName(s))
- pv := strings.ToUpper(filepath.VolumeName(prefix))
- s = s[len(sv):]
- prefix = prefix[len(pv):]
- switch {
- default:
- return false
- case sv != pv:
- return false
- case len(s) == len(prefix):
- return s == prefix
- case prefix == "":
- return true
- case len(s) > len(prefix):
- if prefix[len(prefix)-1] == filepath.Separator {
- return strings.HasPrefix(s, prefix)
- }
- return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
- }
-}
-
-// TrimFilePathPrefix returns s without the leading path elements in prefix.
-// If s does not start with prefix (HasFilePathPrefix with the same arguments
-// returns false), TrimFilePathPrefix returns s. If s equals prefix,
-// TrimFilePathPrefix returns "".
-func TrimFilePathPrefix(s, prefix string) string {
- if !HasFilePathPrefix(s, prefix) {
- return s
- }
- if len(s) == len(prefix) {
- return ""
- }
- return s[len(prefix)+1:]
-}
diff --git a/src/cmd/internal/str/str.go b/src/cmd/internal/str/str.go
deleted file mode 100644
index 409cf8f7b4..0000000000
--- a/src/cmd/internal/str/str.go
+++ /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
-}