| ... | ... | @@ -664,7 +664,7 @@ pub const ArgIteratorWasi = struct { |
| 664 | 664 | pub const ArgIteratorWindows = struct { |
| 665 | 665 | allocator: Allocator, |
| 666 | 666 | /// Encoded as WTF-16 LE. |
| 667 | | cmd_line: [:0]const u16, |
| 667 | cmd_line: []const u16, |
| 668 | 668 | index: usize = 0, |
| 669 | 669 | /// Owned by the iterator. Long enough to hold contiguous NUL-terminated slices |
| 670 | 670 | /// of each argument encoded as WTF-8. |
| ... | ... | @@ -678,21 +678,22 @@ pub const ArgIteratorWindows = struct { |
| 678 | 678 | /// |
| 679 | 679 | /// The iterator stores and uses `cmd_line_w`, so its memory must be valid for |
| 680 | 680 | /// at least as long as the returned ArgIteratorWindows. |
| 681 | | pub fn init(allocator: Allocator, cmd_line_w: [*:0]const u16) InitError!ArgIteratorWindows { |
| 682 | | const cmd_line = mem.sliceTo(cmd_line_w, 0); |
| 683 | | const wtf8_len = unicode.calcWtf8Len(cmd_line); |
| 681 | pub fn init(allocator: Allocator, cmd_line_w: []const u16) InitError!ArgIteratorWindows { |
| 682 | const wtf8_len = unicode.calcWtf8Len(cmd_line_w); |
| 684 | 683 | |
| 685 | 684 | // This buffer must be large enough to contain contiguous NUL-terminated slices |
| 686 | | // of each argument. For arguments past the first one, space for the NUL-terminator |
| 687 | | // is guaranteed due to the necessary whitespace between arugments. However, we need |
| 688 | | // one extra byte to guarantee enough room for the NUL terminator if the command line |
| 689 | | // ends up being exactly 1 argument long with no quotes, etc. |
| 685 | // of each argument. |
| 686 | // - During parsing, the length of a parsed argument will always be equal to |
| 687 | // to less than its unparsed length |
| 688 | // - The first argument needs one extra byte of space allocated for its NUL |
| 689 | // terminator, but for each subsequent argument the necessary whitespace |
| 690 | // between arguments guarantees room for their NUL terminator(s). |
| 690 | 691 | const buffer = try allocator.alloc(u8, wtf8_len + 1); |
| 691 | 692 | errdefer allocator.free(buffer); |
| 692 | 693 | |
| 693 | 694 | return .{ |
| 694 | 695 | .allocator = allocator, |
| 695 | | .cmd_line = cmd_line, |
| 696 | .cmd_line = cmd_line_w, |
| 696 | 697 | .buffer = buffer, |
| 697 | 698 | }; |
| 698 | 699 | } |
| ... | ... | @@ -715,14 +716,20 @@ pub const ArgIteratorWindows = struct { |
| 715 | 716 | |
| 716 | 717 | const eof = null; |
| 717 | 718 | |
| 718 | | fn emitBackslashes(self: *ArgIteratorWindows, count: usize) void { |
| 719 | | for (0..count) |_| emitCharacter(self, '\\'); |
| 719 | /// Returns '\' if any backslashes are emitted, otherwise returns `last_emitted_code_unit`. |
| 720 | fn emitBackslashes(self: *ArgIteratorWindows, count: usize, last_emitted_code_unit: ?u16) ?u16 { |
| 721 | for (0..count) |_| { |
| 722 | self.buffer[self.end] = '\\'; |
| 723 | self.end += 1; |
| 724 | } |
| 725 | return if (count != 0) '\\' else last_emitted_code_unit; |
| 720 | 726 | } |
| 721 | 727 | |
| 722 | | fn emitCharacter(self: *ArgIteratorWindows, code_unit: u16) void { |
| 723 | | const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable; |
| 724 | | self.end += wtf8_len; |
| 725 | | |
| 728 | /// If `last_emitted_code_unit` and `code_unit` form a surrogate pair, then |
| 729 | /// the previously emitted high surrogate is overwritten by the codepoint encoded |
| 730 | /// by the surrogate pair, and `null` is returned. |
| 731 | /// Otherwise, `code_unit` is emitted and returned. |
| 732 | fn emitCharacter(self: *ArgIteratorWindows, code_unit: u16, last_emitted_code_unit: ?u16) ?u16 { |
| 726 | 733 | // Because we are emitting WTF-8, we need to |
| 727 | 734 | // check to see if we've emitted two consecutive surrogate |
| 728 | 735 | // codepoints that form a valid surrogate pair in order |
| ... | ... | @@ -743,28 +750,24 @@ pub const ArgIteratorWindows = struct { |
| 743 | 750 | // and emit the codepoint it encodes, which in this |
| 744 | 751 | // example is U+10437 (𐐷), which is encoded in UTF-8 as: |
| 745 | 752 | // <0xF0><0x90><0x90><0xB7> |
| 746 | | concatSurrogatePair(self); |
| 747 | | } |
| 748 | | |
| 749 | | fn concatSurrogatePair(self: *ArgIteratorWindows) void { |
| 750 | | // Surrogate codepoints are always encoded as 3 bytes, so there |
| 751 | | // must be 6 bytes for a surrogate pair to exist. |
| 752 | | if (self.end - self.start >= 6) { |
| 753 | | const window = self.buffer[self.end - 6 .. self.end]; |
| 754 | | const view = unicode.Wtf8View.init(window) catch return; |
| 755 | | var it = view.iterator(); |
| 756 | | var pair: [2]u16 = undefined; |
| 757 | | pair[0] = std.mem.nativeToLittle(u16, std.math.cast(u16, it.nextCodepoint().?) orelse return); |
| 758 | | if (!unicode.utf16IsHighSurrogate(std.mem.littleToNative(u16, pair[0]))) return; |
| 759 | | pair[1] = std.mem.nativeToLittle(u16, std.math.cast(u16, it.nextCodepoint().?) orelse return); |
| 760 | | if (!unicode.utf16IsLowSurrogate(std.mem.littleToNative(u16, pair[1]))) return; |
| 761 | | // We know we have a valid surrogate pair, so convert |
| 762 | | // it to UTF-8, overwriting the surrogate pair's bytes |
| 763 | | // and then chop off the extra bytes. |
| 764 | | const len = unicode.utf16LeToUtf8(window, &pair) catch unreachable; |
| 765 | | const delta = 6 - len; |
| 766 | | self.end -= delta; |
| 753 | if (last_emitted_code_unit != null and |
| 754 | std.unicode.utf16IsLowSurrogate(code_unit) and |
| 755 | std.unicode.utf16IsHighSurrogate(last_emitted_code_unit.?)) |
| 756 | { |
| 757 | const codepoint = std.unicode.utf16DecodeSurrogatePair(&.{ last_emitted_code_unit.?, code_unit }) catch unreachable; |
| 758 | |
| 759 | // Unpaired surrogate is 3 bytes long |
| 760 | const dest = self.buffer[self.end - 3 ..]; |
| 761 | const len = unicode.utf8Encode(codepoint, dest) catch unreachable; |
| 762 | // All codepoints that require a surrogate pair (> U+FFFF) are encoded as 4 bytes |
| 763 | assert(len == 4); |
| 764 | self.end += 1; |
| 765 | return null; |
| 767 | 766 | } |
| 767 | |
| 768 | const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable; |
| 769 | self.end += wtf8_len; |
| 770 | return code_unit; |
| 768 | 771 | } |
| 769 | 772 | |
| 770 | 773 | fn yieldArg(self: *ArgIteratorWindows) [:0]const u8 { |
| ... | ... | @@ -781,9 +784,13 @@ pub const ArgIteratorWindows = struct { |
| 781 | 784 | |
| 782 | 785 | const eof = false; |
| 783 | 786 | |
| 784 | | fn emitBackslashes(_: *ArgIteratorWindows, _: usize) void {} |
| 787 | fn emitBackslashes(_: *ArgIteratorWindows, _: usize, last_emitted_code_unit: ?u16) ?u16 { |
| 788 | return last_emitted_code_unit; |
| 789 | } |
| 785 | 790 | |
| 786 | | fn emitCharacter(_: *ArgIteratorWindows, _: u16) void {} |
| 791 | fn emitCharacter(_: *ArgIteratorWindows, _: u16, last_emitted_code_unit: ?u16) ?u16 { |
| 792 | return last_emitted_code_unit; |
| 793 | } |
| 787 | 794 | |
| 788 | 795 | fn yieldArg(_: *ArgIteratorWindows) bool { |
| 789 | 796 | return true; |
| ... | ... | @@ -791,6 +798,7 @@ pub const ArgIteratorWindows = struct { |
| 791 | 798 | }; |
| 792 | 799 | |
| 793 | 800 | fn nextWithStrategy(self: *ArgIteratorWindows, comptime strategy: type) strategy.T { |
| 801 | var last_emitted_code_unit: ?u16 = null; |
| 794 | 802 | // The first argument (the executable name) uses different parsing rules. |
| 795 | 803 | if (self.index == 0) { |
| 796 | 804 | if (self.cmd_line.len == 0 or self.cmd_line[0] == 0) { |
| ... | ... | @@ -813,15 +821,15 @@ pub const ArgIteratorWindows = struct { |
| 813 | 821 | inside_quotes = !inside_quotes; |
| 814 | 822 | }, |
| 815 | 823 | ' ', '\t' => { |
| 816 | | if (inside_quotes) |
| 817 | | strategy.emitCharacter(self, char) |
| 818 | | else { |
| 824 | if (inside_quotes) { |
| 825 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); |
| 826 | } else { |
| 819 | 827 | self.index += 1; |
| 820 | 828 | return strategy.yieldArg(self); |
| 821 | 829 | } |
| 822 | 830 | }, |
| 823 | 831 | else => { |
| 824 | | strategy.emitCharacter(self, char); |
| 832 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); |
| 825 | 833 | }, |
| 826 | 834 | } |
| 827 | 835 | } |
| ... | ... | @@ -859,29 +867,28 @@ pub const ArgIteratorWindows = struct { |
| 859 | 867 | 0; |
| 860 | 868 | switch (char) { |
| 861 | 869 | 0 => { |
| 862 | | strategy.emitBackslashes(self, backslash_count); |
| 870 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); |
| 863 | 871 | return strategy.yieldArg(self); |
| 864 | 872 | }, |
| 865 | 873 | ' ', '\t' => { |
| 866 | | strategy.emitBackslashes(self, backslash_count); |
| 874 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); |
| 867 | 875 | backslash_count = 0; |
| 868 | | if (inside_quotes) |
| 869 | | strategy.emitCharacter(self, char) |
| 870 | | else |
| 871 | | return strategy.yieldArg(self); |
| 876 | if (inside_quotes) { |
| 877 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); |
| 878 | } else return strategy.yieldArg(self); |
| 872 | 879 | }, |
| 873 | 880 | '"' => { |
| 874 | 881 | const char_is_escaped_quote = backslash_count % 2 != 0; |
| 875 | | strategy.emitBackslashes(self, backslash_count / 2); |
| 882 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count / 2, last_emitted_code_unit); |
| 876 | 883 | backslash_count = 0; |
| 877 | 884 | if (char_is_escaped_quote) { |
| 878 | | strategy.emitCharacter(self, '"'); |
| 885 | last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit); |
| 879 | 886 | } else { |
| 880 | 887 | if (inside_quotes and |
| 881 | 888 | self.index + 1 != self.cmd_line.len and |
| 882 | 889 | mem.littleToNative(u16, self.cmd_line[self.index + 1]) == '"') |
| 883 | 890 | { |
| 884 | | strategy.emitCharacter(self, '"'); |
| 891 | last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit); |
| 885 | 892 | self.index += 1; |
| 886 | 893 | } else { |
| 887 | 894 | inside_quotes = !inside_quotes; |
| ... | ... | @@ -892,9 +899,9 @@ pub const ArgIteratorWindows = struct { |
| 892 | 899 | backslash_count += 1; |
| 893 | 900 | }, |
| 894 | 901 | else => { |
| 895 | | strategy.emitBackslashes(self, backslash_count); |
| 902 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); |
| 896 | 903 | backslash_count = 0; |
| 897 | | strategy.emitCharacter(self, char); |
| 904 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); |
| 898 | 905 | }, |
| 899 | 906 | } |
| 900 | 907 | } |
| ... | ... | @@ -1142,7 +1149,8 @@ pub const ArgIterator = struct { |
| 1142 | 1149 | return ArgIterator{ .inner = try InnerType.init(allocator) }; |
| 1143 | 1150 | } |
| 1144 | 1151 | if (native_os == .windows) { |
| 1145 | | const cmd_line_w = windows.kernel32.GetCommandLineW(); |
| 1152 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; |
| 1153 | const cmd_line_w = cmd_line.Buffer.?[0 .. cmd_line.Length / 2]; |
| 1146 | 1154 | return ArgIterator{ .inner = try InnerType.init(allocator, cmd_line_w) }; |
| 1147 | 1155 | } |
| 1148 | 1156 | |