File System.
pub const has_executable_bit = switch (native_os) {
.windows, .wasi => false,
else => true,
}pub const MAX_PATH_BYTES = @compileError("deprecated; renamed to max_path_bytes")The maximum length of a file path that the operating system will accept.
Paths, including those returned from file system operations, may be longer
than this length, but such paths cannot be successfully passed back in
other file system operations. However, all path components returned by file
system operations are assumed to fit into a u8 array of this length.
The byte count includes room for a null sentinel byte.
[]u8 file paths are encoded as
WTF-8.[]u8 file paths are encoded as valid UTF-8.[]u8 file paths are opaque sequences of bytes with
no particular encoding.pub const max_path_bytes = switch (native_os) {
.linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten, .wasi => posix.PATH_MAX,
// Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes.
// If it would require 4 WTF-8 bytes, then there would be a surrogate
// pair in the WTF-16LE, and we (over)account 3 bytes for it that way.
// +1 for the null byte at the end, which can be encoded in 1 byte.
.windows => windows.PATH_MAX_WIDE * 3 + 1,
else => if (@hasDecl(root, "os") and @hasDecl(root.os, "PATH_MAX"))
root.os.PATH_MAX
else
@compileError("PATH_MAX not implemented for " ++ @tagName(native_os)),
}This represents the maximum size of a []u8 file name component that
the platform's common file systems support. File name components returned by file system
operations are likely to fit into a u8 array of this length, but
(depending on the platform) this assumption may not hold for every configuration.
The byte count does not include a null sentinel byte.
On Windows, []u8 file name components are encoded as WTF-8.
On WASI, file name components are encoded as valid UTF-8.
On other platforms, []u8 components are an opaque sequence of bytes with no particular encoding.
pub const max_name_bytes = switch (native_os) {
.linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => posix.NAME_MAX,
// Haiku's NAME_MAX includes the null terminator, so subtract one.
.haiku => posix.NAME_MAX - 1,
// Each WTF-16LE character may be expanded to 3 WTF-8 bytes.
// If it would require 4 WTF-8 bytes, then there would be a surrogate
// pair in the WTF-16LE, and we (over)account 3 bytes for it that way.
.windows => windows.NAME_MAX * 3,
// For WASI, the MAX_NAME will depend on the host OS, so it needs to be
// as large as the largest max_name_bytes (Windows) in order to work on any host OS.
// TODO determine if this is a reasonable approach
.wasi => windows.NAME_MAX * 3,
else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX"))
root.os.NAME_MAX
else
@compileError("NAME_MAX not implemented for " ++ @tagName(native_os)),
}This represents the maximum size of a []u8 file name component that
the platform's common file systems support. File name components returned by file system
operations are likely to fit into a u8 array of this length, but
(depending on the platform) this assumption may not hold for every configuration.
The byte count does not include a null sentinel byte.
On Windows, []u8 file name components are encoded as WTF-8.
On WASI, file name components are encoded as valid UTF-8.
On other platforms, []u8 components are an opaque sequence of bytes with no particular encoding.
pub const max_name_bytes = switch (native_os) {
.linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => posix.NAME_MAX,
// Haiku's NAME_MAX includes the null terminator, so subtract one.
.haiku => posix.NAME_MAX - 1,
// Each WTF-16LE character may be expanded to 3 WTF-8 bytes.
// If it would require 4 WTF-8 bytes, then there would be a surrogate
// pair in the WTF-16LE, and we (over)account 3 bytes for it that way.
.windows => windows.NAME_MAX * 3,
// For WASI, the MAX_NAME will depend on the host OS, so it needs to be
// as large as the largest max_name_bytes (Windows) in order to work on any host OS.
// TODO determine if this is a reasonable approach
.wasi => windows.NAME_MAX * 3,
else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX"))
root.os.NAME_MAX
else
@compileError("NAME_MAX not implemented for " ++ @tagName(native_os)),
}pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*Base64 encoder, replacing the standard +/ with -_ so that it can be used in a file name on any filesystem.
pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, null)Base64 decoder, replacing the standard +/ with -_ so that it can be used in a file name on any filesystem.
pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null)anyerror means the error set is known only at runtime.
pub const GetAppDataDirError = error{
OutOfMemory,
AppDataDirUnavailable,
}anyerror means the error set is known only at runtime.
In WASI, this error may occur when the file descriptor does not hold the required rights to open a new resource relative to it.
On Windows, antivirus software is enabled by default. It can be disabled, but Windows Update sometimes ignores the user's preference and re-enables it. When enabled, antivirus software on Windows intercepts file system operations and makes them significantly slower in addition to possibly failing with this error code.
Path contains characters that are disallowed by the underlying filesystem.
One of these three things:
The underlying filesystem does not support file locks
Either:
O.EXCL set to false.The file is too large to be opened. This error is unreachable for 64-bit targets, as well as when opening directories.
WASI-only; file paths must be valid UTF-8.
Windows-only; file paths provided by the user must be valid WTF-8. https://simonsapin.github.io/wtf-8/
The path refers to directory but the DIRECTORY flag was not provided.
The path exceeded max_path_bytes bytes.
On Windows, \\server or \\server\share was not found.
A new path cannot be created because the device has no room for the new file.
This error is only reachable when the CREAT flag is provided.
A component used as a directory in the path was not, in fact, a directory, or
DIRECTORY was specified and the path was not a directory.
The path already exists and the CREAT and EXCL flags were provided.
Insufficient kernel memory was available, or the named file is a FIFO and per-user hard limit on memory allocation for pipes has been reached.
The Operating System returned an undocumented error code.
This error is in theory not possible, but it would be better to handle this error than to invoke undefined behavior.
When this error code is observed, it usually means the Zig Standard Library needs a small patch to add the error code to the error set for the respective function.
On Windows, the volume does not contain a recognized file system. File system drivers might not be loaded, or the volume may be corrupt.
pub const OpenSelfExeError = posix.OpenError || SelfExePathError || posix.FlockErroranyerror means the error set is known only at runtime.
On Windows, antivirus software is enabled by default. It can be disabled, but Windows Update sometimes ignores the user's preference and re-enables it. When enabled, antivirus software on Windows intercepts file system operations and makes them significantly slower in addition to possibly failing with this error code.
On Windows, \\server or \\server\share was not found.
The Operating System returned an undocumented error code.
This error is in theory not possible, but it would be better to handle this error than to invoke undefined behavior.
When this error code is observed, it usually means the Zig Standard Library needs a small patch to add the error code to the error set for the respective function.
On Windows, the volume does not contain a recognized file system. File system drivers might not be loaded, or the volume may be corrupt.
pub const SelfExePathError = error{
FileNotFound,
AccessDenied,
NameTooLong,
NotSupported,
NotDir,
SymLinkLoop,
InputOutput,
FileTooBig,
IsDir,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
NoDevice,
SystemResources,
NoSpaceLeft,
FileSystem,
BadPathName,
DeviceBusy,
SharingViolation,
PipeBusy,
NotLink,
PathAlreadyExists,
/// On Windows, `\\server` or `\\server\share` was not found.
NetworkNotFound,
/// On Windows, antivirus software is enabled by default. It can be
/// disabled, but Windows Update sometimes ignores the user's preference
/// and re-enables it. When enabled, antivirus software on Windows
/// intercepts file system operations and makes them significantly slower
/// in addition to possibly failing with this error code.
AntivirusInterference,
/// On Windows, the volume does not contain a recognized file system. File
/// system drivers might not be loaded, or the volume may be corrupt.
UnrecognizedVolume,
} || posix.SysCtlErrorpub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8Return the canonicalized absolute pathname.
Expands all symbolic links and resolves references to ., .., and
extra / characters in pathname.
On Windows, pathname should be encoded as WTF-8.
On other platforms, pathname is an opaque sequence of bytes with no particular encoding.
The return value is a slice of out_buffer, but not necessarily from the beginning.
See also realpathZ and realpathW.
Calling this function is usually a bug.
pathname: []const u8out_buffer: *[max_path_bytes]u8pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
if (native_os == .windows) {
const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
return realpathW(pathname_w.span(), out_buffer);
} else if (native_os == .wasi and !builtin.link_libc) {
@compileError("WASI does not support os.realpath");
}
const pathname_c = try toPosixPath(pathname);
return realpathZ(&pathname_c, out_buffer);
}pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8Same as realpath except pathname is null-terminated.
Calling this function is usually a bug.
pathname: [*:0]const u8out_buffer: *[max_path_bytes]u8pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
if (native_os == .windows) {
const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
return realpathW(pathname_w.span(), out_buffer);
} else if (native_os == .wasi and !builtin.link_libc) {
return realpath(mem.sliceTo(pathname, 0), out_buffer);
}
if (!builtin.link_libc) {
const flags: O = switch (native_os) {
.linux => .{
.NONBLOCK = true,
.CLOEXEC = true,
.PATH = true,
},
else => .{
.NONBLOCK = true,
.CLOEXEC = true,
},
};
const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
error.FileLocksNotSupported => unreachable,
error.WouldBlock => unreachable,
error.FileBusy => unreachable, // not asking for write permissions
error.InvalidUtf8 => unreachable, // WASI-only
else => |e| return e,
};
defer close(fd);
return std.os.getFdPath(fd, out_buffer);
}
const result_path = std.c.realpath(pathname, out_buffer) orelse switch (@as(E, @enumFromInt(std.c._errno().*))) {
.SUCCESS => unreachable,
.INVAL => unreachable,
.BADF => unreachable,
.FAULT => unreachable,
.ACCES => return error.AccessDenied,
.NOENT => return error.FileNotFound,
.OPNOTSUPP => return error.NotSupported,
.NOTDIR => return error.NotDir,
.NAMETOOLONG => return error.NameTooLong,
.LOOP => return error.SymLinkLoop,
.IO => return error.InputOutput,
else => |err| return unexpectedErrno(err),
};
return mem.sliceTo(result_path, 0);
}pub fn realpathW(pathname: []const u16, out_buffer: *[max_path_bytes]u8) RealPathError![]u8Same as realpath except pathname is WTF16LE-encoded.
The result is encoded as WTF-8.
Calling this function is usually a bug.
pathname: []const u16out_buffer: *[max_path_bytes]u8pub fn realpathW(pathname: []const u16, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
const w = windows;
const dir = fs.cwd().fd;
const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE;
const creation = w.FILE_OPEN;
const h_file = blk: {
const res = w.OpenFile(pathname, .{
.dir = dir,
.access_mask = access_mask,
.share_access = share_access,
.creation = creation,
.filter = .any,
}) catch |err| switch (err) {
error.WouldBlock => unreachable,
else => |e| return e,
};
break :blk res;
};
defer w.CloseHandle(h_file);
return std.os.getFdPath(h_file, out_buffer);
}pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDirError![]u8Caller owns returned memory. TODO determine if we can remove the allocator requirement
test getAppDataDir {
if (native_os == .wasi) return error.SkipZigTest;
// We can't actually validate the result
const dir = getAppDataDir(std.testing.allocator, "zig") catch return;
defer std.testing.allocator.free(dir);
}pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
switch (native_os) {
.windows => {
const local_app_data_dir = std.process.getEnvVarOwned(allocator, "LOCALAPPDATA") catch |err| switch (err) {
error.OutOfMemory => |e| return e,
else => return error.AppDataDirUnavailable,
};
defer allocator.free(local_app_data_dir);
return fs.path.join(allocator, &[_][]const u8{ local_app_data_dir, appname });
},
.macos => {
const home_dir = posix.getenv("HOME") orelse {
// TODO look in /etc/passwd
return error.AppDataDirUnavailable;
};
return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname });
},
.linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => {
if (posix.getenv("XDG_DATA_HOME")) |xdg| {
if (xdg.len > 0) {
return fs.path.join(allocator, &[_][]const u8{ xdg, appname });
}
}
const home_dir = posix.getenv("HOME") orelse {
// TODO look in /etc/passwd
return error.AppDataDirUnavailable;
};
return fs.path.join(allocator, &[_][]const u8{ home_dir, ".local", "share", appname });
},
.haiku => {
var dir_path_buf: [std.fs.max_path_bytes]u8 = undefined;
const rc = std.c.find_directory(.B_USER_SETTINGS_DIRECTORY, -1, true, &dir_path_buf, dir_path_buf.len);
const settings_dir = try allocator.dupeZ(u8, mem.sliceTo(&dir_path_buf, 0));
defer allocator.free(settings_dir);
switch (rc) {
0 => return fs.path.join(allocator, &[_][]const u8{ settings_dir, appname }),
else => return error.AppDataDirUnavailable,
}
},
else => @compileError("Unsupported OS"),
}
}pub fn atomicSymLink(_: Allocator, existing_path: []const u8, new_path: []const u8) !voidDeprecated. Use cwd().atomicSymLink() instead.
pub fn updateFileAbsolute( source_path: []const u8, dest_path: []const u8, args: Dir.CopyFileOptions, ) !Dir.PrevStatusSame as Dir.updateFile, except asserts that both source_path and dest_path
are absolute. See Dir.updateFile for a function that operates on both
absolute and relative paths.
On Windows, both paths should be encoded as WTF-8.
On WASI, both paths should be encoded as valid UTF-8.
On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn updateFileAbsolute(
source_path: []const u8,
dest_path: []const u8,
args: Dir.CopyFileOptions,
) !Dir.PrevStatus {
assert(path.isAbsolute(source_path));
assert(path.isAbsolute(dest_path));
const my_cwd = cwd();
return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, args);
}pub fn copyFileAbsolute( source_path: []const u8, dest_path: []const u8, args: Dir.CopyFileOptions, ) !voidSame as Dir.copyFile, except asserts that both source_path and dest_path
are absolute. See Dir.copyFile for a function that operates on both
absolute and relative paths.
On Windows, both paths should be encoded as WTF-8.
On WASI, both paths should be encoded as valid UTF-8.
On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn copyFileAbsolute(
source_path: []const u8,
dest_path: []const u8,
args: Dir.CopyFileOptions,
) !void {
assert(path.isAbsolute(source_path));
assert(path.isAbsolute(dest_path));
const my_cwd = cwd();
return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args);
}pub fn makeDirAbsolute(absolute_path: []const u8) !voidCreate a new directory, based on an absolute path.
Asserts that the path is absolute. See Dir.makeDir for a function that operates
on both absolute and relative paths.
On Windows, absolute_path should be encoded as WTF-8.
On WASI, absolute_path should be encoded as valid UTF-8.
On other platforms, absolute_path is an opaque sequence of bytes with no particular encoding.
absolute_path: []const u8pub fn makeDirAbsolute(absolute_path: []const u8) !void {
assert(path.isAbsolute(absolute_path));
return posix.mkdir(absolute_path, Dir.default_mode);
}pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !voidSame as makeDirAbsolute except the parameter is null-terminated.
absolute_path_z: [*:0]const u8pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
assert(path.isAbsoluteZ(absolute_path_z));
return posix.mkdirZ(absolute_path_z, Dir.default_mode);
}pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !voidSame as makeDirAbsolute except the parameter is a null-terminated WTF-16 LE-encoded string.
absolute_path_w: [*:0]const u16pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
assert(path.isAbsoluteWindowsW(absolute_path_w));
return posix.mkdirW(mem.span(absolute_path_w), Dir.default_mode);
}pub fn deleteDirAbsolute(dir_path: []const u8) !voidSame as Dir.deleteDir except the path is absolute.
On Windows, dir_path should be encoded as WTF-8.
On WASI, dir_path should be encoded as valid UTF-8.
On other platforms, dir_path is an opaque sequence of bytes with no particular encoding.
dir_path: []const u8pub fn deleteDirAbsolute(dir_path: []const u8) !void {
assert(path.isAbsolute(dir_path));
return posix.rmdir(dir_path);
}pub fn deleteDirAbsoluteZ(dir_path: [*:0]const u8) !voidSame as deleteDirAbsolute except the path parameter is null-terminated.
dir_path: [*:0]const u8pub fn deleteDirAbsoluteZ(dir_path: [*:0]const u8) !void {
assert(path.isAbsoluteZ(dir_path));
return posix.rmdirZ(dir_path);
}pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !voidSame as deleteDirAbsolute except the path parameter is WTF-16 and target OS is assumed Windows.
dir_path: [*:0]const u16pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !voidSame as Dir.rename except the paths are absolute.
On Windows, both paths should be encoded as WTF-8.
On WASI, both paths should be encoded as valid UTF-8.
On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
old_path: []const u8new_path: []const u8pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void {
assert(path.isAbsolute(old_path));
assert(path.isAbsolute(new_path));
return posix.rename(old_path, new_path);
}pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !voidSame as renameAbsolute except the path parameters are null-terminated.
old_path: [*:0]const u8new_path: [*:0]const u8pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void {
assert(path.isAbsoluteZ(old_path));
assert(path.isAbsoluteZ(new_path));
return posix.renameZ(old_path, new_path);
}pub fn renameAbsoluteW(old_path: [*:0]const u16, new_path: [*:0]const u16) !voidSame as renameAbsolute except the path parameters are WTF-16 and target OS is assumed Windows.
old_path: [*:0]const u16new_path: [*:0]const u16pub fn renameAbsoluteW(old_path: [*:0]const u16, new_path: [*:0]const u16) !void {
assert(path.isAbsoluteWindowsW(old_path));
assert(path.isAbsoluteWindowsW(new_path));
return posix.renameW(old_path, new_path);
}Same as Dir.rename, except new_sub_path is relative to new_dir
pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_sub_path_z: [*:0]const u8) !voidSame as rename except the parameters are null-terminated.
pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_path_w: []const u16) !voidSame as rename except the parameters are WTF16LE, NT prefixed.
This function is Windows-only.
pub fn cwd() DirReturns a handle to the current working directory. It is not opened with iteration capability.
Closing the returned Dir is checked illegal behavior. Iterating over the result is illegal behavior.
On POSIX targets, this function is comptime-callable.
pub fn openDirAbsolute(absolute_path: []const u8, flags: Dir.OpenOptions) File.OpenError!DirOpens a directory at the given path. The directory is a system resource that remains
open until close is called on the result.
See openDirAbsoluteZ for a function that accepts a null-terminated path.
Asserts that the path parameter has no null bytes.
On Windows, absolute_path should be encoded as WTF-8.
On WASI, absolute_path should be encoded as valid UTF-8.
On other platforms, absolute_path is an opaque sequence of bytes with no particular encoding.
absolute_path: []const u8flags: Dir.OpenOptionspub fn openDirAbsolute(absolute_path: []const u8, flags: Dir.OpenOptions) File.OpenError!Dir {
assert(path.isAbsolute(absolute_path));
return cwd().openDir(absolute_path, flags);
}pub fn openDirAbsoluteZ(absolute_path_c: [*:0]const u8, flags: Dir.OpenOptions) File.OpenError!DirSame as openDirAbsolute but the path parameter is null-terminated.
absolute_path_c: [*:0]const u8flags: Dir.OpenOptionspub fn openDirAbsoluteZ(absolute_path_c: [*:0]const u8, flags: Dir.OpenOptions) File.OpenError!Dir {
assert(path.isAbsoluteZ(absolute_path_c));
return cwd().openDirZ(absolute_path_c, flags);
}pub fn openDirAbsoluteW(absolute_path_c: [*:0]const u16, flags: Dir.OpenOptions) File.OpenError!DirSame as openDirAbsolute but the path parameter is null-terminated.
absolute_path_c: [*:0]const u16flags: Dir.OpenOptionspub fn openDirAbsoluteW(absolute_path_c: [*:0]const u16, flags: Dir.OpenOptions) File.OpenError!Dir {
assert(path.isAbsoluteWindowsW(absolute_path_c));
return cwd().openDirW(absolute_path_c, flags);
}Opens a file for reading or writing, without attempting to create a new file, based on an absolute path.
Call File.close to release the resource.
Asserts that the path is absolute. See Dir.openFile for a function that
operates on both absolute and relative paths.
Asserts that the path parameter has no null bytes. See openFileAbsoluteZ for a function
that accepts a null-terminated path.
On Windows, absolute_path should be encoded as WTF-8.
On WASI, absolute_path should be encoded as valid UTF-8.
On other platforms, absolute_path is an opaque sequence of bytes with no particular encoding.
Same as openFileAbsolute but the path parameter is null-terminated.
Same as openFileAbsolute but the path parameter is WTF-16-encoded.
pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!voidTest accessing path.
Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function.
For example, instead of testing if a file exists and then opening it, just
open it and handle the error for file not found.
See accessAbsoluteZ for a function that accepts a null-terminated path.
On Windows, absolute_path should be encoded as WTF-8.
On WASI, absolute_path should be encoded as valid UTF-8.
On other platforms, absolute_path is an opaque sequence of bytes with no particular encoding.
pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!void {
assert(path.isAbsolute(absolute_path));
try cwd().access(absolute_path, flags);
}pub fn accessAbsoluteZ(absolute_path: [*:0]const u8, flags: File.OpenFlags) Dir.AccessError!voidSame as accessAbsolute but the path parameter is null-terminated.
pub fn accessAbsoluteZ(absolute_path: [*:0]const u8, flags: File.OpenFlags) Dir.AccessError!void {
assert(path.isAbsoluteZ(absolute_path));
try cwd().accessZ(absolute_path, flags);
}pub fn accessAbsoluteW(absolute_path: [*:0]const u16, flags: File.OpenFlags) Dir.AccessError!voidSame as accessAbsolute but the path parameter is WTF-16 encoded.
pub fn accessAbsoluteW(absolute_path: [*:0]const u16, flags: File.OpenFlags) Dir.AccessError!void {
assert(path.isAbsoluteWindowsW(absolute_path));
try cwd().accessW(absolute_path, flags);
}pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!FileCreates, opens, or overwrites a file with write access, based on an absolute path.
Call File.close to release the resource.
Asserts that the path is absolute. See Dir.createFile for a function that
operates on both absolute and relative paths.
Asserts that the path parameter has no null bytes. See createFileAbsoluteC for a function
that accepts a null-terminated path.
On Windows, absolute_path should be encoded as WTF-8.
On WASI, absolute_path should be encoded as valid UTF-8.
On other platforms, absolute_path is an opaque sequence of bytes with no particular encoding.
absolute_path: []const u8flags: File.CreateFlagspub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
assert(path.isAbsolute(absolute_path));
return cwd().createFile(absolute_path, flags);
}pub fn createFileAbsoluteZ(absolute_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!FileSame as createFileAbsolute but the path parameter is null-terminated.
absolute_path_c: [*:0]const u8flags: File.CreateFlagspub fn createFileAbsoluteZ(absolute_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
assert(path.isAbsoluteZ(absolute_path_c));
return cwd().createFileZ(absolute_path_c, flags);
}pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!FileSame as createFileAbsolute but the path parameter is WTF-16 encoded.
absolute_path_w: [*:0]const u16flags: File.CreateFlagspub fn deleteFileAbsolute(absolute_path: []const u8) Dir.DeleteFileError!voidDelete a file name and possibly the file it refers to, based on an absolute path.
Asserts that the path is absolute. See Dir.deleteFile for a function that
operates on both absolute and relative paths.
Asserts that the path parameter has no null bytes.
On Windows, absolute_path should be encoded as WTF-8.
On WASI, absolute_path should be encoded as valid UTF-8.
On other platforms, absolute_path is an opaque sequence of bytes with no particular encoding.
absolute_path: []const u8pub fn deleteFileAbsolute(absolute_path: []const u8) Dir.DeleteFileError!void {
assert(path.isAbsolute(absolute_path));
return cwd().deleteFile(absolute_path);
}pub fn deleteFileAbsoluteZ(absolute_path_c: [*:0]const u8) Dir.DeleteFileError!voidSame as deleteFileAbsolute except the parameter is null-terminated.
absolute_path_c: [*:0]const u8pub fn deleteFileAbsoluteZ(absolute_path_c: [*:0]const u8) Dir.DeleteFileError!void {
assert(path.isAbsoluteZ(absolute_path_c));
return cwd().deleteFileZ(absolute_path_c);
}pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) Dir.DeleteFileError!voidSame as deleteFileAbsolute except the parameter is WTF-16 encoded.
absolute_path_w: [*:0]const u16pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) Dir.DeleteFileError!void {
assert(path.isAbsoluteWindowsW(absolute_path_w));
return cwd().deleteFileW(mem.span(absolute_path_w));
}pub fn deleteTreeAbsolute(absolute_path: []const u8) !voidRemoves a symlink, file, or directory.
This is equivalent to Dir.deleteTree with the base directory.
Asserts that the path is absolute. See Dir.deleteTree for a function that
operates on both absolute and relative paths.
Asserts that the path parameter has no null bytes.
On Windows, absolute_path should be encoded as WTF-8.
On WASI, absolute_path should be encoded as valid UTF-8.
On other platforms, absolute_path is an opaque sequence of bytes with no particular encoding.
absolute_path: []const u8pub fn deleteTreeAbsolute(absolute_path: []const u8) !void {
assert(path.isAbsolute(absolute_path));
const dirname = path.dirname(absolute_path) orelse return error{
/// Attempt to remove the root file system path.
/// This error is unreachable if `absolute_path` is relative.
CannotDeleteRootDirectory,
}.CannotDeleteRootDirectory;
var dir = try cwd().openDir(dirname, .{});
defer dir.close();
return dir.deleteTree(path.basename(absolute_path));
}pub fn readLinkAbsolute(pathname: []const u8, buffer: *[max_path_bytes]u8) ![]u8Same as Dir.readLink, except it asserts the path is absolute.
On Windows, pathname should be encoded as WTF-8.
On WASI, pathname should be encoded as valid UTF-8.
On other platforms, pathname is an opaque sequence of bytes with no particular encoding.
pathname: []const u8buffer: *[max_path_bytes]u8pub fn readLinkAbsolute(pathname: []const u8, buffer: *[max_path_bytes]u8) ![]u8 {
assert(path.isAbsolute(pathname));
return posix.readlink(pathname, buffer);
}pub fn readlinkAbsoluteW(pathname_w: [*:0]const u16, buffer: *[max_path_bytes]u8) ![]u8Windows-only. Same as readlinkW, except the path parameter is null-terminated, WTF16
encoded.
pathname_w: [*:0]const u16buffer: *[max_path_bytes]u8pub fn readlinkAbsoluteW(pathname_w: [*:0]const u16, buffer: *[max_path_bytes]u8) ![]u8 {
assert(path.isAbsoluteWindowsW(pathname_w));
return posix.readlinkW(mem.span(pathname_w), buffer);
}pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[max_path_bytes]u8) ![]u8Same as readLink, except the path parameter is null-terminated.
pathname_c: [*:0]const u8buffer: *[max_path_bytes]u8pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[max_path_bytes]u8) ![]u8 {
assert(path.isAbsoluteZ(pathname_c));
return posix.readlinkZ(pathname_c, buffer);
}pub fn symLinkAbsolute( target_path: []const u8, sym_link_path: []const u8, flags: Dir.SymLinkFlags, ) !voidCreates a symbolic link named sym_link_path which contains the string target_path.
A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
one; the latter case is known as a dangling link.
If sym_link_path exists, it will not be overwritten.
See also symLinkAbsoluteZ and symLinkAbsoluteW.
On Windows, both paths should be encoded as WTF-8.
On WASI, both paths should be encoded as valid UTF-8.
On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn symLinkAbsolute(
target_path: []const u8,
sym_link_path: []const u8,
flags: Dir.SymLinkFlags,
) !void {
assert(path.isAbsolute(target_path));
assert(path.isAbsolute(sym_link_path));
if (native_os == .windows) {
const target_path_w = try windows.sliceToPrefixedFileW(null, target_path);
const sym_link_path_w = try windows.sliceToPrefixedFileW(null, sym_link_path);
return windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
}
return posix.symlink(target_path, sym_link_path);
}pub fn symLinkAbsoluteW( target_path_w: [*:0]const u16, sym_link_path_w: [*:0]const u16, flags: Dir.SymLinkFlags, ) !voidWindows-only. Same as symLinkAbsolute except the parameters are null-terminated, WTF16 LE encoded.
Note that this function will by default try creating a symbolic link to a file. If you would
like to create a symbolic link to a directory, specify this with SymLinkFlags{ .is_directory = true }.
See also symLinkAbsolute, symLinkAbsoluteZ.
pub fn symLinkAbsoluteW(
target_path_w: [*:0]const u16,
sym_link_path_w: [*:0]const u16,
flags: Dir.SymLinkFlags,
) !void {
assert(path.isAbsoluteWindowsW(target_path_w));
assert(path.isAbsoluteWindowsW(sym_link_path_w));
return windows.CreateSymbolicLink(null, mem.span(sym_link_path_w), mem.span(target_path_w), flags.is_directory);
}pub fn symLinkAbsoluteZ( target_path_c: [*:0]const u8, sym_link_path_c: [*:0]const u8, flags: Dir.SymLinkFlags, ) !voidSame as symLinkAbsolute except the parameters are null-terminated pointers.
See also symLinkAbsolute.
pub fn symLinkAbsoluteZ(
target_path_c: [*:0]const u8,
sym_link_path_c: [*:0]const u8,
flags: Dir.SymLinkFlags,
) !void {
assert(path.isAbsoluteZ(target_path_c));
assert(path.isAbsoluteZ(sym_link_path_c));
if (native_os == .windows) {
const target_path_w = try windows.cStrToPrefixedFileW(null, target_path_c);
const sym_link_path_w = try windows.cStrToPrefixedFileW(null, sym_link_path_c);
return windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
}
return posix.symlinkZ(target_path_c, sym_link_path_c);
}pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!Filepub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
if (native_os == .linux) {
return openFileAbsoluteZ("/proc/self/exe", flags);
}
if (native_os == .windows) {
// If ImagePathName is a symlink, then it will contain the path of the symlink,
// not the path that the symlink points to. However, because we are opening
// the file, we can let the openFileW call follow the symlink for us.
const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
return cwd().openFileW(prefixed_path_w.span(), flags);
}
// Use of max_path_bytes here is valid as the resulting path is immediately
// opened with no modification.
var buf: [max_path_bytes]u8 = undefined;
const self_exe_path = try selfExePath(&buf);
buf[self_exe_path.len] = 0;
return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags);
}pub fn selfExePathAlloc(allocator: Allocator) ![]u8selfExePath except allocates the result on the heap.
Caller owns returned memory.
allocator: Allocatorpub fn selfExePathAlloc(allocator: Allocator) ![]u8 {
// Use of max_path_bytes here is justified as, at least on one tested Linux
// system, readlink will completely fail to return a result larger than
// PATH_MAX even if given a sufficiently large buffer. This makes it
// fundamentally impossible to get the selfExePath of a program running in
// a very deeply nested directory chain in this way.
// TODO(#4812): Investigate other systems and whether it is possible to get
// this path by trying larger and larger buffers until one succeeds.
var buf: [max_path_bytes]u8 = undefined;
return allocator.dupe(u8, try selfExePath(&buf));
}pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8Get the path to the current executable. Follows symlinks. If you only need the directory, use selfExeDirPath. If you only want an open file handle, use openSelfExe. This function may return an error if the current executable was deleted after spawning. Returned value is a slice of out_buffer. On Windows, the result is encoded as WTF-8. On other platforms, the result is an opaque sequence of bytes with no particular encoding.
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).
TODO make the return type of this a null terminated pointer
out_buffer: []u8pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
if (is_darwin) {
// Note that _NSGetExecutablePath() will return "a path" to
// the executable not a "real path" to the executable.
var symlink_path_buf: [max_path_bytes:0]u8 = undefined;
var u32_len: u32 = max_path_bytes + 1; // include the sentinel
const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &u32_len);
if (rc != 0) return error.NameTooLong;
var real_path_buf: [max_path_bytes]u8 = undefined;
const real_path = std.posix.realpathZ(&symlink_path_buf, &real_path_buf) catch |err| switch (err) {
error.InvalidWtf8 => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
else => |e| return e,
};
if (real_path.len > out_buffer.len) return error.NameTooLong;
const result = out_buffer[0..real_path.len];
@memcpy(result, real_path);
return result;
}
switch (native_os) {
.linux => return posix.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) {
error.InvalidUtf8 => unreachable, // WASI-only
error.InvalidWtf8 => unreachable, // Windows-only
error.UnsupportedReparsePointType => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
else => |e| return e,
},
.solaris, .illumos => return posix.readlinkZ("/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
error.InvalidUtf8 => unreachable, // WASI-only
error.InvalidWtf8 => unreachable, // Windows-only
error.UnsupportedReparsePointType => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
else => |e| return e,
},
.freebsd, .dragonfly => {
var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
var out_len: usize = out_buffer.len;
try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
// TODO could this slice from 0 to out_len instead?
return mem.sliceTo(out_buffer, 0);
},
.netbsd => {
var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
var out_len: usize = out_buffer.len;
try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
// TODO could this slice from 0 to out_len instead?
return mem.sliceTo(out_buffer, 0);
},
.openbsd, .haiku => {
// OpenBSD doesn't support getting the path of a running process, so try to guess it
if (std.os.argv.len == 0)
return error.FileNotFound;
const argv0 = mem.span(std.os.argv[0]);
if (mem.indexOf(u8, argv0, "/") != null) {
// argv[0] is a path (relative or absolute): use realpath(3) directly
var real_path_buf: [max_path_bytes]u8 = undefined;
const real_path = posix.realpathZ(std.os.argv[0], &real_path_buf) catch |err| switch (err) {
error.InvalidWtf8 => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
else => |e| return e,
};
if (real_path.len > out_buffer.len)
return error.NameTooLong;
const result = out_buffer[0..real_path.len];
@memcpy(result, real_path);
return result;
} else if (argv0.len != 0) {
// argv[0] is not empty (and not a path): search it inside PATH
const PATH = posix.getenvZ("PATH") orelse return error.FileNotFound;
var path_it = mem.tokenizeScalar(u8, PATH, path.delimiter);
while (path_it.next()) |a_path| {
var resolved_path_buf: [max_path_bytes - 1:0]u8 = undefined;
const resolved_path = std.fmt.bufPrintZ(&resolved_path_buf, "{s}/{s}", .{
a_path,
std.os.argv[0],
}) catch continue;
var real_path_buf: [max_path_bytes]u8 = undefined;
if (posix.realpathZ(resolved_path, &real_path_buf)) |real_path| {
// found a file, and hope it is the right file
if (real_path.len > out_buffer.len)
return error.NameTooLong;
const result = out_buffer[0..real_path.len];
@memcpy(result, real_path);
return result;
} else |_| continue;
}
}
return error.FileNotFound;
},
.windows => {
const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
// If ImagePathName is a symlink, then it will contain the path of the
// symlink, not the path that the symlink points to. We want the path
// that the symlink points to, though, so we need to get the realpath.
const pathname_w = try windows.wToPrefixedFileW(null, image_path_name);
return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) {
error.InvalidWtf8 => unreachable,
else => |e| return e,
};
},
else => @compileError("std.fs.selfExePath not supported for this target"),
}
}pub fn selfExeDirPathAlloc(allocator: Allocator) ![]u8selfExeDirPath except allocates the result on the heap.
Caller owns returned memory.
allocator: Allocatorpub fn selfExeDirPathAlloc(allocator: Allocator) ![]u8 {
// Use of max_path_bytes here is justified as, at least on one tested Linux
// system, readlink will completely fail to return a result larger than
// PATH_MAX even if given a sufficiently large buffer. This makes it
// fundamentally impossible to get the selfExeDirPath of a program running
// in a very deeply nested directory chain in this way.
// TODO(#4812): Investigate other systems and whether it is possible to get
// this path by trying larger and larger buffers until one succeeds.
var buf: [max_path_bytes]u8 = undefined;
return allocator.dupe(u8, try selfExeDirPath(&buf));
}pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8Get the directory path that contains the current executable. Returned value is a slice of out_buffer. On Windows, the result is encoded as WTF-8. On other platforms, the result is an opaque sequence of bytes with no particular encoding.
out_buffer: []u8pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 {
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).?;
}pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8realpath, except caller must free the returned memory.
On Windows, the result is encoded as WTF-8.
On other platforms, the result is an opaque sequence of bytes with no particular encoding.
See also Dir.realpath.
allocator: Allocatorpathname: []const u8pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 {
// Use of max_path_bytes here is valid as the realpath function does not
// have a variant that takes an arbitrary-size buffer.
// TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
// NULL out parameter (GNU's canonicalize_file_name) to handle overelong
// paths. musl supports passing NULL but restricts the output to PATH_MAX
// anyway.
var buf: [max_path_bytes]u8 = undefined;
return allocator.dupe(u8, try posix.realpath(pathname, &buf));
}//! File System.
const std = @import("std.zig");
const builtin = @import("builtin");
const root = @import("root");
const mem = std.mem;
const base64 = std.base64;
const crypto = std.crypto;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const native_os = builtin.os.tag;
const posix = std.posix;
const windows = std.os.windows;
const is_darwin = native_os.isDarwin();
pub const AtomicFile = @import("fs/AtomicFile.zig");
pub const Dir = @import("fs/Dir.zig");
pub const File = @import("fs/File.zig");
pub const path = @import("fs/path.zig");
pub const has_executable_bit = switch (native_os) {
.windows, .wasi => false,
else => true,
};
pub const wasi = @import("fs/wasi.zig");
// TODO audit these APIs with respect to Dir and absolute paths
pub const realpath = posix.realpath;
pub const realpathZ = posix.realpathZ;
pub const realpathW = posix.realpathW;
pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;
pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;
pub const MAX_PATH_BYTES = @compileError("deprecated; renamed to max_path_bytes");
/// The maximum length of a file path that the operating system will accept.
///
/// Paths, including those returned from file system operations, may be longer
/// than this length, but such paths cannot be successfully passed back in
/// other file system operations. However, all path components returned by file
/// system operations are assumed to fit into a `u8` array of this length.
///
/// The byte count includes room for a null sentinel byte.
///
/// * On Windows, `[]u8` file paths are encoded as
/// [WTF-8](https://simonsapin.github.io/wtf-8/).
/// * On WASI, `[]u8` file paths are encoded as valid UTF-8.
/// * On other platforms, `[]u8` file paths are opaque sequences of bytes with
/// no particular encoding.
pub const max_path_bytes = switch (native_os) {
.linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten, .wasi => posix.PATH_MAX,
// Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes.
// If it would require 4 WTF-8 bytes, then there would be a surrogate
// pair in the WTF-16LE, and we (over)account 3 bytes for it that way.
// +1 for the null byte at the end, which can be encoded in 1 byte.
.windows => windows.PATH_MAX_WIDE * 3 + 1,
else => if (@hasDecl(root, "os") and @hasDecl(root.os, "PATH_MAX"))
root.os.PATH_MAX
else
@compileError("PATH_MAX not implemented for " ++ @tagName(native_os)),
};
/// This represents the maximum size of a `[]u8` file name component that
/// the platform's common file systems support. File name components returned by file system
/// operations are likely to fit into a `u8` array of this length, but
/// (depending on the platform) this assumption may not hold for every configuration.
/// The byte count does not include a null sentinel byte.
/// On Windows, `[]u8` file name components are encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, file name components are encoded as valid UTF-8.
/// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding.
pub const max_name_bytes = switch (native_os) {
.linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => posix.NAME_MAX,
// Haiku's NAME_MAX includes the null terminator, so subtract one.
.haiku => posix.NAME_MAX - 1,
// Each WTF-16LE character may be expanded to 3 WTF-8 bytes.
// If it would require 4 WTF-8 bytes, then there would be a surrogate
// pair in the WTF-16LE, and we (over)account 3 bytes for it that way.
.windows => windows.NAME_MAX * 3,
// For WASI, the MAX_NAME will depend on the host OS, so it needs to be
// as large as the largest max_name_bytes (Windows) in order to work on any host OS.
// TODO determine if this is a reasonable approach
.wasi => windows.NAME_MAX * 3,
else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX"))
root.os.NAME_MAX
else
@compileError("NAME_MAX not implemented for " ++ @tagName(native_os)),
};
/// Deprecated: use `max_name_bytes`
pub const MAX_NAME_BYTES = max_name_bytes;
pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
/// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, null);
/// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null);
/// Deprecated. Use `cwd().atomicSymLink()` instead.
pub fn atomicSymLink(_: Allocator, existing_path: []const u8, new_path: []const u8) !void {
try cwd().atomicSymLink(existing_path, new_path, .{});
}
/// Same as `Dir.updateFile`, except asserts that both `source_path` and `dest_path`
/// are absolute. See `Dir.updateFile` for a function that operates on both
/// absolute and relative paths.
/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, both paths should be encoded as valid UTF-8.
/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn updateFileAbsolute(
source_path: []const u8,
dest_path: []const u8,
args: Dir.CopyFileOptions,
) !Dir.PrevStatus {
assert(path.isAbsolute(source_path));
assert(path.isAbsolute(dest_path));
const my_cwd = cwd();
return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, args);
}
/// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path`
/// are absolute. See `Dir.copyFile` for a function that operates on both
/// absolute and relative paths.
/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, both paths should be encoded as valid UTF-8.
/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn copyFileAbsolute(
source_path: []const u8,
dest_path: []const u8,
args: Dir.CopyFileOptions,
) !void {
assert(path.isAbsolute(source_path));
assert(path.isAbsolute(dest_path));
const my_cwd = cwd();
return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args);
}
/// Create a new directory, based on an absolute path.
/// Asserts that the path is absolute. See `Dir.makeDir` for a function that operates
/// on both absolute and relative paths.
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
pub fn makeDirAbsolute(absolute_path: []const u8) !void {
assert(path.isAbsolute(absolute_path));
return posix.mkdir(absolute_path, Dir.default_mode);
}
/// Same as `makeDirAbsolute` except the parameter is null-terminated.
pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
assert(path.isAbsoluteZ(absolute_path_z));
return posix.mkdirZ(absolute_path_z, Dir.default_mode);
}
/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 LE-encoded string.
pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
assert(path.isAbsoluteWindowsW(absolute_path_w));
return posix.mkdirW(mem.span(absolute_path_w), Dir.default_mode);
}
/// Same as `Dir.deleteDir` except the path is absolute.
/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, `dir_path` should be encoded as valid UTF-8.
/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
pub fn deleteDirAbsolute(dir_path: []const u8) !void {
assert(path.isAbsolute(dir_path));
return posix.rmdir(dir_path);
}
/// Same as `deleteDirAbsolute` except the path parameter is null-terminated.
pub fn deleteDirAbsoluteZ(dir_path: [*:0]const u8) !void {
assert(path.isAbsoluteZ(dir_path));
return posix.rmdirZ(dir_path);
}
/// Same as `deleteDirAbsolute` except the path parameter is WTF-16 and target OS is assumed Windows.
pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {
assert(path.isAbsoluteWindowsW(dir_path));
return posix.rmdirW(mem.span(dir_path));
}
/// Same as `Dir.rename` except the paths are absolute.
/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, both paths should be encoded as valid UTF-8.
/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void {
assert(path.isAbsolute(old_path));
assert(path.isAbsolute(new_path));
return posix.rename(old_path, new_path);
}
/// Same as `renameAbsolute` except the path parameters are null-terminated.
pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void {
assert(path.isAbsoluteZ(old_path));
assert(path.isAbsoluteZ(new_path));
return posix.renameZ(old_path, new_path);
}
/// Same as `renameAbsolute` except the path parameters are WTF-16 and target OS is assumed Windows.
pub fn renameAbsoluteW(old_path: [*:0]const u16, new_path: [*:0]const u16) !void {
assert(path.isAbsoluteWindowsW(old_path));
assert(path.isAbsoluteWindowsW(new_path));
return posix.renameW(old_path, new_path);
}
/// Same as `Dir.rename`, except `new_sub_path` is relative to `new_dir`
pub fn rename(old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) !void {
return posix.renameat(old_dir.fd, old_sub_path, new_dir.fd, new_sub_path);
}
/// Same as `rename` except the parameters are null-terminated.
pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_sub_path_z: [*:0]const u8) !void {
return posix.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z);
}
/// Same as `rename` except the parameters are WTF16LE, NT prefixed.
/// This function is Windows-only.
pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_path_w: []const u16) !void {
return posix.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w, windows.TRUE);
}
/// Returns a handle to the current working directory. It is not opened with iteration capability.
/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
/// On POSIX targets, this function is comptime-callable.
pub fn cwd() Dir {
if (native_os == .windows) {
return .{ .fd = windows.peb().ProcessParameters.CurrentDirectory.Handle };
} else if (native_os == .wasi) {
return .{ .fd = std.options.wasiCwd() };
} else {
return .{ .fd = posix.AT.FDCWD };
}
}
pub fn defaultWasiCwd() std.os.wasi.fd_t {
// Expect the first preopen to be current working directory.
return 3;
}
/// Opens a directory at the given path. The directory is a system resource that remains
/// open until `close` is called on the result.
/// See `openDirAbsoluteZ` for a function that accepts a null-terminated path.
///
/// Asserts that the path parameter has no null bytes.
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
pub fn openDirAbsolute(absolute_path: []const u8, flags: Dir.OpenOptions) File.OpenError!Dir {
assert(path.isAbsolute(absolute_path));
return cwd().openDir(absolute_path, flags);
}
/// Same as `openDirAbsolute` but the path parameter is null-terminated.
pub fn openDirAbsoluteZ(absolute_path_c: [*:0]const u8, flags: Dir.OpenOptions) File.OpenError!Dir {
assert(path.isAbsoluteZ(absolute_path_c));
return cwd().openDirZ(absolute_path_c, flags);
}
/// Same as `openDirAbsolute` but the path parameter is null-terminated.
pub fn openDirAbsoluteW(absolute_path_c: [*:0]const u16, flags: Dir.OpenOptions) File.OpenError!Dir {
assert(path.isAbsoluteWindowsW(absolute_path_c));
return cwd().openDirW(absolute_path_c, flags);
}
/// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path.
/// Call `File.close` to release the resource.
/// Asserts that the path is absolute. See `Dir.openFile` for a function that
/// operates on both absolute and relative paths.
/// Asserts that the path parameter has no null bytes. See `openFileAbsoluteZ` for a function
/// that accepts a null-terminated path.
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
assert(path.isAbsolute(absolute_path));
return cwd().openFile(absolute_path, flags);
}
/// Same as `openFileAbsolute` but the path parameter is null-terminated.
pub fn openFileAbsoluteZ(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
assert(path.isAbsoluteZ(absolute_path_c));
return cwd().openFileZ(absolute_path_c, flags);
}
/// Same as `openFileAbsolute` but the path parameter is WTF-16-encoded.
pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File {
assert(path.isAbsoluteWindowsWTF16(absolute_path_w));
return cwd().openFileW(absolute_path_w, flags);
}
/// Test accessing `path`.
/// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function.
/// For example, instead of testing if a file exists and then opening it, just
/// open it and handle the error for file not found.
/// See `accessAbsoluteZ` for a function that accepts a null-terminated path.
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!void {
assert(path.isAbsolute(absolute_path));
try cwd().access(absolute_path, flags);
}
/// Same as `accessAbsolute` but the path parameter is null-terminated.
pub fn accessAbsoluteZ(absolute_path: [*:0]const u8, flags: File.OpenFlags) Dir.AccessError!void {
assert(path.isAbsoluteZ(absolute_path));
try cwd().accessZ(absolute_path, flags);
}
/// Same as `accessAbsolute` but the path parameter is WTF-16 encoded.
pub fn accessAbsoluteW(absolute_path: [*:0]const u16, flags: File.OpenFlags) Dir.AccessError!void {
assert(path.isAbsoluteWindowsW(absolute_path));
try cwd().accessW(absolute_path, flags);
}
/// Creates, opens, or overwrites a file with write access, based on an absolute path.
/// Call `File.close` to release the resource.
/// Asserts that the path is absolute. See `Dir.createFile` for a function that
/// operates on both absolute and relative paths.
/// Asserts that the path parameter has no null bytes. See `createFileAbsoluteC` for a function
/// that accepts a null-terminated path.
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
assert(path.isAbsolute(absolute_path));
return cwd().createFile(absolute_path, flags);
}
/// Same as `createFileAbsolute` but the path parameter is null-terminated.
pub fn createFileAbsoluteZ(absolute_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
assert(path.isAbsoluteZ(absolute_path_c));
return cwd().createFileZ(absolute_path_c, flags);
}
/// Same as `createFileAbsolute` but the path parameter is WTF-16 encoded.
pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File {
assert(path.isAbsoluteWindowsW(absolute_path_w));
return cwd().createFileW(mem.span(absolute_path_w), flags);
}
/// Delete a file name and possibly the file it refers to, based on an absolute path.
/// Asserts that the path is absolute. See `Dir.deleteFile` for a function that
/// operates on both absolute and relative paths.
/// Asserts that the path parameter has no null bytes.
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
pub fn deleteFileAbsolute(absolute_path: []const u8) Dir.DeleteFileError!void {
assert(path.isAbsolute(absolute_path));
return cwd().deleteFile(absolute_path);
}
/// Same as `deleteFileAbsolute` except the parameter is null-terminated.
pub fn deleteFileAbsoluteZ(absolute_path_c: [*:0]const u8) Dir.DeleteFileError!void {
assert(path.isAbsoluteZ(absolute_path_c));
return cwd().deleteFileZ(absolute_path_c);
}
/// Same as `deleteFileAbsolute` except the parameter is WTF-16 encoded.
pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) Dir.DeleteFileError!void {
assert(path.isAbsoluteWindowsW(absolute_path_w));
return cwd().deleteFileW(mem.span(absolute_path_w));
}
/// Removes a symlink, file, or directory.
/// This is equivalent to `Dir.deleteTree` with the base directory.
/// Asserts that the path is absolute. See `Dir.deleteTree` for a function that
/// operates on both absolute and relative paths.
/// Asserts that the path parameter has no null bytes.
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
pub fn deleteTreeAbsolute(absolute_path: []const u8) !void {
assert(path.isAbsolute(absolute_path));
const dirname = path.dirname(absolute_path) orelse return error{
/// Attempt to remove the root file system path.
/// This error is unreachable if `absolute_path` is relative.
CannotDeleteRootDirectory,
}.CannotDeleteRootDirectory;
var dir = try cwd().openDir(dirname, .{});
defer dir.close();
return dir.deleteTree(path.basename(absolute_path));
}
/// Same as `Dir.readLink`, except it asserts the path is absolute.
/// On Windows, `pathname` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, `pathname` should be encoded as valid UTF-8.
/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
pub fn readLinkAbsolute(pathname: []const u8, buffer: *[max_path_bytes]u8) ![]u8 {
assert(path.isAbsolute(pathname));
return posix.readlink(pathname, buffer);
}
/// Windows-only. Same as `readlinkW`, except the path parameter is null-terminated, WTF16
/// encoded.
pub fn readlinkAbsoluteW(pathname_w: [*:0]const u16, buffer: *[max_path_bytes]u8) ![]u8 {
assert(path.isAbsoluteWindowsW(pathname_w));
return posix.readlinkW(mem.span(pathname_w), buffer);
}
/// Same as `readLink`, except the path parameter is null-terminated.
pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[max_path_bytes]u8) ![]u8 {
assert(path.isAbsoluteZ(pathname_c));
return posix.readlinkZ(pathname_c, buffer);
}
/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
/// one; the latter case is known as a dangling link.
/// If `sym_link_path` exists, it will not be overwritten.
/// See also `symLinkAbsoluteZ` and `symLinkAbsoluteW`.
/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On WASI, both paths should be encoded as valid UTF-8.
/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn symLinkAbsolute(
target_path: []const u8,
sym_link_path: []const u8,
flags: Dir.SymLinkFlags,
) !void {
assert(path.isAbsolute(target_path));
assert(path.isAbsolute(sym_link_path));
if (native_os == .windows) {
const target_path_w = try windows.sliceToPrefixedFileW(null, target_path);
const sym_link_path_w = try windows.sliceToPrefixedFileW(null, sym_link_path);
return windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
}
return posix.symlink(target_path, sym_link_path);
}
/// Windows-only. Same as `symLinkAbsolute` except the parameters are null-terminated, WTF16 LE encoded.
/// Note that this function will by default try creating a symbolic link to a file. If you would
/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.
/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.
pub fn symLinkAbsoluteW(
target_path_w: [*:0]const u16,
sym_link_path_w: [*:0]const u16,
flags: Dir.SymLinkFlags,
) !void {
assert(path.isAbsoluteWindowsW(target_path_w));
assert(path.isAbsoluteWindowsW(sym_link_path_w));
return windows.CreateSymbolicLink(null, mem.span(sym_link_path_w), mem.span(target_path_w), flags.is_directory);
}
/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.
/// See also `symLinkAbsolute`.
pub fn symLinkAbsoluteZ(
target_path_c: [*:0]const u8,
sym_link_path_c: [*:0]const u8,
flags: Dir.SymLinkFlags,
) !void {
assert(path.isAbsoluteZ(target_path_c));
assert(path.isAbsoluteZ(sym_link_path_c));
if (native_os == .windows) {
const target_path_w = try windows.cStrToPrefixedFileW(null, target_path_c);
const sym_link_path_w = try windows.cStrToPrefixedFileW(null, sym_link_path_c);
return windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
}
return posix.symlinkZ(target_path_c, sym_link_path_c);
}
pub const OpenSelfExeError = posix.OpenError || SelfExePathError || posix.FlockError;
pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
if (native_os == .linux) {
return openFileAbsoluteZ("/proc/self/exe", flags);
}
if (native_os == .windows) {
// If ImagePathName is a symlink, then it will contain the path of the symlink,
// not the path that the symlink points to. However, because we are opening
// the file, we can let the openFileW call follow the symlink for us.
const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
return cwd().openFileW(prefixed_path_w.span(), flags);
}
// Use of max_path_bytes here is valid as the resulting path is immediately
// opened with no modification.
var buf: [max_path_bytes]u8 = undefined;
const self_exe_path = try selfExePath(&buf);
buf[self_exe_path.len] = 0;
return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags);
}
// This is `posix.ReadLinkError || posix.RealPathError` with impossible errors excluded
pub const SelfExePathError = error{
FileNotFound,
AccessDenied,
NameTooLong,
NotSupported,
NotDir,
SymLinkLoop,
InputOutput,
FileTooBig,
IsDir,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
NoDevice,
SystemResources,
NoSpaceLeft,
FileSystem,
BadPathName,
DeviceBusy,
SharingViolation,
PipeBusy,
NotLink,
PathAlreadyExists,
/// On Windows, `\\server` or `\\server\share` was not found.
NetworkNotFound,
/// On Windows, antivirus software is enabled by default. It can be
/// disabled, but Windows Update sometimes ignores the user's preference
/// and re-enables it. When enabled, antivirus software on Windows
/// intercepts file system operations and makes them significantly slower
/// in addition to possibly failing with this error code.
AntivirusInterference,
/// On Windows, the volume does not contain a recognized file system. File
/// system drivers might not be loaded, or the volume may be corrupt.
UnrecognizedVolume,
} || posix.SysCtlError;
/// `selfExePath` except allocates the result on the heap.
/// Caller owns returned memory.
pub fn selfExePathAlloc(allocator: Allocator) ![]u8 {
// Use of max_path_bytes here is justified as, at least on one tested Linux
// system, readlink will completely fail to return a result larger than
// PATH_MAX even if given a sufficiently large buffer. This makes it
// fundamentally impossible to get the selfExePath of a program running in
// a very deeply nested directory chain in this way.
// TODO(#4812): Investigate other systems and whether it is possible to get
// this path by trying larger and larger buffers until one succeeds.
var buf: [max_path_bytes]u8 = undefined;
return allocator.dupe(u8, try selfExePath(&buf));
}
/// Get the path to the current executable. Follows symlinks.
/// If you only need the directory, use selfExeDirPath.
/// If you only want an open file handle, use openSelfExe.
/// This function may return an error if the current executable
/// was deleted after spawning.
/// Returned value is a slice of out_buffer.
/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
///
/// 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)`.
/// TODO make the return type of this a null terminated pointer
pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
if (is_darwin) {
// Note that _NSGetExecutablePath() will return "a path" to
// the executable not a "real path" to the executable.
var symlink_path_buf: [max_path_bytes:0]u8 = undefined;
var u32_len: u32 = max_path_bytes + 1; // include the sentinel
const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &u32_len);
if (rc != 0) return error.NameTooLong;
var real_path_buf: [max_path_bytes]u8 = undefined;
const real_path = std.posix.realpathZ(&symlink_path_buf, &real_path_buf) catch |err| switch (err) {
error.InvalidWtf8 => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
else => |e| return e,
};
if (real_path.len > out_buffer.len) return error.NameTooLong;
const result = out_buffer[0..real_path.len];
@memcpy(result, real_path);
return result;
}
switch (native_os) {
.linux => return posix.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) {
error.InvalidUtf8 => unreachable, // WASI-only
error.InvalidWtf8 => unreachable, // Windows-only
error.UnsupportedReparsePointType => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
else => |e| return e,
},
.solaris, .illumos => return posix.readlinkZ("/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
error.InvalidUtf8 => unreachable, // WASI-only
error.InvalidWtf8 => unreachable, // Windows-only
error.UnsupportedReparsePointType => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
else => |e| return e,
},
.freebsd, .dragonfly => {
var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
var out_len: usize = out_buffer.len;
try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
// TODO could this slice from 0 to out_len instead?
return mem.sliceTo(out_buffer, 0);
},
.netbsd => {
var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
var out_len: usize = out_buffer.len;
try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
// TODO could this slice from 0 to out_len instead?
return mem.sliceTo(out_buffer, 0);
},
.openbsd, .haiku => {
// OpenBSD doesn't support getting the path of a running process, so try to guess it
if (std.os.argv.len == 0)
return error.FileNotFound;
const argv0 = mem.span(std.os.argv[0]);
if (mem.indexOf(u8, argv0, "/") != null) {
// argv[0] is a path (relative or absolute): use realpath(3) directly
var real_path_buf: [max_path_bytes]u8 = undefined;
const real_path = posix.realpathZ(std.os.argv[0], &real_path_buf) catch |err| switch (err) {
error.InvalidWtf8 => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
else => |e| return e,
};
if (real_path.len > out_buffer.len)
return error.NameTooLong;
const result = out_buffer[0..real_path.len];
@memcpy(result, real_path);
return result;
} else if (argv0.len != 0) {
// argv[0] is not empty (and not a path): search it inside PATH
const PATH = posix.getenvZ("PATH") orelse return error.FileNotFound;
var path_it = mem.tokenizeScalar(u8, PATH, path.delimiter);
while (path_it.next()) |a_path| {
var resolved_path_buf: [max_path_bytes - 1:0]u8 = undefined;
const resolved_path = std.fmt.bufPrintZ(&resolved_path_buf, "{s}/{s}", .{
a_path,
std.os.argv[0],
}) catch continue;
var real_path_buf: [max_path_bytes]u8 = undefined;
if (posix.realpathZ(resolved_path, &real_path_buf)) |real_path| {
// found a file, and hope it is the right file
if (real_path.len > out_buffer.len)
return error.NameTooLong;
const result = out_buffer[0..real_path.len];
@memcpy(result, real_path);
return result;
} else |_| continue;
}
}
return error.FileNotFound;
},
.windows => {
const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
// If ImagePathName is a symlink, then it will contain the path of the
// symlink, not the path that the symlink points to. We want the path
// that the symlink points to, though, so we need to get the realpath.
const pathname_w = try windows.wToPrefixedFileW(null, image_path_name);
return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) {
error.InvalidWtf8 => unreachable,
else => |e| return e,
};
},
else => @compileError("std.fs.selfExePath not supported for this target"),
}
}
/// `selfExeDirPath` except allocates the result on the heap.
/// Caller owns returned memory.
pub fn selfExeDirPathAlloc(allocator: Allocator) ![]u8 {
// Use of max_path_bytes here is justified as, at least on one tested Linux
// system, readlink will completely fail to return a result larger than
// PATH_MAX even if given a sufficiently large buffer. This makes it
// fundamentally impossible to get the selfExeDirPath of a program running
// in a very deeply nested directory chain in this way.
// TODO(#4812): Investigate other systems and whether it is possible to get
// this path by trying larger and larger buffers until one succeeds.
var buf: [max_path_bytes]u8 = undefined;
return allocator.dupe(u8, try selfExeDirPath(&buf));
}
/// Get the directory path that contains the current executable.
/// Returned value is a slice of out_buffer.
/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 {
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).?;
}
/// `realpath`, except caller must free the returned memory.
/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
/// See also `Dir.realpath`.
pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 {
// Use of max_path_bytes here is valid as the realpath function does not
// have a variant that takes an arbitrary-size buffer.
// TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
// NULL out parameter (GNU's canonicalize_file_name) to handle overelong
// paths. musl supports passing NULL but restricts the output to PATH_MAX
// anyway.
var buf: [max_path_bytes]u8 = undefined;
return allocator.dupe(u8, try posix.realpath(pathname, &buf));
}
test {
if (native_os != .wasi) {
_ = &makeDirAbsolute;
_ = &makeDirAbsoluteZ;
_ = ©FileAbsolute;
_ = &updateFileAbsolute;
}
_ = &AtomicFile;
_ = &Dir;
_ = &File;
_ = &path;
_ = @import("fs/test.zig");
_ = @import("fs/get_app_data_dir.zig");
}