diff --git a/lib/std/os.zig b/lib/std/os.zig index e7cb233f65..db5803641f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -41,20 +41,6 @@ test { _ = windows; } -/// Deprecated in favor of accepting a `std.process.Init` argument to main. -/// To be removed after 0.14.0 is tagged. -/// See https://github.com/ziglang/zig/issues/4524 for more details. -pub var environ: [][*:0]u8 = undefined; - -/// Deprecated in favor of accepting a `std.process.Init` argument to main. -/// To be removed after 0.14.0 is tagged. -/// See https://github.com/ziglang/zig/issues/4524 for more details. -pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (native_os) { - .windows => @compileError("argv isn't supported on Windows: use std.process.argsAlloc instead"), - .wasi => @compileError("argv isn't supported on WASI: use std.process.argsAlloc instead"), - else => undefined, -}; - /// Call from Windows-specific code if you already have a WTF-16LE encoded, null terminated string. /// Otherwise use `access` or `accessZ`. pub fn accessW(path: [*:0]const u16) windows.GetFileAttributesError!void { diff --git a/lib/std/process.zig b/lib/std/process.zig index 0c8133cedf..1bd41a638c 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1137,484 +1137,6 @@ test hasEnvVar { try testing.expect(!has_env); } -/// Deprecated, to be removed after 0.14.0 is tagged. -pub const ArgIteratorPosix = Init.Args.Iterator.Posix; -/// Deprecated, to be removed after 0.14.0 is tagged. -pub const ArgIteratorWasi = Init.Args.Iterator.Wasi; -/// Deprecated, to be removed after 0.14.0 is tagged. -pub const ArgIteratorWindows = Init.Args.Iterator.Windows; -/// Deprecated, to be removed after 0.14.0 is tagged. -pub const ArgIterator = Init.Args.Iterator; - -/// Deprecated, to be removed after 0.14.0 is tagged. -pub const ArgIteratorGeneralOptions = struct { - comments: bool = false, - single_quotes: bool = false, -}; - -/// Deprecated, to be removed after 0.14.0 is tagged. -pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { - return struct { - allocator: Allocator, - index: usize = 0, - cmd_line: []const u8, - - /// Should the cmd_line field be free'd (using the allocator) on deinit()? - free_cmd_line_on_deinit: bool, - - /// buffer MUST be long enough to hold the cmd_line plus a null terminator. - /// buffer will we free'd (using the allocator) on deinit() - buffer: []u8, - start: usize = 0, - end: usize = 0, - - pub const Self = @This(); - - pub const InitError = error{OutOfMemory}; - - /// cmd_line_utf8 MUST remain valid and constant while using this instance - pub fn init(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self { - const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1); - errdefer allocator.free(buffer); - - return Self{ - .allocator = allocator, - .cmd_line = cmd_line_utf8, - .free_cmd_line_on_deinit = false, - .buffer = buffer, - }; - } - - /// cmd_line_utf8 will be free'd (with the allocator) on deinit() - pub fn initTakeOwnership(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self { - const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1); - errdefer allocator.free(buffer); - - return Self{ - .allocator = allocator, - .cmd_line = cmd_line_utf8, - .free_cmd_line_on_deinit = true, - .buffer = buffer, - }; - } - - // Skips over whitespace in the cmd_line. - // Returns false if the terminating sentinel is reached, true otherwise. - // Also skips over comments (if supported). - fn skipWhitespace(self: *Self) bool { - while (true) : (self.index += 1) { - const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; - switch (character) { - 0 => return false, - ' ', '\t', '\r', '\n' => continue, - '#' => { - if (options.comments) { - while (true) : (self.index += 1) { - switch (self.cmd_line[self.index]) { - '\n' => break, - 0 => return false, - else => continue, - } - } - continue; - } else { - break; - } - }, - else => break, - } - } - return true; - } - - pub fn skip(self: *Self) bool { - if (!self.skipWhitespace()) { - return false; - } - - var backslash_count: usize = 0; - var in_quote = false; - while (true) : (self.index += 1) { - 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; - } - }, - '\\' => { - backslash_count += 1; - }, - ' ', '\t', '\r', '\n' => { - if (!in_quote) { - return true; - } - backslash_count = 0; - }, - else => { - backslash_count = 0; - continue; - }, - } - } - } - - /// Returns a slice of the internal buffer that contains the next argument. - /// Returns null when it reaches the end. - pub fn next(self: *Self) ?[:0]const u8 { - if (!self.skipWhitespace()) { - return null; - } - - var backslash_count: usize = 0; - var in_quote = false; - while (true) : (self.index += 1) { - const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; - switch (character) { - 0 => { - self.emitBackslashes(backslash_count); - self.buffer[self.end] = 0; - const token = self.buffer[self.start..self.end :0]; - self.end += 1; - 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; - - if (quote_is_real) { - in_quote = !in_quote; - } else { - self.emitCharacter('"'); - } - }, - '\\' => { - backslash_count += 1; - }, - ' ', '\t', '\r', '\n' => { - self.emitBackslashes(backslash_count); - backslash_count = 0; - if (in_quote) { - self.emitCharacter(character); - } else { - self.buffer[self.end] = 0; - const token = self.buffer[self.start..self.end :0]; - self.end += 1; - self.start = self.end; - return token; - } - }, - else => { - self.emitBackslashes(backslash_count); - backslash_count = 0; - self.emitCharacter(character); - }, - } - } - } - - fn emitBackslashes(self: *Self, emit_count: usize) void { - var i: usize = 0; - while (i < emit_count) : (i += 1) { - self.emitCharacter('\\'); - } - } - - fn emitCharacter(self: *Self, char: u8) void { - self.buffer[self.end] = char; - self.end += 1; - } - - /// Call to free the internal buffer of the iterator. - pub fn deinit(self: *Self) void { - self.allocator.free(self.buffer); - - if (self.free_cmd_line_on_deinit) { - self.allocator.free(self.cmd_line); - } - } - }; -} - -/// Deprecated in favor of `Init.Args.iterate`. -/// To be removed after 0.14.0 is tagged. -pub fn args() Init.Args.Iterator { - var a: Init.Args = .{ .data = if (Init.Args.Data == void) {} else std.os.argv }; - return a.iterate(); -} - -/// Deprecated in favor of `Init.Args.iterate`. -/// To be removed after 0.14.0 is tagged. -pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!Init.Args.Iterator { - var a: Init.Args = .{ .data = if (Init.Args.Data == void) {} else std.os.argv }; - return a.iterateWithAllocator(allocator); -} - -/// Deprecated in favor of `Init.Args.toSlice`. -/// To be removed after 0.14.0 is tagged. -pub fn argsAlloc(allocator: Allocator) ![][:0]u8 { - var a: Init.Args = .{ .data = if (Init.Args.Data == void) {} else std.os.argv }; - return a.toSlice(allocator); -} - -/// Deprecated in favor of `Init.Args.freeSlice`. -/// To be removed after 0.14.0 is tagged. -pub fn argsFree(allocator: Allocator, args_alloc: []const [:0]u8) void { - Init.Args.freeSlice(allocator, args_alloc); -} - -test ArgIteratorWindows { - const t = testArgIteratorWindows; - - try t( - \\"C:\Program Files\zig\zig.exe" run .\src\main.zig -target x86_64-windows-gnu -O ReleaseSafe -- --emoji=🗿 --eval="new Regex(\"Dwayne \\\"The Rock\\\" Johnson\")" - , &.{ - \\C:\Program Files\zig\zig.exe - , - \\run - , - \\.\src\main.zig - , - \\-target - , - \\x86_64-windows-gnu - , - \\-O - , - \\ReleaseSafe - , - \\-- - , - \\--emoji=🗿 - , - \\--eval=new Regex("Dwayne \"The Rock\" Johnson") - , - }); - - // Empty - try t("", &.{}); - - // Separators - try t("aa bb cc", &.{ "aa", "bb", "cc" }); - try t("aa\tbb\tcc", &.{ "aa", "bb", "cc" }); - try t("aa\nbb\ncc", &.{"aa\nbb\ncc"}); - try t("aa\r\nbb\r\ncc", &.{"aa\r\nbb\r\ncc"}); - try t("aa\rbb\rcc", &.{"aa\rbb\rcc"}); - try t("aa\x07bb\x07cc", &.{"aa\x07bb\x07cc"}); - try t("aa\x7Fbb\x7Fcc", &.{"aa\x7Fbb\x7Fcc"}); - try t("aa🦎bb🦎cc", &.{"aa🦎bb🦎cc"}); - - // Leading/trailing whitespace - try t(" ", &.{""}); - try t(" aa bb ", &.{ "", "aa", "bb" }); - try t("\t\t", &.{""}); - try t("\t\taa\t\tbb\t\t", &.{ "", "aa", "bb" }); - try t("\n\n", &.{"\n\n"}); - try t("\n\naa\n\nbb\n\n", &.{"\n\naa\n\nbb\n\n"}); - - // Executable name with quotes/backslashes - try t("\"aa bb\tcc\ndd\"", &.{"aa bb\tcc\ndd"}); - try t("\"", &.{""}); - try t("\"\"", &.{""}); - try t("\"\"\"", &.{""}); - try t("\"\"\"\"", &.{""}); - try t("\"\"\"\"\"", &.{""}); - try t("aa\"bb\"cc\"dd", &.{"aabbccdd"}); - try t("aa\"bb cc\"dd", &.{"aabb ccdd"}); - try t("\"aa\\\"bb\"", &.{"aa\\bb"}); - try t("\"aa\\\\\"", &.{"aa\\\\"}); - try t("aa\\\"bb", &.{"aa\\bb"}); - try t("aa\\\\\"bb", &.{"aa\\\\bb"}); - - // Arguments with quotes/backslashes - try t(". \"aa bb\tcc\ndd\"", &.{ ".", "aa bb\tcc\ndd" }); - try t(". aa\" \"bb\"\t\"cc\"\n\"dd\"", &.{ ".", "aa bb\tcc\ndd" }); - try t(". ", &.{"."}); - try t(". \"", &.{ ".", "" }); - try t(". \"\"", &.{ ".", "" }); - try t(". \"\"\"", &.{ ".", "\"" }); - try t(". \"\"\"\"", &.{ ".", "\"" }); - try t(". \"\"\"\"\"", &.{ ".", "\"\"" }); - try t(". \"\"\"\"\"\"", &.{ ".", "\"\"" }); - try t(". \" \"", &.{ ".", " " }); - try t(". \" \"\"", &.{ ".", " \"" }); - try t(". \" \"\"\"", &.{ ".", " \"" }); - try t(". \" \"\"\"\"", &.{ ".", " \"\"" }); - try t(". \" \"\"\"\"\"", &.{ ".", " \"\"" }); - try t(". \" \"\"\"\"\"\"", &.{ ".", " \"\"\"" }); - try t(". \\\"", &.{ ".", "\"" }); - try t(". \\\"\"", &.{ ".", "\"" }); - try t(". \\\"\"\"", &.{ ".", "\"" }); - try t(". \\\"\"\"\"", &.{ ".", "\"\"" }); - try t(". \\\"\"\"\"\"", &.{ ".", "\"\"" }); - try t(". \\\"\"\"\"\"\"", &.{ ".", "\"\"\"" }); - try t(". \" \\\"", &.{ ".", " \"" }); - try t(". \" \\\"\"", &.{ ".", " \"" }); - try t(". \" \\\"\"\"", &.{ ".", " \"\"" }); - try t(". \" \\\"\"\"\"", &.{ ".", " \"\"" }); - try t(". \" \\\"\"\"\"\"", &.{ ".", " \"\"\"" }); - try t(". \" \\\"\"\"\"\"\"", &.{ ".", " \"\"\"" }); - try t(". aa\\bb\\\\cc\\\\\\dd", &.{ ".", "aa\\bb\\\\cc\\\\\\dd" }); - try t(". \\\\\\\"aa bb\"", &.{ ".", "\\\"aa", "bb" }); - try t(". \\\\\\\\\"aa bb\"", &.{ ".", "\\\\aa bb" }); - - // From https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args#results-of-parsing-command-lines - try t( - \\foo.exe "abc" d e - , &.{ "foo.exe", "abc", "d", "e" }); - try t( - \\foo.exe a\\b d"e f"g h - , &.{ "foo.exe", "a\\\\b", "de fg", "h" }); - try t( - \\foo.exe a\\\"b c d - , &.{ "foo.exe", "a\\\"b", "c", "d" }); - try t( - \\foo.exe a\\\\"b c" d e - , &.{ "foo.exe", "a\\\\b c", "d", "e" }); - try t( - \\foo.exe a"b"" c d - , &.{ "foo.exe", "ab\" c d" }); - - // From https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESEX - try t("foo.exe CallMeIshmael", &.{ "foo.exe", "CallMeIshmael" }); - try t("foo.exe \"Call Me Ishmael\"", &.{ "foo.exe", "Call Me Ishmael" }); - try t("foo.exe Cal\"l Me I\"shmael", &.{ "foo.exe", "Call Me Ishmael" }); - try t("foo.exe CallMe\\\"Ishmael", &.{ "foo.exe", "CallMe\"Ishmael" }); - try t("foo.exe \"CallMe\\\"Ishmael\"", &.{ "foo.exe", "CallMe\"Ishmael" }); - try t("foo.exe \"Call Me Ishmael\\\\\"", &.{ "foo.exe", "Call Me Ishmael\\" }); - try t("foo.exe \"CallMe\\\\\\\"Ishmael\"", &.{ "foo.exe", "CallMe\\\"Ishmael" }); - try t("foo.exe a\\\\\\b", &.{ "foo.exe", "a\\\\\\b" }); - try t("foo.exe \"a\\\\\\b\"", &.{ "foo.exe", "a\\\\\\b" }); - - // Surrogate pair encoding of 𐐷 separated by quotes. - // Encoded as WTF-16: - // "<0xD801>"<0xDC37> - // Encoded as WTF-8: - // "<0xED><0xA0><0x81>"<0xED><0xB0><0xB7> - // During parsing, the quotes drop out and the surrogate pair - // should end up encoded as its normal UTF-8 representation. - try t("foo.exe \"\xed\xa0\x81\"\xed\xb0\xb7", &.{ "foo.exe", "𐐷" }); -} - -fn testArgIteratorWindows(cmd_line: []const u8, expected_args: []const []const u8) !void { - const cmd_line_w = try unicode.wtf8ToWtf16LeAllocZ(testing.allocator, cmd_line); - defer testing.allocator.free(cmd_line_w); - - // next - { - var it = try ArgIteratorWindows.init(testing.allocator, cmd_line_w); - defer it.deinit(); - - for (expected_args) |expected| { - if (it.next()) |actual| { - try testing.expectEqualStrings(expected, actual); - } else { - return error.TestUnexpectedResult; - } - } - try testing.expect(it.next() == null); - } - - // skip - { - var it = try ArgIteratorWindows.init(testing.allocator, cmd_line_w); - defer it.deinit(); - - for (0..expected_args.len) |_| { - try testing.expect(it.skip()); - } - try testing.expect(!it.skip()); - } -} - -test "general arg parsing" { - 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\"", &.{ - ".\\..\\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(.{}).init(std.testing.allocator, input_cmd_line); - defer it.deinit(); - for (expected_args) |expected_arg| { - const arg = it.next().?; - try testing.expectEqualStrings(expected_arg, arg); - } - try testing.expect(it.next() == null); -} - -test "response file arg parsing" { - try testResponseFileCmdLine( - \\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 - \\ - , &.{ "j", "k", "l", "m" }); - - try testResponseFileCmdLine( - \\ "" q "" - \\ "r s # t" "u\" v" #another comment - \\ - , &.{ "", "q", "", "r s # t", "u\" v" }); - - try testResponseFileCmdLine( - \\ -l"advapi32" 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 = true, .single_quotes = true }) - .init(std.testing.allocator, input_cmd_line); - defer it.deinit(); - for (expected_args) |expected_arg| { - const arg = it.next().?; - try testing.expectEqualStrings(expected_arg, arg); - } - try testing.expect(it.next() == null); -} - pub const UserInfo = struct { uid: posix.uid_t, gid: posix.gid_t,