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