| ... | ... | @@ -663,11 +663,11 @@ pub const ArgIteratorWasi = struct { |
| 663 | 663 | /// - https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES |
| 664 | 664 | pub const ArgIteratorWindows = struct { |
| 665 | 665 | allocator: Allocator, |
| 666 | | /// Owned by the iterator. |
| 667 | | /// Encoded as WTF-8. |
| 668 | | cmd_line: []const u8, |
| 666 | /// Encoded as WTF-16 LE. |
| 667 | cmd_line: [:0]const u16, |
| 669 | 668 | index: usize = 0, |
| 670 | | /// Owned by the iterator. Long enough to hold the entire `cmd_line` plus a null terminator. |
| 669 | /// Owned by the iterator. Long enough to hold contiguous NUL-terminated slices |
| 670 | /// of each argument encoded as WTF-8. |
| 671 | 671 | buffer: []u8, |
| 672 | 672 | start: usize = 0, |
| 673 | 673 | end: usize = 0, |
| ... | ... | @@ -676,13 +676,18 @@ pub const ArgIteratorWindows = struct { |
| 676 | 676 | |
| 677 | 677 | /// `cmd_line_w` *must* be a WTF16-LE-encoded string. |
| 678 | 678 | /// |
| 679 | | /// The iterator makes a copy of `cmd_line_w` converted WTF-8 and keeps it; it does *not* take |
| 680 | | /// ownership of `cmd_line_w`. |
| 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. |
| 681 | 681 | pub fn init(allocator: Allocator, cmd_line_w: [*:0]const u16) InitError!ArgIteratorWindows { |
| 682 | | const cmd_line = try unicode.wtf16LeToWtf8Alloc(allocator, mem.sliceTo(cmd_line_w, 0)); |
| 683 | | errdefer allocator.free(cmd_line); |
| 684 | | |
| 685 | | const buffer = try allocator.alloc(u8, cmd_line.len + 1); |
| 682 | const cmd_line = mem.sliceTo(cmd_line_w, 0); |
| 683 | const wtf8_len = unicode.calcWtf8Len(cmd_line); |
| 684 | |
| 685 | // 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. |
| 690 | const buffer = try allocator.alloc(u8, wtf8_len + 1); |
| 686 | 691 | errdefer allocator.free(buffer); |
| 687 | 692 | |
| 688 | 693 | return .{ |
| ... | ... | @@ -714,11 +719,11 @@ pub const ArgIteratorWindows = struct { |
| 714 | 719 | for (0..count) |_| emitCharacter(self, '\\'); |
| 715 | 720 | } |
| 716 | 721 | |
| 717 | | fn emitCharacter(self: *ArgIteratorWindows, char: u8) void { |
| 718 | | self.buffer[self.end] = char; |
| 719 | | self.end += 1; |
| 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; |
| 720 | 725 | |
| 721 | | // Because we are emitting WTF-8 byte-by-byte, we need to |
| 726 | // Because we are emitting WTF-8, we need to |
| 722 | 727 | // check to see if we've emitted two consecutive surrogate |
| 723 | 728 | // codepoints that form a valid surrogate pair in order |
| 724 | 729 | // to ensure that we're always emitting well-formed WTF-8 |
| ... | ... | @@ -732,9 +737,7 @@ pub const ArgIteratorWindows = struct { |
| 732 | 737 | // This is relevant when dealing with a WTF-16 encoded |
| 733 | 738 | // command line like this: |
| 734 | 739 | // "<0xD801>"<0xDC37> |
| 735 | | // which would get converted to WTF-8 in `cmd_line` as: |
| 736 | | // "<0xED><0xA0><0x81>"<0xED><0xB0><0xB7> |
| 737 | | // and then after parsing it'd naively get emitted as: |
| 740 | // which would get parsed and converted to WTF-8 as: |
| 738 | 741 | // <0xED><0xA0><0x81><0xED><0xB0><0xB7> |
| 739 | 742 | // but instead, we need to recognize the surrogate pair |
| 740 | 743 | // and emit the codepoint it encodes, which in this |
| ... | ... | @@ -780,7 +783,7 @@ pub const ArgIteratorWindows = struct { |
| 780 | 783 | |
| 781 | 784 | fn emitBackslashes(_: *ArgIteratorWindows, _: usize) void {} |
| 782 | 785 | |
| 783 | | fn emitCharacter(_: *ArgIteratorWindows, _: u8) void {} |
| 786 | fn emitCharacter(_: *ArgIteratorWindows, _: u16) void {} |
| 784 | 787 | |
| 785 | 788 | fn yieldArg(_: *ArgIteratorWindows) bool { |
| 786 | 789 | return true; |
| ... | ... | @@ -798,7 +801,10 @@ pub const ArgIteratorWindows = struct { |
| 798 | 801 | |
| 799 | 802 | var inside_quotes = false; |
| 800 | 803 | while (true) : (self.index += 1) { |
| 801 | | const char = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; |
| 804 | const char = if (self.index != self.cmd_line.len) |
| 805 | mem.littleToNative(u16, self.cmd_line[self.index]) |
| 806 | else |
| 807 | 0; |
| 802 | 808 | switch (char) { |
| 803 | 809 | 0 => { |
| 804 | 810 | return strategy.yieldArg(self); |
| ... | ... | @@ -823,7 +829,10 @@ pub const ArgIteratorWindows = struct { |
| 823 | 829 | |
| 824 | 830 | // Skip spaces and tabs. The iterator completes if we reach the end of the string here. |
| 825 | 831 | while (true) : (self.index += 1) { |
| 826 | | const char = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; |
| 832 | const char = if (self.index != self.cmd_line.len) |
| 833 | mem.littleToNative(u16, self.cmd_line[self.index]) |
| 834 | else |
| 835 | 0; |
| 827 | 836 | switch (char) { |
| 828 | 837 | 0 => return strategy.eof, |
| 829 | 838 | ' ', '\t' => continue, |
| ... | ... | @@ -844,7 +853,10 @@ pub const ArgIteratorWindows = struct { |
| 844 | 853 | var backslash_count: usize = 0; |
| 845 | 854 | var inside_quotes = false; |
| 846 | 855 | while (true) : (self.index += 1) { |
| 847 | | const char = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; |
| 856 | const char = if (self.index != self.cmd_line.len) |
| 857 | mem.littleToNative(u16, self.cmd_line[self.index]) |
| 858 | else |
| 859 | 0; |
| 848 | 860 | switch (char) { |
| 849 | 861 | 0 => { |
| 850 | 862 | strategy.emitBackslashes(self, backslash_count); |
| ... | ... | @@ -867,7 +879,7 @@ pub const ArgIteratorWindows = struct { |
| 867 | 879 | } else { |
| 868 | 880 | if (inside_quotes and |
| 869 | 881 | self.index + 1 != self.cmd_line.len and |
| 870 | | self.cmd_line[self.index + 1] == '"') |
| 882 | mem.littleToNative(u16, self.cmd_line[self.index + 1]) == '"') |
| 871 | 883 | { |
| 872 | 884 | strategy.emitCharacter(self, '"'); |
| 873 | 885 | self.index += 1; |
| ... | ... | @@ -892,7 +904,6 @@ pub const ArgIteratorWindows = struct { |
| 892 | 904 | /// argument slices. |
| 893 | 905 | pub fn deinit(self: *ArgIteratorWindows) void { |
| 894 | 906 | self.allocator.free(self.buffer); |
| 895 | | self.allocator.free(self.cmd_line); |
| 896 | 907 | } |
| 897 | 908 | }; |
| 898 | 909 | |