From 7d04ab1f14269b25a7ac03c3f787b3f3ee3453c3 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 4 Feb 2022 19:55:32 +0200 Subject: [PATCH] std.process: add option to support single quotes to ArgIteratorGeneral --- lib/std/process.zig | 62 +++++++++++++++++++++++++++++---------------- src/main.zig | 2 +- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 96bfed3718..699c994abf 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -303,7 +303,8 @@ pub const ArgIteratorWasi = struct { /// Optional parameters for `ArgIteratorGeneral` pub const ArgIteratorGeneralOptions = struct { - comments_supported: bool = false, + comments: bool = false, + single_quotes: bool = false, }; /// A general Iterator to parse a string into a set of arguments @@ -387,7 +388,7 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { 0 => return false, ' ', '\t', '\r', '\n' => continue, '#' => { - if (options.comments_supported) { + if (options.comments) { while (true) : (self.index += 1) { switch (self.cmd_line[self.index]) { '\n' => break, @@ -417,7 +418,11 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; switch (character) { 0 => return true, - '"' => { + '"', '\'' => { + if (!options.single_quotes and character == '\'') { + backslash_count = 0; + continue; + } const quote_is_real = backslash_count % 2 == 0; if (quote_is_real) { in_quote = !in_quote; @@ -460,7 +465,13 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { self.start = self.end; return token; }, - '"' => { + '"', '\'' => { + if (!options.single_quotes and character == '\'') { + self.emitBackslashes(backslash_count); + backslash_count = 0; + self.emitCharacter(character); + continue; + } const quote_is_real = backslash_count % 2 == 0; self.emitBackslashes(backslash_count / 2); backslash_count = 0; @@ -522,7 +533,7 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { /// Cross-platform command line argument iterator. pub const ArgIterator = struct { const InnerType = switch (builtin.os.tag) { - .windows => ArgIteratorGeneral(.{ .comments_supported = false }), + .windows => ArgIteratorGeneral(.{}), .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi, else => ArgIteratorPosix, }; @@ -664,27 +675,30 @@ pub fn argsFree(allocator: mem.Allocator, args_alloc: []const [:0]u8) void { } test "general arg parsing" { - try testGeneralCmdLine("a b\tc d", &[_][]const u8{ "a", "b", "c", "d" }); - try testGeneralCmdLine("\"abc\" d e", &[_][]const u8{ "abc", "d", "e" }); - try testGeneralCmdLine("a\\\\\\b d\"e f\"g h", &[_][]const u8{ "a\\\\\\b", "de fg", "h" }); - try testGeneralCmdLine("a\\\\\\\"b c d", &[_][]const u8{ "a\\\"b", "c", "d" }); - try testGeneralCmdLine("a\\\\\\\\\"b c\" d e", &[_][]const u8{ "a\\\\b c", "d", "e" }); - try testGeneralCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "d f" }); - try testGeneralCmdLine("j k l\\", &[_][]const u8{ "j", "k", "l\\" }); - try testGeneralCmdLine("\"\" x y z\\\\", &[_][]const u8{ "", "x", "y", "z\\\\" }); + try testGeneralCmdLine("a b\tc d", &.{ "a", "b", "c", "d" }); + try testGeneralCmdLine("\"abc\" d e", &.{ "abc", "d", "e" }); + try testGeneralCmdLine("a\\\\\\b d\"e f\"g h", &.{ "a\\\\\\b", "de fg", "h" }); + try testGeneralCmdLine("a\\\\\\\"b c d", &.{ "a\\\"b", "c", "d" }); + try testGeneralCmdLine("a\\\\\\\\\"b c\" d e", &.{ "a\\\\b c", "d", "e" }); + try testGeneralCmdLine("a b\tc \"d f", &.{ "a", "b", "c", "d f" }); + try testGeneralCmdLine("j k l\\", &.{ "j", "k", "l\\" }); + try testGeneralCmdLine("\"\" x y z\\\\", &.{ "", "x", "y", "z\\\\" }); - try testGeneralCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &[_][]const u8{ + try testGeneralCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &.{ ".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help", }); + + try testGeneralCmdLine( + \\ 'foo' "bar" + , &.{ "'foo'", "bar" }); } fn testGeneralCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { - var it = try ArgIteratorGeneral(.{ .comments_supported = false }) - .init(std.testing.allocator, input_cmd_line); + var it = try ArgIteratorGeneral(.{}).init(std.testing.allocator, input_cmd_line); defer it.deinit(); for (expected_args) |expected_arg| { const arg = it.next().?; @@ -697,30 +711,34 @@ test "response file arg parsing" { try testResponseFileCmdLine( \\a b \\c d\ - , &[_][]const u8{ "a", "b", "c", "d\\" }); - try testResponseFileCmdLine("a b c d\\", &[_][]const u8{ "a", "b", "c", "d\\" }); + , &.{ "a", "b", "c", "d\\" }); + try testResponseFileCmdLine("a b c d\\", &.{ "a", "b", "c", "d\\" }); try testResponseFileCmdLine( \\j \\ k l # this is a comment \\ \\\ \\\\ "none" "\\" "\\\" \\ "m" #another comment \\ - , &[_][]const u8{ "j", "k", "l", "m" }); + , &.{ "j", "k", "l", "m" }); try testResponseFileCmdLine( \\ "" q "" \\ "r s # t" "u\" v" #another comment \\ - , &[_][]const u8{ "", "q", "", "r s # t", "u\" v" }); + , &.{ "", "q", "", "r s # t", "u\" v" }); try testResponseFileCmdLine( \\ -l"advapi32" a# b#c d# \\e\\\ - , &[_][]const u8{ "-ladvapi32", "a#", "b#c", "d#", "e\\\\\\" }); + , &.{ "-ladvapi32", "a#", "b#c", "d#", "e\\\\\\" }); + + try testResponseFileCmdLine( + \\ 'foo' "bar" + , &.{ "foo", "bar" }); } fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { - var it = try ArgIteratorGeneral(.{ .comments_supported = true }) + var it = try ArgIteratorGeneral(.{ .comments = true, .single_quotes = true }) .init(std.testing.allocator, input_cmd_line); defer it.deinit(); for (expected_args) |expected_arg| { diff --git a/src/main.zig b/src/main.zig index bd25c5d234..bbdb948c90 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4230,7 +4230,7 @@ pub const ClangArgIterator = struct { }; } - const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments_supported = true }); + const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments = true, .single_quotes = true }); /// Initialize the arguments from a Response File. "*.rsp" fn initArgIteratorResponseFile(allocator: Allocator, resp_file_path: []const u8) !ArgIteratorResponseFile {