diff --git a/doc/docgen.zig b/doc/docgen.zig index 4d66b43efd..3145c4483e 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -34,10 +34,10 @@ pub fn main() !void { const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg")); defer allocator.free(out_file_name); - var in_file = try os.File.openRead(allocator, in_file_name); + var in_file = try os.File.openRead(in_file_name); defer in_file.close(); - var out_file = try os.File.openWrite(allocator, out_file_name); + var out_file = try os.File.openWrite(out_file_name); defer out_file.close(); var file_in_stream = io.FileInStream.init(&in_file); @@ -738,7 +738,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try out.print("
{}
", escaped_source); const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name); const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext); - try io.writeFile(allocator, tmp_source_file_name, trimmed_raw_source); + try io.writeFile(tmp_source_file_name, trimmed_raw_source); switch (code.id) { Code.Id.Exe => |expected_outcome| { diff --git a/example/cat/main.zig b/example/cat/main.zig index 27690d2695..120ba1da39 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -20,7 +20,7 @@ pub fn main() !void { } else if (arg[0] == '-') { return usage(exe); } else { - var file = os.File.openRead(allocator, arg) catch |err| { + var file = os.File.openRead(arg) catch |err| { warn("Unable to open file: {}\n", @errorName(err)); return err; }; diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 70730e6a82..a8c3e13e33 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -257,8 +257,6 @@ pub const Compilation = struct { pub const BuildError = error{ OutOfMemory, EndOfStream, - BadFd, - Io, IsDir, Unexpected, SystemResources, @@ -273,7 +271,6 @@ pub const Compilation = struct { NameTooLong, SystemFdQuotaExceeded, NoDevice, - PathNotFound, NoSpaceLeft, NotDir, FileSystem, @@ -962,7 +959,7 @@ pub const Compilation = struct { if (self.root_src_path) |root_src_path| { const root_scope = blk: { // TODO async/await os.path.real - const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { + const root_src_real_path = os.path.realAlloc(self.gpa(), root_src_path) catch |err| { try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err)); return; }; diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index 687ef14c6b..d41f82f755 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -22,7 +22,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![ /// Caller must free result pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 { - const self_exe_path = try os.selfExeDirPath(allocator); + const self_exe_path = try os.selfExeDirPathAlloc(allocator); defer allocator.free(self_exe_path); var cur_path: []const u8 = self_exe_path; diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 74167a1080..5e292ff8b2 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -453,7 +453,7 @@ fn fileExists(path: []const u8) !bool { if (std.os.File.access(path)) |_| { return true; } else |err| switch (err) { - error.FileNotFound, error.PathNotFound, error.PermissionDenied => return false, + error.FileNotFound, error.PermissionDenied => return false, else => return error.FileSystem, } } diff --git a/std/build.zig b/std/build.zig index 5300a20e17..08bb5635d9 100644 --- a/std/build.zig +++ b/std/build.zig @@ -1491,11 +1491,14 @@ pub const LibExeObjStep = struct { } if (!is_darwin) { - const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable); + const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc( + builder.allocator, + builder.pathFromRoot(builder.cache_root), + )); defer builder.allocator.free(rpath_arg); - cc_args.append(rpath_arg) catch unreachable; + try cc_args.append(rpath_arg); - cc_args.append("-rdynamic") catch unreachable; + try cc_args.append("-rdynamic"); } for (self.full_path_libs.toSliceConst()) |full_path_lib| { @@ -1566,11 +1569,14 @@ pub const LibExeObjStep = struct { cc_args.append("-o") catch unreachable; cc_args.append(output_path) catch unreachable; - const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable); + const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc( + builder.allocator, + builder.pathFromRoot(builder.cache_root), + )); defer builder.allocator.free(rpath_arg); - cc_args.append(rpath_arg) catch unreachable; + try cc_args.append(rpath_arg); - cc_args.append("-rdynamic") catch unreachable; + try cc_args.append("-rdynamic"); { var it = self.link_libs.iterator(); diff --git a/std/debug/index.zig b/std/debug/index.zig index 98a665914e..4dbd8bf08d 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -255,7 +255,7 @@ pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address address, compile_unit_name, ); - if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { + if (printLineFromFile(out_stream, line_info)) { if (line_info.column == 0) { try out_stream.write("\n"); } else { diff --git a/std/event/fs.zig b/std/event/fs.zig index 6daa45a8ec..ac99f13c9b 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -78,8 +78,7 @@ pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, o builtin.Os.macosx, builtin.Os.linux, => return await (async pwritevPosix(loop, fd, data, offset) catch unreachable), - builtin.Os.windows, - => return await (async pwritevWindows(loop, fd, data, offset) catch unreachable), + builtin.Os.windows => return await (async pwritevWindows(loop, fd, data, offset) catch unreachable), else => @compileError("Unsupported OS"), } } @@ -147,7 +146,6 @@ pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, off } } - /// data - just the inner references - must live until pwritev promise completes. pub async fn pwritevPosix(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void { // 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: builtin.Os.macosx, builtin.Os.linux, => return await (async preadvPosix(loop, fd, data, offset) catch unreachable), - builtin.Os.windows, - => return await (async preadvWindows(loop, fd, data, offset) catch unreachable), + builtin.Os.windows => return await (async preadvWindows(loop, fd, data, offset) catch unreachable), else => @compileError("Unsupported OS"), } } @@ -222,7 +219,7 @@ pub async fn preadvWindows(loop: *Loop, fd: os.FileHandle, data: []const []u8, o var inner_off: usize = 0; while (true) { const v = data_copy[iov_i]; - const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len-inner_off], offset + off) catch unreachable); + const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off) catch unreachable); off += amt_read; inner_off += amt_read; if (inner_off == v.len) { @@ -340,8 +337,7 @@ pub async fn openPosix( resume @handle(); } - const path_with_null = try std.cstr.addNullByte(loop.allocator, path); - defer loop.allocator.free(path_with_null); + const path_c = try std.os.toPosixPath(path); var req_node = RequestNode{ .prev = null, @@ -349,7 +345,7 @@ pub async fn openPosix( .data = Request{ .msg = Request.Msg{ .Open = Request.Msg.Open{ - .path = path_with_null[0..path.len], + .path = path_c[0..path.len], .flags = flags, .mode = mode, .result = undefined, @@ -408,8 +404,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable); }, - builtin.Os.windows, - => return os.windowsOpen( + builtin.Os.windows => return os.windowsOpen( path, windows.GENERIC_WRITE, windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, @@ -434,7 +429,7 @@ pub async fn openReadWrite( builtin.Os.windows => return os.windowsOpen( path, - windows.GENERIC_WRITE|windows.GENERIC_READ, + windows.GENERIC_WRITE | windows.GENERIC_READ, windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, windows.OPEN_ALWAYS, windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED, @@ -510,8 +505,7 @@ pub const CloseOperation = struct { self.loop.allocator.destroy(self); } }, - builtin.Os.windows, - => { + builtin.Os.windows => { if (self.os_data.handle) |handle| { os.close(handle); } @@ -529,8 +523,7 @@ pub const CloseOperation = struct { self.os_data.close_req_node.data.msg.Close.fd = handle; self.os_data.have_fd = true; }, - builtin.Os.windows, - => { + builtin.Os.windows => { self.os_data.handle = handle; }, else => @compileError("Unsupported OS"), @@ -545,8 +538,7 @@ pub const CloseOperation = struct { => { self.os_data.have_fd = false; }, - builtin.Os.windows, - => { + builtin.Os.windows => { self.os_data.handle = null; }, else => @compileError("Unsupported OS"), @@ -561,8 +553,7 @@ pub const CloseOperation = struct { assert(self.os_data.have_fd); return self.os_data.close_req_node.data.msg.Close.fd; }, - builtin.Os.windows, - => { + builtin.Os.windows => { return self.os_data.handle.?; }, else => @compileError("Unsupported OS"), @@ -582,8 +573,7 @@ pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, builtin.Os.linux, builtin.Os.macosx, => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable), - builtin.Os.windows, - => return await (async writeFileWindows(loop, path, contents) catch unreachable), + builtin.Os.windows => return await (async writeFileWindows(loop, path, contents) catch unreachable), else => @compileError("Unsupported OS"), } } @@ -1000,7 +990,7 @@ pub fn Watch(comptime V: type) type { const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename); var basename_utf16le_null_consumed = false; defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null); - const basename_utf16le_no_null = basename_utf16le_null[0..basename_utf16le_null.len-1]; + const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1]; const dir_handle = windows.CreateFileW( dirname_utf16le.ptr, @@ -1014,9 +1004,8 @@ pub fn Watch(comptime V: type) type { if (dir_handle == windows.INVALID_HANDLE_VALUE) { const err = windows.GetLastError(); switch (err) { - windows.ERROR.FILE_NOT_FOUND, - windows.ERROR.PATH_NOT_FOUND, - => return error.PathNotFound, + windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, else => return os.unexpectedErrorWindows(err), } } @@ -1102,7 +1091,10 @@ pub fn Watch(comptime V: type) type { // TODO handle this error not in the channel but in the setup _ = os.windowsCreateIoCompletionPort( - dir_handle, self.channel.loop.os_data.io_port, completion_key, undefined, + dir_handle, + self.channel.loop.os_data.io_port, + completion_key, + undefined, ) catch |err| { await (async self.channel.put(err) catch unreachable); return; @@ -1122,10 +1114,10 @@ pub fn Watch(comptime V: type) type { &event_buf, @intCast(windows.DWORD, event_buf.len), windows.FALSE, // watch subtree - windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME | - windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE | - windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS | - windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY, + windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME | + windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE | + windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS | + windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY, null, // number of bytes transferred (unused for async) &overlapped, null, // completion routine - unused because we use IOCP @@ -1152,7 +1144,7 @@ pub fn Watch(comptime V: type) type { else => null, }; if (emit) |id| { - const basename_utf16le = ([*]u16)(&ev.FileName)[0..ev.FileNameLength/2]; + const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2]; const user_value = blk: { const held = await (async dir.table_lock.acquire() catch unreachable); defer held.release(); diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 1ee5839c19..b79a8de16f 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -349,14 +349,7 @@ pub const ChildProcess = struct { }; const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); - const dev_null_fd = if (any_ignore) blk: { - const dev_null_path = "/dev/null"; - var fixed_buffer_mem: [dev_null_path.len + 1]u8 = undefined; - var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - break :blk try os.posixOpen(&fixed_allocator.allocator, "/dev/null", posix.O_RDWR, 0); - } else blk: { - break :blk undefined; - }; + const dev_null_fd = if (any_ignore) try os.posixOpenC(c"/dev/null", posix.O_RDWR, 0) else undefined; defer { if (any_ignore) os.close(dev_null_fd); } diff --git a/std/os/file.zig b/std/os/file.zig index da3e791217..1f5ce7cf9d 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -28,13 +28,26 @@ pub const File = struct { pub const OpenError = os.WindowsOpenError || os.PosixOpenError; + /// `openRead` except with a null terminated path + pub fn openReadC(path: [*]const u8) OpenError!File { + if (is_posix) { + const flags = posix.O_LARGEFILE | posix.O_RDONLY; + const fd = try os.posixOpenC(path, flags, 0); + return openHandle(fd); + } + if (is_windows) { + return openRead(mem.toSliceConst(u8, path)); + } + @compileError("Unsupported OS"); + } + /// Call close to clean up. pub fn openRead(path: []const u8) OpenError!File { if (is_posix) { - const flags = posix.O_LARGEFILE | posix.O_RDONLY; - const fd = try os.posixOpen(path, flags, 0); - return openHandle(fd); - } else if (is_windows) { + const path_c = try os.toPosixPath(path); + return openReadC(&path_c); + } + if (is_windows) { const handle = try os.windowsOpen( path, windows.GENERIC_READ, @@ -43,9 +56,8 @@ pub const File = struct { windows.FILE_ATTRIBUTE_NORMAL, ); return openHandle(handle); - } else { - @compileError("TODO implement openRead for this OS"); } + @compileError("Unsupported OS"); } /// Calls `openWriteMode` with os.File.default_mode for the mode. @@ -103,13 +115,11 @@ pub const File = struct { pub const AccessError = error{ PermissionDenied, - PathNotFound, FileNotFound, NameTooLong, - BadMode, - BadPathName, - Io, + InputOutput, SystemResources, + BadPathName, /// On Windows, file paths must be valid Unicode. InvalidUtf8, @@ -127,7 +137,7 @@ pub const File = struct { const err = windows.GetLastError(); switch (err) { windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return error.PathNotFound, + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, else => return os.unexpectedErrorWindows(err), } @@ -149,13 +159,13 @@ pub const File = struct { posix.EROFS => return error.PermissionDenied, posix.ELOOP => return error.PermissionDenied, posix.ETXTBSY => return error.PermissionDenied, - posix.ENOTDIR => return error.NotFound, - posix.ENOENT => return error.NotFound, + posix.ENOTDIR => return error.FileNotFound, + posix.ENOENT => return error.FileNotFound, posix.ENAMETOOLONG => return error.NameTooLong, posix.EINVAL => unreachable, - posix.EFAULT => return error.BadPathName, - posix.EIO => return error.Io, + posix.EFAULT => unreachable, + posix.EIO => return error.InputOutput, posix.ENOMEM => return error.SystemResources, else => return os.unexpectedErrorPosix(err), } @@ -197,7 +207,9 @@ pub const File = struct { const err = posix.getErrno(result); if (err > 0) { return switch (err) { - posix.EBADF => error.BadFd, + // We do not make this an error code because if you get EBADF it's always a bug, + // since the fd could have been reused. + posix.EBADF => unreachable, posix.EINVAL => error.Unseekable, posix.EOVERFLOW => error.Unseekable, posix.ESPIPE => error.Unseekable, @@ -210,7 +222,7 @@ pub const File = struct { if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) { const err = windows.GetLastError(); return switch (err) { - windows.ERROR.INVALID_PARAMETER => error.BadFd, + windows.ERROR.INVALID_PARAMETER => unreachable, else => os.unexpectedErrorWindows(err), }; } @@ -227,7 +239,9 @@ pub const File = struct { const err = posix.getErrno(result); if (err > 0) { return switch (err) { - posix.EBADF => error.BadFd, + // We do not make this an error code because if you get EBADF it's always a bug, + // since the fd could have been reused. + posix.EBADF => unreachable, posix.EINVAL => error.Unseekable, posix.EOVERFLOW => error.Unseekable, posix.ESPIPE => error.Unseekable, @@ -241,7 +255,7 @@ pub const File = struct { if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) { const err = windows.GetLastError(); return switch (err) { - windows.ERROR.INVALID_PARAMETER => error.BadFd, + windows.ERROR.INVALID_PARAMETER => unreachable, else => os.unexpectedErrorWindows(err), }; } @@ -257,7 +271,9 @@ pub const File = struct { const err = posix.getErrno(result); if (err > 0) { return switch (err) { - posix.EBADF => error.BadFd, + // We do not make this an error code because if you get EBADF it's always a bug, + // since the fd could have been reused. + posix.EBADF => unreachable, posix.EINVAL => error.Unseekable, posix.EOVERFLOW => error.Unseekable, posix.ESPIPE => error.Unseekable, @@ -272,7 +288,7 @@ pub const File = struct { if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) { const err = windows.GetLastError(); return switch (err) { - windows.ERROR.INVALID_PARAMETER => error.BadFd, + windows.ERROR.INVALID_PARAMETER => unreachable, else => os.unexpectedErrorWindows(err), }; } @@ -305,7 +321,6 @@ pub const File = struct { } pub const ModeError = error{ - BadFd, SystemResources, Unexpected, }; @@ -316,7 +331,9 @@ pub const File = struct { const err = posix.getErrno(posix.fstat(self.handle, &stat)); if (err > 0) { return switch (err) { - posix.EBADF => error.BadFd, + // We do not make this an error code because if you get EBADF it's always a bug, + // since the fd could have been reused. + posix.EBADF => unreachable, posix.ENOMEM => error.SystemResources, else => os.unexpectedErrorPosix(err), }; diff --git a/std/os/index.zig b/std/os/index.zig index cafd5ddcb7..e7e187b00f 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -436,7 +436,7 @@ pub const PosixOpenError = error{ NameTooLong, SystemFdQuotaExceeded, NoDevice, - PathNotFound, + FileNotFound, SystemResources, NoSpaceLeft, NotDir, @@ -450,11 +450,8 @@ pub const PosixOpenError = error{ /// Calls POSIX open, keeps trying if it gets interrupted, and translates /// the return value into zig errors. pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { - var path_with_null: [posix.PATH_MAX]u8 = undefined; - if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; - mem.copy(u8, path_with_null[0..], file_path); - path_with_null[file_path.len] = 0; - return posixOpenC(&path_with_null, flags, perm); + const file_path_c = try toPosixPath(file_path); + return posixOpenC(&file_path_c, flags, perm); } // TODO https://github.com/ziglang/zig/issues/265 @@ -476,7 +473,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { posix.ENAMETOOLONG => return PosixOpenError.NameTooLong, posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded, posix.ENODEV => return PosixOpenError.NoDevice, - posix.ENOENT => return PosixOpenError.PathNotFound, + posix.ENOENT => return PosixOpenError.FileNotFound, posix.ENOMEM => return PosixOpenError.SystemResources, posix.ENOSPC => return PosixOpenError.NoSpaceLeft, posix.ENOTDIR => return PosixOpenError.NotDir, @@ -489,6 +486,16 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { } } +/// Used to convert a slice to a null terminated slice on the stack. +/// TODO well defined copy elision +pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 { + var path_with_null: [posix.PATH_MAX]u8 = undefined; + if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; + mem.copy(u8, path_with_null[0..], file_path); + path_with_null[file_path.len] = 0; + return path_with_null; +} + pub fn posixDup2(old_fd: i32, new_fd: i32) !void { while (true) { const err = posix.getErrno(posix.dup2(old_fd, new_fd)); @@ -742,7 +749,6 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { pub const GetCwdError = error{Unexpected}; /// The result is a slice of out_buffer. -/// TODO with well defined copy elision we could make the API of this function better. pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 { switch (builtin.os) { Os.windows => { @@ -960,11 +966,8 @@ pub fn deleteFilePosixC(file_path: [*]const u8) !void { } pub fn deleteFilePosix(file_path: []const u8) !void { - var path_with_null: [posix.PATH_MAX]u8 = undefined; - if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; - mem.copy(u8, path_with_null[0..], file_path); - path_with_null[file_path.len] = 0; - return deleteFilePosixC(&path_with_null); + const file_path_c = try toPosixPath(file_path); + return deleteFilePosixC(&file_path_c); } /// 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 { } } } else { - var old_path_with_null: [posix.PATH_MAX]u8 = undefined; - if (old_path.len >= posix.PATH_MAX) return error.NameTooLong; - mem.copy(u8, old_path_with_null[0..], old_path); - old_path_with_null[old_path.len] = 0; - - var new_path_with_null: [posix.PATH_MAX]u8 = undefined; - if (new_path.len >= posix.PATH_MAX) return error.NameTooLong; - mem.copy(u8, new_path_with_null[0..], new_path); - new_path_with_null[new_path.len] = 0; - - return renameC(&old_path_with_null, &new_path_with_null); + const old_path_c = try toPosixPath(old_path); + const new_path_c = try toPosixPath(new_path); + return renameC(&old_path_c, &new_path_c); } } @@ -1156,7 +1151,7 @@ pub fn makeDirWindows(dir_path: []const u8) !void { } pub fn makeDirPosixC(dir_path: [*]const u8) !void { - const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755)); + const err = posix.getErrno(posix.mkdir(dir_path, 0o755)); switch (err) { 0 => return, posix.EACCES => return error.AccessDenied, @@ -1177,11 +1172,8 @@ pub fn makeDirPosixC(dir_path: [*]const u8) !void { } pub fn makeDirPosix(dir_path: []const u8) !void { - var path_with_null: [posix.PATH_MAX]u8 = undefined; - if (dir_path.len >= posix.PATH_MAX) return error.NameTooLong; - mem.copy(u8, path_with_null[0..], dir_path); - path_with_null[dir_path.len] = 0; - return makeDirPosixC(&path_with_null); + const dir_path_c = try toPosixPath(dir_path); + return makeDirPosixC(&dir_path_c); } /// Calls makeDir recursively to make an entire path. Returns success if the path @@ -1290,7 +1282,6 @@ const DeleteTreeError = error{ NameTooLong, SystemFdQuotaExceeded, NoDevice, - PathNotFound, SystemResources, NoSpaceLeft, PathAlreadyExists, @@ -1354,7 +1345,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! error.NameTooLong, error.SystemFdQuotaExceeded, error.NoDevice, - error.PathNotFound, + error.FileNotFound, error.SystemResources, error.NoSpaceLeft, error.PathAlreadyExists, @@ -1424,7 +1415,7 @@ pub const Dir = struct { }; pub const OpenError = error{ - PathNotFound, + FileNotFound, NotDir, AccessDenied, FileTooBig, @@ -1443,6 +1434,7 @@ pub const Dir = struct { Unexpected, }; + /// TODO remove the allocator requirement from this API pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir { return Dir{ .allocator = allocator, @@ -1458,7 +1450,6 @@ pub const Dir = struct { }, Os.macosx, Os.ios => Handle{ .fd = try posixOpen( - allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0, @@ -1470,7 +1461,6 @@ pub const Dir = struct { }, Os.linux => Handle{ .fd = try posixOpen( - allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0, @@ -1668,12 +1658,12 @@ pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void { /// Read value of a symbolic link. /// The return value is a slice of out_buffer. -pub fn readLinkC(pathname: [*]const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 { +pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 { const rc = posix.readlink(pathname, out_buffer, out_buffer.len); const err = posix.getErrno(rc); switch (err) { 0 => return out_buffer[0..rc], - posix.EACCES => error.AccessDenied, + posix.EACCES => return error.AccessDenied, posix.EFAULT => unreachable, posix.EINVAL => unreachable, posix.EIO => return error.FileSystem, @@ -1688,12 +1678,9 @@ pub fn readLinkC(pathname: [*]const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 { /// Read value of a symbolic link. /// The return value is a slice of out_buffer. -pub fn readLink(file_path: []const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 { - var path_with_null: [posix.PATH_MAX]u8 = undefined; - if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; - mem.copy(u8, path_with_null[0..], file_path); - path_with_null[file_path.len] = 0; - return readLinkC(&path_with_null, out_buffer); +pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 { + const file_path_c = try toPosixPath(file_path); + return readLinkC(out_buffer, &file_path_c); } pub fn posix_setuid(uid: u32) !void { @@ -2080,17 +2067,12 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { pub fn openSelfExe() !os.File { switch (builtin.os) { - Os.linux => { - const proc_file_path = "/proc/self/exe"; - var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined; - var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - return os.File.openRead(&fixed_allocator.allocator, proc_file_path); - }, + Os.linux => return os.File.openReadC(c"/proc/self/exe"), Os.macosx, Os.ios => { - var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined; - var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - const self_exe_path = try selfExePath(&fixed_allocator.allocator); - return os.File.openRead(&fixed_allocator.allocator, self_exe_path); + var buf: [MAX_PATH_BYTES]u8 = undefined; + const self_exe_path = try selfExePath(&buf); + buf[self_exe_path.len] = 0; + return os.File.openReadC(self_exe_path.ptr); }, else => @compileError("Unsupported OS"), } @@ -2099,7 +2081,7 @@ pub fn openSelfExe() !os.File { test "openSelfExe" { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(), - else => return, // Unsupported OS. + else => return error.SkipZigTest, // Unsupported OS } } @@ -2108,69 +2090,67 @@ test "openSelfExe" { /// If you only want an open file handle, use openSelfExe. /// This function may return an error if the current executable /// was deleted after spawning. -/// Caller owns returned memory. -pub fn selfExePath(allocator: *mem.Allocator) ![]u8 { +/// Returned value is a slice of out_buffer. +/// +/// On Linux, depends on procfs being mounted. If the currently executing binary has +/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. +pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { switch (builtin.os) { - Os.linux => { - // If the currently executing binary has been deleted, - // the file path looks something like `/a/b/c/exe (deleted)` - return readLink(allocator, "/proc/self/exe"); - }, + Os.linux => return readLink(out_buffer, "/proc/self/exe"), Os.windows => { - var out_path = try Buffer.initSize(allocator, 0xff); - errdefer out_path.deinit(); - while (true) { - const dword_len = try math.cast(windows.DWORD, out_path.len()); - const copied_amt = windows.GetModuleFileNameA(null, out_path.ptr(), dword_len); - if (copied_amt <= 0) { - const err = windows.GetLastError(); - return switch (err) { - else => unexpectedErrorWindows(err), - }; + var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; + const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast + const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len); + assert(rc <= utf16le_buf.len); + if (rc == 0) { + const err = windows.GetLastError(); + switch (err) { + else => return unexpectedErrorWindows(err), } - if (copied_amt < out_path.len()) { - out_path.shrink(copied_amt); - return out_path.toOwnedSlice(); - } - const new_len = (out_path.len() << 1) | 0b1; - try out_path.resize(new_len); } + const utf16le_slice = utf16le_buf[0..rc]; + // Trust that Windows gives us valid UTF-16LE. + const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; + return out_buffer[0..end_index]; }, Os.macosx, Os.ios => { - var u32_len: u32 = 0; - const ret1 = c._NSGetExecutablePath(undefined, &u32_len); - assert(ret1 != 0); - const bytes = try allocator.alloc(u8, u32_len); - errdefer allocator.free(bytes); - const ret2 = c._NSGetExecutablePath(bytes.ptr, &u32_len); - assert(ret2 == 0); - return bytes; + var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast + const rc = c._NSGetExecutablePath(out_buffer, &u32_len); + if (rc != 0) return error.NameTooLong; + return out_buffer[0..u32_len]; }, else => @compileError("Unsupported OS"), } } -/// Get the directory path that contains the current executable. +/// `selfExeDirPath` except allocates the result on the heap. /// Caller owns returned memory. -pub fn selfExeDirPath(allocator: *mem.Allocator) ![]u8 { +pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { + var buf: [MAX_PATH_BYTES]u8 = undefined; + return mem.dupe(allocator, u8, try selfExeDirPath(&buf)); +} + +/// Get the directory path that contains the current executable. +/// Returned value is a slice of out_buffer. +pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { switch (builtin.os) { Os.linux => { // If the currently executing binary has been deleted, // the file path looks something like `/a/b/c/exe (deleted)` // This path cannot be opened, but it's valid for determining the directory // the executable was in when it was run. - const full_exe_path = try readLink(allocator, "/proc/self/exe"); - errdefer allocator.free(full_exe_path); - const dir = path.dirname(full_exe_path) orelse "."; - return allocator.shrink(u8, full_exe_path, dir.len); + const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe"); + // Assume that /proc/self/exe has an absolute path, and therefore dirname + // will not return null. + return path.dirname(full_exe_path).?; }, Os.windows, Os.macosx, Os.ios => { - const self_exe_path = try selfExePath(allocator); - errdefer allocator.free(self_exe_path); - const dirname = os.path.dirname(self_exe_path) orelse "."; - return allocator.shrink(u8, self_exe_path, dirname.len); + const self_exe_path = try selfExePath(out_buffer); + // Assume that the OS APIs return absolute paths, and therefore dirname + // will not return null. + return path.dirname(self_exe_path).?; }, - else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)), + else => @compileError("Unsupported OS"), } } @@ -2991,7 +2971,9 @@ pub fn posixFStat(fd: i32) !posix.Stat { const err = posix.getErrno(posix.fstat(fd, &stat)); if (err > 0) { return switch (err) { - posix.EBADF => error.BadFd, + // We do not make this an error code because if you get EBADF it's always a bug, + // since the fd could have been reused. + posix.EBADF => unreachable, posix.ENOMEM => error.SystemResources, else => os.unexpectedErrorPosix(err), }; diff --git a/std/os/path.zig b/std/os/path.zig index b31d9264d3..b3cfec1a3a 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -11,6 +11,7 @@ const math = std.math; const posix = os.posix; const windows = os.windows; const cstr = std.cstr; +const windows_util = @import("windows/util.zig"); pub const sep_windows = '\\'; pub const sep_posix = '/'; @@ -1075,113 +1076,148 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons assert(mem.eql(u8, result, expected_output)); } -/// Return the canonicalized absolute pathname. -/// Expands all symbolic links and resolves references to `.`, `..`, and -/// extra `/` characters in ::pathname. -/// Caller must deallocate result. -/// TODO rename this to realAlloc and provide real with no allocator. See #1392 -pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 { +pub const RealError = error{ + FileNotFound, + AccessDenied, + NameTooLong, + NotSupported, + NotDir, + SymLinkLoop, + InputOutput, + FileTooBig, + IsDir, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NoDevice, + SystemResources, + NoSpaceLeft, + FileSystem, + BadPathName, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, + + /// TODO remove this possibility + PathAlreadyExists, + + /// TODO remove this possibility + Unexpected, +}; + +/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. +/// Otherwise use `real` or `realC`. +pub fn realW(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u16) RealError![]u8 { + const h_file = windows.CreateFileW( + pathname, + windows.GENERIC_READ, + windows.FILE_SHARE_READ, + null, + windows.OPEN_EXISTING, + windows.FILE_ATTRIBUTE_NORMAL, + null, + ); + if (h_file == windows.INVALID_HANDLE_VALUE) { + const err = windows.GetLastError(); + switch (err) { + windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, + windows.ERROR.ACCESS_DENIED => return error.AccessDenied, + windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, + else => return os.unexpectedErrorWindows(err), + } + } + defer os.close(h_file); + var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; + const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast + const result = windows.GetFinalPathNameByHandleW(h_file, &utf16le_buf, casted_len, windows.VOLUME_NAME_DOS); + assert(result <= utf16le_buf.len); + if (result == 0) { + const err = windows.GetLastError(); + switch (err) { + windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, + windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources, + windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, + windows.ERROR.INVALID_PARAMETER => unreachable, + else => return os.unexpectedErrorWindows(err), + } + } + const utf16le_slice = utf16le_buf[0..result]; + + // windows returns \\?\ prepended to the path + // we strip it because nobody wants \\?\ prepended to their path + const prefix = []u16{ '\\', '\\', '?', '\\' }; + const start_index = if (mem.startsWith(u16, utf16le_slice, prefix)) prefix.len else 0; + + // Trust that Windows gives us valid UTF-16LE. + const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice[start_index..]) catch unreachable; + return out_buffer[0..end_index]; +} + +/// See `real` +/// Use this when you have a null terminated pointer path. +pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealError![]u8 { switch (builtin.os) { Os.windows => { - const pathname_buf = try allocator.alloc(u8, pathname.len + 1); - defer allocator.free(pathname_buf); - - mem.copy(u8, pathname_buf, pathname); - pathname_buf[pathname.len] = 0; - - const h_file = windows.CreateFileA(pathname_buf.ptr, windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null); - if (h_file == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, - windows.ERROR.ACCESS_DENIED => error.AccessDenied, - windows.ERROR.FILENAME_EXCED_RANGE => error.NameTooLong, - else => os.unexpectedErrorWindows(err), - }; - } - defer os.close(h_file); - var buf = try allocator.alloc(u8, 256); - errdefer allocator.free(buf); - while (true) { - const buf_len = math.cast(windows.DWORD, buf.len) catch return error.NameTooLong; - const result = windows.GetFinalPathNameByHandleA(h_file, buf.ptr, buf_len, windows.VOLUME_NAME_DOS); - - if (result == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, - windows.ERROR.NOT_ENOUGH_MEMORY => error.OutOfMemory, - windows.ERROR.INVALID_PARAMETER => unreachable, - else => os.unexpectedErrorWindows(err), - }; - } - - if (result > buf.len) { - buf = try allocator.realloc(u8, buf, result); - continue; - } - - // windows returns \\?\ prepended to the path - // we strip it because nobody wants \\?\ prepended to their path - const final_len = x: { - if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) { - var i: usize = 4; - while (i < result) : (i += 1) { - buf[i - 4] = buf[i]; - } - break :x result - 4; - } else { - break :x result; - } - }; - - return allocator.shrink(u8, buf, final_len); - } + const pathname_w = try windows_util.cStrToPrefixedFileW(pathname); + return realW(out_buffer, pathname_w); }, Os.macosx, Os.ios => { - // TODO instead of calling the libc function here, port the implementation - // to Zig, and then remove the NameTooLong error possibility. - const pathname_buf = try allocator.alloc(u8, pathname.len + 1); - defer allocator.free(pathname_buf); - - const result_buf = try allocator.alloc(u8, posix.PATH_MAX); - errdefer allocator.free(result_buf); - - mem.copy(u8, pathname_buf, pathname); - pathname_buf[pathname.len] = 0; - - const err = posix.getErrno(posix.realpath(pathname_buf.ptr, result_buf.ptr)); - if (err > 0) { - return switch (err) { - posix.EINVAL => unreachable, - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EACCES => error.AccessDenied, - posix.ENOENT => error.FileNotFound, - posix.ENOTSUP => error.NotSupported, - posix.ENOTDIR => error.NotDir, - posix.ENAMETOOLONG => error.NameTooLong, - posix.ELOOP => error.SymLinkLoop, - posix.EIO => error.InputOutput, - else => os.unexpectedErrorPosix(err), - }; + // TODO instead of calling the libc function here, port the implementation to Zig + const err = posix.getErrno(posix.realpath(pathname, out_buffer)); + switch (err) { + 0 => return mem.toSlice(u8, out_buffer), + posix.EINVAL => unreachable, + posix.EBADF => unreachable, + posix.EFAULT => unreachable, + posix.EACCES => return error.AccessDenied, + posix.ENOENT => return error.FileNotFound, + posix.ENOTSUP => return error.NotSupported, + posix.ENOTDIR => return error.NotDir, + posix.ENAMETOOLONG => return error.NameTooLong, + posix.ELOOP => return error.SymLinkLoop, + posix.EIO => return error.InputOutput, + else => return os.unexpectedErrorPosix(err), } - return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr)); }, Os.linux => { - const fd = try os.posixOpen(allocator, pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0); + const fd = try os.posixOpenC(pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0); defer os.close(fd); var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined; - const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd) catch unreachable; + const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable; - return os.readLink(allocator, proc_path); + return os.readLinkC(out_buffer, proc_path.ptr); }, else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)), } } +/// Return the canonicalized absolute pathname. +/// Expands all symbolic links and resolves references to `.`, `..`, and +/// extra `/` characters in ::pathname. +/// The return value is a slice of out_buffer, and not necessarily from the beginning. +pub fn real(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: []const u8) RealError![]u8 { + switch (builtin.os) { + Os.windows => { + const pathname_w = try windows_util.sliceToPrefixedFileW(pathname); + return realW(out_buffer, &pathname_w); + }, + Os.macosx, Os.ios, Os.linux => { + const pathname_c = try os.toPosixPath(pathname); + return realC(out_buffer, &pathname_c); + }, + else => @compileError("Unsupported OS"), + } +} + +/// `real`, except caller must free the returned memory. +pub fn realAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 { + var buf: [os.MAX_PATH_BYTES]u8 = undefined; + return mem.dupe(allocator, u8, try real(&buf, pathname)); +} + test "os.path.real" { // at least call it so it gets compiled - _ = real(debug.global_allocator, "some_path"); + var buf: [os.MAX_PATH_BYTES]u8 = undefined; + std.debug.assertError(real(&buf, "definitely_bogus_does_not_exist1234"), error.FileNotFound); } diff --git a/std/os/test.zig b/std/os/test.zig index d6e61c768d..653ab13fd8 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -17,7 +17,7 @@ test "makePath, put some files in it, deleteTree" { if (os.Dir.open(a, "os_test_tmp")) |dir| { @panic("expected error"); } else |err| { - assert(err == error.PathNotFound); + assert(err == error.FileNotFound); } } diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index e5763fa5c6..66b5291189 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -4,8 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn CreateDirectoryA( lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; -pub extern "kernel32" stdcallcc fn CreateDirectoryW( lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; +pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; +pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; pub extern "kernel32" stdcallcc fn CreateFileA( lpFileName: [*]const u8, // TODO null terminated pointer type @@ -89,7 +89,8 @@ pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LAR pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD; pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD; -pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; +pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD; +pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD; pub extern "kernel32" stdcallcc fn GetLastError() DWORD; @@ -107,6 +108,13 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( dwFlags: DWORD, ) DWORD; +pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW( + hFile: HANDLE, + lpszFilePath: [*]u16, + cchFilePath: DWORD, + dwFlags: DWORD, +) DWORD; + pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL; pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index e1be379653..72de896996 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -97,12 +97,12 @@ pub const OpenError = error{ SharingViolation, PathAlreadyExists, - /// When all the path components are found but the file component is not. + /// When any of the path components can not be found or the file component can not + /// be found. Some operating systems distinguish between path components not found and + /// file components not found, but they are collapsed into FileNotFound to gain + /// consistency across operating systems. FileNotFound, - /// When one or more path components are not found. - PathNotFound, - AccessDenied, PipeBusy, NameTooLong, @@ -136,7 +136,7 @@ pub fn windowsOpen( windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists, windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists, windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return OpenError.PathNotFound, + windows.ERROR.PATH_NOT_FOUND => return OpenError.FileNotFound, windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied, windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy, else => return os.unexpectedErrorWindows(err), @@ -216,9 +216,8 @@ pub fn windowsFindFirstFile( if (handle == windows.INVALID_HANDLE_VALUE) { const err = windows.GetLastError(); switch (err) { - windows.ERROR.FILE_NOT_FOUND, - windows.ERROR.PATH_NOT_FOUND, - => return error.PathNotFound, + windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, else => return os.unexpectedErrorWindows(err), } } @@ -284,13 +283,13 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t return WindowsWaitResult.Normal; } -pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE+1]u16 { +pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); } -pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE+1]u16 { +pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { // TODO well defined copy elision - var result: [PATH_MAX_WIDE+1]u16 = undefined; + var result: [PATH_MAX_WIDE + 1]u16 = undefined; // > File I/O functions in the Windows API convert "/" to "\" as part of // > 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 { // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation // Because we want the larger maximum path length for absolute paths, we // disallow forward slashes in zig std lib file functions on Windows. - for (s) |byte| switch (byte) { + for (s) |byte| + switch (byte) { '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, else => {}, }; const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { - const prefix = []u16{'\\', '\\', '?', '\\'}; + const prefix = []u16{ '\\', '\\', '?', '\\' }; mem.copy(u16, result[0..], prefix); break :blk prefix.len; }; diff --git a/std/unicode.zig b/std/unicode.zig index 59b6ec227e..105c38627f 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -495,7 +495,7 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 } /// Asserts that the output buffer is big enough. -/// Returns end index. +/// Returns end byte index into utf8. pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !usize { var end_index: usize = 0; var it = Utf16LeIterator.init(utf16le); diff --git a/test/cases/merge_error_sets.zig b/test/cases/merge_error_sets.zig index 189bd16a4d..147b580232 100644 --- a/test/cases/merge_error_sets.zig +++ b/test/cases/merge_error_sets.zig @@ -1,5 +1,5 @@ const A = error{ - PathNotFound, + FileNotFound, NotDir, }; const B = error{OutOfMemory}; @@ -15,7 +15,7 @@ test "merge error sets" { @panic("unexpected"); } else |err| switch (err) { error.OutOfMemory => @panic("unexpected"), - error.PathNotFound => @panic("unexpected"), + error.FileNotFound => @panic("unexpected"), error.NotDir => {}, } }