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)
This commit is contained in:
Andrew Kelley 2018-08-21 20:28:37 -04:00
parent 51852d2587
commit ea1b21dbdb
17 changed files with 319 additions and 288 deletions

View File

@ -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("<pre><code class=\"zig\">{}</code></pre>", 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| {

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;

View File

@ -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,
}
}

View File

@ -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();

View File

@ -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 {

View File

@ -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();

View File

@ -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);
}

View File

@ -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),
};

View File

@ -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),
};

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
};

View File

@ -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);

View File

@ -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 => {},
}
}