| author | |
| committer | |
| log | f107d654e073a7cc0c2e920ee6fac9b2b34dba0c |
| tree | 364c995c839ac3521522c2f9ca83368a162d8c66 |
| parent | 4c8b937fb016a54b09d7447ca634b7cf1af78fce |
| parent | 89a97a7a278e69a440fd35665d4f1af864d023d9 |
closes #48375 files changed, 98 insertions(+), 37 deletions(-)
lib/std/debug.zig+30-19| ... | ... | @@ -670,10 +670,12 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { |
| 670 | 670 | } |
| 671 | 671 | } |
| 672 | 672 | |
| 673 | /// This takes ownership of coff_file: users of this function should not close | |
| 674 | /// it themselves, even on error. | |
| 673 | 675 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 674 | fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo { | |
| 676 | /// TODO it's weird to take ownership even on error, rework this code. | |
| 677 | fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInfo { | |
| 675 | 678 | nosuspend { |
| 676 | const coff_file = try std.fs.openFileAbsoluteW(coff_file_path, .{ .intended_io_mode = .blocking }); | |
| 677 | 679 | errdefer coff_file.close(); |
| 678 | 680 | |
| 679 | 681 | const coff_obj = try allocator.create(coff.Coff); |
| ... | ... | @@ -851,10 +853,13 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { |
| 851 | 853 | return ptr[start..end]; |
| 852 | 854 | } |
| 853 | 855 | |
| 856 | /// This takes ownership of elf_file: users of this function should not close | |
| 857 | /// it themselves, even on error. | |
| 854 | 858 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 855 | pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo { | |
| 859 | /// TODO it's weird to take ownership even on error, rework this code. | |
| 860 | pub fn readElfDebugInfo(allocator: *mem.Allocator, elf_file: File) !ModuleDebugInfo { | |
| 856 | 861 | nosuspend { |
| 857 | const mapped_mem = try mapWholeFile(elf_file_path); | |
| 862 | const mapped_mem = try mapWholeFile(elf_file); | |
| 858 | 863 | const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]); |
| 859 | 864 | if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; |
| 860 | 865 | if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; |
| ... | ... | @@ -921,8 +926,11 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !M |
| 921 | 926 | } |
| 922 | 927 | |
| 923 | 928 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 924 | fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ModuleDebugInfo { | |
| 925 | const mapped_mem = try mapWholeFile(macho_file_path); | |
| 929 | /// This takes ownership of coff_file: users of this function should not close | |
| 930 | /// it themselves, even on error. | |
| 931 | /// TODO it's weird to take ownership even on error, rework this code. | |
| 932 | fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugInfo { | |
| 933 | const mapped_mem = try mapWholeFile(macho_file); | |
| 926 | 934 | |
| 927 | 935 | const hdr = @ptrCast( |
| 928 | 936 | *const macho.mach_header_64, |
| ... | ... | @@ -1055,9 +1063,11 @@ const MachoSymbol = struct { |
| 1055 | 1063 | } |
| 1056 | 1064 | }; |
| 1057 | 1065 | |
| 1058 | fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 { | |
| 1066 | /// `file` is expected to have been opened with .intended_io_mode == .blocking. | |
| 1067 | /// Takes ownership of file, even on error. | |
| 1068 | /// TODO it's weird to take ownership even on error, rework this code. | |
| 1069 | fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 { | |
| 1059 | 1070 | nosuspend { |
| 1060 | const file = try fs.cwd().openFile(path, .{ .intended_io_mode = .blocking }); | |
| 1061 | 1071 | defer file.close(); |
| 1062 | 1072 | |
| 1063 | 1073 | const file_len = try math.cast(usize, try file.getEndPos()); |
| ... | ... | @@ -1140,10 +1150,11 @@ pub const DebugInfo = struct { |
| 1140 | 1150 | errdefer self.allocator.destroy(obj_di); |
| 1141 | 1151 | |
| 1142 | 1152 | const macho_path = mem.spanZ(std.c._dyld_get_image_name(i)); |
| 1143 | obj_di.* = openMachODebugInfo(self.allocator, macho_path) catch |err| switch (err) { | |
| 1153 | const macho_file = fs.cwd().openFile(macho_path, .{ .intended_io_mode = .blocking }) catch |err| switch (err) { | |
| 1144 | 1154 | error.FileNotFound => return error.MissingDebugInfo, |
| 1145 | 1155 | else => return err, |
| 1146 | 1156 | }; |
| 1157 | obj_di.* = try readMachODebugInfo(self.allocator, macho_file); | |
| 1147 | 1158 | obj_di.base_address = base_address; |
| 1148 | 1159 | |
| 1149 | 1160 | try self.address_map.putNoClobber(base_address, obj_di); |
| ... | ... | @@ -1221,10 +1232,11 @@ pub const DebugInfo = struct { |
| 1221 | 1232 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1222 | 1233 | errdefer self.allocator.destroy(obj_di); |
| 1223 | 1234 | |
| 1224 | obj_di.* = openCoffDebugInfo(self.allocator, name_buffer[0 .. len + 4 :0]) catch |err| switch (err) { | |
| 1235 | const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) { | |
| 1225 | 1236 | error.FileNotFound => return error.MissingDebugInfo, |
| 1226 | 1237 | else => return err, |
| 1227 | 1238 | }; |
| 1239 | obj_di.* = try readCoffDebugInfo(self.allocator, coff_file); | |
| 1228 | 1240 | obj_di.base_address = seg_start; |
| 1229 | 1241 | |
| 1230 | 1242 | try self.address_map.putNoClobber(seg_start, obj_di); |
| ... | ... | @@ -1280,20 +1292,18 @@ pub const DebugInfo = struct { |
| 1280 | 1292 | return obj_di; |
| 1281 | 1293 | } |
| 1282 | 1294 | |
| 1283 | const elf_path = if (ctx.name.len > 0) | |
| 1284 | ctx.name | |
| 1285 | else blk: { | |
| 1286 | var buf: [fs.MAX_PATH_BYTES]u8 = undefined; | |
| 1287 | break :blk try fs.selfExePath(&buf); | |
| 1288 | }; | |
| 1289 | ||
| 1290 | 1295 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1291 | 1296 | errdefer self.allocator.destroy(obj_di); |
| 1292 | 1297 | |
| 1293 | obj_di.* = openElfDebugInfo(self.allocator, elf_path) catch |err| switch (err) { | |
| 1298 | const elf_file = (if (ctx.name.len > 0) | |
| 1299 | fs.cwd().openFile(ctx.name, .{ .intended_io_mode = .blocking }) | |
| 1300 | else | |
| 1301 | fs.openSelfExe(.{ .intended_io_mode = .blocking })) catch |err| switch (err) { | |
| 1294 | 1302 | error.FileNotFound => return error.MissingDebugInfo, |
| 1295 | 1303 | else => return err, |
| 1296 | 1304 | }; |
| 1305 | ||
| 1306 | obj_di.* = try readElfDebugInfo(self.allocator, elf_file); | |
| 1297 | 1307 | obj_di.base_address = ctx.base_address; |
| 1298 | 1308 | |
| 1299 | 1309 | try self.address_map.putNoClobber(ctx.base_address, obj_di); |
| ... | ... | @@ -1329,7 +1339,8 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { |
| 1329 | 1339 | } |
| 1330 | 1340 | |
| 1331 | 1341 | fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo { |
| 1332 | const mapped_mem = try mapWholeFile(o_file_path); | |
| 1342 | const o_file = try fs.cwd().openFile(o_file_path, .{ .intended_io_mode = .blocking }); | |
| 1343 | const mapped_mem = try mapWholeFile(o_file); | |
| 1333 | 1344 | |
| 1334 | 1345 | const hdr = @ptrCast( |
| 1335 | 1346 | *const macho.mach_header_64, |
lib/std/fs.zig+42-14| ... | ... | @@ -33,8 +33,11 @@ pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirE |
| 33 | 33 | |
| 34 | 34 | pub const Watch = @import("fs/watch.zig").Watch; |
| 35 | 35 | |
| 36 | /// This represents the maximum size of a UTF-8 encoded file path. | |
| 37 | /// All file system operations which return a path are guaranteed to | |
| 36 | /// This represents the maximum size of a UTF-8 encoded file path that the | |
| 37 | /// operating system will accept. Paths, including those returned from file | |
| 38 | /// system operations, may be longer than this length, but such paths cannot | |
| 39 | /// be successfully passed back in other file system operations. However, | |
| 40 | /// all path components returned by file system operations are assumed to | |
| 38 | 41 | /// fit into a UTF-8 encoded array of this length. |
| 39 | 42 | /// The byte count includes room for a null sentinel byte. |
| 40 | 43 | pub const MAX_PATH_BYTES = switch (builtin.os.tag) { |
| ... | ... | @@ -1194,7 +1197,7 @@ pub const Dir = struct { |
| 1194 | 1197 | /// Read value of a symbolic link. |
| 1195 | 1198 | /// The return value is a slice of `buffer`, from index `0`. |
| 1196 | 1199 | /// Asserts that the path parameter has no null bytes. |
| 1197 | pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | |
| 1200 | pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 { | |
| 1198 | 1201 | const sub_path_c = try os.toPosixPath(sub_path); |
| 1199 | 1202 | return self.readLinkZ(&sub_path_c, buffer); |
| 1200 | 1203 | } |
| ... | ... | @@ -1202,7 +1205,7 @@ pub const Dir = struct { |
| 1202 | 1205 | pub const readLinkC = @compileError("deprecated: renamed to readLinkZ"); |
| 1203 | 1206 | |
| 1204 | 1207 | /// Same as `readLink`, except the `pathname` parameter is null-terminated. |
| 1205 | pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | |
| 1208 | pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 { | |
| 1206 | 1209 | return os.readlinkatZ(self.fd, sub_path_c, buffer); |
| 1207 | 1210 | } |
| 1208 | 1211 | |
| ... | ... | @@ -1320,6 +1323,9 @@ pub const Dir = struct { |
| 1320 | 1323 | var cleanup_dir = true; |
| 1321 | 1324 | defer if (cleanup_dir) dir.close(); |
| 1322 | 1325 | |
| 1326 | // Valid use of MAX_PATH_BYTES because dir_name_buf will only | |
| 1327 | // ever store a single path component that was returned from the | |
| 1328 | // filesystem. | |
| 1323 | 1329 | var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1324 | 1330 | var dir_name: []const u8 = sub_path; |
| 1325 | 1331 | |
| ... | ... | @@ -1772,19 +1778,21 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { |
| 1772 | 1778 | |
| 1773 | 1779 | pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError; |
| 1774 | 1780 | |
| 1775 | pub fn openSelfExe() OpenSelfExeError!File { | |
| 1781 | pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File { | |
| 1776 | 1782 | if (builtin.os.tag == .linux) { |
| 1777 | return openFileAbsoluteZ("/proc/self/exe", .{}); | |
| 1783 | return openFileAbsoluteZ("/proc/self/exe", flags); | |
| 1778 | 1784 | } |
| 1779 | 1785 | if (builtin.os.tag == .windows) { |
| 1780 | 1786 | const wide_slice = selfExePathW(); |
| 1781 | 1787 | const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); |
| 1782 | return cwd().openFileW(prefixed_path_w.span(), .{}); | |
| 1788 | return cwd().openFileW(prefixed_path_w.span(), flags); | |
| 1783 | 1789 | } |
| 1790 | // Use of MAX_PATH_BYTES here is valid as the resulting path is immediately | |
| 1791 | // opened with no modification. | |
| 1784 | 1792 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1785 | 1793 | const self_exe_path = try selfExePath(&buf); |
| 1786 | 1794 | buf[self_exe_path.len] = 0; |
| 1787 | return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, .{}); | |
| 1795 | return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags); | |
| 1788 | 1796 | } |
| 1789 | 1797 | |
| 1790 | 1798 | pub const SelfExePathError = os.ReadLinkError || os.SysCtlError; |
| ... | ... | @@ -1792,6 +1800,13 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError; |
| 1792 | 1800 | /// `selfExePath` except allocates the result on the heap. |
| 1793 | 1801 | /// Caller owns returned memory. |
| 1794 | 1802 | pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { |
| 1803 | // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux | |
| 1804 | // system, readlink will completely fail to return a result larger than | |
| 1805 | // PATH_MAX even if given a sufficiently large buffer. This makes it | |
| 1806 | // fundamentally impossible to get the selfExePath of a program running in | |
| 1807 | // a very deeply nested directory chain in this way. | |
| 1808 | // TODO(#4812): Investigate other systems and whether it is possible to get | |
| 1809 | // this path by trying larger and larger buffers until one succeeds. | |
| 1795 | 1810 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1796 | 1811 | return mem.dupe(allocator, u8, try selfExePath(&buf)); |
| 1797 | 1812 | } |
| ... | ... | @@ -1806,10 +1821,10 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { |
| 1806 | 1821 | /// On Linux, depends on procfs being mounted. If the currently executing binary has |
| 1807 | 1822 | /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. |
| 1808 | 1823 | /// TODO make the return type of this a null terminated pointer |
| 1809 | pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { | |
| 1824 | pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { | |
| 1810 | 1825 | if (is_darwin) { |
| 1811 | var u32_len: u32 = out_buffer.len; | |
| 1812 | const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len); | |
| 1826 | var u32_len: u32 = @intCast(u32, math.min(out_buffer.len, math.maxInt(u32))); | |
| 1827 | const rc = std.c._NSGetExecutablePath(out_buffer.ptr, &u32_len); | |
| 1813 | 1828 | if (rc != 0) return error.NameTooLong; |
| 1814 | 1829 | return mem.spanZ(@ptrCast([*:0]u8, out_buffer)); |
| 1815 | 1830 | } |
| ... | ... | @@ -1818,14 +1833,14 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { |
| 1818 | 1833 | .freebsd, .dragonfly => { |
| 1819 | 1834 | var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 }; |
| 1820 | 1835 | var out_len: usize = out_buffer.len; |
| 1821 | try os.sysctl(&mib, out_buffer, &out_len, null, 0); | |
| 1836 | try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0); | |
| 1822 | 1837 | // TODO could this slice from 0 to out_len instead? |
| 1823 | 1838 | return mem.spanZ(@ptrCast([*:0]u8, out_buffer)); |
| 1824 | 1839 | }, |
| 1825 | 1840 | .netbsd => { |
| 1826 | 1841 | var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME }; |
| 1827 | 1842 | var out_len: usize = out_buffer.len; |
| 1828 | try os.sysctl(&mib, out_buffer, &out_len, null, 0); | |
| 1843 | try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0); | |
| 1829 | 1844 | // TODO could this slice from 0 to out_len instead? |
| 1830 | 1845 | return mem.spanZ(@ptrCast([*:0]u8, out_buffer)); |
| 1831 | 1846 | }, |
| ... | ... | @@ -1848,13 +1863,20 @@ pub fn selfExePathW() [:0]const u16 { |
| 1848 | 1863 | /// `selfExeDirPath` except allocates the result on the heap. |
| 1849 | 1864 | /// Caller owns returned memory. |
| 1850 | 1865 | pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { |
| 1866 | // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux | |
| 1867 | // system, readlink will completely fail to return a result larger than | |
| 1868 | // PATH_MAX even if given a sufficiently large buffer. This makes it | |
| 1869 | // fundamentally impossible to get the selfExeDirPath of a program running | |
| 1870 | // in a very deeply nested directory chain in this way. | |
| 1871 | // TODO(#4812): Investigate other systems and whether it is possible to get | |
| 1872 | // this path by trying larger and larger buffers until one succeeds. | |
| 1851 | 1873 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1852 | 1874 | return mem.dupe(allocator, u8, try selfExeDirPath(&buf)); |
| 1853 | 1875 | } |
| 1854 | 1876 | |
| 1855 | 1877 | /// Get the directory path that contains the current executable. |
| 1856 | 1878 | /// Returned value is a slice of out_buffer. |
| 1857 | pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 { | |
| 1879 | pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 { | |
| 1858 | 1880 | const self_exe_path = try selfExePath(out_buffer); |
| 1859 | 1881 | // Assume that the OS APIs return absolute paths, and therefore dirname |
| 1860 | 1882 | // will not return null. |
| ... | ... | @@ -1864,6 +1886,12 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const |
| 1864 | 1886 | /// `realpath`, except caller must free the returned memory. |
| 1865 | 1887 | /// TODO integrate with `Dir` |
| 1866 | 1888 | pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 { |
| 1889 | // Use of MAX_PATH_BYTES here is valid as the realpath function does not | |
| 1890 | // have a variant that takes an arbitrary-size buffer. | |
| 1891 | // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008 | |
| 1892 | // NULL out parameter (GNU's canonicalize_file_name) to handle overelong | |
| 1893 | // paths. musl supports passing NULL but restricts the output to PATH_MAX | |
| 1894 | // anyway. | |
| 1867 | 1895 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1868 | 1896 | return mem.dupe(allocator, u8, try os.realpath(pathname, &buf)); |
| 1869 | 1897 | } |
lib/std/fs/test.zig+1-1| ... | ... | @@ -6,7 +6,7 @@ const File = std.fs.File; |
| 6 | 6 | test "openSelfExe" { |
| 7 | 7 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 8 | 8 | |
| 9 | const self_exe_file = try std.fs.openSelfExe(); | |
| 9 | const self_exe_file = try std.fs.openSelfExe(.{}); | |
| 10 | 10 | self_exe_file.close(); |
| 11 | 11 | } |
| 12 | 12 |
lib/std/os.zig+2| ... | ... | @@ -1236,6 +1236,8 @@ pub fn execvpeZ_expandArg0( |
| 1236 | 1236 | if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp); |
| 1237 | 1237 | |
| 1238 | 1238 | const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; |
| 1239 | // Use of MAX_PATH_BYTES here is valid as the path_buf will be passed | |
| 1240 | // directly to the operating system in execveZ. | |
| 1239 | 1241 | var path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1240 | 1242 | var it = mem.tokenize(PATH, ":"); |
| 1241 | 1243 | var seen_eacces = false; |
lib/std/process.zig+23-3| ... | ... | @@ -15,14 +15,34 @@ pub const changeCurDir = os.chdir; |
| 15 | 15 | pub const changeCurDirC = os.chdirC; |
| 16 | 16 | |
| 17 | 17 | /// The result is a slice of `out_buffer`, from index `0`. |
| 18 | pub fn getCwd(out_buffer: *[fs.MAX_PATH_BYTES]u8) ![]u8 { | |
| 18 | pub fn getCwd(out_buffer: []u8) ![]u8 { | |
| 19 | 19 | return os.getcwd(out_buffer); |
| 20 | 20 | } |
| 21 | 21 | |
| 22 | 22 | /// Caller must free the returned memory. |
| 23 | 23 | pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { |
| 24 | var buf: [fs.MAX_PATH_BYTES]u8 = undefined; | |
| 25 | return mem.dupe(allocator, u8, try os.getcwd(&buf)); | |
| 24 | // The use of MAX_PATH_BYTES here is just a heuristic: most paths will fit | |
| 25 | // in stack_buf, avoiding an extra allocation in the common case. | |
| 26 | var stack_buf: [fs.MAX_PATH_BYTES]u8 = undefined; | |
| 27 | var heap_buf: ?[]u8 = null; | |
| 28 | defer if (heap_buf) |buf| allocator.free(buf); | |
| 29 | ||
| 30 | var current_buf: []u8 = &stack_buf; | |
| 31 | while (true) { | |
| 32 | if (os.getcwd(current_buf)) |slice| { | |
| 33 | return mem.dupe(allocator, u8, slice); | |
| 34 | } else |err| switch (err) { | |
| 35 | error.NameTooLong => { | |
| 36 | // The path is too long to fit in stack_buf. Allocate geometrically | |
| 37 | // increasing buffers until we find one that works | |
| 38 | const new_capacity = current_buf.len * 2; | |
| 39 | if (heap_buf) |buf| allocator.free(buf); | |
| 40 | current_buf = try allocator.alloc(u8, new_capacity); | |
| 41 | heap_buf = current_buf; | |
| 42 | }, | |
| 43 | else => |e| return e, | |
| 44 | } | |
| 45 | } | |
| 26 | 46 | } |
| 27 | 47 | |
| 28 | 48 | test "getCwdAlloc" { |