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 }