authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-21 20:28:37-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-21 20:31:50-04:00
logea1b21dbdb3d5e680b133be68d174dcc0067fa1e
tree88114bd3c7235e8b0478a1e98ddc48b25d013a95
parent51852d2587b931767a12d42ce39d5c191eea10ea

fix linux

* error.BadFd is not a valid error code. it would always be a bug to get this error code. * merge error.Io with existing error.InputOutput * merge error.PathNotFound with existing error.FileNotFound. Not all OS's support both. * add os.File.openReadC * add error.BadPathName for windows file operations with invalid characters * add os.toPosixPath to help stack allocate a null terminating byte * add some TODOs for other functions to investigate removing the allocator requirement * optimize some implementations to use the alternate functions when a null byte is already available * add a missing error.SkipZigTest * os.selfExePath uses a non-allocating API * os.selfExeDirPath uses a non-allocating API * os.path.real uses a non-allocating API * add os.path.realAlloc and os.path.realC * convert many windows syscalls to use the W versions (See #534)

17 files changed, 319 insertions(+), 288 deletions(-)

doc/docgen.zig+3-3
...@@ -34,10 +34,10 @@ pub fn main() !void {...@@ -34,10 +34,10 @@ pub fn main() !void {
34 const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));34 const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));
35 defer allocator.free(out_file_name);35 defer allocator.free(out_file_name);
3636
37 var in_file = try os.File.openRead(allocator, in_file_name);37 var in_file = try os.File.openRead(in_file_name);
38 defer in_file.close();38 defer in_file.close();
3939
40 var out_file = try os.File.openWrite(allocator, out_file_name);40 var out_file = try os.File.openWrite(out_file_name);
41 defer out_file.close();41 defer out_file.close();
4242
43 var file_in_stream = io.FileInStream.init(&in_file);43 var file_in_stream = io.FileInStream.init(&in_file);
...@@ -738,7 +738,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -738,7 +738,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
738 try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);738 try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);
739 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);739 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
740 const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);740 const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
741 try io.writeFile(allocator, tmp_source_file_name, trimmed_raw_source);741 try io.writeFile(tmp_source_file_name, trimmed_raw_source);
742742
743 switch (code.id) {743 switch (code.id) {
744 Code.Id.Exe => |expected_outcome| {744 Code.Id.Exe => |expected_outcome| {
example/cat/main.zig+1-1
...@@ -20,7 +20,7 @@ pub fn main() !void {...@@ -20,7 +20,7 @@ pub fn main() !void {
20 } else if (arg[0] == '-') {20 } else if (arg[0] == '-') {
21 return usage(exe);21 return usage(exe);
22 } else {22 } else {
23 var file = os.File.openRead(allocator, arg) catch |err| {23 var file = os.File.openRead(arg) catch |err| {
24 warn("Unable to open file: {}\n", @errorName(err));24 warn("Unable to open file: {}\n", @errorName(err));
25 return err;25 return err;
26 };26 };
src-self-hosted/compilation.zig+1-4
...@@ -257,8 +257,6 @@ pub const Compilation = struct {...@@ -257,8 +257,6 @@ pub const Compilation = struct {
257 pub const BuildError = error{257 pub const BuildError = error{
258 OutOfMemory,258 OutOfMemory,
259 EndOfStream,259 EndOfStream,
260 BadFd,
261 Io,
262 IsDir,260 IsDir,
263 Unexpected,261 Unexpected,
264 SystemResources,262 SystemResources,
...@@ -273,7 +271,6 @@ pub const Compilation = struct {...@@ -273,7 +271,6 @@ pub const Compilation = struct {
273 NameTooLong,271 NameTooLong,
274 SystemFdQuotaExceeded,272 SystemFdQuotaExceeded,
275 NoDevice,273 NoDevice,
276 PathNotFound,
277 NoSpaceLeft,274 NoSpaceLeft,
278 NotDir,275 NotDir,
279 FileSystem,276 FileSystem,
...@@ -962,7 +959,7 @@ pub const Compilation = struct {...@@ -962,7 +959,7 @@ pub const Compilation = struct {
962 if (self.root_src_path) |root_src_path| {959 if (self.root_src_path) |root_src_path| {
963 const root_scope = blk: {960 const root_scope = blk: {
964 // TODO async/await os.path.real961 // TODO async/await os.path.real
965 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {962 const root_src_real_path = os.path.realAlloc(self.gpa(), root_src_path) catch |err| {
966 try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err));963 try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err));
967 return;964 return;
968 };965 };
src-self-hosted/introspect.zig+1-1
...@@ -22,7 +22,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![...@@ -22,7 +22,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![
2222
23/// Caller must free result23/// Caller must free result
24pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {24pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
25 const self_exe_path = try os.selfExeDirPath(allocator);25 const self_exe_path = try os.selfExeDirPathAlloc(allocator);
26 defer allocator.free(self_exe_path);26 defer allocator.free(self_exe_path);
2727
28 var cur_path: []const u8 = self_exe_path;28 var cur_path: []const u8 = self_exe_path;
src-self-hosted/libc_installation.zig+1-1
...@@ -453,7 +453,7 @@ fn fileExists(path: []const u8) !bool {...@@ -453,7 +453,7 @@ fn fileExists(path: []const u8) !bool {
453 if (std.os.File.access(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.FileNotFound, error.PathNotFound, error.PermissionDenied => return false,456 error.FileNotFound, error.PermissionDenied => return false,
457 else => return error.FileSystem,457 else => return error.FileSystem,
458 }458 }
459}459}
std/build.zig+12-6
...@@ -1491,11 +1491,14 @@ pub const LibExeObjStep = struct {...@@ -1491,11 +1491,14 @@ pub const LibExeObjStep = struct {
1491 }1491 }
14921492
1493 if (!is_darwin) {1493 if (!is_darwin) {
1494 const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable);1494 const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc(
1495 builder.allocator,
1496 builder.pathFromRoot(builder.cache_root),
1497 ));
1495 defer builder.allocator.free(rpath_arg);1498 defer builder.allocator.free(rpath_arg);
1496 cc_args.append(rpath_arg) catch unreachable;1499 try cc_args.append(rpath_arg);
14971500
1498 cc_args.append("-rdynamic") catch unreachable;1501 try cc_args.append("-rdynamic");
1499 }1502 }
15001503
1501 for (self.full_path_libs.toSliceConst()) |full_path_lib| {1504 for (self.full_path_libs.toSliceConst()) |full_path_lib| {
...@@ -1566,11 +1569,14 @@ pub const LibExeObjStep = struct {...@@ -1566,11 +1569,14 @@ pub const LibExeObjStep = struct {
1566 cc_args.append("-o") catch unreachable;1569 cc_args.append("-o") catch unreachable;
1567 cc_args.append(output_path) catch unreachable;1570 cc_args.append(output_path) catch unreachable;
15681571
1569 const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable);1572 const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc(
1573 builder.allocator,
1574 builder.pathFromRoot(builder.cache_root),
1575 ));
1570 defer builder.allocator.free(rpath_arg);1576 defer builder.allocator.free(rpath_arg);
1571 cc_args.append(rpath_arg) catch unreachable;1577 try cc_args.append(rpath_arg);
15721578
1573 cc_args.append("-rdynamic") catch unreachable;1579 try cc_args.append("-rdynamic");
15741580
1575 {1581 {
1576 var it = self.link_libs.iterator();1582 var it = self.link_libs.iterator();
std/debug/index.zig+1-1
...@@ -255,7 +255,7 @@ pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address...@@ -255,7 +255,7 @@ pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address
255 address,255 address,
256 compile_unit_name,256 compile_unit_name,
257 );257 );
258 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {258 if (printLineFromFile(out_stream, line_info)) {
259 if (line_info.column == 0) {259 if (line_info.column == 0) {
260 try out_stream.write("\n");260 try out_stream.write("\n");
261 } else {261 } else {
std/event/fs.zig+24-32
...@@ -78,8 +78,7 @@ pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, o...@@ -78,8 +78,7 @@ pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, o
78 builtin.Os.macosx,78 builtin.Os.macosx,
79 builtin.Os.linux,79 builtin.Os.linux,
80 => return await (async pwritevPosix(loop, fd, data, offset) catch unreachable),80 => return await (async pwritevPosix(loop, fd, data, offset) catch unreachable),
81 builtin.Os.windows,81 builtin.Os.windows => return await (async pwritevWindows(loop, fd, data, offset) catch unreachable),
82 => return await (async pwritevWindows(loop, fd, data, offset) catch unreachable),
83 else => @compileError("Unsupported OS"),82 else => @compileError("Unsupported OS"),
84 }83 }
85}84}
...@@ -147,7 +146,6 @@ pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, off...@@ -147,7 +146,6 @@ pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, off
147 }146 }
148}147}
149148
150
151/// data - just the inner references - must live until pwritev promise completes.149/// data - just the inner references - must live until pwritev promise completes.
152pub async fn pwritevPosix(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {150pub async fn pwritevPosix(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
153 // workaround for https://github.com/ziglang/zig/issues/1194151 // workaround for https://github.com/ziglang/zig/issues/1194
...@@ -203,8 +201,7 @@ pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset:...@@ -203,8 +201,7 @@ pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset:
203 builtin.Os.macosx,201 builtin.Os.macosx,
204 builtin.Os.linux,202 builtin.Os.linux,
205 => return await (async preadvPosix(loop, fd, data, offset) catch unreachable),203 => return await (async preadvPosix(loop, fd, data, offset) catch unreachable),
206 builtin.Os.windows,204 builtin.Os.windows => return await (async preadvWindows(loop, fd, data, offset) catch unreachable),
207 => return await (async preadvWindows(loop, fd, data, offset) catch unreachable),
208 else => @compileError("Unsupported OS"),205 else => @compileError("Unsupported OS"),
209 }206 }
210}207}
...@@ -222,7 +219,7 @@ pub async fn preadvWindows(loop: *Loop, fd: os.FileHandle, data: []const []u8, o...@@ -222,7 +219,7 @@ pub async fn preadvWindows(loop: *Loop, fd: os.FileHandle, data: []const []u8, o
222 var inner_off: usize = 0;219 var inner_off: usize = 0;
223 while (true) {220 while (true) {
224 const v = data_copy[iov_i];221 const v = data_copy[iov_i];
225 const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len-inner_off], offset + off) catch unreachable);222 const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off) catch unreachable);
226 off += amt_read;223 off += amt_read;
227 inner_off += amt_read;224 inner_off += amt_read;
228 if (inner_off == v.len) {225 if (inner_off == v.len) {
...@@ -340,8 +337,7 @@ pub async fn openPosix(...@@ -340,8 +337,7 @@ pub async fn openPosix(
340 resume @handle();337 resume @handle();
341 }338 }
342339
343 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);340 const path_c = try std.os.toPosixPath(path);
344 defer loop.allocator.free(path_with_null);
345341
346 var req_node = RequestNode{342 var req_node = RequestNode{
347 .prev = null,343 .prev = null,
...@@ -349,7 +345,7 @@ pub async fn openPosix(...@@ -349,7 +345,7 @@ pub async fn openPosix(
349 .data = Request{345 .data = Request{
350 .msg = Request.Msg{346 .msg = Request.Msg{
351 .Open = Request.Msg.Open{347 .Open = Request.Msg.Open{
352 .path = path_with_null[0..path.len],348 .path = path_c[0..path.len],
353 .flags = flags,349 .flags = flags,
354 .mode = mode,350 .mode = mode,
355 .result = undefined,351 .result = undefined,
...@@ -408,8 +404,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os...@@ -408,8 +404,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os
408 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;404 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
409 return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable);405 return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable);
410 },406 },
411 builtin.Os.windows,407 builtin.Os.windows => return os.windowsOpen(
412 => return os.windowsOpen(
413 path,408 path,
414 windows.GENERIC_WRITE,409 windows.GENERIC_WRITE,
415 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,410 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
...@@ -434,7 +429,7 @@ pub async fn openReadWrite(...@@ -434,7 +429,7 @@ pub async fn openReadWrite(
434429
435 builtin.Os.windows => return os.windowsOpen(430 builtin.Os.windows => return os.windowsOpen(
436 path,431 path,
437 windows.GENERIC_WRITE|windows.GENERIC_READ,432 windows.GENERIC_WRITE | windows.GENERIC_READ,
438 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,433 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
439 windows.OPEN_ALWAYS,434 windows.OPEN_ALWAYS,
440 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,435 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
...@@ -510,8 +505,7 @@ pub const CloseOperation = struct {...@@ -510,8 +505,7 @@ pub const CloseOperation = struct {
510 self.loop.allocator.destroy(self);505 self.loop.allocator.destroy(self);
511 }506 }
512 },507 },
513 builtin.Os.windows,508 builtin.Os.windows => {
514 => {
515 if (self.os_data.handle) |handle| {509 if (self.os_data.handle) |handle| {
516 os.close(handle);510 os.close(handle);
517 }511 }
...@@ -529,8 +523,7 @@ pub const CloseOperation = struct {...@@ -529,8 +523,7 @@ pub const CloseOperation = struct {
529 self.os_data.close_req_node.data.msg.Close.fd = handle;523 self.os_data.close_req_node.data.msg.Close.fd = handle;
530 self.os_data.have_fd = true;524 self.os_data.have_fd = true;
531 },525 },
532 builtin.Os.windows,526 builtin.Os.windows => {
533 => {
534 self.os_data.handle = handle;527 self.os_data.handle = handle;
535 },528 },
536 else => @compileError("Unsupported OS"),529 else => @compileError("Unsupported OS"),
...@@ -545,8 +538,7 @@ pub const CloseOperation = struct {...@@ -545,8 +538,7 @@ pub const CloseOperation = struct {
545 => {538 => {
546 self.os_data.have_fd = false;539 self.os_data.have_fd = false;
547 },540 },
548 builtin.Os.windows,541 builtin.Os.windows => {
549 => {
550 self.os_data.handle = null;542 self.os_data.handle = null;
551 },543 },
552 else => @compileError("Unsupported OS"),544 else => @compileError("Unsupported OS"),
...@@ -561,8 +553,7 @@ pub const CloseOperation = struct {...@@ -561,8 +553,7 @@ pub const CloseOperation = struct {
561 assert(self.os_data.have_fd);553 assert(self.os_data.have_fd);
562 return self.os_data.close_req_node.data.msg.Close.fd;554 return self.os_data.close_req_node.data.msg.Close.fd;
563 },555 },
564 builtin.Os.windows,556 builtin.Os.windows => {
565 => {
566 return self.os_data.handle.?;557 return self.os_data.handle.?;
567 },558 },
568 else => @compileError("Unsupported OS"),559 else => @compileError("Unsupported OS"),
...@@ -582,8 +573,7 @@ pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8,...@@ -582,8 +573,7 @@ pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8,
582 builtin.Os.linux,573 builtin.Os.linux,
583 builtin.Os.macosx,574 builtin.Os.macosx,
584 => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable),575 => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable),
585 builtin.Os.windows,576 builtin.Os.windows => return await (async writeFileWindows(loop, path, contents) catch unreachable),
586 => return await (async writeFileWindows(loop, path, contents) catch unreachable),
587 else => @compileError("Unsupported OS"),577 else => @compileError("Unsupported OS"),
588 }578 }
589}579}
...@@ -1000,7 +990,7 @@ pub fn Watch(comptime V: type) type {...@@ -1000,7 +990,7 @@ pub fn Watch(comptime V: type) type {
1000 const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename);990 const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename);
1001 var basename_utf16le_null_consumed = false;991 var basename_utf16le_null_consumed = false;
1002 defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null);992 defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null);
1003 const basename_utf16le_no_null = basename_utf16le_null[0..basename_utf16le_null.len-1];993 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
1004994
1005 const dir_handle = windows.CreateFileW(995 const dir_handle = windows.CreateFileW(
1006 dirname_utf16le.ptr,996 dirname_utf16le.ptr,
...@@ -1014,9 +1004,8 @@ pub fn Watch(comptime V: type) type {...@@ -1014,9 +1004,8 @@ pub fn Watch(comptime V: type) type {
1014 if (dir_handle == windows.INVALID_HANDLE_VALUE) {1004 if (dir_handle == windows.INVALID_HANDLE_VALUE) {
1015 const err = windows.GetLastError();1005 const err = windows.GetLastError();
1016 switch (err) {1006 switch (err) {
1017 windows.ERROR.FILE_NOT_FOUND,1007 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1018 windows.ERROR.PATH_NOT_FOUND,1008 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1019 => return error.PathNotFound,
1020 else => return os.unexpectedErrorWindows(err),1009 else => return os.unexpectedErrorWindows(err),
1021 }1010 }
1022 }1011 }
...@@ -1102,7 +1091,10 @@ pub fn Watch(comptime V: type) type {...@@ -1102,7 +1091,10 @@ pub fn Watch(comptime V: type) type {
11021091
1103 // TODO handle this error not in the channel but in the setup1092 // TODO handle this error not in the channel but in the setup
1104 _ = os.windowsCreateIoCompletionPort(1093 _ = os.windowsCreateIoCompletionPort(
1105 dir_handle, self.channel.loop.os_data.io_port, completion_key, undefined,1094 dir_handle,
1095 self.channel.loop.os_data.io_port,
1096 completion_key,
1097 undefined,
1106 ) catch |err| {1098 ) catch |err| {
1107 await (async self.channel.put(err) catch unreachable);1099 await (async self.channel.put(err) catch unreachable);
1108 return;1100 return;
...@@ -1122,10 +1114,10 @@ pub fn Watch(comptime V: type) type {...@@ -1122,10 +1114,10 @@ pub fn Watch(comptime V: type) type {
1122 &event_buf,1114 &event_buf,
1123 @intCast(windows.DWORD, event_buf.len),1115 @intCast(windows.DWORD, event_buf.len),
1124 windows.FALSE, // watch subtree1116 windows.FALSE, // watch subtree
1125 windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |1117 windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
1126 windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |1118 windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
1127 windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |1119 windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
1128 windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,1120 windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
1129 null, // number of bytes transferred (unused for async)1121 null, // number of bytes transferred (unused for async)
1130 &overlapped,1122 &overlapped,
1131 null, // completion routine - unused because we use IOCP1123 null, // completion routine - unused because we use IOCP
...@@ -1152,7 +1144,7 @@ pub fn Watch(comptime V: type) type {...@@ -1152,7 +1144,7 @@ pub fn Watch(comptime V: type) type {
1152 else => null,1144 else => null,
1153 };1145 };
1154 if (emit) |id| {1146 if (emit) |id| {
1155 const basename_utf16le = ([*]u16)(&ev.FileName)[0..ev.FileNameLength/2];1147 const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];
1156 const user_value = blk: {1148 const user_value = blk: {
1157 const held = await (async dir.table_lock.acquire() catch unreachable);1149 const held = await (async dir.table_lock.acquire() catch unreachable);
1158 defer held.release();1150 defer held.release();
std/os/child_process.zig+1-8
...@@ -349,14 +349,7 @@ pub const ChildProcess = struct {...@@ -349,14 +349,7 @@ pub const ChildProcess = struct {
349 };349 };
350350
351 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);351 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
352 const dev_null_fd = if (any_ignore) blk: {352 const dev_null_fd = if (any_ignore) try os.posixOpenC(c"/dev/null", posix.O_RDWR, 0) else undefined;
353 const dev_null_path = "/dev/null";
354 var fixed_buffer_mem: [dev_null_path.len + 1]u8 = undefined;
355 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
356 break :blk try os.posixOpen(&fixed_allocator.allocator, "/dev/null", posix.O_RDWR, 0);
357 } else blk: {
358 break :blk undefined;
359 };
360 defer {353 defer {
361 if (any_ignore) os.close(dev_null_fd);354 if (any_ignore) os.close(dev_null_fd);
362 }355 }
std/os/file.zig+40-23
...@@ -28,13 +28,26 @@ pub const File = struct {...@@ -28,13 +28,26 @@ pub const File = struct {
2828
29 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;29 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
3030
31 /// Call close to clean up.31 /// `openRead` except with a null terminated path
32 pub fn openRead(path: []const u8) OpenError!File {32 pub fn openReadC(path: [*]const u8) OpenError!File {
33 if (is_posix) {33 if (is_posix) {
34 const flags = posix.O_LARGEFILE | posix.O_RDONLY;34 const flags = posix.O_LARGEFILE | posix.O_RDONLY;
35 const fd = try os.posixOpen(path, flags, 0);35 const fd = try os.posixOpenC(path, flags, 0);
36 return openHandle(fd);36 return openHandle(fd);
37 } else if (is_windows) {37 }
38 if (is_windows) {
39 return openRead(mem.toSliceConst(u8, path));
40 }
41 @compileError("Unsupported OS");
42 }
43
44 /// Call close to clean up.
45 pub fn openRead(path: []const u8) OpenError!File {
46 if (is_posix) {
47 const path_c = try os.toPosixPath(path);
48 return openReadC(&path_c);
49 }
50 if (is_windows) {
38 const handle = try os.windowsOpen(51 const handle = try os.windowsOpen(
39 path,52 path,
40 windows.GENERIC_READ,53 windows.GENERIC_READ,
...@@ -43,9 +56,8 @@ pub const File = struct {...@@ -43,9 +56,8 @@ pub const File = struct {
43 windows.FILE_ATTRIBUTE_NORMAL,56 windows.FILE_ATTRIBUTE_NORMAL,
44 );57 );
45 return openHandle(handle);58 return openHandle(handle);
46 } else {
47 @compileError("TODO implement openRead for this OS");
48 }59 }
60 @compileError("Unsupported OS");
49 }61 }
5062
51 /// Calls `openWriteMode` with os.File.default_mode for the mode.63 /// Calls `openWriteMode` with os.File.default_mode for the mode.
...@@ -103,13 +115,11 @@ pub const File = struct {...@@ -103,13 +115,11 @@ pub const File = struct {
103115
104 pub const AccessError = error{116 pub const AccessError = error{
105 PermissionDenied,117 PermissionDenied,
106 PathNotFound,
107 FileNotFound,118 FileNotFound,
108 NameTooLong,119 NameTooLong,
109 BadMode,120 InputOutput,
110 BadPathName,
111 Io,
112 SystemResources,121 SystemResources,
122 BadPathName,
113123
114 /// On Windows, file paths must be valid Unicode.124 /// On Windows, file paths must be valid Unicode.
115 InvalidUtf8,125 InvalidUtf8,
...@@ -127,7 +137,7 @@ pub const File = struct {...@@ -127,7 +137,7 @@ pub const File = struct {
127 const err = windows.GetLastError();137 const err = windows.GetLastError();
128 switch (err) {138 switch (err) {
129 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,139 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
130 windows.ERROR.PATH_NOT_FOUND => return error.PathNotFound,140 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
131 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,141 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
132 else => return os.unexpectedErrorWindows(err),142 else => return os.unexpectedErrorWindows(err),
133 }143 }
...@@ -149,13 +159,13 @@ pub const File = struct {...@@ -149,13 +159,13 @@ pub const File = struct {
149 posix.EROFS => return error.PermissionDenied,159 posix.EROFS => return error.PermissionDenied,
150 posix.ELOOP => return error.PermissionDenied,160 posix.ELOOP => return error.PermissionDenied,
151 posix.ETXTBSY => return error.PermissionDenied,161 posix.ETXTBSY => return error.PermissionDenied,
152 posix.ENOTDIR => return error.NotFound,162 posix.ENOTDIR => return error.FileNotFound,
153 posix.ENOENT => return error.NotFound,163 posix.ENOENT => return error.FileNotFound,
154164
155 posix.ENAMETOOLONG => return error.NameTooLong,165 posix.ENAMETOOLONG => return error.NameTooLong,
156 posix.EINVAL => unreachable,166 posix.EINVAL => unreachable,
157 posix.EFAULT => return error.BadPathName,167 posix.EFAULT => unreachable,
158 posix.EIO => return error.Io,168 posix.EIO => return error.InputOutput,
159 posix.ENOMEM => return error.SystemResources,169 posix.ENOMEM => return error.SystemResources,
160 else => return os.unexpectedErrorPosix(err),170 else => return os.unexpectedErrorPosix(err),
161 }171 }
...@@ -197,7 +207,9 @@ pub const File = struct {...@@ -197,7 +207,9 @@ pub const File = struct {
197 const err = posix.getErrno(result);207 const err = posix.getErrno(result);
198 if (err > 0) {208 if (err > 0) {
199 return switch (err) {209 return switch (err) {
200 posix.EBADF => error.BadFd,210 // We do not make this an error code because if you get EBADF it's always a bug,
211 // since the fd could have been reused.
212 posix.EBADF => unreachable,
201 posix.EINVAL => error.Unseekable,213 posix.EINVAL => error.Unseekable,
202 posix.EOVERFLOW => error.Unseekable,214 posix.EOVERFLOW => error.Unseekable,
203 posix.ESPIPE => error.Unseekable,215 posix.ESPIPE => error.Unseekable,
...@@ -210,7 +222,7 @@ pub const File = struct {...@@ -210,7 +222,7 @@ pub const File = struct {
210 if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {222 if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {
211 const err = windows.GetLastError();223 const err = windows.GetLastError();
212 return switch (err) {224 return switch (err) {
213 windows.ERROR.INVALID_PARAMETER => error.BadFd,225 windows.ERROR.INVALID_PARAMETER => unreachable,
214 else => os.unexpectedErrorWindows(err),226 else => os.unexpectedErrorWindows(err),
215 };227 };
216 }228 }
...@@ -227,7 +239,9 @@ pub const File = struct {...@@ -227,7 +239,9 @@ pub const File = struct {
227 const err = posix.getErrno(result);239 const err = posix.getErrno(result);
228 if (err > 0) {240 if (err > 0) {
229 return switch (err) {241 return switch (err) {
230 posix.EBADF => error.BadFd,242 // We do not make this an error code because if you get EBADF it's always a bug,
243 // since the fd could have been reused.
244 posix.EBADF => unreachable,
231 posix.EINVAL => error.Unseekable,245 posix.EINVAL => error.Unseekable,
232 posix.EOVERFLOW => error.Unseekable,246 posix.EOVERFLOW => error.Unseekable,
233 posix.ESPIPE => error.Unseekable,247 posix.ESPIPE => error.Unseekable,
...@@ -241,7 +255,7 @@ pub const File = struct {...@@ -241,7 +255,7 @@ pub const File = struct {
241 if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {255 if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {
242 const err = windows.GetLastError();256 const err = windows.GetLastError();
243 return switch (err) {257 return switch (err) {
244 windows.ERROR.INVALID_PARAMETER => error.BadFd,258 windows.ERROR.INVALID_PARAMETER => unreachable,
245 else => os.unexpectedErrorWindows(err),259 else => os.unexpectedErrorWindows(err),
246 };260 };
247 }261 }
...@@ -257,7 +271,9 @@ pub const File = struct {...@@ -257,7 +271,9 @@ pub const File = struct {
257 const err = posix.getErrno(result);271 const err = posix.getErrno(result);
258 if (err > 0) {272 if (err > 0) {
259 return switch (err) {273 return switch (err) {
260 posix.EBADF => error.BadFd,274 // We do not make this an error code because if you get EBADF it's always a bug,
275 // since the fd could have been reused.
276 posix.EBADF => unreachable,
261 posix.EINVAL => error.Unseekable,277 posix.EINVAL => error.Unseekable,
262 posix.EOVERFLOW => error.Unseekable,278 posix.EOVERFLOW => error.Unseekable,
263 posix.ESPIPE => error.Unseekable,279 posix.ESPIPE => error.Unseekable,
...@@ -272,7 +288,7 @@ pub const File = struct {...@@ -272,7 +288,7 @@ pub const File = struct {
272 if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {288 if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
273 const err = windows.GetLastError();289 const err = windows.GetLastError();
274 return switch (err) {290 return switch (err) {
275 windows.ERROR.INVALID_PARAMETER => error.BadFd,291 windows.ERROR.INVALID_PARAMETER => unreachable,
276 else => os.unexpectedErrorWindows(err),292 else => os.unexpectedErrorWindows(err),
277 };293 };
278 }294 }
...@@ -305,7 +321,6 @@ pub const File = struct {...@@ -305,7 +321,6 @@ pub const File = struct {
305 }321 }
306322
307 pub const ModeError = error{323 pub const ModeError = error{
308 BadFd,
309 SystemResources,324 SystemResources,
310 Unexpected,325 Unexpected,
311 };326 };
...@@ -316,7 +331,9 @@ pub const File = struct {...@@ -316,7 +331,9 @@ pub const File = struct {
316 const err = posix.getErrno(posix.fstat(self.handle, &stat));331 const err = posix.getErrno(posix.fstat(self.handle, &stat));
317 if (err > 0) {332 if (err > 0) {
318 return switch (err) {333 return switch (err) {
319 posix.EBADF => error.BadFd,334 // We do not make this an error code because if you get EBADF it's always a bug,
335 // since the fd could have been reused.
336 posix.EBADF => unreachable,
320 posix.ENOMEM => error.SystemResources,337 posix.ENOMEM => error.SystemResources,
321 else => os.unexpectedErrorPosix(err),338 else => os.unexpectedErrorPosix(err),
322 };339 };
std/os/index.zig+79-97
...@@ -436,7 +436,7 @@ pub const PosixOpenError = error{...@@ -436,7 +436,7 @@ pub const PosixOpenError = error{
436 NameTooLong,436 NameTooLong,
437 SystemFdQuotaExceeded,437 SystemFdQuotaExceeded,
438 NoDevice,438 NoDevice,
439 PathNotFound,439 FileNotFound,
440 SystemResources,440 SystemResources,
441 NoSpaceLeft,441 NoSpaceLeft,
442 NotDir,442 NotDir,
...@@ -450,11 +450,8 @@ pub const PosixOpenError = error{...@@ -450,11 +450,8 @@ pub const PosixOpenError = error{
450/// Calls POSIX open, keeps trying if it gets interrupted, and translates450/// Calls POSIX open, keeps trying if it gets interrupted, and translates
451/// the return value into zig errors.451/// the return value into zig errors.
452pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {452pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
453 var path_with_null: [posix.PATH_MAX]u8 = undefined;453 const file_path_c = try toPosixPath(file_path);
454 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;454 return posixOpenC(&file_path_c, flags, perm);
455 mem.copy(u8, path_with_null[0..], file_path);
456 path_with_null[file_path.len] = 0;
457 return posixOpenC(&path_with_null, flags, perm);
458}455}
459456
460// TODO https://github.com/ziglang/zig/issues/265457// TODO https://github.com/ziglang/zig/issues/265
...@@ -476,7 +473,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {...@@ -476,7 +473,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {
476 posix.ENAMETOOLONG => return PosixOpenError.NameTooLong,473 posix.ENAMETOOLONG => return PosixOpenError.NameTooLong,
477 posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded,474 posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded,
478 posix.ENODEV => return PosixOpenError.NoDevice,475 posix.ENODEV => return PosixOpenError.NoDevice,
479 posix.ENOENT => return PosixOpenError.PathNotFound,476 posix.ENOENT => return PosixOpenError.FileNotFound,
480 posix.ENOMEM => return PosixOpenError.SystemResources,477 posix.ENOMEM => return PosixOpenError.SystemResources,
481 posix.ENOSPC => return PosixOpenError.NoSpaceLeft,478 posix.ENOSPC => return PosixOpenError.NoSpaceLeft,
482 posix.ENOTDIR => return PosixOpenError.NotDir,479 posix.ENOTDIR => return PosixOpenError.NotDir,
...@@ -489,6 +486,16 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {...@@ -489,6 +486,16 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {
489 }486 }
490}487}
491488
489/// Used to convert a slice to a null terminated slice on the stack.
490/// TODO well defined copy elision
491pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 {
492 var path_with_null: [posix.PATH_MAX]u8 = undefined;
493 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
494 mem.copy(u8, path_with_null[0..], file_path);
495 path_with_null[file_path.len] = 0;
496 return path_with_null;
497}
498
492pub fn posixDup2(old_fd: i32, new_fd: i32) !void {499pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
493 while (true) {500 while (true) {
494 const err = posix.getErrno(posix.dup2(old_fd, new_fd));501 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
...@@ -742,7 +749,6 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {...@@ -742,7 +749,6 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
742pub const GetCwdError = error{Unexpected};749pub const GetCwdError = error{Unexpected};
743750
744/// The result is a slice of out_buffer.751/// The result is a slice of out_buffer.
745/// TODO with well defined copy elision we could make the API of this function better.
746pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {752pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {
747 switch (builtin.os) {753 switch (builtin.os) {
748 Os.windows => {754 Os.windows => {
...@@ -960,11 +966,8 @@ pub fn deleteFilePosixC(file_path: [*]const u8) !void {...@@ -960,11 +966,8 @@ pub fn deleteFilePosixC(file_path: [*]const u8) !void {
960}966}
961967
962pub fn deleteFilePosix(file_path: []const u8) !void {968pub fn deleteFilePosix(file_path: []const u8) !void {
963 var path_with_null: [posix.PATH_MAX]u8 = undefined;969 const file_path_c = try toPosixPath(file_path);
964 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;970 return deleteFilePosixC(&file_path_c);
965 mem.copy(u8, path_with_null[0..], file_path);
966 path_with_null[file_path.len] = 0;
967 return deleteFilePosixC(&path_with_null);
968}971}
969972
970/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is973/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
...@@ -1120,17 +1123,9 @@ pub fn rename(old_path: []const u8, new_path: []const u8) !void {...@@ -1120,17 +1123,9 @@ pub fn rename(old_path: []const u8, new_path: []const u8) !void {
1120 }1123 }
1121 }1124 }
1122 } else {1125 } else {
1123 var old_path_with_null: [posix.PATH_MAX]u8 = undefined;1126 const old_path_c = try toPosixPath(old_path);
1124 if (old_path.len >= posix.PATH_MAX) return error.NameTooLong;1127 const new_path_c = try toPosixPath(new_path);
1125 mem.copy(u8, old_path_with_null[0..], old_path);1128 return renameC(&old_path_c, &new_path_c);
1126 old_path_with_null[old_path.len] = 0;
1127
1128 var new_path_with_null: [posix.PATH_MAX]u8 = undefined;
1129 if (new_path.len >= posix.PATH_MAX) return error.NameTooLong;
1130 mem.copy(u8, new_path_with_null[0..], new_path);
1131 new_path_with_null[new_path.len] = 0;
1132
1133 return renameC(&old_path_with_null, &new_path_with_null);
1134 }1129 }
1135}1130}
11361131
...@@ -1156,7 +1151,7 @@ pub fn makeDirWindows(dir_path: []const u8) !void {...@@ -1156,7 +1151,7 @@ pub fn makeDirWindows(dir_path: []const u8) !void {
1156}1151}
11571152
1158pub fn makeDirPosixC(dir_path: [*]const u8) !void {1153pub fn makeDirPosixC(dir_path: [*]const u8) !void {
1159 const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));1154 const err = posix.getErrno(posix.mkdir(dir_path, 0o755));
1160 switch (err) {1155 switch (err) {
1161 0 => return,1156 0 => return,
1162 posix.EACCES => return error.AccessDenied,1157 posix.EACCES => return error.AccessDenied,
...@@ -1177,11 +1172,8 @@ pub fn makeDirPosixC(dir_path: [*]const u8) !void {...@@ -1177,11 +1172,8 @@ pub fn makeDirPosixC(dir_path: [*]const u8) !void {
1177}1172}
11781173
1179pub fn makeDirPosix(dir_path: []const u8) !void {1174pub fn makeDirPosix(dir_path: []const u8) !void {
1180 var path_with_null: [posix.PATH_MAX]u8 = undefined;1175 const dir_path_c = try toPosixPath(dir_path);
1181 if (dir_path.len >= posix.PATH_MAX) return error.NameTooLong;1176 return makeDirPosixC(&dir_path_c);
1182 mem.copy(u8, path_with_null[0..], dir_path);
1183 path_with_null[dir_path.len] = 0;
1184 return makeDirPosixC(&path_with_null);
1185}1177}
11861178
1187/// Calls makeDir recursively to make an entire path. Returns success if the path1179/// Calls makeDir recursively to make an entire path. Returns success if the path
...@@ -1290,7 +1282,6 @@ const DeleteTreeError = error{...@@ -1290,7 +1282,6 @@ const DeleteTreeError = error{
1290 NameTooLong,1282 NameTooLong,
1291 SystemFdQuotaExceeded,1283 SystemFdQuotaExceeded,
1292 NoDevice,1284 NoDevice,
1293 PathNotFound,
1294 SystemResources,1285 SystemResources,
1295 NoSpaceLeft,1286 NoSpaceLeft,
1296 PathAlreadyExists,1287 PathAlreadyExists,
...@@ -1354,7 +1345,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1354,7 +1345,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1354 error.NameTooLong,1345 error.NameTooLong,
1355 error.SystemFdQuotaExceeded,1346 error.SystemFdQuotaExceeded,
1356 error.NoDevice,1347 error.NoDevice,
1357 error.PathNotFound,1348 error.FileNotFound,
1358 error.SystemResources,1349 error.SystemResources,
1359 error.NoSpaceLeft,1350 error.NoSpaceLeft,
1360 error.PathAlreadyExists,1351 error.PathAlreadyExists,
...@@ -1424,7 +1415,7 @@ pub const Dir = struct {...@@ -1424,7 +1415,7 @@ pub const Dir = struct {
1424 };1415 };
14251416
1426 pub const OpenError = error{1417 pub const OpenError = error{
1427 PathNotFound,1418 FileNotFound,
1428 NotDir,1419 NotDir,
1429 AccessDenied,1420 AccessDenied,
1430 FileTooBig,1421 FileTooBig,
...@@ -1443,6 +1434,7 @@ pub const Dir = struct {...@@ -1443,6 +1434,7 @@ pub const Dir = struct {
1443 Unexpected,1434 Unexpected,
1444 };1435 };
14451436
1437 /// TODO remove the allocator requirement from this API
1446 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {1438 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
1447 return Dir{1439 return Dir{
1448 .allocator = allocator,1440 .allocator = allocator,
...@@ -1458,7 +1450,6 @@ pub const Dir = struct {...@@ -1458,7 +1450,6 @@ pub const Dir = struct {
1458 },1450 },
1459 Os.macosx, Os.ios => Handle{1451 Os.macosx, Os.ios => Handle{
1460 .fd = try posixOpen(1452 .fd = try posixOpen(
1461 allocator,
1462 dir_path,1453 dir_path,
1463 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,1454 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
1464 0,1455 0,
...@@ -1470,7 +1461,6 @@ pub const Dir = struct {...@@ -1470,7 +1461,6 @@ pub const Dir = struct {
1470 },1461 },
1471 Os.linux => Handle{1462 Os.linux => Handle{
1472 .fd = try posixOpen(1463 .fd = try posixOpen(
1473 allocator,
1474 dir_path,1464 dir_path,
1475 posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,1465 posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
1476 0,1466 0,
...@@ -1668,12 +1658,12 @@ pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void {...@@ -1668,12 +1658,12 @@ pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void {
16681658
1669/// Read value of a symbolic link.1659/// Read value of a symbolic link.
1670/// The return value is a slice of out_buffer.1660/// The return value is a slice of out_buffer.
1671pub fn readLinkC(pathname: [*]const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {1661pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 {
1672 const rc = posix.readlink(pathname, out_buffer, out_buffer.len);1662 const rc = posix.readlink(pathname, out_buffer, out_buffer.len);
1673 const err = posix.getErrno(rc);1663 const err = posix.getErrno(rc);
1674 switch (err) {1664 switch (err) {
1675 0 => return out_buffer[0..rc],1665 0 => return out_buffer[0..rc],
1676 posix.EACCES => error.AccessDenied,1666 posix.EACCES => return error.AccessDenied,
1677 posix.EFAULT => unreachable,1667 posix.EFAULT => unreachable,
1678 posix.EINVAL => unreachable,1668 posix.EINVAL => unreachable,
1679 posix.EIO => return error.FileSystem,1669 posix.EIO => return error.FileSystem,
...@@ -1688,12 +1678,9 @@ pub fn readLinkC(pathname: [*]const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {...@@ -1688,12 +1678,9 @@ pub fn readLinkC(pathname: [*]const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {
16881678
1689/// Read value of a symbolic link.1679/// Read value of a symbolic link.
1690/// The return value is a slice of out_buffer.1680/// The return value is a slice of out_buffer.
1691pub fn readLink(file_path: []const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {1681pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 {
1692 var path_with_null: [posix.PATH_MAX]u8 = undefined;1682 const file_path_c = try toPosixPath(file_path);
1693 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;1683 return readLinkC(out_buffer, &file_path_c);
1694 mem.copy(u8, path_with_null[0..], file_path);
1695 path_with_null[file_path.len] = 0;
1696 return readLinkC(&path_with_null, out_buffer);
1697}1684}
16981685
1699pub fn posix_setuid(uid: u32) !void {1686pub fn posix_setuid(uid: u32) !void {
...@@ -2080,17 +2067,12 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {...@@ -2080,17 +2067,12 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
20802067
2081pub fn openSelfExe() !os.File {2068pub fn openSelfExe() !os.File {
2082 switch (builtin.os) {2069 switch (builtin.os) {
2083 Os.linux => {2070 Os.linux => return os.File.openReadC(c"/proc/self/exe"),
2084 const proc_file_path = "/proc/self/exe";
2085 var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined;
2086 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
2087 return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
2088 },
2089 Os.macosx, Os.ios => {2071 Os.macosx, Os.ios => {
2090 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;2072 var buf: [MAX_PATH_BYTES]u8 = undefined;
2091 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);2073 const self_exe_path = try selfExePath(&buf);
2092 const self_exe_path = try selfExePath(&fixed_allocator.allocator);2074 buf[self_exe_path.len] = 0;
2093 return os.File.openRead(&fixed_allocator.allocator, self_exe_path);2075 return os.File.openReadC(self_exe_path.ptr);
2094 },2076 },
2095 else => @compileError("Unsupported OS"),2077 else => @compileError("Unsupported OS"),
2096 }2078 }
...@@ -2099,7 +2081,7 @@ pub fn openSelfExe() !os.File {...@@ -2099,7 +2081,7 @@ pub fn openSelfExe() !os.File {
2099test "openSelfExe" {2081test "openSelfExe" {
2100 switch (builtin.os) {2082 switch (builtin.os) {
2101 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),2083 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
2102 else => return, // Unsupported OS.2084 else => return error.SkipZigTest, // Unsupported OS
2103 }2085 }
2104}2086}
21052087
...@@ -2108,69 +2090,67 @@ test "openSelfExe" {...@@ -2108,69 +2090,67 @@ test "openSelfExe" {
2108/// If you only want an open file handle, use openSelfExe.2090/// If you only want an open file handle, use openSelfExe.
2109/// This function may return an error if the current executable2091/// This function may return an error if the current executable
2110/// was deleted after spawning.2092/// was deleted after spawning.
2111/// Caller owns returned memory.2093/// Returned value is a slice of out_buffer.
2112pub fn selfExePath(allocator: *mem.Allocator) ![]u8 {2094///
2095/// On Linux, depends on procfs being mounted. If the currently executing binary has
2096/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
2097pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
2113 switch (builtin.os) {2098 switch (builtin.os) {
2114 Os.linux => {2099 Os.linux => return readLink(out_buffer, "/proc/self/exe"),
2115 // If the currently executing binary has been deleted,
2116 // the file path looks something like `/a/b/c/exe (deleted)`
2117 return readLink(allocator, "/proc/self/exe");
2118 },
2119 Os.windows => {2100 Os.windows => {
2120 var out_path = try Buffer.initSize(allocator, 0xff);2101 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
2121 errdefer out_path.deinit();2102 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
2122 while (true) {2103 const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len);
2123 const dword_len = try math.cast(windows.DWORD, out_path.len());2104 assert(rc <= utf16le_buf.len);
2124 const copied_amt = windows.GetModuleFileNameA(null, out_path.ptr(), dword_len);2105 if (rc == 0) {
2125 if (copied_amt <= 0) {2106 const err = windows.GetLastError();
2126 const err = windows.GetLastError();2107 switch (err) {
2127 return switch (err) {2108 else => return unexpectedErrorWindows(err),
2128 else => unexpectedErrorWindows(err),
2129 };
2130 }
2131 if (copied_amt < out_path.len()) {
2132 out_path.shrink(copied_amt);
2133 return out_path.toOwnedSlice();
2134 }2109 }
2135 const new_len = (out_path.len() << 1) | 0b1;
2136 try out_path.resize(new_len);
2137 }2110 }
2111 const utf16le_slice = utf16le_buf[0..rc];
2112 // Trust that Windows gives us valid UTF-16LE.
2113 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
2114 return out_buffer[0..end_index];
2138 },2115 },
2139 Os.macosx, Os.ios => {2116 Os.macosx, Os.ios => {
2140 var u32_len: u32 = 0;2117 var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast
2141 const ret1 = c._NSGetExecutablePath(undefined, &u32_len);2118 const rc = c._NSGetExecutablePath(out_buffer, &u32_len);
2142 assert(ret1 != 0);2119 if (rc != 0) return error.NameTooLong;
2143 const bytes = try allocator.alloc(u8, u32_len);2120 return out_buffer[0..u32_len];
2144 errdefer allocator.free(bytes);
2145 const ret2 = c._NSGetExecutablePath(bytes.ptr, &u32_len);
2146 assert(ret2 == 0);
2147 return bytes;
2148 },2121 },
2149 else => @compileError("Unsupported OS"),2122 else => @compileError("Unsupported OS"),
2150 }2123 }
2151}2124}
21522125
2153/// Get the directory path that contains the current executable.2126/// `selfExeDirPath` except allocates the result on the heap.
2154/// Caller owns returned memory.2127/// Caller owns returned memory.
2155pub fn selfExeDirPath(allocator: *mem.Allocator) ![]u8 {2128pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
2129 var buf: [MAX_PATH_BYTES]u8 = undefined;
2130 return mem.dupe(allocator, u8, try selfExeDirPath(&buf));
2131}
2132
2133/// Get the directory path that contains the current executable.
2134/// Returned value is a slice of out_buffer.
2135pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 {
2156 switch (builtin.os) {2136 switch (builtin.os) {
2157 Os.linux => {2137 Os.linux => {
2158 // If the currently executing binary has been deleted,2138 // If the currently executing binary has been deleted,
2159 // the file path looks something like `/a/b/c/exe (deleted)`2139 // the file path looks something like `/a/b/c/exe (deleted)`
2160 // This path cannot be opened, but it's valid for determining the directory2140 // This path cannot be opened, but it's valid for determining the directory
2161 // the executable was in when it was run.2141 // the executable was in when it was run.
2162 const full_exe_path = try readLink(allocator, "/proc/self/exe");2142 const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe");
2163 errdefer allocator.free(full_exe_path);2143 // Assume that /proc/self/exe has an absolute path, and therefore dirname
2164 const dir = path.dirname(full_exe_path) orelse ".";2144 // will not return null.
2165 return allocator.shrink(u8, full_exe_path, dir.len);2145 return path.dirname(full_exe_path).?;
2166 },2146 },
2167 Os.windows, Os.macosx, Os.ios => {2147 Os.windows, Os.macosx, Os.ios => {
2168 const self_exe_path = try selfExePath(allocator);2148 const self_exe_path = try selfExePath(out_buffer);
2169 errdefer allocator.free(self_exe_path);2149 // Assume that the OS APIs return absolute paths, and therefore dirname
2170 const dirname = os.path.dirname(self_exe_path) orelse ".";2150 // will not return null.
2171 return allocator.shrink(u8, self_exe_path, dirname.len);2151 return path.dirname(self_exe_path).?;
2172 },2152 },
2173 else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)),2153 else => @compileError("Unsupported OS"),
2174 }2154 }
2175}2155}
21762156
...@@ -2991,7 +2971,9 @@ pub fn posixFStat(fd: i32) !posix.Stat {...@@ -2991,7 +2971,9 @@ pub fn posixFStat(fd: i32) !posix.Stat {
2991 const err = posix.getErrno(posix.fstat(fd, &stat));2971 const err = posix.getErrno(posix.fstat(fd, &stat));
2992 if (err > 0) {2972 if (err > 0) {
2993 return switch (err) {2973 return switch (err) {
2994 posix.EBADF => error.BadFd,2974 // We do not make this an error code because if you get EBADF it's always a bug,
2975 // since the fd could have been reused.
2976 posix.EBADF => unreachable,
2995 posix.ENOMEM => error.SystemResources,2977 posix.ENOMEM => error.SystemResources,
2996 else => os.unexpectedErrorPosix(err),2978 else => os.unexpectedErrorPosix(err),
2997 };2979 };
std/os/path.zig+127-91
...@@ -11,6 +11,7 @@ const math = std.math;...@@ -11,6 +11,7 @@ const math = std.math;
11const posix = os.posix;11const posix = os.posix;
12const windows = os.windows;12const windows = os.windows;
13const cstr = std.cstr;13const cstr = std.cstr;
14const windows_util = @import("windows/util.zig");
1415
15pub const sep_windows = '\\';16pub const sep_windows = '\\';
16pub const sep_posix = '/';17pub const sep_posix = '/';
...@@ -1075,113 +1076,148 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons...@@ -1075,113 +1076,148 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
1075 assert(mem.eql(u8, result, expected_output));1076 assert(mem.eql(u8, result, expected_output));
1076}1077}
10771078
1078/// Return the canonicalized absolute pathname.1079pub const RealError = error{
1079/// Expands all symbolic links and resolves references to `.`, `..`, and1080 FileNotFound,
1080/// extra `/` characters in ::pathname.1081 AccessDenied,
1081/// Caller must deallocate result.1082 NameTooLong,
1082/// TODO rename this to realAlloc and provide real with no allocator. See #13921083 NotSupported,
1083pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 {1084 NotDir,
1084 switch (builtin.os) {1085 SymLinkLoop,
1085 Os.windows => {1086 InputOutput,
1086 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);1087 FileTooBig,
1087 defer allocator.free(pathname_buf);1088 IsDir,
10881089 ProcessFdQuotaExceeded,
1089 mem.copy(u8, pathname_buf, pathname);1090 SystemFdQuotaExceeded,
1090 pathname_buf[pathname.len] = 0;1091 NoDevice,
10911092 SystemResources,
1092 const h_file = windows.CreateFileA(pathname_buf.ptr, windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null);1093 NoSpaceLeft,
1093 if (h_file == windows.INVALID_HANDLE_VALUE) {1094 FileSystem,
1094 const err = windows.GetLastError();1095 BadPathName,
1095 return switch (err) {1096
1096 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,1097 /// On Windows, file paths must be valid Unicode.
1097 windows.ERROR.ACCESS_DENIED => error.AccessDenied,1098 InvalidUtf8,
1098 windows.ERROR.FILENAME_EXCED_RANGE => error.NameTooLong,1099
1099 else => os.unexpectedErrorWindows(err),1100 /// TODO remove this possibility
1100 };1101 PathAlreadyExists,
1101 }1102
1102 defer os.close(h_file);1103 /// TODO remove this possibility
1103 var buf = try allocator.alloc(u8, 256);1104 Unexpected,
1104 errdefer allocator.free(buf);1105};
1105 while (true) {
1106 const buf_len = math.cast(windows.DWORD, buf.len) catch return error.NameTooLong;
1107 const result = windows.GetFinalPathNameByHandleA(h_file, buf.ptr, buf_len, windows.VOLUME_NAME_DOS);
1108
1109 if (result == 0) {
1110 const err = windows.GetLastError();
1111 return switch (err) {
1112 windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
1113 windows.ERROR.NOT_ENOUGH_MEMORY => error.OutOfMemory,
1114 windows.ERROR.INVALID_PARAMETER => unreachable,
1115 else => os.unexpectedErrorWindows(err),
1116 };
1117 }
11181106
1119 if (result > buf.len) {1107/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
1120 buf = try allocator.realloc(u8, buf, result);1108/// Otherwise use `real` or `realC`.
1121 continue;1109pub fn realW(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u16) RealError![]u8 {
1122 }1110 const h_file = windows.CreateFileW(
1111 pathname,
1112 windows.GENERIC_READ,
1113 windows.FILE_SHARE_READ,
1114 null,
1115 windows.OPEN_EXISTING,
1116 windows.FILE_ATTRIBUTE_NORMAL,
1117 null,
1118 );
1119 if (h_file == windows.INVALID_HANDLE_VALUE) {
1120 const err = windows.GetLastError();
1121 switch (err) {
1122 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1123 windows.ERROR.ACCESS_DENIED => return error.AccessDenied,
1124 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
1125 else => return os.unexpectedErrorWindows(err),
1126 }
1127 }
1128 defer os.close(h_file);
1129 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
1130 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
1131 const result = windows.GetFinalPathNameByHandleW(h_file, &utf16le_buf, casted_len, windows.VOLUME_NAME_DOS);
1132 assert(result <= utf16le_buf.len);
1133 if (result == 0) {
1134 const err = windows.GetLastError();
1135 switch (err) {
1136 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1137 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1138 windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
1139 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
1140 windows.ERROR.INVALID_PARAMETER => unreachable,
1141 else => return os.unexpectedErrorWindows(err),
1142 }
1143 }
1144 const utf16le_slice = utf16le_buf[0..result];
11231145
1124 // windows returns \\?\ prepended to the path1146 // windows returns \\?\ prepended to the path
1125 // we strip it because nobody wants \\?\ prepended to their path1147 // we strip it because nobody wants \\?\ prepended to their path
1126 const final_len = x: {1148 const prefix = []u16{ '\\', '\\', '?', '\\' };
1127 if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {1149 const start_index = if (mem.startsWith(u16, utf16le_slice, prefix)) prefix.len else 0;
1128 var i: usize = 4;1150
1129 while (i < result) : (i += 1) {1151 // Trust that Windows gives us valid UTF-16LE.
1130 buf[i - 4] = buf[i];1152 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice[start_index..]) catch unreachable;
1131 }1153 return out_buffer[0..end_index];
1132 break :x result - 4;1154}
1133 } else {1155
1134 break :x result;1156/// See `real`
1135 }1157/// Use this when you have a null terminated pointer path.
1136 };1158pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealError![]u8 {
11371159 switch (builtin.os) {
1138 return allocator.shrink(u8, buf, final_len);1160 Os.windows => {
1139 }1161 const pathname_w = try windows_util.cStrToPrefixedFileW(pathname);
1162 return realW(out_buffer, pathname_w);
1140 },1163 },
1141 Os.macosx, Os.ios => {1164 Os.macosx, Os.ios => {
1142 // TODO instead of calling the libc function here, port the implementation1165 // TODO instead of calling the libc function here, port the implementation to Zig
1143 // to Zig, and then remove the NameTooLong error possibility.1166 const err = posix.getErrno(posix.realpath(pathname, out_buffer));
1144 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);1167 switch (err) {
1145 defer allocator.free(pathname_buf);1168 0 => return mem.toSlice(u8, out_buffer),
11461169 posix.EINVAL => unreachable,
1147 const result_buf = try allocator.alloc(u8, posix.PATH_MAX);1170 posix.EBADF => unreachable,
1148 errdefer allocator.free(result_buf);1171 posix.EFAULT => unreachable,
11491172 posix.EACCES => return error.AccessDenied,
1150 mem.copy(u8, pathname_buf, pathname);1173 posix.ENOENT => return error.FileNotFound,
1151 pathname_buf[pathname.len] = 0;1174 posix.ENOTSUP => return error.NotSupported,
11521175 posix.ENOTDIR => return error.NotDir,
1153 const err = posix.getErrno(posix.realpath(pathname_buf.ptr, result_buf.ptr));1176 posix.ENAMETOOLONG => return error.NameTooLong,
1154 if (err > 0) {1177 posix.ELOOP => return error.SymLinkLoop,
1155 return switch (err) {1178 posix.EIO => return error.InputOutput,
1156 posix.EINVAL => unreachable,1179 else => return os.unexpectedErrorPosix(err),
1157 posix.EBADF => unreachable,
1158 posix.EFAULT => unreachable,
1159 posix.EACCES => error.AccessDenied,
1160 posix.ENOENT => error.FileNotFound,
1161 posix.ENOTSUP => error.NotSupported,
1162 posix.ENOTDIR => error.NotDir,
1163 posix.ENAMETOOLONG => error.NameTooLong,
1164 posix.ELOOP => error.SymLinkLoop,
1165 posix.EIO => error.InputOutput,
1166 else => os.unexpectedErrorPosix(err),
1167 };
1168 }1180 }
1169 return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));
1170 },1181 },
1171 Os.linux => {1182 Os.linux => {
1172 const fd = try os.posixOpen(allocator, pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);1183 const fd = try os.posixOpenC(pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
1173 defer os.close(fd);1184 defer os.close(fd);
11741185
1175 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;1186 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
1176 const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd) catch unreachable;1187 const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable;
11771188
1178 return os.readLink(allocator, proc_path);1189 return os.readLinkC(out_buffer, proc_path.ptr);
1179 },1190 },
1180 else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)),1191 else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)),
1181 }1192 }
1182}1193}
11831194
1195/// Return the canonicalized absolute pathname.
1196/// Expands all symbolic links and resolves references to `.`, `..`, and
1197/// extra `/` characters in ::pathname.
1198/// The return value is a slice of out_buffer, and not necessarily from the beginning.
1199pub fn real(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: []const u8) RealError![]u8 {
1200 switch (builtin.os) {
1201 Os.windows => {
1202 const pathname_w = try windows_util.sliceToPrefixedFileW(pathname);
1203 return realW(out_buffer, &pathname_w);
1204 },
1205 Os.macosx, Os.ios, Os.linux => {
1206 const pathname_c = try os.toPosixPath(pathname);
1207 return realC(out_buffer, &pathname_c);
1208 },
1209 else => @compileError("Unsupported OS"),
1210 }
1211}
1212
1213/// `real`, except caller must free the returned memory.
1214pub fn realAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
1215 var buf: [os.MAX_PATH_BYTES]u8 = undefined;
1216 return mem.dupe(allocator, u8, try real(&buf, pathname));
1217}
1218
1184test "os.path.real" {1219test "os.path.real" {
1185 // at least call it so it gets compiled1220 // at least call it so it gets compiled
1186 _ = real(debug.global_allocator, "some_path");1221 var buf: [os.MAX_PATH_BYTES]u8 = undefined;
1222 std.debug.assertError(real(&buf, "definitely_bogus_does_not_exist1234"), error.FileNotFound);
1187}1223}
std/os/test.zig+1-1
...@@ -17,7 +17,7 @@ test "makePath, put some files in it, deleteTree" {...@@ -17,7 +17,7 @@ test "makePath, put some files in it, deleteTree" {
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");
19 } else |err| {19 } else |err| {
20 assert(err == error.PathNotFound);20 assert(err == error.FileNotFound);
21 }21 }
22}22}
2323
std/os/windows/kernel32.zig+11-3
...@@ -4,8 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE...@@ -4,8 +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( lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;7pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
8pub extern "kernel32" stdcallcc fn CreateDirectoryW( lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;8pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
99
10pub extern "kernel32" stdcallcc fn CreateFileA(10pub extern "kernel32" stdcallcc fn CreateFileA(
11 lpFileName: [*]const u8, // TODO null terminated pointer type11 lpFileName: [*]const u8, // TODO null terminated pointer type
...@@ -89,7 +89,8 @@ pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LAR...@@ -89,7 +89,8 @@ pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LAR
89pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;89pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
90pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) 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: [*]u8, nSize: DWORD) DWORD;
93pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
9394
94pub extern "kernel32" stdcallcc fn GetLastError() DWORD;95pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
9596
...@@ -107,6 +108,13 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(...@@ -107,6 +108,13 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
107 dwFlags: DWORD,108 dwFlags: DWORD,
108) DWORD;109) DWORD;
109110
111pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
112 hFile: HANDLE,
113 lpszFilePath: [*]u16,
114 cchFilePath: DWORD,
115 dwFlags: DWORD,
116) DWORD;
117
110pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;118pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
111119
112pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;120pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
std/os/windows/util.zig+13-13
...@@ -97,12 +97,12 @@ pub const OpenError = error{...@@ -97,12 +97,12 @@ pub const OpenError = error{
97 SharingViolation,97 SharingViolation,
98 PathAlreadyExists,98 PathAlreadyExists,
9999
100 /// When all the path components are found but the file component is not.100 /// When any of the path components can not be found or the file component can not
101 /// be found. Some operating systems distinguish between path components not found and
102 /// file components not found, but they are collapsed into FileNotFound to gain
103 /// consistency across operating systems.
101 FileNotFound,104 FileNotFound,
102105
103 /// When one or more path components are not found.
104 PathNotFound,
105
106 AccessDenied,106 AccessDenied,
107 PipeBusy,107 PipeBusy,
108 NameTooLong,108 NameTooLong,
...@@ -136,7 +136,7 @@ pub fn windowsOpen(...@@ -136,7 +136,7 @@ pub fn windowsOpen(
136 windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists,136 windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists,
137 windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists,137 windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists,
138 windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound,138 windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound,
139 windows.ERROR.PATH_NOT_FOUND => return OpenError.PathNotFound,139 windows.ERROR.PATH_NOT_FOUND => return OpenError.FileNotFound,
140 windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied,140 windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied,
141 windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy,141 windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy,
142 else => return os.unexpectedErrorWindows(err),142 else => return os.unexpectedErrorWindows(err),
...@@ -216,9 +216,8 @@ pub fn windowsFindFirstFile(...@@ -216,9 +216,8 @@ pub fn windowsFindFirstFile(
216 if (handle == windows.INVALID_HANDLE_VALUE) {216 if (handle == windows.INVALID_HANDLE_VALUE) {
217 const err = windows.GetLastError();217 const err = windows.GetLastError();
218 switch (err) {218 switch (err) {
219 windows.ERROR.FILE_NOT_FOUND,219 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
220 windows.ERROR.PATH_NOT_FOUND,220 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
221 => return error.PathNotFound,
222 else => return os.unexpectedErrorWindows(err),221 else => return os.unexpectedErrorWindows(err),
223 }222 }
224 }223 }
...@@ -284,13 +283,13 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t...@@ -284,13 +283,13 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t
284 return WindowsWaitResult.Normal;283 return WindowsWaitResult.Normal;
285}284}
286285
287pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE+1]u16 {286pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
288 return sliceToPrefixedFileW(mem.toSliceConst(u8, s));287 return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
289}288}
290289
291pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE+1]u16 {290pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
292 // TODO well defined copy elision291 // TODO well defined copy elision
293 var result: [PATH_MAX_WIDE+1]u16 = undefined;292 var result: [PATH_MAX_WIDE + 1]u16 = undefined;
294293
295 // > File I/O functions in the Windows API convert "/" to "\" as part of294 // > 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 "\\?\"295 // > converting the name to an NT-style name, except when using the "\\?\"
...@@ -298,12 +297,13 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE+1]u16 {...@@ -298,12 +297,13 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE+1]u16 {
298 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation297 // 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, we298 // Because we want the larger maximum path length for absolute paths, we
300 // disallow forward slashes in zig std lib file functions on Windows.299 // disallow forward slashes in zig std lib file functions on Windows.
301 for (s) |byte| switch (byte) {300 for (s) |byte|
301 switch (byte) {
302 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,302 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
303 else => {},303 else => {},
304 };304 };
305 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {305 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
306 const prefix = []u16{'\\', '\\', '?', '\\'};306 const prefix = []u16{ '\\', '\\', '?', '\\' };
307 mem.copy(u16, result[0..], prefix);307 mem.copy(u16, result[0..], prefix);
308 break :blk prefix.len;308 break :blk prefix.len;
309 };309 };
std/unicode.zig+1-1
...@@ -495,7 +495,7 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8...@@ -495,7 +495,7 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8
495}495}
496496
497/// Asserts that the output buffer is big enough.497/// Asserts that the output buffer is big enough.
498/// Returns end index.498/// Returns end byte index into utf8.
499pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !usize {499pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !usize {
500 var end_index: usize = 0;500 var end_index: usize = 0;
501 var it = Utf16LeIterator.init(utf16le);501 var it = Utf16LeIterator.init(utf16le);
test/cases/merge_error_sets.zig+2-2
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const A = error{1const A = error{
2 PathNotFound,2 FileNotFound,
3 NotDir,3 NotDir,
4};4};
5const B = error{OutOfMemory};5const B = error{OutOfMemory};
...@@ -15,7 +15,7 @@ test "merge error sets" {...@@ -15,7 +15,7 @@ test "merge error sets" {
15 @panic("unexpected");15 @panic("unexpected");
16 } else |err| switch (err) {16 } else |err| switch (err) {
17 error.OutOfMemory => @panic("unexpected"),17 error.OutOfMemory => @panic("unexpected"),
18 error.PathNotFound => @panic("unexpected"),18 error.FileNotFound => @panic("unexpected"),
19 error.NotDir => {},19 error.NotDir => {},
20 }20 }
21}21}