github.com/bir3/gocompiler@v0.9.2202/src/cmd/internal/quoted/quoted.go (about)

     1  // Copyright 2017 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 quoted provides string manipulation utilities.
     6  package quoted
     7  
     8  import (
     9  	"fmt"
    10  	"strings"
    11  	"unicode"
    12  )
    13  
    14  func isSpaceByte(c byte) bool {
    15  	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
    16  }
    17  
    18  // Split splits s into a list of fields,
    19  // allowing single or double quotes around elements.
    20  // There is no unescaping or other processing within
    21  // quoted fields.
    22  //
    23  // Keep in sync with cmd/dist/quoted.go
    24  func Split(s string) ([]string, error) {
    25  	// Split fields allowing '' or "" around elements.
    26  	// Quotes further inside the string do not count.
    27  	var f []string
    28  	for len(s) > 0 {
    29  		for len(s) > 0 && isSpaceByte(s[0]) {
    30  			s = s[1:]
    31  		}
    32  		if len(s) == 0 {
    33  			break
    34  		}
    35  		// Accepted quoted string. No unescaping inside.
    36  		if s[0] == '"' || s[0] == '\'' {
    37  			quote := s[0]
    38  			s = s[1:]
    39  			i := 0
    40  			for i < len(s) && s[i] != quote {
    41  				i++
    42  			}
    43  			if i >= len(s) {
    44  				return nil, fmt.Errorf("unterminated %c string", quote)
    45  			}
    46  			f = append(f, s[:i])
    47  			s = s[i+1:]
    48  			continue
    49  		}
    50  		i := 0
    51  		for i < len(s) && !isSpaceByte(s[i]) {
    52  			i++
    53  		}
    54  		f = append(f, s[:i])
    55  		s = s[i:]
    56  	}
    57  	return f, nil
    58  }
    59  
    60  // Join joins a list of arguments into a string that can be parsed
    61  // with Split. Arguments are quoted only if necessary; arguments
    62  // without spaces or quotes are kept as-is. No argument may contain both
    63  // single and double quotes.
    64  func Join(args []string) (string, error) {
    65  	var buf []byte
    66  	for i, arg := range args {
    67  		if i > 0 {
    68  			buf = append(buf, ' ')
    69  		}
    70  		var sawSpace, sawSingleQuote, sawDoubleQuote bool
    71  		for _, c := range arg {
    72  			switch {
    73  			case c > unicode.MaxASCII:
    74  				continue
    75  			case isSpaceByte(byte(c)):
    76  				sawSpace = true
    77  			case c == '\'':
    78  				sawSingleQuote = true
    79  			case c == '"':
    80  				sawDoubleQuote = true
    81  			}
    82  		}
    83  		switch {
    84  		case !sawSpace && !sawSingleQuote && !sawDoubleQuote:
    85  			buf = append(buf, arg...)
    86  
    87  		case !sawSingleQuote:
    88  			buf = append(buf, '\'')
    89  			buf = append(buf, arg...)
    90  			buf = append(buf, '\'')
    91  
    92  		case !sawDoubleQuote:
    93  			buf = append(buf, '"')
    94  			buf = append(buf, arg...)
    95  			buf = append(buf, '"')
    96  
    97  		default:
    98  			return "", fmt.Errorf("argument %q contains both single and double quotes and cannot be quoted", arg)
    99  		}
   100  	}
   101  	return string(buf), nil
   102  }
   103  
   104  // A Flag parses a list of string arguments encoded with Join.
   105  // It is useful for flags like cmd/link's -extldflags.
   106  type Flag []string
   107  
   108  
   109  func (f *Flag) Set(v string) error {
   110  	fs, err := Split(v)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	*f = fs[:len(fs):len(fs)]
   115  	return nil
   116  }
   117  
   118  func (f *Flag) String() string {
   119  	if f == nil {
   120  		return ""
   121  	}
   122  	s, err := Join(*f)
   123  	if err != nil {
   124  		return strings.Join(*f, " ")
   125  	}
   126  	return s
   127  }