| /* eslint-disable @typescript-eslint/quotes */ |
| /*--------------------------------------------------------- |
| * 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; |
| } |