| author | |
| committer | |
| log | 51852d2587b931767a12d42ce39d5c191eea10ea |
| tree | fd875c1aa365a8264510eb4c43f874c438a6036d |
| parent | bda5539e9d8b5f15b8165393e4118c8601188276 |
20 files changed, 305 insertions(+), 117 deletions(-)
src-self-hosted/compilation.zig+1| ... | ... | @@ -302,6 +302,7 @@ pub const Compilation = struct { |
| 302 | 302 | UnsupportedLinkArchitecture, |
| 303 | 303 | UserResourceLimitReached, |
| 304 | 304 | InvalidUtf8, |
| 305 | BadPathName, | |
| 305 | 306 | }; |
| 306 | 307 | |
| 307 | 308 | pub const Event = union(enum) { |
src-self-hosted/errmsg.zig+1-1| ... | ... | @@ -235,7 +235,7 @@ pub const Msg = struct { |
| 235 | 235 | const allocator = msg.getAllocator(); |
| 236 | 236 | const tree = msg.getTree(); |
| 237 | 237 | |
| 238 | const cwd = try os.getCwd(allocator); | |
| 238 | const cwd = try os.getCwdAlloc(allocator); | |
| 239 | 239 | defer allocator.free(cwd); |
| 240 | 240 | |
| 241 | 241 | const relpath = try os.path.relative(allocator, cwd, msg.realpath); |
src-self-hosted/introspect.zig+1-1| ... | ... | @@ -14,7 +14,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![ |
| 14 | 14 | const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig"); |
| 15 | 15 | defer allocator.free(test_index_file); |
| 16 | 16 | |
| 17 | var file = try os.File.openRead(allocator, test_index_file); | |
| 17 | var file = try os.File.openRead(test_index_file); | |
| 18 | 18 | file.close(); |
| 19 | 19 | |
| 20 | 20 | return test_zig_dir; |
src-self-hosted/libc_installation.zig+7-8| ... | ... | @@ -233,7 +233,7 @@ pub const LibCInstallation = struct { |
| 233 | 233 | const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h"); |
| 234 | 234 | defer loop.allocator.free(stdlib_path); |
| 235 | 235 | |
| 236 | if (try fileExists(loop.allocator, stdlib_path)) { | |
| 236 | if (try fileExists(stdlib_path)) { | |
| 237 | 237 | self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path); |
| 238 | 238 | return; |
| 239 | 239 | } |
| ... | ... | @@ -257,7 +257,7 @@ pub const LibCInstallation = struct { |
| 257 | 257 | const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h"); |
| 258 | 258 | defer loop.allocator.free(stdlib_path); |
| 259 | 259 | |
| 260 | if (try fileExists(loop.allocator, stdlib_path)) { | |
| 260 | if (try fileExists(stdlib_path)) { | |
| 261 | 261 | self.include_dir = result_buf.toOwnedSlice(); |
| 262 | 262 | return; |
| 263 | 263 | } |
| ... | ... | @@ -285,7 +285,7 @@ pub const LibCInstallation = struct { |
| 285 | 285 | } |
| 286 | 286 | const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib"); |
| 287 | 287 | defer loop.allocator.free(ucrt_lib_path); |
| 288 | if (try fileExists(loop.allocator, ucrt_lib_path)) { | |
| 288 | if (try fileExists(ucrt_lib_path)) { | |
| 289 | 289 | self.lib_dir = result_buf.toOwnedSlice(); |
| 290 | 290 | return; |
| 291 | 291 | } |
| ... | ... | @@ -360,7 +360,7 @@ pub const LibCInstallation = struct { |
| 360 | 360 | } |
| 361 | 361 | const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib"); |
| 362 | 362 | defer loop.allocator.free(kernel32_path); |
| 363 | if (try fileExists(loop.allocator, kernel32_path)) { | |
| 363 | if (try fileExists(kernel32_path)) { | |
| 364 | 364 | self.kernel32_lib_dir = result_buf.toOwnedSlice(); |
| 365 | 365 | return; |
| 366 | 366 | } |
| ... | ... | @@ -449,12 +449,11 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search { |
| 449 | 449 | return search_buf[0..search_end]; |
| 450 | 450 | } |
| 451 | 451 | |
| 452 | fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool { | |
| 453 | if (std.os.File.access(allocator, path)) |_| { | |
| 452 | fn fileExists(path: []const u8) !bool { | |
| 453 | if (std.os.File.access(path)) |_| { | |
| 454 | 454 | return true; |
| 455 | 455 | } else |err| switch (err) { |
| 456 | error.NotFound, error.PermissionDenied => return false, | |
| 457 | error.OutOfMemory => return error.OutOfMemory, | |
| 456 | error.FileNotFound, error.PathNotFound, error.PermissionDenied => return false, | |
| 458 | 457 | else => return error.FileSystem, |
| 459 | 458 | } |
| 460 | 459 | } |
src-self-hosted/test.zig+2-2| ... | ... | @@ -94,7 +94,7 @@ pub const TestContext = struct { |
| 94 | 94 | } |
| 95 | 95 | |
| 96 | 96 | // TODO async I/O |
| 97 | try std.io.writeFile(allocator, file1_path, source); | |
| 97 | try std.io.writeFile(file1_path, source); | |
| 98 | 98 | |
| 99 | 99 | var comp = try Compilation.create( |
| 100 | 100 | &self.zig_compiler, |
| ... | ... | @@ -128,7 +128,7 @@ pub const TestContext = struct { |
| 128 | 128 | } |
| 129 | 129 | |
| 130 | 130 | // TODO async I/O |
| 131 | try std.io.writeFile(allocator, file1_path, source); | |
| 131 | try std.io.writeFile(file1_path, source); | |
| 132 | 132 | |
| 133 | 133 | var comp = try Compilation.create( |
| 134 | 134 | &self.zig_compiler, |
std/build.zig+3-3| ... | ... | @@ -267,7 +267,7 @@ pub const Builder = struct { |
| 267 | 267 | if (self.verbose) { |
| 268 | 268 | warn("rm {}\n", installed_file); |
| 269 | 269 | } |
| 270 | _ = os.deleteFile(self.allocator, installed_file); | |
| 270 | _ = os.deleteFile(installed_file); | |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | 273 | // TODO remove empty directories |
| ... | ... | @@ -1182,7 +1182,7 @@ pub const LibExeObjStep = struct { |
| 1182 | 1182 | |
| 1183 | 1183 | if (self.build_options_contents.len() > 0) { |
| 1184 | 1184 | const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name)); |
| 1185 | try std.io.writeFile(builder.allocator, build_options_file, self.build_options_contents.toSliceConst()); | |
| 1185 | try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst()); | |
| 1186 | 1186 | try zig_args.append("--pkg-begin"); |
| 1187 | 1187 | try zig_args.append("build_options"); |
| 1188 | 1188 | try zig_args.append(builder.pathFromRoot(build_options_file)); |
| ... | ... | @@ -1917,7 +1917,7 @@ pub const WriteFileStep = struct { |
| 1917 | 1917 | warn("unable to make path {}: {}\n", full_path_dir, @errorName(err)); |
| 1918 | 1918 | return err; |
| 1919 | 1919 | }; |
| 1920 | io.writeFile(self.builder.allocator, full_path, self.data) catch |err| { | |
| 1920 | io.writeFile(full_path, self.data) catch |err| { | |
| 1921 | 1921 | warn("unable to write {}: {}\n", full_path, @errorName(err)); |
| 1922 | 1922 | return err; |
| 1923 | 1923 | }; |
std/cstr.zig+6-5| ... | ... | @@ -9,10 +9,9 @@ pub const line_sep = switch (builtin.os) { |
| 9 | 9 | else => "\n", |
| 10 | 10 | }; |
| 11 | 11 | |
| 12 | /// Deprecated, use mem.len | |
| 12 | 13 | pub fn len(ptr: [*]const u8) usize { |
| 13 | var count: usize = 0; | |
| 14 | while (ptr[count] != 0) : (count += 1) {} | |
| 15 | return count; | |
| 14 | return mem.len(u8, ptr); | |
| 16 | 15 | } |
| 17 | 16 | |
| 18 | 17 | pub fn cmp(a: [*]const u8, b: [*]const u8) i8 { |
| ... | ... | @@ -27,12 +26,14 @@ pub fn cmp(a: [*]const u8, b: [*]const u8) i8 { |
| 27 | 26 | } |
| 28 | 27 | } |
| 29 | 28 | |
| 29 | /// Deprecated, use mem.toSliceConst | |
| 30 | 30 | pub fn toSliceConst(str: [*]const u8) []const u8 { |
| 31 | return str[0..len(str)]; | |
| 31 | return mem.toSliceConst(u8, str); | |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | /// Deprecated, use mem.toSlice | |
| 34 | 35 | pub fn toSlice(str: [*]u8) []u8 { |
| 35 | return str[0..len(str)]; | |
| 36 | return mem.toSlice(u8, str); | |
| 36 | 37 | } |
| 37 | 38 | |
| 38 | 39 | test "cstr fns" { |
std/event/fs.zig-4| ... | ... | @@ -382,7 +382,6 @@ pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHa |
| 382 | 382 | }, |
| 383 | 383 | |
| 384 | 384 | builtin.Os.windows => return os.windowsOpen( |
| 385 | loop.allocator, | |
| 386 | 385 | path, |
| 387 | 386 | windows.GENERIC_READ, |
| 388 | 387 | windows.FILE_SHARE_READ, |
| ... | ... | @@ -411,7 +410,6 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os |
| 411 | 410 | }, |
| 412 | 411 | builtin.Os.windows, |
| 413 | 412 | => return os.windowsOpen( |
| 414 | loop.allocator, | |
| 415 | 413 | path, |
| 416 | 414 | windows.GENERIC_WRITE, |
| 417 | 415 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, |
| ... | ... | @@ -435,7 +433,6 @@ pub async fn openReadWrite( |
| 435 | 433 | }, |
| 436 | 434 | |
| 437 | 435 | builtin.Os.windows => return os.windowsOpen( |
| 438 | loop.allocator, | |
| 439 | 436 | path, |
| 440 | 437 | windows.GENERIC_WRITE|windows.GENERIC_READ, |
| 441 | 438 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, |
| ... | ... | @@ -593,7 +590,6 @@ pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, |
| 593 | 590 | |
| 594 | 591 | async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void { |
| 595 | 592 | const handle = try os.windowsOpen( |
| 596 | loop.allocator, | |
| 597 | 593 | path, |
| 598 | 594 | windows.GENERIC_WRITE, |
| 599 | 595 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, |
std/io.zig+3-4| ... | ... | @@ -254,9 +254,8 @@ pub fn OutStream(comptime WriteError: type) type { |
| 254 | 254 | }; |
| 255 | 255 | } |
| 256 | 256 | |
| 257 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | |
| 258 | pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8) !void { | |
| 259 | var file = try File.openWrite(allocator, path); | |
| 257 | pub fn writeFile(path: []const u8, data: []const u8) !void { | |
| 258 | var file = try File.openWrite(path); | |
| 260 | 259 | defer file.close(); |
| 261 | 260 | try file.write(data); |
| 262 | 261 | } |
| ... | ... | @@ -268,7 +267,7 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { |
| 268 | 267 | |
| 269 | 268 | /// On success, caller owns returned buffer. |
| 270 | 269 | pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 { |
| 271 | var file = try File.openRead(allocator, path); | |
| 270 | var file = try File.openRead(path); | |
| 272 | 271 | defer file.close(); |
| 273 | 272 | |
| 274 | 273 | const size = try file.getEndPos(); |
std/io_test.zig+1-1| ... | ... | @@ -45,7 +45,7 @@ test "write a file, read it, then delete it" { |
| 45 | 45 | assert(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); |
| 46 | 46 | assert(mem.eql(u8, contents[contents.len - "end".len ..], "end")); |
| 47 | 47 | } |
| 48 | try os.deleteFile(allocator, tmp_file_name); | |
| 48 | try os.deleteFile(tmp_file_name); | |
| 49 | 49 | } |
| 50 | 50 | |
| 51 | 51 | test "BufferOutStream" { |
std/mem.zig+17-2| ... | ... | @@ -179,8 +179,8 @@ pub fn secureZero(comptime T: type, s: []T) void { |
| 179 | 179 | // NOTE: We do not use a volatile slice cast here since LLVM cannot |
| 180 | 180 | // see that it can be replaced by a memset. |
| 181 | 181 | const ptr = @ptrCast([*]volatile u8, s.ptr); |
| 182 | const len = s.len * @sizeOf(T); | |
| 183 | @memset(ptr, 0, len); | |
| 182 | const length = s.len * @sizeOf(T); | |
| 183 | @memset(ptr, 0, length); | |
| 184 | 184 | } |
| 185 | 185 | |
| 186 | 186 | test "mem.secureZero" { |
| ... | ... | @@ -252,6 +252,20 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool { |
| 252 | 252 | return true; |
| 253 | 253 | } |
| 254 | 254 | |
| 255 | pub fn len(comptime T: type, ptr: [*]const T) usize { | |
| 256 | var count: usize = 0; | |
| 257 | while (ptr[count] != 0) : (count += 1) {} | |
| 258 | return count; | |
| 259 | } | |
| 260 | ||
| 261 | pub fn toSliceConst(comptime T: type, ptr: [*]const T) []const T { | |
| 262 | return ptr[0..len(T, ptr)]; | |
| 263 | } | |
| 264 | ||
| 265 | pub fn toSlice(comptime T: type, ptr: [*]T) []T { | |
| 266 | return ptr[0..len(T, ptr)]; | |
| 267 | } | |
| 268 | ||
| 255 | 269 | /// Returns true if all elements in a slice are equal to the scalar value provided |
| 256 | 270 | pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { |
| 257 | 271 | for (slice) |item| { |
| ... | ... | @@ -809,3 +823,4 @@ pub fn endianSwap(comptime T: type, x: T) T { |
| 809 | 823 | test "std.mem.endianSwap" { |
| 810 | 824 | assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE); |
| 811 | 825 | } |
| 826 |
std/os/child_process.zig+1-4| ... | ... | @@ -453,10 +453,7 @@ pub const ChildProcess = struct { |
| 453 | 453 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); |
| 454 | 454 | |
| 455 | 455 | const nul_handle = if (any_ignore) blk: { |
| 456 | const nul_file_path = "NUL"; | |
| 457 | var fixed_buffer_mem: [nul_file_path.len + 1]u8 = undefined; | |
| 458 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 459 | break :blk try os.windowsOpen(&fixed_allocator.allocator, "NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL); | |
| 456 | break :blk try os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL); | |
| 460 | 457 | } else blk: { |
| 461 | 458 | break :blk undefined; |
| 462 | 459 | }; |
std/os/file.zig+30-22| ... | ... | @@ -7,6 +7,7 @@ const assert = std.debug.assert; |
| 7 | 7 | const posix = os.posix; |
| 8 | 8 | const windows = os.windows; |
| 9 | 9 | const Os = builtin.Os; |
| 10 | const windows_util = @import("windows/util.zig"); | |
| 10 | 11 | |
| 11 | 12 | const is_posix = builtin.os != builtin.Os.windows; |
| 12 | 13 | const is_windows = builtin.os == builtin.Os.windows; |
| ... | ... | @@ -102,21 +103,42 @@ pub const File = struct { |
| 102 | 103 | |
| 103 | 104 | pub const AccessError = error{ |
| 104 | 105 | PermissionDenied, |
| 105 | NotFound, | |
| 106 | PathNotFound, | |
| 107 | FileNotFound, | |
| 106 | 108 | NameTooLong, |
| 107 | 109 | BadMode, |
| 108 | 110 | BadPathName, |
| 109 | 111 | Io, |
| 110 | 112 | SystemResources, |
| 111 | OutOfMemory, | |
| 113 | ||
| 114 | /// On Windows, file paths must be valid Unicode. | |
| 115 | InvalidUtf8, | |
| 112 | 116 | |
| 113 | 117 | Unexpected, |
| 114 | 118 | }; |
| 115 | 119 | |
| 120 | /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. | |
| 121 | /// Otherwise use `access` or `accessC`. | |
| 122 | pub fn accessW(path: [*]const u16) AccessError!void { | |
| 123 | if (os.windows.GetFileAttributesW(path) != os.windows.INVALID_FILE_ATTRIBUTES) { | |
| 124 | return; | |
| 125 | } | |
| 126 | ||
| 127 | const err = windows.GetLastError(); | |
| 128 | switch (err) { | |
| 129 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | |
| 130 | windows.ERROR.PATH_NOT_FOUND => return error.PathNotFound, | |
| 131 | windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, | |
| 132 | else => return os.unexpectedErrorWindows(err), | |
| 133 | } | |
| 134 | } | |
| 135 | ||
| 136 | /// Call if you have a UTF-8 encoded, null-terminated string. | |
| 137 | /// Otherwise use `access` or `accessW`. | |
| 116 | 138 | pub fn accessC(path: [*]const u8) AccessError!void { |
| 117 | 139 | if (is_windows) { |
| 118 | // this needs to convert to UTF-16LE and call accessW | |
| 119 | @compileError("TODO support windows"); | |
| 140 | const path_w = try windows_util.cStrToPrefixedFileW(path); | |
| 141 | return accessW(&path_w); | |
| 120 | 142 | } |
| 121 | 143 | if (is_posix) { |
| 122 | 144 | const result = posix.access(path, posix.F_OK); |
| ... | ... | @@ -137,28 +159,14 @@ pub const File = struct { |
| 137 | 159 | posix.ENOMEM => return error.SystemResources, |
| 138 | 160 | else => return os.unexpectedErrorPosix(err), |
| 139 | 161 | } |
| 140 | } else if (is_windows) { | |
| 141 | if (os.windows.GetFileAttributesA(path) != os.windows.INVALID_FILE_ATTRIBUTES) { | |
| 142 | return; | |
| 143 | } | |
| 144 | ||
| 145 | const err = windows.GetLastError(); | |
| 146 | switch (err) { | |
| 147 | windows.ERROR.FILE_NOT_FOUND, | |
| 148 | windows.ERROR.PATH_NOT_FOUND, | |
| 149 | => return error.NotFound, | |
| 150 | windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, | |
| 151 | else => return os.unexpectedErrorWindows(err), | |
| 152 | } | |
| 153 | } else { | |
| 154 | @compileError("TODO implement access for this OS"); | |
| 155 | 162 | } |
| 163 | @compileError("Unsupported OS"); | |
| 156 | 164 | } |
| 157 | 165 | |
| 158 | 166 | pub fn access(path: []const u8) AccessError!void { |
| 159 | 167 | if (is_windows) { |
| 160 | // this needs to convert to UTF-16LE and call accessW | |
| 161 | @compileError("TODO support windows"); | |
| 168 | const path_w = try windows_util.sliceToPrefixedFileW(path); | |
| 169 | return accessW(&path_w); | |
| 162 | 170 | } |
| 163 | 171 | if (is_posix) { |
| 164 | 172 | var path_with_null: [posix.PATH_MAX]u8 = undefined; |
| ... | ... | @@ -167,7 +175,7 @@ pub const File = struct { |
| 167 | 175 | path_with_null[path.len] = 0; |
| 168 | 176 | return accessC(&path_with_null); |
| 169 | 177 | } |
| 170 | @compileError("TODO implement access for this OS"); | |
| 178 | @compileError("Unsupported OS"); | |
| 171 | 179 | } |
| 172 | 180 | |
| 173 | 181 | /// Upon success, the stream is in an uninitialized state. To continue using it, |
std/os/get_app_data_dir.zig+2-1| ... | ... | @@ -10,6 +10,7 @@ pub const GetAppDataDirError = error{ |
| 10 | 10 | }; |
| 11 | 11 | |
| 12 | 12 | /// Caller owns returned memory. |
| 13 | /// TODO determine if we can remove the allocator requirement | |
| 13 | 14 | pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { |
| 14 | 15 | switch (builtin.os) { |
| 15 | 16 | builtin.Os.windows => { |
| ... | ... | @@ -22,7 +23,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD |
| 22 | 23 | )) { |
| 23 | 24 | os.windows.S_OK => { |
| 24 | 25 | defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); |
| 25 | const global_dir = unicode.utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) { | |
| 26 | const global_dir = unicode.utf16leToUtf8Alloc(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) { | |
| 26 | 27 | error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, |
| 27 | 28 | error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, |
| 28 | 29 | error.DanglingSurrogateHalf => return error.AppDataDirUnavailable, |
std/os/index.zig+91-20| ... | ... | @@ -45,7 +45,7 @@ pub const MAX_PATH_BYTES = switch (builtin.os) { |
| 45 | 45 | // If it would require 4 UTF-8 bytes, then there would be a surrogate |
| 46 | 46 | // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. |
| 47 | 47 | // +1 for the null byte at the end, which can be encoded in 1 byte. |
| 48 | Os.windows => 32767 * 3 + 1, | |
| 48 | Os.windows => windows_util.PATH_MAX_WIDE * 3 + 1, | |
| 49 | 49 | else => @compileError("Unsupported OS"), |
| 50 | 50 | }; |
| 51 | 51 | |
| ... | ... | @@ -326,6 +326,8 @@ pub const PosixWriteError = error{ |
| 326 | 326 | NoSpaceLeft, |
| 327 | 327 | AccessDenied, |
| 328 | 328 | BrokenPipe, |
| 329 | ||
| 330 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 329 | 331 | Unexpected, |
| 330 | 332 | }; |
| 331 | 333 | |
| ... | ... | @@ -439,6 +441,8 @@ pub const PosixOpenError = error{ |
| 439 | 441 | NoSpaceLeft, |
| 440 | 442 | NotDir, |
| 441 | 443 | PathAlreadyExists, |
| 444 | ||
| 445 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 442 | 446 | Unexpected, |
| 443 | 447 | }; |
| 444 | 448 | |
| ... | ... | @@ -600,6 +604,8 @@ pub const PosixExecveError = error{ |
| 600 | 604 | FileNotFound, |
| 601 | 605 | NotDir, |
| 602 | 606 | FileBusy, |
| 607 | ||
| 608 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 603 | 609 | Unexpected, |
| 604 | 610 | }; |
| 605 | 611 | |
| ... | ... | @@ -736,20 +742,25 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { |
| 736 | 742 | pub const GetCwdError = error{Unexpected}; |
| 737 | 743 | |
| 738 | 744 | /// The result is a slice of out_buffer. |
| 745 | /// TODO with well defined copy elision we could make the API of this function better. | |
| 739 | 746 | pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 { |
| 740 | 747 | switch (builtin.os) { |
| 741 | 748 | Os.windows => { |
| 742 | var utf16le_buf: [windows_util.PATH_MAX_UTF16]u16 = undefined; | |
| 743 | const result = windows.GetCurrentDirectoryW(utf16le_buf.len, &utf16le_buf); | |
| 749 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | |
| 750 | const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast | |
| 751 | const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast | |
| 752 | const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); | |
| 744 | 753 | if (result == 0) { |
| 745 | 754 | const err = windows.GetLastError(); |
| 746 | 755 | switch (err) { |
| 747 | 756 | else => return unexpectedErrorWindows(err), |
| 748 | 757 | } |
| 749 | 758 | } |
| 750 | assert(result <= buf.len); | |
| 759 | assert(result <= utf16le_buf.len); | |
| 751 | 760 | const utf16le_slice = utf16le_buf[0..result]; |
| 752 | return std.unicode.utf16leToUtf8(out_buffer, utf16le_buf); | |
| 761 | // Trust that Windows gives us valid UTF-16LE. | |
| 762 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | |
| 763 | return out_buffer[0..end_index]; | |
| 753 | 764 | }, |
| 754 | 765 | else => { |
| 755 | 766 | const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len)); |
| ... | ... | @@ -764,7 +775,9 @@ pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 { |
| 764 | 775 | |
| 765 | 776 | test "os.getCwd" { |
| 766 | 777 | // at least call it so it gets compiled |
| 767 | _ = getCwd(debug.global_allocator); | |
| 778 | _ = getCwdAlloc(debug.global_allocator); | |
| 779 | var buf: [MAX_PATH_BYTES]u8 = undefined; | |
| 780 | _ = getCwd(&buf); | |
| 768 | 781 | } |
| 769 | 782 | |
| 770 | 783 | pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; |
| ... | ... | @@ -779,6 +792,8 @@ pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []con |
| 779 | 792 | |
| 780 | 793 | pub const WindowsSymLinkError = error{ |
| 781 | 794 | OutOfMemory, |
| 795 | ||
| 796 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 782 | 797 | Unexpected, |
| 783 | 798 | }; |
| 784 | 799 | |
| ... | ... | @@ -809,6 +824,8 @@ pub const PosixSymLinkError = error{ |
| 809 | 824 | NoSpaceLeft, |
| 810 | 825 | ReadOnlyFileSystem, |
| 811 | 826 | NotDir, |
| 827 | ||
| 828 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 812 | 829 | Unexpected, |
| 813 | 830 | }; |
| 814 | 831 | |
| ... | ... | @@ -867,7 +884,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: |
| 867 | 884 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); |
| 868 | 885 | |
| 869 | 886 | if (symLink(allocator, existing_path, tmp_path)) { |
| 870 | return rename(allocator, tmp_path, new_path); | |
| 887 | return rename(tmp_path, new_path); | |
| 871 | 888 | } else |err| switch (err) { |
| 872 | 889 | error.PathAlreadyExists => continue, |
| 873 | 890 | else => return err, // TODO zig should know this set does not include PathAlreadyExists |
| ... | ... | @@ -886,8 +903,15 @@ pub const DeleteFileError = error{ |
| 886 | 903 | NotDir, |
| 887 | 904 | SystemResources, |
| 888 | 905 | ReadOnlyFileSystem, |
| 889 | OutOfMemory, | |
| 890 | 906 | |
| 907 | /// On Windows, file paths must be valid Unicode. | |
| 908 | InvalidUtf8, | |
| 909 | ||
| 910 | /// On Windows, file paths cannot contain these characters: | |
| 911 | /// '/', '*', '?', '"', '<', '>', '|' | |
| 912 | BadPathName, | |
| 913 | ||
| 914 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 891 | 915 | Unexpected, |
| 892 | 916 | }; |
| 893 | 917 | |
| ... | ... | @@ -900,7 +924,18 @@ pub fn deleteFile(file_path: []const u8) DeleteFileError!void { |
| 900 | 924 | } |
| 901 | 925 | |
| 902 | 926 | pub fn deleteFileWindows(file_path: []const u8) !void { |
| 903 | @compileError("TODO rewrite with DeleteFileW and no allocator"); | |
| 927 | const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); | |
| 928 | ||
| 929 | if (windows.DeleteFileW(&file_path_w) == 0) { | |
| 930 | const err = windows.GetLastError(); | |
| 931 | switch (err) { | |
| 932 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | |
| 933 | windows.ERROR.ACCESS_DENIED => return error.AccessDenied, | |
| 934 | windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, | |
| 935 | windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, | |
| 936 | else => return unexpectedErrorWindows(err), | |
| 937 | } | |
| 938 | } | |
| 904 | 939 | } |
| 905 | 940 | |
| 906 | 941 | pub fn deleteFilePosixC(file_path: [*]const u8) !void { |
| ... | ... | @@ -1028,7 +1063,7 @@ pub const AtomicFile = struct { |
| 1028 | 1063 | pub fn deinit(self: *AtomicFile) void { |
| 1029 | 1064 | if (!self.finished) { |
| 1030 | 1065 | self.file.close(); |
| 1031 | deleteFile(self.allocator, self.tmp_path) catch {}; | |
| 1066 | deleteFile(self.tmp_path) catch {}; | |
| 1032 | 1067 | self.allocator.free(self.tmp_path); |
| 1033 | 1068 | self.finished = true; |
| 1034 | 1069 | } |
| ... | ... | @@ -1037,7 +1072,7 @@ pub const AtomicFile = struct { |
| 1037 | 1072 | pub fn finish(self: *AtomicFile) !void { |
| 1038 | 1073 | assert(!self.finished); |
| 1039 | 1074 | self.file.close(); |
| 1040 | try rename(self.allocator, self.tmp_path, self.dest_path); | |
| 1075 | try rename(self.tmp_path, self.dest_path); | |
| 1041 | 1076 | self.allocator.free(self.tmp_path); |
| 1042 | 1077 | self.finished = true; |
| 1043 | 1078 | } |
| ... | ... | @@ -1075,7 +1110,15 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { |
| 1075 | 1110 | |
| 1076 | 1111 | pub fn rename(old_path: []const u8, new_path: []const u8) !void { |
| 1077 | 1112 | if (is_windows) { |
| 1078 | @compileError("TODO rewrite with MoveFileExW and no allocator"); | |
| 1113 | const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; | |
| 1114 | const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); | |
| 1115 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); | |
| 1116 | if (windows.MoveFileExW(&old_path_w, &new_path_w, flags) == 0) { | |
| 1117 | const err = windows.GetLastError(); | |
| 1118 | switch (err) { | |
| 1119 | else => return unexpectedErrorWindows(err), | |
| 1120 | } | |
| 1121 | } | |
| 1079 | 1122 | } else { |
| 1080 | 1123 | var old_path_with_null: [posix.PATH_MAX]u8 = undefined; |
| 1081 | 1124 | if (old_path.len >= posix.PATH_MAX) return error.NameTooLong; |
| ... | ... | @@ -1099,11 +1142,10 @@ pub fn makeDir(dir_path: []const u8) !void { |
| 1099 | 1142 | } |
| 1100 | 1143 | } |
| 1101 | 1144 | |
| 1102 | pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void { | |
| 1103 | const path_buf = try cstr.addNullByte(allocator, dir_path); | |
| 1104 | defer allocator.free(path_buf); | |
| 1145 | pub fn makeDirWindows(dir_path: []const u8) !void { | |
| 1146 | const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); | |
| 1105 | 1147 | |
| 1106 | if (windows.CreateDirectoryA(path_buf.ptr, null) == 0) { | |
| 1148 | if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { | |
| 1107 | 1149 | const err = windows.GetLastError(); |
| 1108 | 1150 | return switch (err) { |
| 1109 | 1151 | windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists, |
| ... | ... | @@ -1144,13 +1186,14 @@ pub fn makeDirPosix(dir_path: []const u8) !void { |
| 1144 | 1186 | |
| 1145 | 1187 | /// Calls makeDir recursively to make an entire path. Returns success if the path |
| 1146 | 1188 | /// already exists and is a directory. |
| 1189 | /// TODO determine if we can remove the allocator requirement from this function | |
| 1147 | 1190 | pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { |
| 1148 | 1191 | const resolved_path = try path.resolve(allocator, full_path); |
| 1149 | 1192 | defer allocator.free(resolved_path); |
| 1150 | 1193 | |
| 1151 | 1194 | var end_index: usize = resolved_path.len; |
| 1152 | 1195 | while (true) { |
| 1153 | makeDir(allocator, resolved_path[0..end_index]) catch |err| switch (err) { | |
| 1196 | makeDir(resolved_path[0..end_index]) catch |err| switch (err) { | |
| 1154 | 1197 | error.PathAlreadyExists => { |
| 1155 | 1198 | // TODO stat the file and return an error if it's not a directory |
| 1156 | 1199 | // this is important because otherwise a dangling symlink |
| ... | ... | @@ -1188,6 +1231,7 @@ pub const DeleteDirError = error{ |
| 1188 | 1231 | ReadOnlyFileSystem, |
| 1189 | 1232 | OutOfMemory, |
| 1190 | 1233 | |
| 1234 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 1191 | 1235 | Unexpected, |
| 1192 | 1236 | }; |
| 1193 | 1237 | |
| ... | ... | @@ -1256,20 +1300,30 @@ const DeleteTreeError = error{ |
| 1256 | 1300 | FileSystem, |
| 1257 | 1301 | FileBusy, |
| 1258 | 1302 | DirNotEmpty, |
| 1303 | ||
| 1304 | /// On Windows, file paths must be valid Unicode. | |
| 1305 | InvalidUtf8, | |
| 1306 | ||
| 1307 | /// On Windows, file paths cannot contain these characters: | |
| 1308 | /// '/', '*', '?', '"', '<', '>', '|' | |
| 1309 | BadPathName, | |
| 1310 | ||
| 1311 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 1259 | 1312 | Unexpected, |
| 1260 | 1313 | }; |
| 1314 | ||
| 1315 | /// TODO determine if we can remove the allocator requirement | |
| 1261 | 1316 | pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { |
| 1262 | 1317 | start_over: while (true) { |
| 1263 | 1318 | var got_access_denied = false; |
| 1264 | 1319 | // First, try deleting the item as a file. This way we don't follow sym links. |
| 1265 | if (deleteFile(allocator, full_path)) { | |
| 1320 | if (deleteFile(full_path)) { | |
| 1266 | 1321 | return; |
| 1267 | 1322 | } else |err| switch (err) { |
| 1268 | 1323 | error.FileNotFound => return, |
| 1269 | 1324 | error.IsDir => {}, |
| 1270 | 1325 | error.AccessDenied => got_access_denied = true, |
| 1271 | 1326 | |
| 1272 | error.OutOfMemory, | |
| 1273 | 1327 | error.SymLinkLoop, |
| 1274 | 1328 | error.NameTooLong, |
| 1275 | 1329 | error.SystemResources, |
| ... | ... | @@ -1277,6 +1331,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! |
| 1277 | 1331 | error.NotDir, |
| 1278 | 1332 | error.FileSystem, |
| 1279 | 1333 | error.FileBusy, |
| 1334 | error.InvalidUtf8, | |
| 1335 | error.BadPathName, | |
| 1280 | 1336 | error.Unexpected, |
| 1281 | 1337 | => return err, |
| 1282 | 1338 | } |
| ... | ... | @@ -1383,6 +1439,7 @@ pub const Dir = struct { |
| 1383 | 1439 | PathAlreadyExists, |
| 1384 | 1440 | OutOfMemory, |
| 1385 | 1441 | |
| 1442 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 1386 | 1443 | Unexpected, |
| 1387 | 1444 | }; |
| 1388 | 1445 | |
| ... | ... | @@ -1685,6 +1742,8 @@ pub fn posix_setregid(rgid: u32, egid: u32) !void { |
| 1685 | 1742 | |
| 1686 | 1743 | pub const WindowsGetStdHandleErrs = error{ |
| 1687 | 1744 | NoStdHandles, |
| 1745 | ||
| 1746 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 1688 | 1747 | Unexpected, |
| 1689 | 1748 | }; |
| 1690 | 1749 | |
| ... | ... | @@ -2012,7 +2071,7 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { |
| 2012 | 2071 | /// Call this when you made a windows DLL call or something that does SetLastError |
| 2013 | 2072 | /// and you get an unexpected error. |
| 2014 | 2073 | pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { |
| 2015 | if (unexpected_error_tracing) { | |
| 2074 | if (true) { | |
| 2016 | 2075 | debug.warn("unexpected GetLastError(): {}\n", err); |
| 2017 | 2076 | debug.dumpCurrentStackTrace(null); |
| 2018 | 2077 | } |
| ... | ... | @@ -2215,6 +2274,7 @@ pub const PosixBindError = error{ |
| 2215 | 2274 | /// The socket inode would reside on a read-only filesystem. |
| 2216 | 2275 | ReadOnlyFileSystem, |
| 2217 | 2276 | |
| 2277 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2218 | 2278 | Unexpected, |
| 2219 | 2279 | }; |
| 2220 | 2280 | |
| ... | ... | @@ -2258,6 +2318,7 @@ const PosixListenError = error{ |
| 2258 | 2318 | /// The socket is not of a type that supports the listen() operation. |
| 2259 | 2319 | OperationNotSupported, |
| 2260 | 2320 | |
| 2321 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2261 | 2322 | Unexpected, |
| 2262 | 2323 | }; |
| 2263 | 2324 | |
| ... | ... | @@ -2311,6 +2372,7 @@ pub const PosixAcceptError = error{ |
| 2311 | 2372 | /// Firewall rules forbid connection. |
| 2312 | 2373 | BlockedByFirewall, |
| 2313 | 2374 | |
| 2375 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2314 | 2376 | Unexpected, |
| 2315 | 2377 | }; |
| 2316 | 2378 | |
| ... | ... | @@ -2356,6 +2418,7 @@ pub const LinuxEpollCreateError = error{ |
| 2356 | 2418 | /// There was insufficient memory to create the kernel object. |
| 2357 | 2419 | SystemResources, |
| 2358 | 2420 | |
| 2421 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2359 | 2422 | Unexpected, |
| 2360 | 2423 | }; |
| 2361 | 2424 | |
| ... | ... | @@ -2410,6 +2473,7 @@ pub const LinuxEpollCtlError = error{ |
| 2410 | 2473 | /// for example, a regular file or a directory. |
| 2411 | 2474 | FileDescriptorIncompatibleWithEpoll, |
| 2412 | 2475 | |
| 2476 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2413 | 2477 | Unexpected, |
| 2414 | 2478 | }; |
| 2415 | 2479 | |
| ... | ... | @@ -2452,6 +2516,7 @@ pub const LinuxEventFdError = error{ |
| 2452 | 2516 | ProcessFdQuotaExceeded, |
| 2453 | 2517 | SystemFdQuotaExceeded, |
| 2454 | 2518 | |
| 2519 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2455 | 2520 | Unexpected, |
| 2456 | 2521 | }; |
| 2457 | 2522 | |
| ... | ... | @@ -2474,6 +2539,7 @@ pub const PosixGetSockNameError = error{ |
| 2474 | 2539 | /// Insufficient resources were available in the system to perform the operation. |
| 2475 | 2540 | SystemResources, |
| 2476 | 2541 | |
| 2542 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2477 | 2543 | Unexpected, |
| 2478 | 2544 | }; |
| 2479 | 2545 | |
| ... | ... | @@ -2527,6 +2593,7 @@ pub const PosixConnectError = error{ |
| 2527 | 2593 | /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. |
| 2528 | 2594 | ConnectionTimedOut, |
| 2529 | 2595 | |
| 2596 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2530 | 2597 | Unexpected, |
| 2531 | 2598 | }; |
| 2532 | 2599 | |
| ... | ... | @@ -2748,6 +2815,7 @@ pub const SpawnThreadError = error{ |
| 2748 | 2815 | /// Not enough userland memory to spawn the thread. |
| 2749 | 2816 | OutOfMemory, |
| 2750 | 2817 | |
| 2818 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2751 | 2819 | Unexpected, |
| 2752 | 2820 | }; |
| 2753 | 2821 | |
| ... | ... | @@ -2935,6 +3003,8 @@ pub fn posixFStat(fd: i32) !posix.Stat { |
| 2935 | 3003 | pub const CpuCountError = error{ |
| 2936 | 3004 | OutOfMemory, |
| 2937 | 3005 | PermissionDenied, |
| 3006 | ||
| 3007 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 2938 | 3008 | Unexpected, |
| 2939 | 3009 | }; |
| 2940 | 3010 | |
| ... | ... | @@ -3005,6 +3075,7 @@ pub const BsdKQueueError = error{ |
| 3005 | 3075 | /// The system-wide limit on the total number of open files has been reached. |
| 3006 | 3076 | SystemFdQuotaExceeded, |
| 3007 | 3077 | |
| 3078 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 3008 | 3079 | Unexpected, |
| 3009 | 3080 | }; |
| 3010 | 3081 |
std/os/path.zig+9-7| ... | ... | @@ -16,6 +16,8 @@ pub const sep_windows = '\\'; |
| 16 | 16 | pub const sep_posix = '/'; |
| 17 | 17 | pub const sep = if (is_windows) sep_windows else sep_posix; |
| 18 | 18 | |
| 19 | pub const sep_str = [1]u8{sep}; | |
| 20 | ||
| 19 | 21 | pub const delimiter_windows = ';'; |
| 20 | 22 | pub const delimiter_posix = ':'; |
| 21 | 23 | pub const delimiter = if (is_windows) delimiter_windows else delimiter_posix; |
| ... | ... | @@ -337,7 +339,7 @@ pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 337 | 339 | pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 338 | 340 | if (paths.len == 0) { |
| 339 | 341 | assert(is_windows); // resolveWindows called on non windows can't use getCwd |
| 340 | return os.getCwd(allocator); | |
| 342 | return os.getCwdAlloc(allocator); | |
| 341 | 343 | } |
| 342 | 344 | |
| 343 | 345 | // determine which disk designator we will result with, if any |
| ... | ... | @@ -432,7 +434,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 432 | 434 | }, |
| 433 | 435 | WindowsPath.Kind.None => { |
| 434 | 436 | assert(is_windows); // resolveWindows called on non windows can't use getCwd |
| 435 | const cwd = try os.getCwd(allocator); | |
| 437 | const cwd = try os.getCwdAlloc(allocator); | |
| 436 | 438 | defer allocator.free(cwd); |
| 437 | 439 | const parsed_cwd = windowsParsePath(cwd); |
| 438 | 440 | result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1); |
| ... | ... | @@ -448,7 +450,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 448 | 450 | } else { |
| 449 | 451 | assert(is_windows); // resolveWindows called on non windows can't use getCwd |
| 450 | 452 | // TODO call get cwd for the result_disk_designator instead of the global one |
| 451 | const cwd = try os.getCwd(allocator); | |
| 453 | const cwd = try os.getCwdAlloc(allocator); | |
| 452 | 454 | defer allocator.free(cwd); |
| 453 | 455 | |
| 454 | 456 | result = try allocator.alloc(u8, max_size + cwd.len + 1); |
| ... | ... | @@ -516,7 +518,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 516 | 518 | pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 517 | 519 | if (paths.len == 0) { |
| 518 | 520 | assert(!is_windows); // resolvePosix called on windows can't use getCwd |
| 519 | return os.getCwd(allocator); | |
| 521 | return os.getCwdAlloc(allocator); | |
| 520 | 522 | } |
| 521 | 523 | |
| 522 | 524 | var first_index: usize = 0; |
| ... | ... | @@ -538,7 +540,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 538 | 540 | result = try allocator.alloc(u8, max_size); |
| 539 | 541 | } else { |
| 540 | 542 | assert(!is_windows); // resolvePosix called on windows can't use getCwd |
| 541 | const cwd = try os.getCwd(allocator); | |
| 543 | const cwd = try os.getCwdAlloc(allocator); | |
| 542 | 544 | defer allocator.free(cwd); |
| 543 | 545 | result = try allocator.alloc(u8, max_size + cwd.len + 1); |
| 544 | 546 | mem.copy(u8, result, cwd); |
| ... | ... | @@ -577,7 +579,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 577 | 579 | } |
| 578 | 580 | |
| 579 | 581 | test "os.path.resolve" { |
| 580 | const cwd = try os.getCwd(debug.global_allocator); | |
| 582 | const cwd = try os.getCwdAlloc(debug.global_allocator); | |
| 581 | 583 | if (is_windows) { |
| 582 | 584 | if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { |
| 583 | 585 | cwd[0] = asciiUpper(cwd[0]); |
| ... | ... | @@ -591,7 +593,7 @@ test "os.path.resolve" { |
| 591 | 593 | |
| 592 | 594 | test "os.path.resolveWindows" { |
| 593 | 595 | if (is_windows) { |
| 594 | const cwd = try os.getCwd(debug.global_allocator); | |
| 596 | const cwd = try os.getCwdAlloc(debug.global_allocator); | |
| 595 | 597 | const parsed_cwd = windowsParsePath(cwd); |
| 596 | 598 | { |
| 597 | 599 | const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }); |
std/os/test.zig+7-7| ... | ... | @@ -10,9 +10,9 @@ const AtomicRmwOp = builtin.AtomicRmwOp; |
| 10 | 10 | const AtomicOrder = builtin.AtomicOrder; |
| 11 | 11 | |
| 12 | 12 | test "makePath, put some files in it, deleteTree" { |
| 13 | try os.makePath(a, "os_test_tmp/b/c"); | |
| 14 | try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense"); | |
| 15 | try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah"); | |
| 13 | try os.makePath(a, "os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "c"); | |
| 14 | try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "c" ++ os.path.sep_str ++ "file.txt", "nonsense"); | |
| 15 | try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "file2.txt", "blah"); | |
| 16 | 16 | try os.deleteTree(a, "os_test_tmp"); |
| 17 | 17 | if (os.Dir.open(a, "os_test_tmp")) |dir| { |
| 18 | 18 | @panic("expected error"); |
| ... | ... | @@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" { |
| 23 | 23 | |
| 24 | 24 | test "access file" { |
| 25 | 25 | try os.makePath(a, "os_test_tmp"); |
| 26 | if (os.File.access(a, "os_test_tmp/file.txt")) |ok| { | |
| 26 | if (os.File.access("os_test_tmp" ++ os.path.sep_str ++ "file.txt")) |ok| { | |
| 27 | 27 | @panic("expected error"); |
| 28 | 28 | } else |err| { |
| 29 | assert(err == error.NotFound); | |
| 29 | assert(err == error.FileNotFound); | |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | try io.writeFile(a, "os_test_tmp/file.txt", ""); | |
| 33 | try os.File.access(a, "os_test_tmp/file.txt"); | |
| 32 | try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "file.txt", ""); | |
| 33 | try os.File.access("os_test_tmp" ++ os.path.sep_str ++ "file.txt"); | |
| 34 | 34 | try os.deleteTree(a, "os_test_tmp"); |
| 35 | 35 | } |
| 36 | 36 |
std/os/windows/kernel32.zig+16-10| ... | ... | @@ -4,10 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE |
| 4 | 4 | |
| 5 | 5 | pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; |
| 6 | 6 | |
| 7 | pub extern "kernel32" stdcallcc fn CreateDirectoryA( | |
| 8 | lpPathName: LPCSTR, | |
| 9 | lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, | |
| 10 | ) BOOL; | |
| 7 | pub extern "kernel32" stdcallcc fn CreateDirectoryA( lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; | |
| 8 | pub extern "kernel32" stdcallcc fn CreateDirectoryW( lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; | |
| 11 | 9 | |
| 12 | 10 | pub extern "kernel32" stdcallcc fn CreateFileA( |
| 13 | 11 | lpFileName: [*]const u8, // TODO null terminated pointer type |
| ... | ... | @@ -59,7 +57,8 @@ pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, Ex |
| 59 | 57 | |
| 60 | 58 | pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; |
| 61 | 59 | |
| 62 | pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; | |
| 60 | pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL; | |
| 61 | pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL; | |
| 63 | 62 | |
| 64 | 63 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; |
| 65 | 64 | |
| ... | ... | @@ -73,8 +72,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; |
| 73 | 72 | |
| 74 | 73 | pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; |
| 75 | 74 | |
| 76 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?[*]CHAR) DWORD; | |
| 77 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: WORD, lpBuffer: ?[*]WCHAR) DWORD; | |
| 75 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD; | |
| 76 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD; | |
| 78 | 77 | |
| 79 | 78 | pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE; |
| 80 | 79 | pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD; |
| ... | ... | @@ -87,7 +86,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo |
| 87 | 86 | |
| 88 | 87 | pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; |
| 89 | 88 | |
| 90 | pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD; | |
| 89 | pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD; | |
| 90 | pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD; | |
| 91 | 91 | |
| 92 | 92 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; |
| 93 | 93 | |
| ... | ... | @@ -131,8 +131,14 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem |
| 131 | 131 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL; |
| 132 | 132 | |
| 133 | 133 | pub extern "kernel32" stdcallcc fn MoveFileExA( |
| 134 | lpExistingFileName: LPCSTR, | |
| 135 | lpNewFileName: LPCSTR, | |
| 134 | lpExistingFileName: [*]const u8, | |
| 135 | lpNewFileName: [*]const u8, | |
| 136 | dwFlags: DWORD, | |
| 137 | ) BOOL; | |
| 138 | ||
| 139 | pub extern "kernel32" stdcallcc fn MoveFileExW( | |
| 140 | lpExistingFileName: [*]const u16, | |
| 141 | lpNewFileName: [*]const u16, | |
| 136 | 142 | dwFlags: DWORD, |
| 137 | 143 | ) BOOL; |
| 138 | 144 |
std/os/windows/util.zig+74-3| ... | ... | @@ -7,11 +7,17 @@ const mem = std.mem; |
| 7 | 7 | const BufMap = std.BufMap; |
| 8 | 8 | const cstr = std.cstr; |
| 9 | 9 | |
| 10 | pub const PATH_MAX_UTF16 = 32767; | |
| 10 | // > The maximum path of 32,767 characters is approximate, because the "\\?\" | |
| 11 | // > prefix may be expanded to a longer string by the system at run time, and | |
| 12 | // > this expansion applies to the total length. | |
| 13 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | |
| 14 | pub const PATH_MAX_WIDE = 32767; | |
| 11 | 15 | |
| 12 | 16 | pub const WaitError = error{ |
| 13 | 17 | WaitAbandoned, |
| 14 | 18 | WaitTimeOut, |
| 19 | ||
| 20 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 15 | 21 | Unexpected, |
| 16 | 22 | }; |
| 17 | 23 | |
| ... | ... | @@ -39,6 +45,8 @@ pub const WriteError = error{ |
| 39 | 45 | SystemResources, |
| 40 | 46 | OperationAborted, |
| 41 | 47 | BrokenPipe, |
| 48 | ||
| 49 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 42 | 50 | Unexpected, |
| 43 | 51 | }; |
| 44 | 52 | |
| ... | ... | @@ -88,13 +96,28 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { |
| 88 | 96 | pub const OpenError = error{ |
| 89 | 97 | SharingViolation, |
| 90 | 98 | PathAlreadyExists, |
| 99 | ||
| 100 | /// When all the path components are found but the file component is not. | |
| 91 | 101 | FileNotFound, |
| 102 | ||
| 103 | /// When one or more path components are not found. | |
| 104 | PathNotFound, | |
| 105 | ||
| 92 | 106 | AccessDenied, |
| 93 | 107 | PipeBusy, |
| 108 | NameTooLong, | |
| 109 | ||
| 110 | /// On Windows, file paths must be valid Unicode. | |
| 111 | InvalidUtf8, | |
| 112 | ||
| 113 | /// On Windows, file paths cannot contain these characters: | |
| 114 | /// '/', '*', '?', '"', '<', '>', '|' | |
| 115 | BadPathName, | |
| 116 | ||
| 117 | /// See https://github.com/ziglang/zig/issues/1396 | |
| 94 | 118 | Unexpected, |
| 95 | 119 | }; |
| 96 | 120 | |
| 97 | /// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator. | |
| 98 | 121 | pub fn windowsOpen( |
| 99 | 122 | file_path: []const u8, |
| 100 | 123 | desired_access: windows.DWORD, |
| ... | ... | @@ -102,7 +125,25 @@ pub fn windowsOpen( |
| 102 | 125 | creation_disposition: windows.DWORD, |
| 103 | 126 | flags_and_attrs: windows.DWORD, |
| 104 | 127 | ) OpenError!windows.HANDLE { |
| 105 | @compileError("TODO rewrite with CreateFileW and no allocator"); | |
| 128 | const file_path_w = try sliceToPrefixedFileW(file_path); | |
| 129 | ||
| 130 | const result = windows.CreateFileW(&file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); | |
| 131 | ||
| 132 | if (result == windows.INVALID_HANDLE_VALUE) { | |
| 133 | const err = windows.GetLastError(); | |
| 134 | switch (err) { | |
| 135 | windows.ERROR.SHARING_VIOLATION => return OpenError.SharingViolation, | |
| 136 | windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists, | |
| 137 | windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists, | |
| 138 | windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound, | |
| 139 | windows.ERROR.PATH_NOT_FOUND => return OpenError.PathNotFound, | |
| 140 | windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied, | |
| 141 | windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy, | |
| 142 | else => return os.unexpectedErrorWindows(err), | |
| 143 | } | |
| 144 | } | |
| 145 | ||
| 146 | return result; | |
| 106 | 147 | } |
| 107 | 148 | |
| 108 | 149 | /// Caller must free result. |
| ... | ... | @@ -242,3 +283,33 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t |
| 242 | 283 | } |
| 243 | 284 | return WindowsWaitResult.Normal; |
| 244 | 285 | } |
| 286 | ||
| 287 | pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE+1]u16 { | |
| 288 | return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); | |
| 289 | } | |
| 290 | ||
| 291 | pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE+1]u16 { | |
| 292 | // TODO well defined copy elision | |
| 293 | var result: [PATH_MAX_WIDE+1]u16 = undefined; | |
| 294 | ||
| 295 | // > File I/O functions in the Windows API convert "/" to "\" as part of | |
| 296 | // > converting the name to an NT-style name, except when using the "\\?\" | |
| 297 | // > prefix as detailed in the following sections. | |
| 298 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | |
| 299 | // Because we want the larger maximum path length for absolute paths, we | |
| 300 | // disallow forward slashes in zig std lib file functions on Windows. | |
| 301 | for (s) |byte| switch (byte) { | |
| 302 | '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, | |
| 303 | else => {}, | |
| 304 | }; | |
| 305 | const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { | |
| 306 | const prefix = []u16{'\\', '\\', '?', '\\'}; | |
| 307 | mem.copy(u16, result[0..], prefix); | |
| 308 | break :blk prefix.len; | |
| 309 | }; | |
| 310 | const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); | |
| 311 | assert(end_index <= result.len); | |
| 312 | if (end_index == result.len) return error.NameTooLong; | |
| 313 | result[end_index] = 0; | |
| 314 | return result; | |
| 315 | } |
std/unicode.zig+33-12| ... | ... | @@ -247,6 +247,8 @@ pub const Utf16LeIterator = struct { |
| 247 | 247 | } |
| 248 | 248 | |
| 249 | 249 | pub fn nextCodepoint(it: *Utf16LeIterator) !?u32 { |
| 250 | assert(it.i <= it.bytes.len); | |
| 251 | if (it.i == it.bytes.len) return null; | |
| 250 | 252 | const c0: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]); |
| 251 | 253 | if (c0 & ~u32(0x03ff) == 0xd800) { |
| 252 | 254 | // surrogate pair |
| ... | ... | @@ -254,10 +256,12 @@ pub const Utf16LeIterator = struct { |
| 254 | 256 | if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf; |
| 255 | 257 | const c1: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]); |
| 256 | 258 | if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; |
| 259 | it.i += 2; | |
| 257 | 260 | return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); |
| 258 | 261 | } else if (c0 & ~u32(0x03ff) == 0xdc00) { |
| 259 | 262 | return error.UnexpectedSecondSurrogateHalf; |
| 260 | 263 | } else { |
| 264 | it.i += 2; | |
| 261 | 265 | return c0; |
| 262 | 266 | } |
| 263 | 267 | } |
| ... | ... | @@ -490,15 +494,15 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 |
| 490 | 494 | return result.toOwnedSlice(); |
| 491 | 495 | } |
| 492 | 496 | |
| 493 | pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !void { | |
| 494 | var out_index: usize = 0; | |
| 497 | /// Asserts that the output buffer is big enough. | |
| 498 | /// Returns end index. | |
| 499 | pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !usize { | |
| 500 | var end_index: usize = 0; | |
| 495 | 501 | var it = Utf16LeIterator.init(utf16le); |
| 496 | 502 | while (try it.nextCodepoint()) |codepoint| { |
| 497 | const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable; | |
| 498 | try result.resize(result.len + utf8_len); | |
| 499 | assert((utf8Encode(codepoint, result.items[out_index..]) catch unreachable) == utf8_len); | |
| 500 | out_index += utf8_len; | |
| 503 | end_index += try utf8Encode(codepoint, utf8[end_index..]); | |
| 501 | 504 | } |
| 505 | return end_index; | |
| 502 | 506 | } |
| 503 | 507 | |
| 504 | 508 | test "utf16leToUtf8" { |
| ... | ... | @@ -508,14 +512,14 @@ test "utf16leToUtf8" { |
| 508 | 512 | { |
| 509 | 513 | mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little); |
| 510 | 514 | mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little); |
| 511 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | |
| 515 | const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); | |
| 512 | 516 | assert(mem.eql(u8, utf8, "Aa")); |
| 513 | 517 | } |
| 514 | 518 | |
| 515 | 519 | { |
| 516 | 520 | mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little); |
| 517 | 521 | mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little); |
| 518 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | |
| 522 | const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); | |
| 519 | 523 | assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); |
| 520 | 524 | } |
| 521 | 525 | |
| ... | ... | @@ -523,7 +527,7 @@ test "utf16leToUtf8" { |
| 523 | 527 | // the values just outside the surrogate half range |
| 524 | 528 | mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little); |
| 525 | 529 | mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little); |
| 526 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | |
| 530 | const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); | |
| 527 | 531 | assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); |
| 528 | 532 | } |
| 529 | 533 | |
| ... | ... | @@ -531,7 +535,7 @@ test "utf16leToUtf8" { |
| 531 | 535 | // smallest surrogate pair |
| 532 | 536 | mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little); |
| 533 | 537 | mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); |
| 534 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | |
| 538 | const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); | |
| 535 | 539 | assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); |
| 536 | 540 | } |
| 537 | 541 | |
| ... | ... | @@ -539,14 +543,14 @@ test "utf16leToUtf8" { |
| 539 | 543 | // largest surrogate pair |
| 540 | 544 | mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); |
| 541 | 545 | mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little); |
| 542 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | |
| 546 | const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); | |
| 543 | 547 | assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); |
| 544 | 548 | } |
| 545 | 549 | |
| 546 | 550 | { |
| 547 | 551 | mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); |
| 548 | 552 | mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); |
| 549 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | |
| 553 | const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); | |
| 550 | 554 | assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); |
| 551 | 555 | } |
| 552 | 556 | } |
| ... | ... | @@ -567,3 +571,20 @@ pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 |
| 567 | 571 | try result.append(0); |
| 568 | 572 | return result.toOwnedSlice(); |
| 569 | 573 | } |
| 574 | ||
| 575 | /// Returns index of next character. If exact fit, returned index equals output slice length. | |
| 576 | /// If ran out of room, returned index equals output slice length + 1. | |
| 577 | /// TODO support codepoints bigger than 16 bits | |
| 578 | pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { | |
| 579 | const utf16le_as_bytes = @sliceToBytes(utf16le[0..]); | |
| 580 | var end_index: usize = 0; | |
| 581 | ||
| 582 | var it = (try Utf8View.init(utf8)).iterator(); | |
| 583 | while (it.nextCodepoint()) |codepoint| { | |
| 584 | if (end_index == utf16le_as_bytes.len) return (end_index / 2) + 1; | |
| 585 | // TODO surrogate pairs | |
| 586 | mem.writeInt(utf16le_as_bytes[end_index..], @intCast(u16, codepoint), builtin.Endian.Little); | |
| 587 | end_index += 2; | |
| 588 | } | |
| 589 | return end_index / 2; | |
| 590 | } |