github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/shlex/shlex.go (about)

     1  // Copyright 2017-2020 the u-root 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 shlex is a simplified command-line shell-like argument parser.
     6  //
     7  // shlex will parse for example
     8  //
     9  //     start --append="foobar foobaz" --nogood 'food'
    10  //
    11  // into the appropriate argvs to start the command.
    12  package shlex
    13  
    14  func isWhitespace(b byte) bool {
    15  	return b == '\t' || b == '\n' || b == '\v' ||
    16  		b == '\f' || b == '\r' || b == ' '
    17  }
    18  
    19  type quote uint8
    20  
    21  const (
    22  	unquoted quote = iota
    23  	escape
    24  	singleQuote
    25  	doubleQuote
    26  	doubleQuoteEscape
    27  	comment
    28  )
    29  
    30  // Argv splits a command line according to usual simple shell rules.
    31  //
    32  // Argv was written from the spec of Grub quoting at
    33  // https://www.gnu.org/software/grub/manual/grub/grub.html#Quoting
    34  // except that the escaping of newline is not supported
    35  func Argv(s string) []string {
    36  	var ret []string
    37  	var token []byte
    38  
    39  	var context quote
    40  	lastWhiteSpace := true
    41  	for i := range []byte(s) {
    42  		quotes := context != unquoted
    43  		switch context {
    44  		case unquoted:
    45  			switch s[i] {
    46  			case '\\':
    47  				context = escape
    48  				// strip out the quote
    49  				continue
    50  			case '\'':
    51  				context = singleQuote
    52  				// strip out the quote
    53  				continue
    54  			case '"':
    55  				context = doubleQuote
    56  				// strip out the quote
    57  				continue
    58  			case '#':
    59  				if lastWhiteSpace {
    60  					context = comment
    61  					// strip out the rest
    62  					continue
    63  				}
    64  			}
    65  
    66  		case escape:
    67  			context = unquoted
    68  
    69  		case singleQuote:
    70  			if s[i] == '\'' {
    71  				context = unquoted
    72  				// strip out the quote
    73  				continue
    74  			}
    75  
    76  		case doubleQuote:
    77  			switch s[i] {
    78  			case '\\':
    79  				context = doubleQuoteEscape
    80  				// strip out the quote
    81  				continue
    82  			case '"':
    83  				context = unquoted
    84  				// strip out the quote
    85  				continue
    86  			}
    87  
    88  		case doubleQuoteEscape:
    89  			switch s[i] {
    90  			case '$', '"', '\\', '\n': // or newline
    91  			default:
    92  				token = append(token, '\\')
    93  			}
    94  
    95  			context = doubleQuote
    96  
    97  		case comment:
    98  			// should end on newline
    99  
   100  			// strip out the rest
   101  			continue
   102  
   103  		}
   104  
   105  		lastWhiteSpace = isWhitespace(s[i])
   106  
   107  		if !isWhitespace(s[i]) || quotes {
   108  			token = append(token, s[i])
   109  		} else if len(token) > 0 {
   110  			ret = append(ret, string(token))
   111  			token = token[:0]
   112  		}
   113  	}
   114  
   115  	if len(token) > 0 {
   116  		ret = append(ret, string(token))
   117  	}
   118  	return ret
   119  }