| ... | @@ -717,14 +717,20 @@ pub const ArgIteratorWindows = struct { | ... | @@ -717,14 +717,20 @@ pub const ArgIteratorWindows = struct { |
| 717 | | 717 | |
| 718 | const eof = null; | 718 | const eof = null; |
| 719 | | 719 | |
| 720 | fn emitBackslashes(self: *ArgIteratorWindows, count: usize) void { | 720 | /// Returns '\' if any backslashes are emitted, otherwise returns `last_emitted_code_unit`. |
| 721 | for (0..count) |_| emitCharacter(self, '\\'); | 721 | fn emitBackslashes(self: *ArgIteratorWindows, count: usize, last_emitted_code_unit: ?u16) ?u16 { |
| | 722 | for (0..count) |_| { |
| | 723 | self.buffer[self.end] = '\\'; |
| | 724 | self.end += 1; |
| | 725 | } |
| | 726 | return if (count != 0) '\\' else last_emitted_code_unit; |
| 722 | } | 727 | } |
| 723 | | 728 | |
| 724 | fn emitCharacter(self: *ArgIteratorWindows, code_unit: u16) void { | 729 | /// If `last_emitted_code_unit` and `code_unit` form a surrogate pair, then |
| 725 | const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable; | 730 | /// the previously emitted high surrogate is overwritten by the codepoint encoded |
| 726 | self.end += wtf8_len; | 731 | /// by the surrogate pair, and `null` is returned. |
| 727 | | 732 | /// Otherwise, `code_unit` is emitted and returned. |
| | 733 | fn emitCharacter(self: *ArgIteratorWindows, code_unit: u16, last_emitted_code_unit: ?u16) ?u16 { |
| 728 | // Because we are emitting WTF-8, we need to | 734 | // Because we are emitting WTF-8, we need to |
| 729 | // check to see if we've emitted two consecutive surrogate | 735 | // check to see if we've emitted two consecutive surrogate |
| 730 | // codepoints that form a valid surrogate pair in order | 736 | // codepoints that form a valid surrogate pair in order |
| ... | @@ -745,28 +751,24 @@ pub const ArgIteratorWindows = struct { | ... | @@ -745,28 +751,24 @@ pub const ArgIteratorWindows = struct { |
| 745 | // and emit the codepoint it encodes, which in this | 751 | // and emit the codepoint it encodes, which in this |
| 746 | // example is U+10437 (𐐷), which is encoded in UTF-8 as: | 752 | // example is U+10437 (𐐷), which is encoded in UTF-8 as: |
| 747 | // <0xF0><0x90><0x90><0xB7> | 753 | // <0xF0><0x90><0x90><0xB7> |
| 748 | concatSurrogatePair(self); | 754 | if (last_emitted_code_unit != null and |
| 749 | } | 755 | std.unicode.utf16IsLowSurrogate(code_unit) and |
| 750 | | 756 | std.unicode.utf16IsHighSurrogate(last_emitted_code_unit.?)) |
| 751 | fn concatSurrogatePair(self: *ArgIteratorWindows) void { | 757 | { |
| 752 | // Surrogate codepoints are always encoded as 3 bytes, so there | 758 | const codepoint = std.unicode.utf16DecodeSurrogatePair(&.{ last_emitted_code_unit.?, code_unit }) catch unreachable; |
| 753 | // must be 6 bytes for a surrogate pair to exist. | 759 | |
| 754 | if (self.end - self.start >= 6) { | 760 | // Unpaired surrogate is 3 bytes long |
| 755 | const window = self.buffer[self.end - 6 .. self.end]; | 761 | const dest = self.buffer[self.end - 3 ..]; |
| 756 | const view = unicode.Wtf8View.init(window) catch return; | 762 | const len = unicode.utf8Encode(codepoint, dest) catch unreachable; |
| 757 | var it = view.iterator(); | 763 | // All codepoints that require a surrogate pair (> U+FFFF) are encoded as 4 bytes |
| 758 | var pair: [2]u16 = undefined; | 764 | assert(len == 4); |
| 759 | pair[0] = std.mem.nativeToLittle(u16, std.math.cast(u16, it.nextCodepoint().?) orelse return); | 765 | self.end += 1; |
| 760 | if (!unicode.utf16IsHighSurrogate(std.mem.littleToNative(u16, pair[0]))) return; | 766 | return null; |
| 761 | pair[1] = std.mem.nativeToLittle(u16, std.math.cast(u16, it.nextCodepoint().?) orelse return); | | |
| 762 | if (!unicode.utf16IsLowSurrogate(std.mem.littleToNative(u16, pair[1]))) return; | | |
| 763 | // We know we have a valid surrogate pair, so convert | | |
| 764 | // it to UTF-8, overwriting the surrogate pair's bytes | | |
| 765 | // and then chop off the extra bytes. | | |
| 766 | const len = unicode.utf16LeToUtf8(window, &pair) catch unreachable; | | |
| 767 | const delta = 6 - len; | | |
| 768 | self.end -= delta; | | |
| 769 | } | 767 | } |
| | 768 | |
| | 769 | const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable; |
| | 770 | self.end += wtf8_len; |
| | 771 | return code_unit; |
| 770 | } | 772 | } |
| 771 | | 773 | |
| 772 | fn yieldArg(self: *ArgIteratorWindows) [:0]const u8 { | 774 | fn yieldArg(self: *ArgIteratorWindows) [:0]const u8 { |
| ... | @@ -783,9 +785,13 @@ pub const ArgIteratorWindows = struct { | ... | @@ -783,9 +785,13 @@ pub const ArgIteratorWindows = struct { |
| 783 | | 785 | |
| 784 | const eof = false; | 786 | const eof = false; |
| 785 | | 787 | |
| 786 | fn emitBackslashes(_: *ArgIteratorWindows, _: usize) void {} | 788 | fn emitBackslashes(_: *ArgIteratorWindows, _: usize, last_emitted_code_unit: ?u16) ?u16 { |
| | 789 | return last_emitted_code_unit; |
| | 790 | } |
| 787 | | 791 | |
| 788 | fn emitCharacter(_: *ArgIteratorWindows, _: u16) void {} | 792 | fn emitCharacter(_: *ArgIteratorWindows, _: u16, last_emitted_code_unit: ?u16) ?u16 { |
| | 793 | return last_emitted_code_unit; |
| | 794 | } |
| 789 | | 795 | |
| 790 | fn yieldArg(_: *ArgIteratorWindows) bool { | 796 | fn yieldArg(_: *ArgIteratorWindows) bool { |
| 791 | return true; | 797 | return true; |
| ... | @@ -793,6 +799,7 @@ pub const ArgIteratorWindows = struct { | ... | @@ -793,6 +799,7 @@ pub const ArgIteratorWindows = struct { |
| 793 | }; | 799 | }; |
| 794 | | 800 | |
| 795 | fn nextWithStrategy(self: *ArgIteratorWindows, comptime strategy: type) strategy.T { | 801 | fn nextWithStrategy(self: *ArgIteratorWindows, comptime strategy: type) strategy.T { |
| | 802 | var last_emitted_code_unit: ?u16 = null; |
| 796 | // The first argument (the executable name) uses different parsing rules. | 803 | // The first argument (the executable name) uses different parsing rules. |
| 797 | if (self.index == 0) { | 804 | if (self.index == 0) { |
| 798 | if (self.cmd_line.len == 0 or self.cmd_line[0] == 0) { | 805 | if (self.cmd_line.len == 0 or self.cmd_line[0] == 0) { |
| ... | @@ -815,15 +822,15 @@ pub const ArgIteratorWindows = struct { | ... | @@ -815,15 +822,15 @@ pub const ArgIteratorWindows = struct { |
| 815 | inside_quotes = !inside_quotes; | 822 | inside_quotes = !inside_quotes; |
| 816 | }, | 823 | }, |
| 817 | ' ', '\t' => { | 824 | ' ', '\t' => { |
| 818 | if (inside_quotes) | 825 | if (inside_quotes) { |
| 819 | strategy.emitCharacter(self, char) | 826 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); |
| 820 | else { | 827 | } else { |
| 821 | self.index += 1; | 828 | self.index += 1; |
| 822 | return strategy.yieldArg(self); | 829 | return strategy.yieldArg(self); |
| 823 | } | 830 | } |
| 824 | }, | 831 | }, |
| 825 | else => { | 832 | else => { |
| 826 | strategy.emitCharacter(self, char); | 833 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); |
| 827 | }, | 834 | }, |
| 828 | } | 835 | } |
| 829 | } | 836 | } |
| ... | @@ -861,29 +868,28 @@ pub const ArgIteratorWindows = struct { | ... | @@ -861,29 +868,28 @@ pub const ArgIteratorWindows = struct { |
| 861 | 0; | 868 | 0; |
| 862 | switch (char) { | 869 | switch (char) { |
| 863 | 0 => { | 870 | 0 => { |
| 864 | strategy.emitBackslashes(self, backslash_count); | 871 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); |
| 865 | return strategy.yieldArg(self); | 872 | return strategy.yieldArg(self); |
| 866 | }, | 873 | }, |
| 867 | ' ', '\t' => { | 874 | ' ', '\t' => { |
| 868 | strategy.emitBackslashes(self, backslash_count); | 875 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); |
| 869 | backslash_count = 0; | 876 | backslash_count = 0; |
| 870 | if (inside_quotes) | 877 | if (inside_quotes) { |
| 871 | strategy.emitCharacter(self, char) | 878 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); |
| 872 | else | 879 | } else return strategy.yieldArg(self); |
| 873 | return strategy.yieldArg(self); | | |
| 874 | }, | 880 | }, |
| 875 | '"' => { | 881 | '"' => { |
| 876 | const char_is_escaped_quote = backslash_count % 2 != 0; | 882 | const char_is_escaped_quote = backslash_count % 2 != 0; |
| 877 | strategy.emitBackslashes(self, backslash_count / 2); | 883 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count / 2, last_emitted_code_unit); |
| 878 | backslash_count = 0; | 884 | backslash_count = 0; |
| 879 | if (char_is_escaped_quote) { | 885 | if (char_is_escaped_quote) { |
| 880 | strategy.emitCharacter(self, '"'); | 886 | last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit); |
| 881 | } else { | 887 | } else { |
| 882 | if (inside_quotes and | 888 | if (inside_quotes and |
| 883 | self.index + 1 != self.cmd_line.len and | 889 | self.index + 1 != self.cmd_line.len and |
| 884 | mem.littleToNative(u16, self.cmd_line[self.index + 1]) == '"') | 890 | mem.littleToNative(u16, self.cmd_line[self.index + 1]) == '"') |
| 885 | { | 891 | { |
| 886 | strategy.emitCharacter(self, '"'); | 892 | last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit); |
| 887 | self.index += 1; | 893 | self.index += 1; |
| 888 | } else { | 894 | } else { |
| 889 | inside_quotes = !inside_quotes; | 895 | inside_quotes = !inside_quotes; |
| ... | @@ -894,9 +900,9 @@ pub const ArgIteratorWindows = struct { | ... | @@ -894,9 +900,9 @@ pub const ArgIteratorWindows = struct { |
| 894 | backslash_count += 1; | 900 | backslash_count += 1; |
| 895 | }, | 901 | }, |
| 896 | else => { | 902 | else => { |
| 897 | strategy.emitBackslashes(self, backslash_count); | 903 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); |
| 898 | backslash_count = 0; | 904 | backslash_count = 0; |
| 899 | strategy.emitCharacter(self, char); | 905 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); |
| 900 | }, | 906 | }, |
| 901 | } | 907 | } |
| 902 | } | 908 | } |