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