Import Upstream version 0.0~git20150515.0.3f9db97
Konstantinos Margaritis
6 years ago
0 | ||
1 | Apache License | |
2 | Version 2.0, January 2004 | |
3 | http://www.apache.org/licenses/ | |
4 | ||
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
6 | ||
7 | 1. Definitions. | |
8 | ||
9 | "License" shall mean the terms and conditions for use, reproduction, | |
10 | and distribution as defined by Sections 1 through 9 of this document. | |
11 | ||
12 | "Licensor" shall mean the copyright owner or entity authorized by | |
13 | the copyright owner that is granting the License. | |
14 | ||
15 | "Legal Entity" shall mean the union of the acting entity and all | |
16 | other entities that control, are controlled by, or are under common | |
17 | control with that entity. For the purposes of this definition, | |
18 | "control" means (i) the power, direct or indirect, to cause the | |
19 | direction or management of such entity, whether by contract or | |
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
22 | ||
23 | "You" (or "Your") shall mean an individual or Legal Entity | |
24 | exercising permissions granted by this License. | |
25 | ||
26 | "Source" form shall mean the preferred form for making modifications, | |
27 | including but not limited to software source code, documentation | |
28 | source, and configuration files. | |
29 | ||
30 | "Object" form shall mean any form resulting from mechanical | |
31 | transformation or translation of a Source form, including but | |
32 | not limited to compiled object code, generated documentation, | |
33 | and conversions to other media types. | |
34 | ||
35 | "Work" shall mean the work of authorship, whether in Source or | |
36 | Object form, made available under the License, as indicated by a | |
37 | copyright notice that is included in or attached to the work | |
38 | (an example is provided in the Appendix below). | |
39 | ||
40 | "Derivative Works" shall mean any work, whether in Source or Object | |
41 | form, that is based on (or derived from) the Work and for which the | |
42 | editorial revisions, annotations, elaborations, or other modifications | |
43 | represent, as a whole, an original work of authorship. For the purposes | |
44 | of this License, Derivative Works shall not include works that remain | |
45 | separable from, or merely link (or bind by name) to the interfaces of, | |
46 | the Work and Derivative Works thereof. | |
47 | ||
48 | "Contribution" shall mean any work of authorship, including | |
49 | the original version of the Work and any modifications or additions | |
50 | to that Work or Derivative Works thereof, that is intentionally | |
51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
52 | or by an individual or Legal Entity authorized to submit on behalf of | |
53 | the copyright owner. For the purposes of this definition, "submitted" | |
54 | means any form of electronic, verbal, or written communication sent | |
55 | to the Licensor or its representatives, including but not limited to | |
56 | communication on electronic mailing lists, source code control systems, | |
57 | and issue tracking systems that are managed by, or on behalf of, the | |
58 | Licensor for the purpose of discussing and improving the Work, but | |
59 | excluding communication that is conspicuously marked or otherwise | |
60 | designated in writing by the copyright owner as "Not a Contribution." | |
61 | ||
62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
63 | on behalf of whom a Contribution has been received by Licensor and | |
64 | subsequently incorporated within the Work. | |
65 | ||
66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
67 | this License, each Contributor hereby grants to You a perpetual, | |
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
69 | copyright license to reproduce, prepare Derivative Works of, | |
70 | publicly display, publicly perform, sublicense, and distribute the | |
71 | Work and such Derivative Works in Source or Object form. | |
72 | ||
73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
74 | this License, each Contributor hereby grants to You a perpetual, | |
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
76 | (except as stated in this section) patent license to make, have made, | |
77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
78 | where such license applies only to those patent claims licensable | |
79 | by such Contributor that are necessarily infringed by their | |
80 | Contribution(s) alone or by combination of their Contribution(s) | |
81 | with the Work to which such Contribution(s) was submitted. If You | |
82 | institute patent litigation against any entity (including a | |
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
84 | or a Contribution incorporated within the Work constitutes direct | |
85 | or contributory patent infringement, then any patent licenses | |
86 | granted to You under this License for that Work shall terminate | |
87 | as of the date such litigation is filed. | |
88 | ||
89 | 4. Redistribution. You may reproduce and distribute copies of the | |
90 | Work or Derivative Works thereof in any medium, with or without | |
91 | modifications, and in Source or Object form, provided that You | |
92 | meet the following conditions: | |
93 | ||
94 | (a) You must give any other recipients of the Work or | |
95 | Derivative Works a copy of this License; and | |
96 | ||
97 | (b) You must cause any modified files to carry prominent notices | |
98 | stating that You changed the files; and | |
99 | ||
100 | (c) You must retain, in the Source form of any Derivative Works | |
101 | that You distribute, all copyright, patent, trademark, and | |
102 | attribution notices from the Source form of the Work, | |
103 | excluding those notices that do not pertain to any part of | |
104 | the Derivative Works; and | |
105 | ||
106 | (d) If the Work includes a "NOTICE" text file as part of its | |
107 | distribution, then any Derivative Works that You distribute must | |
108 | include a readable copy of the attribution notices contained | |
109 | within such NOTICE file, excluding those notices that do not | |
110 | pertain to any part of the Derivative Works, in at least one | |
111 | of the following places: within a NOTICE text file distributed | |
112 | as part of the Derivative Works; within the Source form or | |
113 | documentation, if provided along with the Derivative Works; or, | |
114 | within a display generated by the Derivative Works, if and | |
115 | wherever such third-party notices normally appear. The contents | |
116 | of the NOTICE file are for informational purposes only and | |
117 | do not modify the License. You may add Your own attribution | |
118 | notices within Derivative Works that You distribute, alongside | |
119 | or as an addendum to the NOTICE text from the Work, provided | |
120 | that such additional attribution notices cannot be construed | |
121 | as modifying the License. | |
122 | ||
123 | You may add Your own copyright statement to Your modifications and | |
124 | may provide additional or different license terms and conditions | |
125 | for use, reproduction, or distribution of Your modifications, or | |
126 | for any such Derivative Works as a whole, provided Your use, | |
127 | reproduction, and distribution of the Work otherwise complies with | |
128 | the conditions stated in this License. | |
129 | ||
130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
131 | any Contribution intentionally submitted for inclusion in the Work | |
132 | by You to the Licensor shall be under the terms and conditions of | |
133 | this License, without any additional terms or conditions. | |
134 | Notwithstanding the above, nothing herein shall supersede or modify | |
135 | the terms of any separate license agreement you may have executed | |
136 | with Licensor regarding such Contributions. | |
137 | ||
138 | 6. Trademarks. This License does not grant permission to use the trade | |
139 | names, trademarks, service marks, or product names of the Licensor, | |
140 | except as required for reasonable and customary use in describing the | |
141 | origin of the Work and reproducing the content of the NOTICE file. | |
142 | ||
143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
144 | agreed to in writing, Licensor provides the Work (and each | |
145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
147 | implied, including, without limitation, any warranties or conditions | |
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
150 | appropriateness of using or redistributing the Work and assume any | |
151 | risks associated with Your exercise of permissions under this License. | |
152 | ||
153 | 8. Limitation of Liability. In no event and under no legal theory, | |
154 | whether in tort (including negligence), contract, or otherwise, | |
155 | unless required by applicable law (such as deliberate and grossly | |
156 | negligent acts) or agreed to in writing, shall any Contributor be | |
157 | liable to You for damages, including any direct, indirect, special, | |
158 | incidental, or consequential damages of any character arising as a | |
159 | result of this License or out of the use or inability to use the | |
160 | Work (including but not limited to damages for loss of goodwill, | |
161 | work stoppage, computer failure or malfunction, or any and all | |
162 | other commercial damages or losses), even if such Contributor | |
163 | has been advised of the possibility of such damages. | |
164 | ||
165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
166 | the Work or Derivative Works thereof, You may choose to offer, | |
167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
168 | or other liability obligations and/or rights consistent with this | |
169 | License. However, in accepting such obligations, You may act only | |
170 | on Your own behalf and on Your sole responsibility, not on behalf | |
171 | of any other Contributor, and only if You agree to indemnify, | |
172 | defend, and hold each Contributor harmless for any liability | |
173 | incurred by, or claims asserted against, such Contributor by reason | |
174 | of your accepting any such warranty or additional liability. | |
175 | ||
176 | END OF TERMS AND CONDITIONS | |
177 | ||
178 | APPENDIX: How to apply the Apache License to your work. | |
179 | ||
180 | To apply the Apache License to your work, attach the following | |
181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
182 | replaced with your own identifying information. (Don't include | |
183 | the brackets!) The text should be enclosed in the appropriate | |
184 | comment syntax for the file format. We also recommend that a | |
185 | file or class name and description of purpose be included on the | |
186 | same "printed page" as the copyright notice for easier | |
187 | identification within third-party archives. | |
188 | ||
189 | Copyright [yyyy] [name of copyright owner] | |
190 | ||
191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
192 | you may not use this file except in compliance with the License. | |
193 | You may obtain a copy of the License at | |
194 | ||
195 | http://www.apache.org/licenses/LICENSE-2.0 | |
196 | ||
197 | Unless required by applicable law or agreed to in writing, software | |
198 | distributed under the License is distributed on an "AS IS" BASIS, | |
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
200 | See the License for the specific language governing permissions and | |
201 | limitations under the License. |
0 | # Copyright 2011 Google Inc. All Rights Reserved. | |
1 | # | |
2 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
3 | # you may not use this file except in compliance with the License. | |
4 | # You may obtain a copy of the License at | |
5 | # | |
6 | # http://www.apache.org/licenses/LICENSE-2.0 | |
7 | # | |
8 | # Unless required by applicable law or agreed to in writing, software | |
9 | # distributed under the License is distributed on an "AS IS" BASIS, | |
10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | # See the License for the specific language governing permissions and | |
12 | # limitations under the License. | |
13 | ||
14 | include $(GOROOT)/src/Make.inc | |
15 | ||
16 | TARG=shlex | |
17 | GOFILES=\ | |
18 | shlex.go\ | |
19 | ||
20 | include $(GOROOT)/src/Make.pkg |
0 | /* | |
1 | Copyright 2012 Google Inc. All Rights Reserved. | |
2 | ||
3 | Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | you may not use this file except in compliance with the License. | |
5 | You may obtain a copy of the License at | |
6 | ||
7 | http://www.apache.org/licenses/LICENSE-2.0 | |
8 | ||
9 | Unless required by applicable law or agreed to in writing, software | |
10 | distributed under the License is distributed on an "AS IS" BASIS, | |
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | See the License for the specific language governing permissions and | |
13 | limitations under the License. | |
14 | */ | |
15 | ||
16 | package shlex | |
17 | ||
18 | /* | |
19 | Package shlex implements a simple lexer which splits input in to tokens using | |
20 | shell-style rules for quoting and commenting. | |
21 | */ | |
22 | import ( | |
23 | "bufio" | |
24 | "errors" | |
25 | "fmt" | |
26 | "io" | |
27 | "strings" | |
28 | ) | |
29 | ||
30 | /* | |
31 | A TokenType is a top-level token; a word, space, comment, unknown. | |
32 | */ | |
33 | type TokenType int | |
34 | ||
35 | /* | |
36 | A RuneTokenType is the type of a UTF-8 character; a character, quote, space, escape. | |
37 | */ | |
38 | type RuneTokenType int | |
39 | ||
40 | type lexerState int | |
41 | ||
42 | type Token struct { | |
43 | tokenType TokenType | |
44 | value string | |
45 | } | |
46 | ||
47 | /* | |
48 | Two tokens are equal if both their types and values are equal. A nil token can | |
49 | never equal another token. | |
50 | */ | |
51 | func (a *Token) Equal(b *Token) bool { | |
52 | if a == nil || b == nil { | |
53 | return false | |
54 | } | |
55 | if a.tokenType != b.tokenType { | |
56 | return false | |
57 | } | |
58 | return a.value == b.value | |
59 | } | |
60 | ||
61 | const ( | |
62 | RUNE_CHAR string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-,/@$*()+=><:;&^%~|!?[]{}" | |
63 | RUNE_SPACE string = " \t\r\n" | |
64 | RUNE_ESCAPING_QUOTE string = "\"" | |
65 | RUNE_NONESCAPING_QUOTE string = "'" | |
66 | RUNE_ESCAPE = "\\" | |
67 | RUNE_COMMENT = "#" | |
68 | ||
69 | RUNETOKEN_UNKNOWN RuneTokenType = 0 | |
70 | RUNETOKEN_CHAR RuneTokenType = 1 | |
71 | RUNETOKEN_SPACE RuneTokenType = 2 | |
72 | RUNETOKEN_ESCAPING_QUOTE RuneTokenType = 3 | |
73 | RUNETOKEN_NONESCAPING_QUOTE RuneTokenType = 4 | |
74 | RUNETOKEN_ESCAPE RuneTokenType = 5 | |
75 | RUNETOKEN_COMMENT RuneTokenType = 6 | |
76 | RUNETOKEN_EOF RuneTokenType = 7 | |
77 | ||
78 | TOKEN_UNKNOWN TokenType = 0 | |
79 | TOKEN_WORD TokenType = 1 | |
80 | TOKEN_SPACE TokenType = 2 | |
81 | TOKEN_COMMENT TokenType = 3 | |
82 | ||
83 | STATE_START lexerState = 0 | |
84 | STATE_INWORD lexerState = 1 | |
85 | STATE_ESCAPING lexerState = 2 | |
86 | STATE_ESCAPING_QUOTED lexerState = 3 | |
87 | STATE_QUOTED_ESCAPING lexerState = 4 | |
88 | STATE_QUOTED lexerState = 5 | |
89 | STATE_COMMENT lexerState = 6 | |
90 | ||
91 | INITIAL_TOKEN_CAPACITY int = 100 | |
92 | ) | |
93 | ||
94 | /* | |
95 | A type for classifying characters. This allows for different sorts of | |
96 | classifiers - those accepting extended non-ascii chars, or strict posix | |
97 | compatibility, for example. | |
98 | */ | |
99 | type TokenClassifier struct { | |
100 | typeMap map[int32]RuneTokenType | |
101 | } | |
102 | ||
103 | func addRuneClass(typeMap *map[int32]RuneTokenType, runes string, tokenType RuneTokenType) { | |
104 | for _, rune := range runes { | |
105 | (*typeMap)[int32(rune)] = tokenType | |
106 | } | |
107 | } | |
108 | ||
109 | /* | |
110 | Create a new classifier for basic ASCII characters. | |
111 | */ | |
112 | func NewDefaultClassifier() *TokenClassifier { | |
113 | typeMap := map[int32]RuneTokenType{} | |
114 | addRuneClass(&typeMap, RUNE_CHAR, RUNETOKEN_CHAR) | |
115 | addRuneClass(&typeMap, RUNE_SPACE, RUNETOKEN_SPACE) | |
116 | addRuneClass(&typeMap, RUNE_ESCAPING_QUOTE, RUNETOKEN_ESCAPING_QUOTE) | |
117 | addRuneClass(&typeMap, RUNE_NONESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE) | |
118 | addRuneClass(&typeMap, RUNE_ESCAPE, RUNETOKEN_ESCAPE) | |
119 | addRuneClass(&typeMap, RUNE_COMMENT, RUNETOKEN_COMMENT) | |
120 | return &TokenClassifier{ | |
121 | typeMap: typeMap} | |
122 | } | |
123 | ||
124 | func (classifier *TokenClassifier) ClassifyRune(rune int32) RuneTokenType { | |
125 | return classifier.typeMap[rune] | |
126 | } | |
127 | ||
128 | /* | |
129 | A type for turning an input stream in to a sequence of strings. Whitespace and | |
130 | comments are skipped. | |
131 | */ | |
132 | type Lexer struct { | |
133 | tokenizer *Tokenizer | |
134 | } | |
135 | ||
136 | /* | |
137 | Create a new lexer. | |
138 | */ | |
139 | func NewLexer(r io.Reader) (*Lexer, error) { | |
140 | ||
141 | tokenizer, err := NewTokenizer(r) | |
142 | if err != nil { | |
143 | return nil, err | |
144 | } | |
145 | lexer := &Lexer{tokenizer: tokenizer} | |
146 | return lexer, nil | |
147 | } | |
148 | ||
149 | /* | |
150 | Return the next word, and an error value. If there are no more words, the error | |
151 | will be io.EOF. | |
152 | */ | |
153 | func (l *Lexer) NextWord() (string, error) { | |
154 | var token *Token | |
155 | var err error | |
156 | for { | |
157 | token, err = l.tokenizer.NextToken() | |
158 | if err != nil { | |
159 | return "", err | |
160 | } | |
161 | switch token.tokenType { | |
162 | case TOKEN_WORD: | |
163 | { | |
164 | return token.value, nil | |
165 | } | |
166 | case TOKEN_COMMENT: | |
167 | { | |
168 | // skip comments | |
169 | } | |
170 | default: | |
171 | { | |
172 | panic(fmt.Sprintf("Unknown token type: %v", token.tokenType)) | |
173 | } | |
174 | } | |
175 | } | |
176 | return "", io.EOF | |
177 | } | |
178 | ||
179 | /* | |
180 | A type for turning an input stream in to a sequence of typed tokens. | |
181 | */ | |
182 | type Tokenizer struct { | |
183 | input *bufio.Reader | |
184 | classifier *TokenClassifier | |
185 | } | |
186 | ||
187 | /* | |
188 | Create a new tokenizer. | |
189 | */ | |
190 | func NewTokenizer(r io.Reader) (*Tokenizer, error) { | |
191 | input := bufio.NewReader(r) | |
192 | classifier := NewDefaultClassifier() | |
193 | tokenizer := &Tokenizer{ | |
194 | input: input, | |
195 | classifier: classifier} | |
196 | return tokenizer, nil | |
197 | } | |
198 | ||
199 | /* | |
200 | Scan the stream for the next token. | |
201 | ||
202 | This uses an internal state machine. It will panic if it encounters a character | |
203 | which it does not know how to handle. | |
204 | */ | |
205 | func (t *Tokenizer) scanStream() (*Token, error) { | |
206 | state := STATE_START | |
207 | var tokenType TokenType | |
208 | value := make([]int32, 0, INITIAL_TOKEN_CAPACITY) | |
209 | var ( | |
210 | nextRune int32 | |
211 | nextRuneType RuneTokenType | |
212 | err error | |
213 | ) | |
214 | SCAN: | |
215 | for { | |
216 | nextRune, _, err = t.input.ReadRune() | |
217 | nextRuneType = t.classifier.ClassifyRune(nextRune) | |
218 | if err != nil { | |
219 | if err == io.EOF { | |
220 | nextRuneType = RUNETOKEN_EOF | |
221 | err = nil | |
222 | } else { | |
223 | return nil, err | |
224 | } | |
225 | } | |
226 | switch state { | |
227 | case STATE_START: // no runes read yet | |
228 | { | |
229 | switch nextRuneType { | |
230 | case RUNETOKEN_EOF: | |
231 | { | |
232 | return nil, io.EOF | |
233 | } | |
234 | case RUNETOKEN_CHAR: | |
235 | { | |
236 | tokenType = TOKEN_WORD | |
237 | value = append(value, nextRune) | |
238 | state = STATE_INWORD | |
239 | } | |
240 | case RUNETOKEN_SPACE: | |
241 | { | |
242 | } | |
243 | case RUNETOKEN_ESCAPING_QUOTE: | |
244 | { | |
245 | tokenType = TOKEN_WORD | |
246 | state = STATE_QUOTED_ESCAPING | |
247 | } | |
248 | case RUNETOKEN_NONESCAPING_QUOTE: | |
249 | { | |
250 | tokenType = TOKEN_WORD | |
251 | state = STATE_QUOTED | |
252 | } | |
253 | case RUNETOKEN_ESCAPE: | |
254 | { | |
255 | tokenType = TOKEN_WORD | |
256 | state = STATE_ESCAPING | |
257 | } | |
258 | case RUNETOKEN_COMMENT: | |
259 | { | |
260 | tokenType = TOKEN_COMMENT | |
261 | state = STATE_COMMENT | |
262 | } | |
263 | default: | |
264 | { | |
265 | return nil, errors.New(fmt.Sprintf("Unknown rune: %v", nextRune)) | |
266 | } | |
267 | } | |
268 | } | |
269 | case STATE_INWORD: // in a regular word | |
270 | { | |
271 | switch nextRuneType { | |
272 | case RUNETOKEN_EOF: | |
273 | { | |
274 | break SCAN | |
275 | } | |
276 | case RUNETOKEN_CHAR, RUNETOKEN_COMMENT: | |
277 | { | |
278 | value = append(value, nextRune) | |
279 | } | |
280 | case RUNETOKEN_SPACE: | |
281 | { | |
282 | t.input.UnreadRune() | |
283 | break SCAN | |
284 | } | |
285 | case RUNETOKEN_ESCAPING_QUOTE: | |
286 | { | |
287 | state = STATE_QUOTED_ESCAPING | |
288 | } | |
289 | case RUNETOKEN_NONESCAPING_QUOTE: | |
290 | { | |
291 | state = STATE_QUOTED | |
292 | } | |
293 | case RUNETOKEN_ESCAPE: | |
294 | { | |
295 | state = STATE_ESCAPING | |
296 | } | |
297 | default: | |
298 | { | |
299 | return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) | |
300 | } | |
301 | } | |
302 | } | |
303 | case STATE_ESCAPING: // the next rune after an escape character | |
304 | { | |
305 | switch nextRuneType { | |
306 | case RUNETOKEN_EOF: | |
307 | { | |
308 | err = errors.New("EOF found after escape character") | |
309 | break SCAN | |
310 | } | |
311 | case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: | |
312 | { | |
313 | state = STATE_INWORD | |
314 | value = append(value, nextRune) | |
315 | } | |
316 | default: | |
317 | { | |
318 | return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) | |
319 | } | |
320 | } | |
321 | } | |
322 | case STATE_ESCAPING_QUOTED: // the next rune after an escape character, in double quotes | |
323 | { | |
324 | switch nextRuneType { | |
325 | case RUNETOKEN_EOF: | |
326 | { | |
327 | err = errors.New("EOF found after escape character") | |
328 | break SCAN | |
329 | } | |
330 | case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: | |
331 | { | |
332 | state = STATE_QUOTED_ESCAPING | |
333 | value = append(value, nextRune) | |
334 | } | |
335 | default: | |
336 | { | |
337 | return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) | |
338 | } | |
339 | } | |
340 | } | |
341 | case STATE_QUOTED_ESCAPING: // in escaping double quotes | |
342 | { | |
343 | switch nextRuneType { | |
344 | case RUNETOKEN_EOF: | |
345 | { | |
346 | err = errors.New("EOF found when expecting closing quote.") | |
347 | break SCAN | |
348 | } | |
349 | case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_COMMENT: | |
350 | { | |
351 | value = append(value, nextRune) | |
352 | } | |
353 | case RUNETOKEN_ESCAPING_QUOTE: | |
354 | { | |
355 | state = STATE_INWORD | |
356 | } | |
357 | case RUNETOKEN_ESCAPE: | |
358 | { | |
359 | state = STATE_ESCAPING_QUOTED | |
360 | } | |
361 | default: | |
362 | { | |
363 | return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) | |
364 | } | |
365 | } | |
366 | } | |
367 | case STATE_QUOTED: // in non-escaping single quotes | |
368 | { | |
369 | switch nextRuneType { | |
370 | case RUNETOKEN_EOF: | |
371 | { | |
372 | err = errors.New("EOF found when expecting closing quote.") | |
373 | break SCAN | |
374 | } | |
375 | case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: | |
376 | { | |
377 | value = append(value, nextRune) | |
378 | } | |
379 | case RUNETOKEN_NONESCAPING_QUOTE: | |
380 | { | |
381 | state = STATE_INWORD | |
382 | } | |
383 | default: | |
384 | { | |
385 | return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) | |
386 | } | |
387 | } | |
388 | } | |
389 | case STATE_COMMENT: | |
390 | { | |
391 | switch nextRuneType { | |
392 | case RUNETOKEN_EOF: | |
393 | { | |
394 | break SCAN | |
395 | } | |
396 | case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT, RUNETOKEN_NONESCAPING_QUOTE: | |
397 | { | |
398 | value = append(value, nextRune) | |
399 | } | |
400 | case RUNETOKEN_SPACE: | |
401 | { | |
402 | if nextRune == '\n' { | |
403 | state = STATE_START | |
404 | break SCAN | |
405 | } else { | |
406 | value = append(value, nextRune) | |
407 | } | |
408 | } | |
409 | default: | |
410 | { | |
411 | return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) | |
412 | } | |
413 | } | |
414 | } | |
415 | default: | |
416 | { | |
417 | panic(fmt.Sprintf("Unexpected state: %v", state)) | |
418 | } | |
419 | } | |
420 | } | |
421 | token := &Token{ | |
422 | tokenType: tokenType, | |
423 | value: string(value)} | |
424 | return token, err | |
425 | } | |
426 | ||
427 | /* | |
428 | Return the next token in the stream, and an error value. If there are no more | |
429 | tokens available, the error value will be io.EOF. | |
430 | */ | |
431 | func (t *Tokenizer) NextToken() (*Token, error) { | |
432 | return t.scanStream() | |
433 | } | |
434 | ||
435 | /* | |
436 | Split a string in to a slice of strings, based upon shell-style rules for | |
437 | quoting, escaping, and spaces. | |
438 | */ | |
439 | func Split(s string) ([]string, error) { | |
440 | l, err := NewLexer(strings.NewReader(s)) | |
441 | if err != nil { | |
442 | return nil, err | |
443 | } | |
444 | subStrings := []string{} | |
445 | for { | |
446 | word, err := l.NextWord() | |
447 | if err != nil { | |
448 | if err == io.EOF { | |
449 | return subStrings, nil | |
450 | } | |
451 | return subStrings, err | |
452 | } | |
453 | subStrings = append(subStrings, word) | |
454 | } | |
455 | return subStrings, nil | |
456 | } |
0 | /* | |
1 | Copyright 2012 Google Inc. All Rights Reserved. | |
2 | ||
3 | Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | you may not use this file except in compliance with the License. | |
5 | You may obtain a copy of the License at | |
6 | ||
7 | http://www.apache.org/licenses/LICENSE-2.0 | |
8 | ||
9 | Unless required by applicable law or agreed to in writing, software | |
10 | distributed under the License is distributed on an "AS IS" BASIS, | |
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | See the License for the specific language governing permissions and | |
13 | limitations under the License. | |
14 | */ | |
15 | ||
16 | package shlex | |
17 | ||
18 | import ( | |
19 | "strings" | |
20 | "testing" | |
21 | ) | |
22 | ||
23 | func checkError(err error, t *testing.T) { | |
24 | if err != nil { | |
25 | t.Error(err) | |
26 | } | |
27 | } | |
28 | ||
29 | func TestClassifier(t *testing.T) { | |
30 | classifier := NewDefaultClassifier() | |
31 | runeTests := map[int32]RuneTokenType{ | |
32 | 'a': RUNETOKEN_CHAR, | |
33 | ' ': RUNETOKEN_SPACE, | |
34 | '"': RUNETOKEN_ESCAPING_QUOTE, | |
35 | '\'': RUNETOKEN_NONESCAPING_QUOTE, | |
36 | '#': RUNETOKEN_COMMENT} | |
37 | for rune, expectedType := range runeTests { | |
38 | foundType := classifier.ClassifyRune(rune) | |
39 | if foundType != expectedType { | |
40 | t.Logf("Expected type: %v for rune '%c'(%v). Found type: %v.", expectedType, rune, rune, foundType) | |
41 | t.Fail() | |
42 | } | |
43 | } | |
44 | } | |
45 | ||
46 | func TestTokenizer(t *testing.T) { | |
47 | testInput := strings.NewReader("one two \"three four\" \"five \\\"six\\\"\" seven#eight # nine # ten\n eleven") | |
48 | expectedTokens := []*Token{ | |
49 | &Token{ | |
50 | tokenType: TOKEN_WORD, | |
51 | value: "one"}, | |
52 | &Token{ | |
53 | tokenType: TOKEN_WORD, | |
54 | value: "two"}, | |
55 | &Token{ | |
56 | tokenType: TOKEN_WORD, | |
57 | value: "three four"}, | |
58 | &Token{ | |
59 | tokenType: TOKEN_WORD, | |
60 | value: "five \"six\""}, | |
61 | &Token{ | |
62 | tokenType: TOKEN_WORD, | |
63 | value: "seven#eight"}, | |
64 | &Token{ | |
65 | tokenType: TOKEN_COMMENT, | |
66 | value: " nine # ten"}, | |
67 | &Token{ | |
68 | tokenType: TOKEN_WORD, | |
69 | value: "eleven"}} | |
70 | ||
71 | tokenizer, err := NewTokenizer(testInput) | |
72 | checkError(err, t) | |
73 | for _, expectedToken := range expectedTokens { | |
74 | foundToken, err := tokenizer.NextToken() | |
75 | checkError(err, t) | |
76 | if !foundToken.Equal(expectedToken) { | |
77 | t.Error("Expected token:", expectedToken, ". Found:", foundToken) | |
78 | } | |
79 | } | |
80 | } | |
81 | ||
82 | func TestLexer(t *testing.T) { | |
83 | testInput := strings.NewReader("one") | |
84 | expectedWord := "one" | |
85 | lexer, err := NewLexer(testInput) | |
86 | checkError(err, t) | |
87 | foundWord, err := lexer.NextWord() | |
88 | checkError(err, t) | |
89 | if expectedWord != foundWord { | |
90 | t.Error("Expected word:", expectedWord, ". Found:", foundWord) | |
91 | } | |
92 | } | |
93 | ||
94 | func TestSplitSimple(t *testing.T) { | |
95 | testInput := "one two three" | |
96 | expectedOutput := []string{"one", "two", "three"} | |
97 | foundOutput, err := Split(testInput) | |
98 | if err != nil { | |
99 | t.Error("Split returned error:", err) | |
100 | } | |
101 | if len(expectedOutput) != len(foundOutput) { | |
102 | t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") | |
103 | } | |
104 | for i := range foundOutput { | |
105 | if foundOutput[i] != expectedOutput[i] { | |
106 | t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) | |
107 | } | |
108 | } | |
109 | } | |
110 | ||
111 | func TestSplitEscapingQuotes(t *testing.T) { | |
112 | testInput := "one \"два ${three}\" four" | |
113 | expectedOutput := []string{"one", "два ${three}", "four"} | |
114 | foundOutput, err := Split(testInput) | |
115 | if err != nil { | |
116 | t.Error("Split returned error:", err) | |
117 | } | |
118 | if len(expectedOutput) != len(foundOutput) { | |
119 | t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") | |
120 | } | |
121 | for i := range foundOutput { | |
122 | if foundOutput[i] != expectedOutput[i] { | |
123 | t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
128 | func TestGlobbingExpressions(t *testing.T) { | |
129 | testInput := "onefile *file one?ile onefil[de]" | |
130 | expectedOutput := []string{"onefile", "*file", "one?ile", "onefil[de]"} | |
131 | foundOutput, err := Split(testInput) | |
132 | if err != nil { | |
133 | t.Error("Split returned error", err) | |
134 | } | |
135 | if len(expectedOutput) != len(foundOutput) { | |
136 | t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") | |
137 | } | |
138 | for i := range foundOutput { | |
139 | if foundOutput[i] != expectedOutput[i] { | |
140 | t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) | |
141 | } | |
142 | } | |
143 | ||
144 | } | |
145 | ||
146 | func TestSplitNonEscapingQuotes(t *testing.T) { | |
147 | testInput := "one 'два ${three}' four" | |
148 | expectedOutput := []string{"one", "два ${three}", "four"} | |
149 | foundOutput, err := Split(testInput) | |
150 | if err != nil { | |
151 | t.Error("Split returned error:", err) | |
152 | } | |
153 | if len(expectedOutput) != len(foundOutput) { | |
154 | t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") | |
155 | } | |
156 | for i := range foundOutput { | |
157 | if foundOutput[i] != expectedOutput[i] { | |
158 | t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) | |
159 | } | |
160 | } | |
161 | } |