| /*--------------------------------------------------------- | |
| * Copyright 2023 The Go Authors. All rights reserved. | |
| * Licensed under the MIT License. See LICENSE in the project root for license information. | |
| *--------------------------------------------------------*/ | |
| type ParseError = string; | |
| /** | |
| * Parses an argument string with shell-like semantics into a list of arguments. | |
| * Returns an error only if the argument string is malformed. | |
| * - Whitespace is treated as the word separator. | |
| * - Each word is treated as an argument to be passed to the invocation process. | |
| * - Single-quotes and double-quotes can be used to escape whitespace characters as literals. | |
| * - A backslash followed by a quote can be used (\' or \") to escape quotes as literals. | |
| * - Null arguments ("" or '') are retained and passed as empty strings. | |
| * When a null argument appears as part of a non-null argument, the null argument is removed. | |
| * That is, the word -d'' or ''-d becomes -d after word splitting and null argument removal. | |
| * @param args The string containing arguments to be parsed. | |
| */ | |
| export function parseArgsString(args: string): string[] | ParseError { | |
| const result: string[] = []; | |
| let word = ''; | |
| let bufferedWord = false; | |
| for (let i = 0; i < args.length;) { | |
| if (args[i] === "'" || args[i] === '"') { | |
| const quoteBegin = args[i]; | |
| let j = i + 1; | |
| let k = i + 1; | |
| for (; k < args.length && args[k] !== quoteBegin;) { | |
| // escaped quotes | |
| if (args[k] === '\\' && k + 1 < args.length && (args[k + 1] === "'" || args[k + 1] === '"')) { | |
| bufferedWord = true; | |
| // buffer everything up to this point, skipping backslash | |
| word += args.slice(j, k); | |
| word += args.charAt(k + 1); | |
| j = k + 2; | |
| k = k + 2; | |
| } else { | |
| k++; | |
| } | |
| } | |
| if (k >= args.length) { | |
| if (quoteBegin === "'") { | |
| return "args has unmatched single quotes ('). starting index: " + i; | |
| } else { | |
| return 'args has unmatched double quotes ("). starting index: ' + i; | |
| } | |
| } | |
| bufferedWord = true; | |
| word += args.slice(j, k); | |
| i = k + 1; | |
| } else if (args[i] === '\\' && i + 1 < args.length && (args[i + 1] === "'" || args[i + 1] === '"')) { | |
| // escaped quotes | |
| bufferedWord = true; | |
| word += args.charAt(i + 1); | |
| i = i + 2; | |
| } else if (args[i] !== ' ') { | |
| // a word | |
| let j = i + 1; | |
| // advance until a whitespace, or special char is encountered | |
| for (; j < args.length && args[j] !== ' ' && args[j] !== "'" && args[j] !== '"' && args[j] !== '\\'; j++) { | |
| // a word | |
| } | |
| bufferedWord = true; | |
| word += args.slice(i, j); | |
| i = j; | |
| } else if (bufferedWord) { | |
| // also true that args[i] === ' ' | |
| result.push(word); | |
| word = ''; | |
| bufferedWord = false; | |
| i++; | |
| } else { | |
| // args[i] === ' ' | |
| i++; | |
| } | |
| } | |
| if (bufferedWord) { | |
| result.push(word); | |
| } | |
| return result; | |
| } |