authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-21 16:07:28-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-21 16:07:28-04:00
log51852d2587b931767a12d42ce39d5c191eea10ea
treefd875c1aa365a8264510eb4c43f874c438a6036d
parentbda5539e9d8b5f15b8165393e4118c8601188276

fix windows


20 files changed, 305 insertions(+), 117 deletions(-)

src-self-hosted/compilation.zig+1
...@@ -302,6 +302,7 @@ pub const Compilation = struct {...@@ -302,6 +302,7 @@ pub const Compilation = struct {
302 UnsupportedLinkArchitecture,302 UnsupportedLinkArchitecture,
303 UserResourceLimitReached,303 UserResourceLimitReached,
304 InvalidUtf8,304 InvalidUtf8,
305 BadPathName,
305 };306 };
306307
307 pub const Event = union(enum) {308 pub const Event = union(enum) {
src-self-hosted/errmsg.zig+1-1
...@@ -235,7 +235,7 @@ pub const Msg = struct {...@@ -235,7 +235,7 @@ pub const Msg = struct {
235 const allocator = msg.getAllocator();235 const allocator = msg.getAllocator();
236 const tree = msg.getTree();236 const tree = msg.getTree();
237237
238 const cwd = try os.getCwd(allocator);238 const cwd = try os.getCwdAlloc(allocator);
239 defer allocator.free(cwd);239 defer allocator.free(cwd);
240240
241 const relpath = try os.path.relative(allocator, cwd, msg.realpath);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,7 +14,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![
14 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");14 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
15 defer allocator.free(test_index_file);15 defer allocator.free(test_index_file);
1616
17 var file = try os.File.openRead(allocator, test_index_file);17 var file = try os.File.openRead(test_index_file);
18 file.close();18 file.close();
1919
20 return test_zig_dir;20 return test_zig_dir;
src-self-hosted/libc_installation.zig+7-8
...@@ -233,7 +233,7 @@ pub const LibCInstallation = struct {...@@ -233,7 +233,7 @@ pub const LibCInstallation = struct {
233 const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");233 const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
234 defer loop.allocator.free(stdlib_path);234 defer loop.allocator.free(stdlib_path);
235235
236 if (try fileExists(loop.allocator, stdlib_path)) {236 if (try fileExists(stdlib_path)) {
237 self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);237 self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
238 return;238 return;
239 }239 }
...@@ -257,7 +257,7 @@ pub const LibCInstallation = struct {...@@ -257,7 +257,7 @@ pub const LibCInstallation = struct {
257 const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");257 const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
258 defer loop.allocator.free(stdlib_path);258 defer loop.allocator.free(stdlib_path);
259259
260 if (try fileExists(loop.allocator, stdlib_path)) {260 if (try fileExists(stdlib_path)) {
261 self.include_dir = result_buf.toOwnedSlice();261 self.include_dir = result_buf.toOwnedSlice();
262 return;262 return;
263 }263 }
...@@ -285,7 +285,7 @@ pub const LibCInstallation = struct {...@@ -285,7 +285,7 @@ pub const LibCInstallation = struct {
285 }285 }
286 const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");286 const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
287 defer loop.allocator.free(ucrt_lib_path);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 self.lib_dir = result_buf.toOwnedSlice();289 self.lib_dir = result_buf.toOwnedSlice();
290 return;290 return;
291 }291 }
...@@ -360,7 +360,7 @@ pub const LibCInstallation = struct {...@@ -360,7 +360,7 @@ pub const LibCInstallation = struct {
360 }360 }
361 const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");361 const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
362 defer loop.allocator.free(kernel32_path);362 defer loop.allocator.free(kernel32_path);
363 if (try fileExists(loop.allocator, kernel32_path)) {363 if (try fileExists(kernel32_path)) {
364 self.kernel32_lib_dir = result_buf.toOwnedSlice();364 self.kernel32_lib_dir = result_buf.toOwnedSlice();
365 return;365 return;
366 }366 }
...@@ -449,12 +449,11 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {...@@ -449,12 +449,11 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
449 return search_buf[0..search_end];449 return search_buf[0..search_end];
450}450}
451451
452fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {452fn fileExists(path: []const u8) !bool {
453 if (std.os.File.access(allocator, path)) |_| {453 if (std.os.File.access(path)) |_| {
454 return true;454 return true;
455 } else |err| switch (err) {455 } else |err| switch (err) {
456 error.NotFound, error.PermissionDenied => return false,456 error.FileNotFound, error.PathNotFound, error.PermissionDenied => return false,
457 error.OutOfMemory => return error.OutOfMemory,
458 else => return error.FileSystem,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,7 +94,7 @@ pub const TestContext = struct {
94 }94 }
9595
96 // TODO async I/O96 // TODO async I/O
97 try std.io.writeFile(allocator, file1_path, source);97 try std.io.writeFile(file1_path, source);
9898
99 var comp = try Compilation.create(99 var comp = try Compilation.create(
100 &self.zig_compiler,100 &self.zig_compiler,
...@@ -128,7 +128,7 @@ pub const TestContext = struct {...@@ -128,7 +128,7 @@ pub const TestContext = struct {
128 }128 }
129129
130 // TODO async I/O130 // TODO async I/O
131 try std.io.writeFile(allocator, file1_path, source);131 try std.io.writeFile(file1_path, source);
132132
133 var comp = try Compilation.create(133 var comp = try Compilation.create(
134 &self.zig_compiler,134 &self.zig_compiler,
std/build.zig+3-3
...@@ -267,7 +267,7 @@ pub const Builder = struct {...@@ -267,7 +267,7 @@ pub const Builder = struct {
267 if (self.verbose) {267 if (self.verbose) {
268 warn("rm {}\n", installed_file);268 warn("rm {}\n", installed_file);
269 }269 }
270 _ = os.deleteFile(self.allocator, installed_file);270 _ = os.deleteFile(installed_file);
271 }271 }
272272
273 // TODO remove empty directories273 // TODO remove empty directories
...@@ -1182,7 +1182,7 @@ pub const LibExeObjStep = struct {...@@ -1182,7 +1182,7 @@ pub const LibExeObjStep = struct {
11821182
1183 if (self.build_options_contents.len() > 0) {1183 if (self.build_options_contents.len() > 0) {
1184 const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name));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 try zig_args.append("--pkg-begin");1186 try zig_args.append("--pkg-begin");
1187 try zig_args.append("build_options");1187 try zig_args.append("build_options");
1188 try zig_args.append(builder.pathFromRoot(build_options_file));1188 try zig_args.append(builder.pathFromRoot(build_options_file));
...@@ -1917,7 +1917,7 @@ pub const WriteFileStep = struct {...@@ -1917,7 +1917,7 @@ pub const WriteFileStep = struct {
1917 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));1917 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
1918 return err;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 warn("unable to write {}: {}\n", full_path, @errorName(err));1921 warn("unable to write {}: {}\n", full_path, @errorName(err));
1922 return err;1922 return err;
1923 };1923 };
std/cstr.zig+6-5
...@@ -9,10 +9,9 @@ pub const line_sep = switch (builtin.os) {...@@ -9,10 +9,9 @@ pub const line_sep = switch (builtin.os) {
9 else => "\n",9 else => "\n",
10};10};
1111
12/// Deprecated, use mem.len
12pub fn len(ptr: [*]const u8) usize {13pub fn len(ptr: [*]const u8) usize {
13 var count: usize = 0;14 return mem.len(u8, ptr);
14 while (ptr[count] != 0) : (count += 1) {}
15 return count;
16}15}
1716
18pub fn cmp(a: [*]const u8, b: [*]const u8) i8 {17pub fn cmp(a: [*]const u8, b: [*]const u8) i8 {
...@@ -27,12 +26,14 @@ 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}
2928
29/// Deprecated, use mem.toSliceConst
30pub fn toSliceConst(str: [*]const u8) []const u8 {30pub fn toSliceConst(str: [*]const u8) []const u8 {
31 return str[0..len(str)];31 return mem.toSliceConst(u8, str);
32}32}
3333
34/// Deprecated, use mem.toSlice
34pub fn toSlice(str: [*]u8) []u8 {35pub fn toSlice(str: [*]u8) []u8 {
35 return str[0..len(str)];36 return mem.toSlice(u8, str);
36}37}
3738
38test "cstr fns" {39test "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,7 +382,6 @@ pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHa
382 },382 },
383383
384 builtin.Os.windows => return os.windowsOpen(384 builtin.Os.windows => return os.windowsOpen(
385 loop.allocator,
386 path,385 path,
387 windows.GENERIC_READ,386 windows.GENERIC_READ,
388 windows.FILE_SHARE_READ,387 windows.FILE_SHARE_READ,
...@@ -411,7 +410,6 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os...@@ -411,7 +410,6 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os
411 },410 },
412 builtin.Os.windows,411 builtin.Os.windows,
413 => return os.windowsOpen(412 => return os.windowsOpen(
414 loop.allocator,
415 path,413 path,
416 windows.GENERIC_WRITE,414 windows.GENERIC_WRITE,
417 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,415 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
...@@ -435,7 +433,6 @@ pub async fn openReadWrite(...@@ -435,7 +433,6 @@ pub async fn openReadWrite(
435 },433 },
436434
437 builtin.Os.windows => return os.windowsOpen(435 builtin.Os.windows => return os.windowsOpen(
438 loop.allocator,
439 path,436 path,
440 windows.GENERIC_WRITE|windows.GENERIC_READ,437 windows.GENERIC_WRITE|windows.GENERIC_READ,
441 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,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,7 +590,6 @@ pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8,
593590
594async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {591async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {
595 const handle = try os.windowsOpen(592 const handle = try os.windowsOpen(
596 loop.allocator,
597 path,593 path,
598 windows.GENERIC_WRITE,594 windows.GENERIC_WRITE,
599 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,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,9 +254,8 @@ pub fn OutStream(comptime WriteError: type) type {
254 };254 };
255}255}
256256
257/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.257pub fn writeFile(path: []const u8, data: []const u8) !void {
258pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8) !void {258 var file = try File.openWrite(path);
259 var file = try File.openWrite(allocator, path);
260 defer file.close();259 defer file.close();
261 try file.write(data);260 try file.write(data);
262}261}
...@@ -268,7 +267,7 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {...@@ -268,7 +267,7 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
268267
269/// On success, caller owns returned buffer.268/// On success, caller owns returned buffer.
270pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {269pub 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 defer file.close();271 defer file.close();
273272
274 const size = try file.getEndPos();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,7 +45,7 @@ test "write a file, read it, then delete it" {
45 assert(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data));45 assert(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data));
46 assert(mem.eql(u8, contents[contents.len - "end".len ..], "end"));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}
5050
51test "BufferOutStream" {51test "BufferOutStream" {
std/mem.zig+17-2
...@@ -179,8 +179,8 @@ pub fn secureZero(comptime T: type, s: []T) void {...@@ -179,8 +179,8 @@ pub fn secureZero(comptime T: type, s: []T) void {
179 // NOTE: We do not use a volatile slice cast here since LLVM cannot179 // NOTE: We do not use a volatile slice cast here since LLVM cannot
180 // see that it can be replaced by a memset.180 // see that it can be replaced by a memset.
181 const ptr = @ptrCast([*]volatile u8, s.ptr);181 const ptr = @ptrCast([*]volatile u8, s.ptr);
182 const len = s.len * @sizeOf(T);182 const length = s.len * @sizeOf(T);
183 @memset(ptr, 0, len);183 @memset(ptr, 0, length);
184}184}
185185
186test "mem.secureZero" {186test "mem.secureZero" {
...@@ -252,6 +252,20 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {...@@ -252,6 +252,20 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
252 return true;252 return true;
253}253}
254254
255pub 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
261pub fn toSliceConst(comptime T: type, ptr: [*]const T) []const T {
262 return ptr[0..len(T, ptr)];
263}
264
265pub fn toSlice(comptime T: type, ptr: [*]T) []T {
266 return ptr[0..len(T, ptr)];
267}
268
255/// Returns true if all elements in a slice are equal to the scalar value provided269/// Returns true if all elements in a slice are equal to the scalar value provided
256pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {270pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {
257 for (slice) |item| {271 for (slice) |item| {
...@@ -809,3 +823,4 @@ pub fn endianSwap(comptime T: type, x: T) T {...@@ -809,3 +823,4 @@ pub fn endianSwap(comptime T: type, x: T) T {
809test "std.mem.endianSwap" {823test "std.mem.endianSwap" {
810 assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);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,10 +453,7 @@ pub const ChildProcess = struct {
453 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);453 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
454454
455 const nul_handle = if (any_ignore) blk: {455 const nul_handle = if (any_ignore) blk: {
456 const nul_file_path = "NUL";456 break :blk try os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL);
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);
460 } else blk: {457 } else blk: {
461 break :blk undefined;458 break :blk undefined;
462 };459 };
std/os/file.zig+30-22
...@@ -7,6 +7,7 @@ const assert = std.debug.assert;...@@ -7,6 +7,7 @@ const assert = std.debug.assert;
7const posix = os.posix;7const posix = os.posix;
8const windows = os.windows;8const windows = os.windows;
9const Os = builtin.Os;9const Os = builtin.Os;
10const windows_util = @import("windows/util.zig");
1011
11const is_posix = builtin.os != builtin.Os.windows;12const is_posix = builtin.os != builtin.Os.windows;
12const is_windows = builtin.os == builtin.Os.windows;13const is_windows = builtin.os == builtin.Os.windows;
...@@ -102,21 +103,42 @@ pub const File = struct {...@@ -102,21 +103,42 @@ pub const File = struct {
102103
103 pub const AccessError = error{104 pub const AccessError = error{
104 PermissionDenied,105 PermissionDenied,
105 NotFound,106 PathNotFound,
107 FileNotFound,
106 NameTooLong,108 NameTooLong,
107 BadMode,109 BadMode,
108 BadPathName,110 BadPathName,
109 Io,111 Io,
110 SystemResources,112 SystemResources,
111 OutOfMemory,113
114 /// On Windows, file paths must be valid Unicode.
115 InvalidUtf8,
112116
113 Unexpected,117 Unexpected,
114 };118 };
115119
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 pub fn accessC(path: [*]const u8) AccessError!void {138 pub fn accessC(path: [*]const u8) AccessError!void {
117 if (is_windows) {139 if (is_windows) {
118 // this needs to convert to UTF-16LE and call accessW140 const path_w = try windows_util.cStrToPrefixedFileW(path);
119 @compileError("TODO support windows");141 return accessW(&path_w);
120 }142 }
121 if (is_posix) {143 if (is_posix) {
122 const result = posix.access(path, posix.F_OK);144 const result = posix.access(path, posix.F_OK);
...@@ -137,28 +159,14 @@ pub const File = struct {...@@ -137,28 +159,14 @@ pub const File = struct {
137 posix.ENOMEM => return error.SystemResources,159 posix.ENOMEM => return error.SystemResources,
138 else => return os.unexpectedErrorPosix(err),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 }
157165
158 pub fn access(path: []const u8) AccessError!void {166 pub fn access(path: []const u8) AccessError!void {
159 if (is_windows) {167 if (is_windows) {
160 // this needs to convert to UTF-16LE and call accessW168 const path_w = try windows_util.sliceToPrefixedFileW(path);
161 @compileError("TODO support windows");169 return accessW(&path_w);
162 }170 }
163 if (is_posix) {171 if (is_posix) {
164 var path_with_null: [posix.PATH_MAX]u8 = undefined;172 var path_with_null: [posix.PATH_MAX]u8 = undefined;
...@@ -167,7 +175,7 @@ pub const File = struct {...@@ -167,7 +175,7 @@ pub const File = struct {
167 path_with_null[path.len] = 0;175 path_with_null[path.len] = 0;
168 return accessC(&path_with_null);176 return accessC(&path_with_null);
169 }177 }
170 @compileError("TODO implement access for this OS");178 @compileError("Unsupported OS");
171 }179 }
172180
173 /// Upon success, the stream is in an uninitialized state. To continue using it,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,6 +10,7 @@ pub const GetAppDataDirError = error{
10};10};
1111
12/// Caller owns returned memory.12/// Caller owns returned memory.
13/// TODO determine if we can remove the allocator requirement
13pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {14pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
14 switch (builtin.os) {15 switch (builtin.os) {
15 builtin.Os.windows => {16 builtin.Os.windows => {
...@@ -22,7 +23,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD...@@ -22,7 +23,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
22 )) {23 )) {
23 os.windows.S_OK => {24 os.windows.S_OK => {
24 defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));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 error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,27 error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
27 error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,28 error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
28 error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,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,7 +45,7 @@ pub const MAX_PATH_BYTES = switch (builtin.os) {
45 // If it would require 4 UTF-8 bytes, then there would be a surrogate45 // If it would require 4 UTF-8 bytes, then there would be a surrogate
46 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.46 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
47 // +1 for the null byte at the end, which can be encoded in 1 byte.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 else => @compileError("Unsupported OS"),49 else => @compileError("Unsupported OS"),
50};50};
5151
...@@ -326,6 +326,8 @@ pub const PosixWriteError = error{...@@ -326,6 +326,8 @@ pub const PosixWriteError = error{
326 NoSpaceLeft,326 NoSpaceLeft,
327 AccessDenied,327 AccessDenied,
328 BrokenPipe,328 BrokenPipe,
329
330 /// See https://github.com/ziglang/zig/issues/1396
329 Unexpected,331 Unexpected,
330};332};
331333
...@@ -439,6 +441,8 @@ pub const PosixOpenError = error{...@@ -439,6 +441,8 @@ pub const PosixOpenError = error{
439 NoSpaceLeft,441 NoSpaceLeft,
440 NotDir,442 NotDir,
441 PathAlreadyExists,443 PathAlreadyExists,
444
445 /// See https://github.com/ziglang/zig/issues/1396
442 Unexpected,446 Unexpected,
443};447};
444448
...@@ -600,6 +604,8 @@ pub const PosixExecveError = error{...@@ -600,6 +604,8 @@ pub const PosixExecveError = error{
600 FileNotFound,604 FileNotFound,
601 NotDir,605 NotDir,
602 FileBusy,606 FileBusy,
607
608 /// See https://github.com/ziglang/zig/issues/1396
603 Unexpected,609 Unexpected,
604};610};
605611
...@@ -736,20 +742,25 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {...@@ -736,20 +742,25 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
736pub const GetCwdError = error{Unexpected};742pub const GetCwdError = error{Unexpected};
737743
738/// The result is a slice of out_buffer.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.
739pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {746pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {
740 switch (builtin.os) {747 switch (builtin.os) {
741 Os.windows => {748 Os.windows => {
742 var utf16le_buf: [windows_util.PATH_MAX_UTF16]u16 = undefined;749 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
743 const result = windows.GetCurrentDirectoryW(utf16le_buf.len, &utf16le_buf);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 if (result == 0) {753 if (result == 0) {
745 const err = windows.GetLastError();754 const err = windows.GetLastError();
746 switch (err) {755 switch (err) {
747 else => return unexpectedErrorWindows(err),756 else => return unexpectedErrorWindows(err),
748 }757 }
749 }758 }
750 assert(result <= buf.len);759 assert(result <= utf16le_buf.len);
751 const utf16le_slice = utf16le_buf[0..result];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 else => {765 else => {
755 const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len));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,7 +775,9 @@ pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {
764775
765test "os.getCwd" {776test "os.getCwd" {
766 // at least call it so it gets compiled777 // 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}
769782
770pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;783pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
...@@ -779,6 +792,8 @@ pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []con...@@ -779,6 +792,8 @@ pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []con
779792
780pub const WindowsSymLinkError = error{793pub const WindowsSymLinkError = error{
781 OutOfMemory,794 OutOfMemory,
795
796 /// See https://github.com/ziglang/zig/issues/1396
782 Unexpected,797 Unexpected,
783};798};
784799
...@@ -809,6 +824,8 @@ pub const PosixSymLinkError = error{...@@ -809,6 +824,8 @@ pub const PosixSymLinkError = error{
809 NoSpaceLeft,824 NoSpaceLeft,
810 ReadOnlyFileSystem,825 ReadOnlyFileSystem,
811 NotDir,826 NotDir,
827
828 /// See https://github.com/ziglang/zig/issues/1396
812 Unexpected,829 Unexpected,
813};830};
814831
...@@ -867,7 +884,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:...@@ -867,7 +884,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
867 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);884 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
868885
869 if (symLink(allocator, existing_path, tmp_path)) {886 if (symLink(allocator, existing_path, tmp_path)) {
870 return rename(allocator, tmp_path, new_path);887 return rename(tmp_path, new_path);
871 } else |err| switch (err) {888 } else |err| switch (err) {
872 error.PathAlreadyExists => continue,889 error.PathAlreadyExists => continue,
873 else => return err, // TODO zig should know this set does not include PathAlreadyExists890 else => return err, // TODO zig should know this set does not include PathAlreadyExists
...@@ -886,8 +903,15 @@ pub const DeleteFileError = error{...@@ -886,8 +903,15 @@ pub const DeleteFileError = error{
886 NotDir,903 NotDir,
887 SystemResources,904 SystemResources,
888 ReadOnlyFileSystem,905 ReadOnlyFileSystem,
889 OutOfMemory,
890906
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 Unexpected,915 Unexpected,
892};916};
893917
...@@ -900,7 +924,18 @@ pub fn deleteFile(file_path: []const u8) DeleteFileError!void {...@@ -900,7 +924,18 @@ pub fn deleteFile(file_path: []const u8) DeleteFileError!void {
900}924}
901925
902pub fn deleteFileWindows(file_path: []const u8) !void {926pub 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}
905940
906pub fn deleteFilePosixC(file_path: [*]const u8) !void {941pub fn deleteFilePosixC(file_path: [*]const u8) !void {
...@@ -1028,7 +1063,7 @@ pub const AtomicFile = struct {...@@ -1028,7 +1063,7 @@ pub const AtomicFile = struct {
1028 pub fn deinit(self: *AtomicFile) void {1063 pub fn deinit(self: *AtomicFile) void {
1029 if (!self.finished) {1064 if (!self.finished) {
1030 self.file.close();1065 self.file.close();
1031 deleteFile(self.allocator, self.tmp_path) catch {};1066 deleteFile(self.tmp_path) catch {};
1032 self.allocator.free(self.tmp_path);1067 self.allocator.free(self.tmp_path);
1033 self.finished = true;1068 self.finished = true;
1034 }1069 }
...@@ -1037,7 +1072,7 @@ pub const AtomicFile = struct {...@@ -1037,7 +1072,7 @@ pub const AtomicFile = struct {
1037 pub fn finish(self: *AtomicFile) !void {1072 pub fn finish(self: *AtomicFile) !void {
1038 assert(!self.finished);1073 assert(!self.finished);
1039 self.file.close();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 self.allocator.free(self.tmp_path);1076 self.allocator.free(self.tmp_path);
1042 self.finished = true;1077 self.finished = true;
1043 }1078 }
...@@ -1075,7 +1110,15 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void {...@@ -1075,7 +1110,15 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void {
10751110
1076pub fn rename(old_path: []const u8, new_path: []const u8) !void {1111pub fn rename(old_path: []const u8, new_path: []const u8) !void {
1077 if (is_windows) {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 } else {1122 } else {
1080 var old_path_with_null: [posix.PATH_MAX]u8 = undefined;1123 var old_path_with_null: [posix.PATH_MAX]u8 = undefined;
1081 if (old_path.len >= posix.PATH_MAX) return error.NameTooLong;1124 if (old_path.len >= posix.PATH_MAX) return error.NameTooLong;
...@@ -1099,11 +1142,10 @@ pub fn makeDir(dir_path: []const u8) !void {...@@ -1099,11 +1142,10 @@ pub fn makeDir(dir_path: []const u8) !void {
1099 }1142 }
1100}1143}
11011144
1102pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void {1145pub fn makeDirWindows(dir_path: []const u8) !void {
1103 const path_buf = try cstr.addNullByte(allocator, dir_path);1146 const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
1104 defer allocator.free(path_buf);
11051147
1106 if (windows.CreateDirectoryA(path_buf.ptr, null) == 0) {1148 if (windows.CreateDirectoryW(&dir_path_w, null) == 0) {
1107 const err = windows.GetLastError();1149 const err = windows.GetLastError();
1108 return switch (err) {1150 return switch (err) {
1109 windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists,1151 windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists,
...@@ -1144,13 +1186,14 @@ pub fn makeDirPosix(dir_path: []const u8) !void {...@@ -1144,13 +1186,14 @@ pub fn makeDirPosix(dir_path: []const u8) !void {
11441186
1145/// Calls makeDir recursively to make an entire path. Returns success if the path1187/// Calls makeDir recursively to make an entire path. Returns success if the path
1146/// already exists and is a directory.1188/// already exists and is a directory.
1189/// TODO determine if we can remove the allocator requirement from this function
1147pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {1190pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
1148 const resolved_path = try path.resolve(allocator, full_path);1191 const resolved_path = try path.resolve(allocator, full_path);
1149 defer allocator.free(resolved_path);1192 defer allocator.free(resolved_path);
11501193
1151 var end_index: usize = resolved_path.len;1194 var end_index: usize = resolved_path.len;
1152 while (true) {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 error.PathAlreadyExists => {1197 error.PathAlreadyExists => {
1155 // TODO stat the file and return an error if it's not a directory1198 // TODO stat the file and return an error if it's not a directory
1156 // this is important because otherwise a dangling symlink1199 // this is important because otherwise a dangling symlink
...@@ -1188,6 +1231,7 @@ pub const DeleteDirError = error{...@@ -1188,6 +1231,7 @@ pub const DeleteDirError = error{
1188 ReadOnlyFileSystem,1231 ReadOnlyFileSystem,
1189 OutOfMemory,1232 OutOfMemory,
11901233
1234 /// See https://github.com/ziglang/zig/issues/1396
1191 Unexpected,1235 Unexpected,
1192};1236};
11931237
...@@ -1256,20 +1300,30 @@ const DeleteTreeError = error{...@@ -1256,20 +1300,30 @@ const DeleteTreeError = error{
1256 FileSystem,1300 FileSystem,
1257 FileBusy,1301 FileBusy,
1258 DirNotEmpty,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 Unexpected,1312 Unexpected,
1260};1313};
1314
1315/// TODO determine if we can remove the allocator requirement
1261pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {1316pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {
1262 start_over: while (true) {1317 start_over: while (true) {
1263 var got_access_denied = false;1318 var got_access_denied = false;
1264 // First, try deleting the item as a file. This way we don't follow sym links.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 return;1321 return;
1267 } else |err| switch (err) {1322 } else |err| switch (err) {
1268 error.FileNotFound => return,1323 error.FileNotFound => return,
1269 error.IsDir => {},1324 error.IsDir => {},
1270 error.AccessDenied => got_access_denied = true,1325 error.AccessDenied => got_access_denied = true,
12711326
1272 error.OutOfMemory,
1273 error.SymLinkLoop,1327 error.SymLinkLoop,
1274 error.NameTooLong,1328 error.NameTooLong,
1275 error.SystemResources,1329 error.SystemResources,
...@@ -1277,6 +1331,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1277,6 +1331,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1277 error.NotDir,1331 error.NotDir,
1278 error.FileSystem,1332 error.FileSystem,
1279 error.FileBusy,1333 error.FileBusy,
1334 error.InvalidUtf8,
1335 error.BadPathName,
1280 error.Unexpected,1336 error.Unexpected,
1281 => return err,1337 => return err,
1282 }1338 }
...@@ -1383,6 +1439,7 @@ pub const Dir = struct {...@@ -1383,6 +1439,7 @@ pub const Dir = struct {
1383 PathAlreadyExists,1439 PathAlreadyExists,
1384 OutOfMemory,1440 OutOfMemory,
13851441
1442 /// See https://github.com/ziglang/zig/issues/1396
1386 Unexpected,1443 Unexpected,
1387 };1444 };
13881445
...@@ -1685,6 +1742,8 @@ pub fn posix_setregid(rgid: u32, egid: u32) !void {...@@ -1685,6 +1742,8 @@ pub fn posix_setregid(rgid: u32, egid: u32) !void {
16851742
1686pub const WindowsGetStdHandleErrs = error{1743pub const WindowsGetStdHandleErrs = error{
1687 NoStdHandles,1744 NoStdHandles,
1745
1746 /// See https://github.com/ziglang/zig/issues/1396
1688 Unexpected,1747 Unexpected,
1689};1748};
16901749
...@@ -2012,7 +2071,7 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {...@@ -2012,7 +2071,7 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
2012/// Call this when you made a windows DLL call or something that does SetLastError2071/// Call this when you made a windows DLL call or something that does SetLastError
2013/// and you get an unexpected error.2072/// and you get an unexpected error.
2014pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {2073pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
2015 if (unexpected_error_tracing) {2074 if (true) {
2016 debug.warn("unexpected GetLastError(): {}\n", err);2075 debug.warn("unexpected GetLastError(): {}\n", err);
2017 debug.dumpCurrentStackTrace(null);2076 debug.dumpCurrentStackTrace(null);
2018 }2077 }
...@@ -2215,6 +2274,7 @@ pub const PosixBindError = error{...@@ -2215,6 +2274,7 @@ pub const PosixBindError = error{
2215 /// The socket inode would reside on a read-only filesystem.2274 /// The socket inode would reside on a read-only filesystem.
2216 ReadOnlyFileSystem,2275 ReadOnlyFileSystem,
22172276
2277 /// See https://github.com/ziglang/zig/issues/1396
2218 Unexpected,2278 Unexpected,
2219};2279};
22202280
...@@ -2258,6 +2318,7 @@ const PosixListenError = error{...@@ -2258,6 +2318,7 @@ const PosixListenError = error{
2258 /// The socket is not of a type that supports the listen() operation.2318 /// The socket is not of a type that supports the listen() operation.
2259 OperationNotSupported,2319 OperationNotSupported,
22602320
2321 /// See https://github.com/ziglang/zig/issues/1396
2261 Unexpected,2322 Unexpected,
2262};2323};
22632324
...@@ -2311,6 +2372,7 @@ pub const PosixAcceptError = error{...@@ -2311,6 +2372,7 @@ pub const PosixAcceptError = error{
2311 /// Firewall rules forbid connection.2372 /// Firewall rules forbid connection.
2312 BlockedByFirewall,2373 BlockedByFirewall,
23132374
2375 /// See https://github.com/ziglang/zig/issues/1396
2314 Unexpected,2376 Unexpected,
2315};2377};
23162378
...@@ -2356,6 +2418,7 @@ pub const LinuxEpollCreateError = error{...@@ -2356,6 +2418,7 @@ pub const LinuxEpollCreateError = error{
2356 /// There was insufficient memory to create the kernel object.2418 /// There was insufficient memory to create the kernel object.
2357 SystemResources,2419 SystemResources,
23582420
2421 /// See https://github.com/ziglang/zig/issues/1396
2359 Unexpected,2422 Unexpected,
2360};2423};
23612424
...@@ -2410,6 +2473,7 @@ pub const LinuxEpollCtlError = error{...@@ -2410,6 +2473,7 @@ pub const LinuxEpollCtlError = error{
2410 /// for example, a regular file or a directory.2473 /// for example, a regular file or a directory.
2411 FileDescriptorIncompatibleWithEpoll,2474 FileDescriptorIncompatibleWithEpoll,
24122475
2476 /// See https://github.com/ziglang/zig/issues/1396
2413 Unexpected,2477 Unexpected,
2414};2478};
24152479
...@@ -2452,6 +2516,7 @@ pub const LinuxEventFdError = error{...@@ -2452,6 +2516,7 @@ pub const LinuxEventFdError = error{
2452 ProcessFdQuotaExceeded,2516 ProcessFdQuotaExceeded,
2453 SystemFdQuotaExceeded,2517 SystemFdQuotaExceeded,
24542518
2519 /// See https://github.com/ziglang/zig/issues/1396
2455 Unexpected,2520 Unexpected,
2456};2521};
24572522
...@@ -2474,6 +2539,7 @@ pub const PosixGetSockNameError = error{...@@ -2474,6 +2539,7 @@ pub const PosixGetSockNameError = error{
2474 /// Insufficient resources were available in the system to perform the operation.2539 /// Insufficient resources were available in the system to perform the operation.
2475 SystemResources,2540 SystemResources,
24762541
2542 /// See https://github.com/ziglang/zig/issues/1396
2477 Unexpected,2543 Unexpected,
2478};2544};
24792545
...@@ -2527,6 +2593,7 @@ pub const PosixConnectError = error{...@@ -2527,6 +2593,7 @@ pub const PosixConnectError = error{
2527 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.2593 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
2528 ConnectionTimedOut,2594 ConnectionTimedOut,
25292595
2596 /// See https://github.com/ziglang/zig/issues/1396
2530 Unexpected,2597 Unexpected,
2531};2598};
25322599
...@@ -2748,6 +2815,7 @@ pub const SpawnThreadError = error{...@@ -2748,6 +2815,7 @@ pub const SpawnThreadError = error{
2748 /// Not enough userland memory to spawn the thread.2815 /// Not enough userland memory to spawn the thread.
2749 OutOfMemory,2816 OutOfMemory,
27502817
2818 /// See https://github.com/ziglang/zig/issues/1396
2751 Unexpected,2819 Unexpected,
2752};2820};
27532821
...@@ -2935,6 +3003,8 @@ pub fn posixFStat(fd: i32) !posix.Stat {...@@ -2935,6 +3003,8 @@ pub fn posixFStat(fd: i32) !posix.Stat {
2935pub const CpuCountError = error{3003pub const CpuCountError = error{
2936 OutOfMemory,3004 OutOfMemory,
2937 PermissionDenied,3005 PermissionDenied,
3006
3007 /// See https://github.com/ziglang/zig/issues/1396
2938 Unexpected,3008 Unexpected,
2939};3009};
29403010
...@@ -3005,6 +3075,7 @@ pub const BsdKQueueError = error{...@@ -3005,6 +3075,7 @@ pub const BsdKQueueError = error{
3005 /// The system-wide limit on the total number of open files has been reached.3075 /// The system-wide limit on the total number of open files has been reached.
3006 SystemFdQuotaExceeded,3076 SystemFdQuotaExceeded,
30073077
3078 /// See https://github.com/ziglang/zig/issues/1396
3008 Unexpected,3079 Unexpected,
3009};3080};
30103081
std/os/path.zig+9-7
...@@ -16,6 +16,8 @@ pub const sep_windows = '\\';...@@ -16,6 +16,8 @@ pub const sep_windows = '\\';
16pub const sep_posix = '/';16pub const sep_posix = '/';
17pub const sep = if (is_windows) sep_windows else sep_posix;17pub const sep = if (is_windows) sep_windows else sep_posix;
1818
19pub const sep_str = [1]u8{sep};
20
19pub const delimiter_windows = ';';21pub const delimiter_windows = ';';
20pub const delimiter_posix = ':';22pub const delimiter_posix = ':';
21pub const delimiter = if (is_windows) delimiter_windows else delimiter_posix;23pub 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,7 +339,7 @@ pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 {
337pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {339pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
338 if (paths.len == 0) {340 if (paths.len == 0) {
339 assert(is_windows); // resolveWindows called on non windows can't use getCwd341 assert(is_windows); // resolveWindows called on non windows can't use getCwd
340 return os.getCwd(allocator);342 return os.getCwdAlloc(allocator);
341 }343 }
342344
343 // determine which disk designator we will result with, if any345 // 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,7 +434,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
432 },434 },
433 WindowsPath.Kind.None => {435 WindowsPath.Kind.None => {
434 assert(is_windows); // resolveWindows called on non windows can't use getCwd436 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 defer allocator.free(cwd);438 defer allocator.free(cwd);
437 const parsed_cwd = windowsParsePath(cwd);439 const parsed_cwd = windowsParsePath(cwd);
438 result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1);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,7 +450,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
448 } else {450 } else {
449 assert(is_windows); // resolveWindows called on non windows can't use getCwd451 assert(is_windows); // resolveWindows called on non windows can't use getCwd
450 // TODO call get cwd for the result_disk_designator instead of the global one452 // 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 defer allocator.free(cwd);454 defer allocator.free(cwd);
453455
454 result = try allocator.alloc(u8, max_size + cwd.len + 1);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,7 +518,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
516pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {518pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
517 if (paths.len == 0) {519 if (paths.len == 0) {
518 assert(!is_windows); // resolvePosix called on windows can't use getCwd520 assert(!is_windows); // resolvePosix called on windows can't use getCwd
519 return os.getCwd(allocator);521 return os.getCwdAlloc(allocator);
520 }522 }
521523
522 var first_index: usize = 0;524 var first_index: usize = 0;
...@@ -538,7 +540,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -538,7 +540,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
538 result = try allocator.alloc(u8, max_size);540 result = try allocator.alloc(u8, max_size);
539 } else {541 } else {
540 assert(!is_windows); // resolvePosix called on windows can't use getCwd542 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 defer allocator.free(cwd);544 defer allocator.free(cwd);
543 result = try allocator.alloc(u8, max_size + cwd.len + 1);545 result = try allocator.alloc(u8, max_size + cwd.len + 1);
544 mem.copy(u8, result, cwd);546 mem.copy(u8, result, cwd);
...@@ -577,7 +579,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -577,7 +579,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
577}579}
578580
579test "os.path.resolve" {581test "os.path.resolve" {
580 const cwd = try os.getCwd(debug.global_allocator);582 const cwd = try os.getCwdAlloc(debug.global_allocator);
581 if (is_windows) {583 if (is_windows) {
582 if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {584 if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {
583 cwd[0] = asciiUpper(cwd[0]);585 cwd[0] = asciiUpper(cwd[0]);
...@@ -591,7 +593,7 @@ test "os.path.resolve" {...@@ -591,7 +593,7 @@ test "os.path.resolve" {
591593
592test "os.path.resolveWindows" {594test "os.path.resolveWindows" {
593 if (is_windows) {595 if (is_windows) {
594 const cwd = try os.getCwd(debug.global_allocator);596 const cwd = try os.getCwdAlloc(debug.global_allocator);
595 const parsed_cwd = windowsParsePath(cwd);597 const parsed_cwd = windowsParsePath(cwd);
596 {598 {
597 const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" });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,9 +10,9 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
10const AtomicOrder = builtin.AtomicOrder;10const AtomicOrder = builtin.AtomicOrder;
1111
12test "makePath, put some files in it, deleteTree" {12test "makePath, put some files in it, deleteTree" {
13 try os.makePath(a, "os_test_tmp/b/c");13 try os.makePath(a, "os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "c");
14 try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");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(a, "os_test_tmp/b/file2.txt", "blah");15 try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "file2.txt", "blah");
16 try os.deleteTree(a, "os_test_tmp");16 try os.deleteTree(a, "os_test_tmp");
17 if (os.Dir.open(a, "os_test_tmp")) |dir| {17 if (os.Dir.open(a, "os_test_tmp")) |dir| {
18 @panic("expected error");18 @panic("expected error");
...@@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" {...@@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" {
2323
24test "access file" {24test "access file" {
25 try os.makePath(a, "os_test_tmp");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 @panic("expected error");27 @panic("expected error");
28 } else |err| {28 } else |err| {
29 assert(err == error.NotFound);29 assert(err == error.FileNotFound);
30 }30 }
3131
32 try io.writeFile(a, "os_test_tmp/file.txt", "");32 try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "file.txt", "");
33 try os.File.access(a, "os_test_tmp/file.txt");33 try os.File.access("os_test_tmp" ++ os.path.sep_str ++ "file.txt");
34 try os.deleteTree(a, "os_test_tmp");34 try os.deleteTree(a, "os_test_tmp");
35}35}
3636
std/os/windows/kernel32.zig+16-10
...@@ -4,10 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE...@@ -4,10 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE
44
5pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;5pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
66
7pub extern "kernel32" stdcallcc fn CreateDirectoryA(7pub extern "kernel32" stdcallcc fn CreateDirectoryA( lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
8 lpPathName: LPCSTR,8pub extern "kernel32" stdcallcc fn CreateDirectoryW( lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
9 lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
10) BOOL;
119
12pub extern "kernel32" stdcallcc fn CreateFileA(10pub extern "kernel32" stdcallcc fn CreateFileA(
13 lpFileName: [*]const u8, // TODO null terminated pointer type11 lpFileName: [*]const u8, // TODO null terminated pointer type
...@@ -59,7 +57,8 @@ pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, Ex...@@ -59,7 +57,8 @@ pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, Ex
5957
60pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;58pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
6159
62pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;60pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL;
61pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
6362
64pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;63pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
6564
...@@ -73,8 +72,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;...@@ -73,8 +72,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
7372
74pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;73pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
7574
76pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?[*]CHAR) DWORD;75pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
77pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: WORD, lpBuffer: ?[*]WCHAR) DWORD;76pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
7877
79pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;78pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
80pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD;79pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD;
...@@ -87,7 +86,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo...@@ -87,7 +86,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
8786
88pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;87pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
8988
90pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;89pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
90pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
9191
92pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;92pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
9393
...@@ -131,8 +131,14 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem...@@ -131,8 +131,14 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
131pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;131pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
132132
133pub extern "kernel32" stdcallcc fn MoveFileExA(133pub extern "kernel32" stdcallcc fn MoveFileExA(
134 lpExistingFileName: LPCSTR,134 lpExistingFileName: [*]const u8,
135 lpNewFileName: LPCSTR,135 lpNewFileName: [*]const u8,
136 dwFlags: DWORD,
137) BOOL;
138
139pub extern "kernel32" stdcallcc fn MoveFileExW(
140 lpExistingFileName: [*]const u16,
141 lpNewFileName: [*]const u16,
136 dwFlags: DWORD,142 dwFlags: DWORD,
137) BOOL;143) BOOL;
138144
std/os/windows/util.zig+74-3
...@@ -7,11 +7,17 @@ const mem = std.mem;...@@ -7,11 +7,17 @@ const mem = std.mem;
7const BufMap = std.BufMap;7const BufMap = std.BufMap;
8const cstr = std.cstr;8const cstr = std.cstr;
99
10pub 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
14pub const PATH_MAX_WIDE = 32767;
1115
12pub const WaitError = error{16pub const WaitError = error{
13 WaitAbandoned,17 WaitAbandoned,
14 WaitTimeOut,18 WaitTimeOut,
19
20 /// See https://github.com/ziglang/zig/issues/1396
15 Unexpected,21 Unexpected,
16};22};
1723
...@@ -39,6 +45,8 @@ pub const WriteError = error{...@@ -39,6 +45,8 @@ pub const WriteError = error{
39 SystemResources,45 SystemResources,
40 OperationAborted,46 OperationAborted,
41 BrokenPipe,47 BrokenPipe,
48
49 /// See https://github.com/ziglang/zig/issues/1396
42 Unexpected,50 Unexpected,
43};51};
4452
...@@ -88,13 +96,28 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool {...@@ -88,13 +96,28 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool {
88pub const OpenError = error{96pub const OpenError = error{
89 SharingViolation,97 SharingViolation,
90 PathAlreadyExists,98 PathAlreadyExists,
99
100 /// When all the path components are found but the file component is not.
91 FileNotFound,101 FileNotFound,
102
103 /// When one or more path components are not found.
104 PathNotFound,
105
92 AccessDenied,106 AccessDenied,
93 PipeBusy,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 Unexpected,118 Unexpected,
95};119};
96120
97/// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator.
98pub fn windowsOpen(121pub fn windowsOpen(
99 file_path: []const u8,122 file_path: []const u8,
100 desired_access: windows.DWORD,123 desired_access: windows.DWORD,
...@@ -102,7 +125,25 @@ pub fn windowsOpen(...@@ -102,7 +125,25 @@ pub fn windowsOpen(
102 creation_disposition: windows.DWORD,125 creation_disposition: windows.DWORD,
103 flags_and_attrs: windows.DWORD,126 flags_and_attrs: windows.DWORD,
104) OpenError!windows.HANDLE {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}
107148
108/// Caller must free result.149/// Caller must free result.
...@@ -242,3 +283,33 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t...@@ -242,3 +283,33 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t
242 }283 }
243 return WindowsWaitResult.Normal;284 return WindowsWaitResult.Normal;
244}285}
286
287pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE+1]u16 {
288 return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
289}
290
291pub 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,6 +247,8 @@ pub const Utf16LeIterator = struct {
247 }247 }
248248
249 pub fn nextCodepoint(it: *Utf16LeIterator) !?u32 {249 pub fn nextCodepoint(it: *Utf16LeIterator) !?u32 {
250 assert(it.i <= it.bytes.len);
251 if (it.i == it.bytes.len) return null;
250 const c0: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);252 const c0: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);
251 if (c0 & ~u32(0x03ff) == 0xd800) {253 if (c0 & ~u32(0x03ff) == 0xd800) {
252 // surrogate pair254 // surrogate pair
...@@ -254,10 +256,12 @@ pub const Utf16LeIterator = struct {...@@ -254,10 +256,12 @@ pub const Utf16LeIterator = struct {
254 if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf;256 if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf;
255 const c1: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);257 const c1: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);
256 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;258 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
259 it.i += 2;
257 return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));260 return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
258 } else if (c0 & ~u32(0x03ff) == 0xdc00) {261 } else if (c0 & ~u32(0x03ff) == 0xdc00) {
259 return error.UnexpectedSecondSurrogateHalf;262 return error.UnexpectedSecondSurrogateHalf;
260 } else {263 } else {
264 it.i += 2;
261 return c0;265 return c0;
262 }266 }
263 }267 }
...@@ -490,15 +494,15 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8...@@ -490,15 +494,15 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8
490 return result.toOwnedSlice();494 return result.toOwnedSlice();
491}495}
492496
493pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !void {497/// Asserts that the output buffer is big enough.
494 var out_index: usize = 0;498/// Returns end index.
499pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !usize {
500 var end_index: usize = 0;
495 var it = Utf16LeIterator.init(utf16le);501 var it = Utf16LeIterator.init(utf16le);
496 while (try it.nextCodepoint()) |codepoint| {502 while (try it.nextCodepoint()) |codepoint| {
497 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;503 end_index += try utf8Encode(codepoint, utf8[end_index..]);
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;
501 }504 }
505 return end_index;
502}506}
503507
504test "utf16leToUtf8" {508test "utf16leToUtf8" {
...@@ -508,14 +512,14 @@ test "utf16leToUtf8" {...@@ -508,14 +512,14 @@ test "utf16leToUtf8" {
508 {512 {
509 mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);513 mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);
510 mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little);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 assert(mem.eql(u8, utf8, "Aa"));516 assert(mem.eql(u8, utf8, "Aa"));
513 }517 }
514518
515 {519 {
516 mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);520 mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);
517 mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little);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 assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));523 assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));
520 }524 }
521525
...@@ -523,7 +527,7 @@ test "utf16leToUtf8" {...@@ -523,7 +527,7 @@ test "utf16leToUtf8" {
523 // the values just outside the surrogate half range527 // the values just outside the surrogate half range
524 mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);528 mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);
525 mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little);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 assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));531 assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));
528 }532 }
529533
...@@ -531,7 +535,7 @@ test "utf16leToUtf8" {...@@ -531,7 +535,7 @@ test "utf16leToUtf8" {
531 // smallest surrogate pair535 // smallest surrogate pair
532 mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);536 mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);
533 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);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 assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));539 assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));
536 }540 }
537541
...@@ -539,14 +543,14 @@ test "utf16leToUtf8" {...@@ -539,14 +543,14 @@ test "utf16leToUtf8" {
539 // largest surrogate pair543 // largest surrogate pair
540 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);544 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
541 mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little);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 assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));547 assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));
544 }548 }
545549
546 {550 {
547 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);551 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
548 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);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 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));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,3 +571,20 @@ pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16
567 try result.append(0);571 try result.append(0);
568 return result.toOwnedSlice();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
578pub 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}