mirror of
https://github.com/ziglang/zig.git
synced 2025-01-25 03:11:02 +00:00
parent
d5968086fe
commit
92f7474359
@ -581,8 +581,6 @@ set(ZIG_STD_FILES
|
||||
"os/windows/ntdll.zig"
|
||||
"os/windows/ole32.zig"
|
||||
"os/windows/shell32.zig"
|
||||
"os/windows/shlwapi.zig"
|
||||
"os/windows/user32.zig"
|
||||
"os/windows/util.zig"
|
||||
"os/zen.zig"
|
||||
"pdb.zig"
|
||||
|
@ -1,5 +1,6 @@
|
||||
const std = @import("../index.zig");
|
||||
const cstr = std.cstr;
|
||||
const unicode = std.unicode;
|
||||
const io = std.io;
|
||||
const os = std.os;
|
||||
const posix = os.posix;
|
||||
@ -12,6 +13,7 @@ const Buffer = std.Buffer;
|
||||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const LinkedList = std.LinkedList;
|
||||
const windows_util = @import("windows/util.zig");
|
||||
|
||||
const is_windows = builtin.os == Os.windows;
|
||||
|
||||
@ -520,8 +522,8 @@ pub const ChildProcess = struct {
|
||||
const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv);
|
||||
defer self.allocator.free(cmd_line);
|
||||
|
||||
var siStartInfo = windows.STARTUPINFOA{
|
||||
.cb = @sizeOf(windows.STARTUPINFOA),
|
||||
var siStartInfo = windows.STARTUPINFOW{
|
||||
.cb = @sizeOf(windows.STARTUPINFOW),
|
||||
.hStdError = g_hChildStd_ERR_Wr,
|
||||
.hStdOutput = g_hChildStd_OUT_Wr,
|
||||
.hStdInput = g_hChildStd_IN_Rd,
|
||||
@ -545,7 +547,9 @@ pub const ChildProcess = struct {
|
||||
|
||||
const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null;
|
||||
defer if (cwd_slice) |cwd| self.allocator.free(cwd);
|
||||
const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
|
||||
const cwd_w = if (cwd_slice) |cwd| try unicode.utf8ToUtf16LeWithNull(self.allocator, cwd) else null;
|
||||
defer if (cwd_w) |cwd| self.allocator.free(cwd);
|
||||
const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
|
||||
|
||||
const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null;
|
||||
defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
|
||||
@ -564,7 +568,13 @@ pub const ChildProcess = struct {
|
||||
};
|
||||
defer self.allocator.free(app_name);
|
||||
|
||||
windowsCreateProcess(app_name.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
|
||||
const app_name_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
|
||||
defer self.allocator.free(app_name_w);
|
||||
|
||||
const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);
|
||||
defer self.allocator.free(cmd_line_w);
|
||||
|
||||
windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
|
||||
if (no_path_err != error.FileNotFound) return no_path_err;
|
||||
|
||||
const PATH = try os.getEnvVarOwned(self.allocator, "PATH");
|
||||
@ -575,7 +585,10 @@ pub const ChildProcess = struct {
|
||||
const joined_path = try os.path.join(self.allocator, search_path, app_name);
|
||||
defer self.allocator.free(joined_path);
|
||||
|
||||
if (windowsCreateProcess(joined_path.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo)) |_| {
|
||||
const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
|
||||
defer self.allocator.free(joined_path_w);
|
||||
|
||||
if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
|
||||
break;
|
||||
} else |err| if (err == error.FileNotFound) {
|
||||
continue;
|
||||
@ -626,15 +639,36 @@ pub const ChildProcess = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
|
||||
if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) {
|
||||
fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
|
||||
// TODO the docs for environment pointer say:
|
||||
// > A pointer to the environment block for the new process. If this parameter
|
||||
// > is NULL, the new process uses the environment of the calling process.
|
||||
// > ...
|
||||
// > An environment block can contain either Unicode or ANSI characters. If
|
||||
// > the environment block pointed to by lpEnvironment contains Unicode
|
||||
// > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT.
|
||||
// > If this parameter is NULL and the environment block of the parent process
|
||||
// > contains Unicode characters, you must also ensure that dwCreationFlags
|
||||
// > includes CREATE_UNICODE_ENVIRONMENT.
|
||||
// This seems to imply that we have to somehow know whether our process parent passed
|
||||
// CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter.
|
||||
// Since we do not know this information that would imply that we must not pass NULL
|
||||
// for the parameter.
|
||||
// However this would imply that programs compiled with -DUNICODE could not pass
|
||||
// environment variables to programs that were not, which seems unlikely.
|
||||
// More investigation is needed.
|
||||
if (windows.CreateProcessW(
|
||||
app_name, cmd_line, null, null, windows.TRUE, windows.CREATE_UNICODE_ENVIRONMENT,
|
||||
@ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation,
|
||||
) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
|
||||
switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.INVALID_PARAMETER => unreachable,
|
||||
windows.ERROR.INVALID_NAME => error.InvalidName,
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
windows.ERROR.INVALID_NAME => return error.InvalidName,
|
||||
else => return os.unexpectedErrorWindows(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
201
std/os/index.zig
201
std/os/index.zig
@ -819,37 +819,40 @@ test "os.getCwd" {
|
||||
|
||||
pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
|
||||
|
||||
pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void {
|
||||
/// TODO add a symLinkC variant
|
||||
pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void {
|
||||
if (is_windows) {
|
||||
return symLinkWindows(allocator, existing_path, new_path);
|
||||
return symLinkWindows(existing_path, new_path);
|
||||
} else {
|
||||
return symLinkPosix(allocator, existing_path, new_path);
|
||||
return symLinkPosix(existing_path, new_path);
|
||||
}
|
||||
}
|
||||
|
||||
pub const WindowsSymLinkError = error{
|
||||
OutOfMemory,
|
||||
NameTooLong,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
|
||||
/// See https://github.com/ziglang/zig/issues/1396
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
|
||||
const existing_with_null = try cstr.addNullByte(allocator, existing_path);
|
||||
defer allocator.free(existing_with_null);
|
||||
const new_with_null = try cstr.addNullByte(allocator, new_path);
|
||||
defer allocator.free(new_with_null);
|
||||
|
||||
if (windows.CreateSymbolicLinkA(existing_with_null.ptr, new_with_null.ptr, 0) == 0) {
|
||||
pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void {
|
||||
if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
else => unexpectedErrorWindows(err),
|
||||
};
|
||||
switch (err) {
|
||||
else => return unexpectedErrorWindows(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
|
||||
const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path);
|
||||
const new_path_w = try windows_util.sliceToPrefixedFileW(new_path);
|
||||
return symLinkW(&existing_path_w, &new_path_w);
|
||||
}
|
||||
|
||||
pub const PosixSymLinkError = error{
|
||||
OutOfMemory,
|
||||
AccessDenied,
|
||||
DiskQuota,
|
||||
PathAlreadyExists,
|
||||
@ -866,43 +869,40 @@ pub const PosixSymLinkError = error{
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
|
||||
const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);
|
||||
defer allocator.free(full_buf);
|
||||
|
||||
const existing_buf = full_buf;
|
||||
mem.copy(u8, existing_buf, existing_path);
|
||||
existing_buf[existing_path.len] = 0;
|
||||
|
||||
const new_buf = full_buf[existing_path.len + 1 ..];
|
||||
mem.copy(u8, new_buf, new_path);
|
||||
new_buf[new_path.len] = 0;
|
||||
|
||||
const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EDQUOT => error.DiskQuota,
|
||||
posix.EEXIST => error.PathAlreadyExists,
|
||||
posix.EIO => error.FileSystem,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOSPC => error.NoSpaceLeft,
|
||||
posix.EROFS => error.ReadOnlyFileSystem,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void {
|
||||
const err = posix.getErrno(posix.symlink(existing_path, new_path));
|
||||
switch (err) {
|
||||
0 => return,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EACCES => return error.AccessDenied,
|
||||
posix.EPERM => return error.AccessDenied,
|
||||
posix.EDQUOT => return error.DiskQuota,
|
||||
posix.EEXIST => return error.PathAlreadyExists,
|
||||
posix.EIO => return error.FileSystem,
|
||||
posix.ELOOP => return error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => return error.NameTooLong,
|
||||
posix.ENOENT => return error.FileNotFound,
|
||||
posix.ENOTDIR => return error.NotDir,
|
||||
posix.ENOMEM => return error.SystemResources,
|
||||
posix.ENOSPC => return error.NoSpaceLeft,
|
||||
posix.EROFS => return error.ReadOnlyFileSystem,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
|
||||
const existing_path_c = try toPosixPath(existing_path);
|
||||
const new_path_c = try toPosixPath(new_path);
|
||||
return symLinkPosixC(&existing_path_c, &new_path_c);
|
||||
}
|
||||
|
||||
// here we replace the standard +/ with -_ so that it can be used in a file name
|
||||
const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
|
||||
|
||||
/// TODO remove the allocator requirement from this API
|
||||
pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
|
||||
if (symLink(allocator, existing_path, new_path)) {
|
||||
if (symLink(existing_path, new_path)) {
|
||||
return;
|
||||
} else |err| switch (err) {
|
||||
error.PathAlreadyExists => {},
|
||||
@ -920,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
|
||||
try getRandomBytes(rand_buf[0..]);
|
||||
b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
|
||||
|
||||
if (symLink(allocator, existing_path, tmp_path)) {
|
||||
if (symLink(existing_path, tmp_path)) {
|
||||
return rename(tmp_path, new_path);
|
||||
} else |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
@ -1252,54 +1252,70 @@ pub const DeleteDirError = error{
|
||||
NotDir,
|
||||
DirNotEmpty,
|
||||
ReadOnlyFileSystem,
|
||||
OutOfMemory,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
|
||||
/// See https://github.com/ziglang/zig/issues/1396
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
/// Returns ::error.DirNotEmpty if the directory is not empty.
|
||||
/// To delete a directory recursively, see ::deleteTree
|
||||
pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {
|
||||
const path_buf = try allocator.alloc(u8, dir_path.len + 1);
|
||||
defer allocator.free(path_buf);
|
||||
|
||||
mem.copy(u8, path_buf, dir_path);
|
||||
path_buf[dir_path.len] = 0;
|
||||
|
||||
pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void {
|
||||
switch (builtin.os) {
|
||||
Os.windows => {
|
||||
if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
|
||||
windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
|
||||
else => unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path);
|
||||
return deleteDirW(&dir_path_w);
|
||||
},
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
const err = posix.getErrno(posix.rmdir(path_buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EBUSY => error.FileBusy,
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
|
||||
posix.EROFS => error.ReadOnlyFileSystem,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
const err = posix.getErrno(posix.rmdir(dir_path));
|
||||
switch (err) {
|
||||
0 => return,
|
||||
posix.EACCES => return error.AccessDenied,
|
||||
posix.EPERM => return error.AccessDenied,
|
||||
posix.EBUSY => return error.FileBusy,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ELOOP => return error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => return error.NameTooLong,
|
||||
posix.ENOENT => return error.FileNotFound,
|
||||
posix.ENOMEM => return error.SystemResources,
|
||||
posix.ENOTDIR => return error.NotDir,
|
||||
posix.EEXIST => return error.DirNotEmpty,
|
||||
posix.ENOTEMPTY => return error.DirNotEmpty,
|
||||
posix.EROFS => return error.ReadOnlyFileSystem,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
},
|
||||
else => @compileError("unimplemented"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void {
|
||||
if (windows.RemoveDirectoryW(dir_path_w) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
|
||||
else => return unexpectedErrorWindows(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns ::error.DirNotEmpty if the directory is not empty.
|
||||
/// To delete a directory recursively, see ::deleteTree
|
||||
pub fn deleteDir(dir_path: []const u8) DeleteDirError!void {
|
||||
switch (builtin.os) {
|
||||
Os.windows => {
|
||||
const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
|
||||
return deleteDirW(&dir_path_w);
|
||||
},
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
const dir_path_c = try toPosixPath(dir_path);
|
||||
return deleteDirC(&dir_path_c);
|
||||
},
|
||||
else => @compileError("unimplemented"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether ::full_path describes a symlink, file, or directory, this function
|
||||
/// removes it. If it cannot be removed because it is a non-empty directory,
|
||||
/// this function recursively removes its entries and then tries again.
|
||||
@ -1346,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
|
||||
error.IsDir => {},
|
||||
error.AccessDenied => got_access_denied = true,
|
||||
|
||||
error.InvalidUtf8,
|
||||
error.SymLinkLoop,
|
||||
error.NameTooLong,
|
||||
error.SystemResources,
|
||||
@ -1353,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
|
||||
error.NotDir,
|
||||
error.FileSystem,
|
||||
error.FileBusy,
|
||||
error.InvalidUtf8,
|
||||
error.BadPathName,
|
||||
error.Unexpected,
|
||||
=> return err,
|
||||
@ -1381,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
|
||||
error.NoSpaceLeft,
|
||||
error.PathAlreadyExists,
|
||||
error.Unexpected,
|
||||
error.InvalidUtf8,
|
||||
error.BadPathName,
|
||||
=> return err,
|
||||
};
|
||||
defer dir.close();
|
||||
@ -1398,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
|
||||
try deleteTree(allocator, full_entry_path);
|
||||
}
|
||||
}
|
||||
return deleteDir(allocator, full_path);
|
||||
return deleteDir(full_path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1422,8 +1440,9 @@ pub const Dir = struct {
|
||||
},
|
||||
Os.windows => struct {
|
||||
handle: windows.HANDLE,
|
||||
find_file_data: windows.WIN32_FIND_DATAA,
|
||||
find_file_data: windows.WIN32_FIND_DATAW,
|
||||
first: bool,
|
||||
name_data: [256]u8,
|
||||
},
|
||||
else => @compileError("unimplemented"),
|
||||
};
|
||||
@ -1460,6 +1479,8 @@ pub const Dir = struct {
|
||||
NoSpaceLeft,
|
||||
PathAlreadyExists,
|
||||
OutOfMemory,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
|
||||
/// See https://github.com/ziglang/zig/issues/1396
|
||||
Unexpected,
|
||||
@ -1471,12 +1492,13 @@ pub const Dir = struct {
|
||||
.allocator = allocator,
|
||||
.handle = switch (builtin.os) {
|
||||
Os.windows => blk: {
|
||||
var find_file_data: windows.WIN32_FIND_DATAA = undefined;
|
||||
const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
|
||||
var find_file_data: windows.WIN32_FIND_DATAW = undefined;
|
||||
const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data);
|
||||
break :blk Handle{
|
||||
.handle = handle,
|
||||
.find_file_data = find_file_data, // TODO guaranteed copy elision
|
||||
.first = true,
|
||||
.name_data = undefined,
|
||||
};
|
||||
},
|
||||
Os.macosx, Os.ios => Handle{
|
||||
@ -1591,9 +1613,12 @@ pub const Dir = struct {
|
||||
if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
|
||||
return null;
|
||||
}
|
||||
const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);
|
||||
if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))
|
||||
const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
|
||||
if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'}))
|
||||
continue;
|
||||
// Trust that Windows gives us valid UTF-16LE
|
||||
const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
|
||||
const name_utf8 = self.handle.name_data[0..name_utf8_len];
|
||||
const kind = blk: {
|
||||
const attrs = self.handle.find_file_data.dwFileAttributes;
|
||||
if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
|
||||
@ -1602,7 +1627,7 @@ pub const Dir = struct {
|
||||
break :blk Entry.Kind.Unknown;
|
||||
};
|
||||
return Entry{
|
||||
.name = name,
|
||||
.name = name_utf8,
|
||||
.kind = kind,
|
||||
};
|
||||
}
|
||||
@ -2070,7 +2095,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons
|
||||
}
|
||||
|
||||
// TODO make this a build variable that you can set
|
||||
const unexpected_error_tracing = true;
|
||||
const unexpected_error_tracing = false;
|
||||
const UnexpectedError = error{
|
||||
/// The Operating System returned an undocumented error code.
|
||||
Unexpected,
|
||||
|
@ -6,8 +6,6 @@ pub use @import("kernel32.zig");
|
||||
pub use @import("ntdll.zig");
|
||||
pub use @import("ole32.zig");
|
||||
pub use @import("shell32.zig");
|
||||
pub use @import("shlwapi.zig");
|
||||
pub use @import("user32.zig");
|
||||
|
||||
test "import" {
|
||||
_ = @import("util.zig");
|
||||
@ -174,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct {
|
||||
dwThreadId: DWORD,
|
||||
};
|
||||
|
||||
pub const STARTUPINFOA = extern struct {
|
||||
pub const STARTUPINFOW = extern struct {
|
||||
cb: DWORD,
|
||||
lpReserved: ?LPSTR,
|
||||
lpDesktop: ?LPSTR,
|
||||
lpTitle: ?LPSTR,
|
||||
lpReserved: ?LPWSTR,
|
||||
lpDesktop: ?LPWSTR,
|
||||
lpTitle: ?LPWSTR,
|
||||
dwX: DWORD,
|
||||
dwY: DWORD,
|
||||
dwXSize: DWORD,
|
||||
@ -238,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
|
||||
pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
|
||||
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
|
||||
|
||||
pub const WIN32_FIND_DATAA = extern struct {
|
||||
pub const WIN32_FIND_DATAW = extern struct {
|
||||
dwFileAttributes: DWORD,
|
||||
ftCreationTime: FILETIME,
|
||||
ftLastAccessTime: FILETIME,
|
||||
@ -247,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct {
|
||||
nFileSizeLow: DWORD,
|
||||
dwReserved0: DWORD,
|
||||
dwReserved1: DWORD,
|
||||
cFileName: [260]CHAR,
|
||||
cAlternateFileName: [14]CHAR,
|
||||
cFileName: [260]u16,
|
||||
cAlternateFileName: [14]u16,
|
||||
};
|
||||
|
||||
pub const FILETIME = extern struct {
|
||||
@ -377,3 +375,5 @@ pub const COORD = extern struct {
|
||||
X: SHORT,
|
||||
Y: SHORT,
|
||||
};
|
||||
|
||||
pub const CREATE_UNICODE_ENVIRONMENT = 1024;
|
||||
|
@ -4,19 +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 CreateFileA(
|
||||
lpFileName: [*]const u8, // TODO null terminated pointer type
|
||||
dwDesiredAccess: DWORD,
|
||||
dwShareMode: DWORD,
|
||||
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
||||
dwCreationDisposition: DWORD,
|
||||
dwFlagsAndAttributes: DWORD,
|
||||
hTemplateFile: ?HANDLE,
|
||||
) HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateFileW(
|
||||
lpFileName: [*]const u16, // TODO null terminated pointer type
|
||||
dwDesiredAccess: DWORD,
|
||||
@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe(
|
||||
nSize: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateProcessA(
|
||||
lpApplicationName: ?LPCSTR,
|
||||
lpCommandLine: LPSTR,
|
||||
pub extern "kernel32" stdcallcc fn CreateProcessW(
|
||||
lpApplicationName: ?LPWSTR,
|
||||
lpCommandLine: LPWSTR,
|
||||
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
bInheritHandles: BOOL,
|
||||
dwCreationFlags: DWORD,
|
||||
lpEnvironment: ?*c_void,
|
||||
lpCurrentDirectory: ?LPCSTR,
|
||||
lpStartupInfo: *STARTUPINFOA,
|
||||
lpCurrentDirectory: ?LPWSTR,
|
||||
lpStartupInfo: *STARTUPINFOW,
|
||||
lpProcessInformation: *PROCESS_INFORMATION,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
|
||||
lpSymlinkFileName: LPCSTR,
|
||||
lpTargetFileName: LPCSTR,
|
||||
dwFlags: DWORD,
|
||||
) BOOLEAN;
|
||||
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
|
||||
|
||||
@ -74,7 +58,6 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
|
||||
@ -88,10 +71,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
|
||||
|
||||
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: [*]u8, nSize: DWORD) DWORD;
|
||||
pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;
|
||||
@ -105,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
|
||||
in_dwBufferSize: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
|
||||
hFile: HANDLE,
|
||||
lpszFilePath: LPSTR,
|
||||
cchFilePath: DWORD,
|
||||
dwFlags: DWORD,
|
||||
) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
|
||||
hFile: HANDLE,
|
||||
lpszFilePath: [*]u16,
|
||||
@ -142,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn MoveFileExA(
|
||||
lpExistingFileName: [*]const u8,
|
||||
lpNewFileName: [*]const u8,
|
||||
dwFlags: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn MoveFileExW(
|
||||
lpExistingFileName: [*]const u16,
|
||||
lpNewFileName: [*]const u16,
|
||||
@ -179,7 +147,7 @@ pub extern "kernel32" stdcallcc fn ReadFile(
|
||||
in_out_lpOverlapped: ?*OVERLAPPED,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
|
||||
|
||||
@ -208,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile(
|
||||
|
||||
pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
|
||||
|
||||
//TODO: call unicode versions instead of relying on ANSI code page
|
||||
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
|
||||
pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
|
||||
|
||||
|
@ -1,4 +0,0 @@
|
||||
use @import("index.zig");
|
||||
|
||||
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
|
||||
|
@ -1,4 +0,0 @@
|
||||
use @import("index.zig");
|
||||
|
||||
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
|
||||
|
@ -1,6 +1,7 @@
|
||||
const std = @import("../../index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const os = std.os;
|
||||
const unicode = std.unicode;
|
||||
const windows = std.os.windows;
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
@ -156,41 +157,51 @@ pub fn windowsOpen(
|
||||
}
|
||||
|
||||
/// Caller must free result.
|
||||
pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 {
|
||||
pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 {
|
||||
// count bytes needed
|
||||
const bytes_needed = x: {
|
||||
var bytes_needed: usize = 1; // 1 for the final null byte
|
||||
const max_chars_needed = x: {
|
||||
var max_chars_needed: usize = 1; // 1 for the final null byte
|
||||
var it = env_map.iterator();
|
||||
while (it.next()) |pair| {
|
||||
// +1 for '='
|
||||
// +1 for null byte
|
||||
bytes_needed += pair.key.len + pair.value.len + 2;
|
||||
max_chars_needed += pair.key.len + pair.value.len + 2;
|
||||
}
|
||||
break :x bytes_needed;
|
||||
break :x max_chars_needed;
|
||||
};
|
||||
const result = try allocator.alloc(u8, bytes_needed);
|
||||
const result = try allocator.alloc(u16, max_chars_needed);
|
||||
errdefer allocator.free(result);
|
||||
|
||||
var it = env_map.iterator();
|
||||
var i: usize = 0;
|
||||
while (it.next()) |pair| {
|
||||
mem.copy(u8, result[i..], pair.key);
|
||||
i += pair.key.len;
|
||||
i += try unicode.utf8ToUtf16Le(result[i..], pair.key);
|
||||
result[i] = '=';
|
||||
i += 1;
|
||||
mem.copy(u8, result[i..], pair.value);
|
||||
i += pair.value.len;
|
||||
i += try unicode.utf8ToUtf16Le(result[i..], pair.value);
|
||||
result[i] = 0;
|
||||
i += 1;
|
||||
}
|
||||
result[i] = 0;
|
||||
return result;
|
||||
i += 1;
|
||||
return allocator.shrink(u16, result, i);
|
||||
}
|
||||
|
||||
pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {
|
||||
const padded_buff = try cstr.addNullByte(allocator, dll_path);
|
||||
defer allocator.free(padded_buff);
|
||||
return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;
|
||||
pub fn windowsLoadDllW(dll_path_w: [*]const u16) !windows.HMODULE {
|
||||
return windows.LoadLibraryW(dll_path_w) orelse {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound,
|
||||
else => return os.unexpectedErrorWindows(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn windowsLoadDll(dll_path: []const u8) !windows.HMODULE {
|
||||
const dll_path_w = try sliceToPrefixedFileW(dll_path);
|
||||
return windowsLoadDllW(&dll_path_w);
|
||||
}
|
||||
|
||||
pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
|
||||
@ -200,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
|
||||
test "InvalidDll" {
|
||||
if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
|
||||
|
||||
const DllName = "asdf.dll";
|
||||
const allocator = std.debug.global_allocator;
|
||||
const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
|
||||
assert(err == error.DllNotFound);
|
||||
const handle = os.windowsLoadDll("asdf.dll") catch |err| {
|
||||
assert(err == error.FileNotFound);
|
||||
return;
|
||||
};
|
||||
@panic("Expected error from function");
|
||||
}
|
||||
|
||||
pub fn windowsFindFirstFile(
|
||||
allocator: *mem.Allocator,
|
||||
dir_path: []const u8,
|
||||
find_file_data: *windows.WIN32_FIND_DATAA,
|
||||
find_file_data: *windows.WIN32_FIND_DATAW,
|
||||
) !windows.HANDLE {
|
||||
const wild_and_null = []u8{ '\\', '*', 0 };
|
||||
const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);
|
||||
defer allocator.free(path_with_wild_and_null);
|
||||
|
||||
mem.copy(u8, path_with_wild_and_null, dir_path);
|
||||
mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null);
|
||||
|
||||
const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data);
|
||||
const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{'\\', '*', 0});
|
||||
const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
|
||||
|
||||
if (handle == windows.INVALID_HANDLE_VALUE) {
|
||||
const err = windows.GetLastError();
|
||||
@ -235,8 +238,8 @@ pub fn windowsFindFirstFile(
|
||||
}
|
||||
|
||||
/// Returns `true` if there was another file, `false` otherwise.
|
||||
pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {
|
||||
if (windows.FindNextFileA(handle, find_file_data) == 0) {
|
||||
pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
|
||||
if (windows.FindNextFileW(handle, find_file_data) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.NO_MORE_FILES => false,
|
||||
@ -297,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
|
||||
}
|
||||
|
||||
pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
|
||||
return sliceToPrefixedSuffixedFileW(s, []u16{0});
|
||||
}
|
||||
|
||||
pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 {
|
||||
// TODO well defined copy elision
|
||||
var result: [PATH_MAX_WIDE + 1]u16 = undefined;
|
||||
var result: [PATH_MAX_WIDE + suffix.len]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 "\\?\"
|
||||
@ -306,11 +313,12 @@ 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|
|
||||
for (s) |byte| {
|
||||
switch (byte) {
|
||||
'/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
|
||||
else => {},
|
||||
};
|
||||
'/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
|
||||
const prefix = []u16{ '\\', '\\', '?', '\\' };
|
||||
mem.copy(u16, result[0..], prefix);
|
||||
@ -318,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
|
||||
};
|
||||
const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
|
||||
assert(end_index <= result.len);
|
||||
if (end_index == result.len) return error.NameTooLong;
|
||||
result[end_index] = 0;
|
||||
if (end_index + suffix.len > result.len) return error.NameTooLong;
|
||||
mem.copy(u16, result[end_index..], suffix);
|
||||
return result;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user