Skip to content

Instantly share code, notes, and snippets.

@matanlurey
Last active August 1, 2022 04:43
Show Gist options
  • Save matanlurey/b146e0f31efb8345a734d3644b46a5c1 to your computer and use it in GitHub Desktop.
Save matanlurey/b146e0f31efb8345a734d3644b46a5c1 to your computer and use it in GitHub Desktop.
Emulates shell-style string parsing into a list of arguments.
/// Parses a shell-style string [input] into a list of arguments.
///
/// ```dart
/// // [-x, 3, -y, 4, -abc, -beep=boop, foo, bar, baz]
/// print(argv('-x 3 -y 4 -abc -beep=boop foo "bar" \'baz\''));
/// ```
List<String> argv(String input) {
const $space = 0x20;
const $tab = 0x09;
const $newLine = 0x0a;
const $backSlash = 0x5c;
const $doubleQuote = 0x22;
const $singleQuote = 0x27;
final output = <String>[];
StringBuffer? argument;
int startingQuoteIndex;
for (var i = 0; i < input.length; i++) {
final code = input.codeUnitAt(i);
// If we've started an argument, then end it, otherwise keep waiting.
if (code == $space || code == $tab || code == $newLine) {
if (argument != null) {
output.add(argument.toString());
argument = null;
}
continue;
}
// Start composing a new argument.
argument ??= StringBuffer();
switch (code) {
case $backSlash:
if (i + 1 < input.length) {
final lookAhead = input.codeUnitAt(i + 1);
switch (lookAhead) {
case $space:
case $tab:
case $newLine:
break;
default:
argument.writeCharCode(lookAhead);
break;
}
} else {
throw FormatException(
'Unexpected terminal backslash',
input,
input.length - 1,
);
}
i++;
break;
case $doubleQuote:
case $singleQuote:
startingQuoteIndex = i;
while (++i < input.length) {
if (code == input.codeUnitAt(i)) {
argument.write(input.substring(startingQuoteIndex + 1, i));
break;
}
}
if (i == input.length) {
final type = code == $doubleQuote ? 'double' : 'single';
throw FormatException(
'Unterminated $type quote',
input,
startingQuoteIndex,
);
}
break;
default:
argument.writeCharCode(code);
break;
}
}
if (argument != null && argument.isNotEmpty) {
output.add(argument.toString());
}
return output;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment