Skip to content

Instantly share code, notes, and snippets.

@raygun101
Last active September 14, 2020 11:13
Show Gist options
  • Save raygun101/1d93f2a7f04e1c51bfb9b82290a5b453 to your computer and use it in GitHub Desktop.
Save raygun101/1d93f2a7f04e1c51bfb9b82290a5b453 to your computer and use it in GitHub Desktop.
🍰 Layered Cakewalk [Dart] - Parses/splits a command-line String into a List<String> of the individual argument elements processing quotes.
//
// Original implementation: https://stackoverflow.com/a/39177861/638848
//
// Re-made by: Leslie Godwin (leslie.godwin@gmail.com)
import 'dart:convert';
/// Parses/splits a command-line String into a List<String> of the individual argument elements.
/// Handles whitespace, quotes and back\slash escape.
///
/// ### Example:
/// ```
/// command-line $> command -x value -y "value with 'spaces'" -z value\ with\'\ spaces
/// result-arguments : ["command", "-x", "value", "y", "value with 'spaces'", "-z", "value with' spaces"]
/// ```
class CommandlineSplittingConverter extends Converter<String, List<String>>
{
@override
List<String> convert(String input)
{
if (input == null || input.isEmpty) { return []; } // ⤴️
final result = <String>[];
var current = "";
String inEscape = null;
int inPosition = null;
for (int index = 0; index < input.length; index++)
{
final token = input[index];
if (inEscape != null)
{
switch (inEscape)
{
case "\\":
inEscape = null;
inPosition = null;
current += token;
continue; // ⤴️
default:
if (token == inEscape)
{
result.add(current); // allow empty
current = "";
inEscape = null;
inPosition = null;
}
else
{
current += token;
}
}
}
else
{
switch (token)
{
case "'": // ' (Single quote)
case '"': // "" (Double quote)
case "\\": // \ (Escape)
inEscape = token;
inPosition = index;
continue; // ⤴️
case " ": // space
if (current.isNotEmpty)
{
result.add(current);
current = "";
}
continue; // ⤴️
default:
current += token;
}
}
}
if (inEscape != null)
{
throw new FormatException("Unbalanced quote $inEscape in input.", input, inPosition);
}
if (current.isNotEmpty)
{
result.add(current);
}
return result;
}
}
// //Tests
//
// Set<E> diff <E> (Iterable<E> lhs, Iterable<E> rhs)
// {
// final a = Set.of(lhs);
// final b = Set.of(rhs);
// return a.difference(b)..addAll(b.difference(a));
// }
// Set<String> commandlineDiff (String lhs, List<String> rhs)
// {
// return diff(CommandlineSplittingConverter().convert(lhs), rhs);
// }
// test("$CommandlineSplittingConverter", ()
// {
// expect
// (
// commandlineDiff("", []),
// isEmpty
// );
// expect
// (
// commandlineDiff("command", ["command"]),
// isEmpty
// );
// expect
// (
// commandlineDiff("command -arg 1 -arg2 'f r\"ed'", ["command", "-arg", "1", "-arg2", "f r\"ed"]),
// isEmpty
// );
// expect
// (
// commandlineDiff('command -arg 1 -arg2 "f r\'ed"', ["command", "-arg", "1", "-arg2", "f r'ed"]),
// isEmpty
// );
// expect
// (
// commandlineDiff("command -x value -y \"value with 'spaces'\" -z value\\ with\\'\\ spaces", ["command", "-x", "value", "-y", "value with 'spaces'", "-z", "value with' spaces"]),
// isEmpty
// );
// expect
// (
// () => CommandlineSplittingConverter().convert('command -arg 1 -arg2 "f r\'ed'),
// throwsFormatException
// );
// });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment