*WIP* std.os assumes comptime-known max path size
this allows us to remove the requirement of allocators for a lot of functions See #1392
This commit is contained in:
parent
302936309a
commit
bda5539e9d
@ -340,7 +340,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {
|
||||
}
|
||||
}
|
||||
|
||||
fn printLineFromFile(allocator: *mem.Allocator, out_stream: var, line_info: *const LineInfo) !void {
|
||||
fn printLineFromFile(out_stream: var, line_info: *const LineInfo) !void {
|
||||
var f = try os.File.openRead(line_info.file_name);
|
||||
defer f.close();
|
||||
// TODO fstat and make sure that the file has the correct size
|
||||
|
@ -16,7 +16,7 @@ test "write a file, read it, then delete it" {
|
||||
prng.random.bytes(data[0..]);
|
||||
const tmp_file_name = "temp_test_file.txt";
|
||||
{
|
||||
var file = try os.File.openWrite(allocator, tmp_file_name);
|
||||
var file = try os.File.openWrite(tmp_file_name);
|
||||
defer file.close();
|
||||
|
||||
var file_out_stream = io.FileOutStream.init(&file);
|
||||
@ -63,7 +63,7 @@ test "BufferOutStream" {
|
||||
}
|
||||
|
||||
test "SliceInStream" {
|
||||
const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7 };
|
||||
const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7 };
|
||||
var ss = io.SliceInStream.init(bytes);
|
||||
|
||||
var dest: [4]u8 = undefined;
|
||||
@ -81,7 +81,7 @@ test "SliceInStream" {
|
||||
}
|
||||
|
||||
test "PeekStream" {
|
||||
const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
var ss = io.SliceInStream.init(bytes);
|
||||
var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream);
|
||||
|
||||
|
@ -27,7 +27,6 @@ pub const File = struct {
|
||||
|
||||
pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
|
||||
|
||||
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
||||
/// Call close to clean up.
|
||||
pub fn openRead(path: []const u8) OpenError!File {
|
||||
if (is_posix) {
|
||||
@ -49,15 +48,14 @@ pub const File = struct {
|
||||
}
|
||||
|
||||
/// Calls `openWriteMode` with os.File.default_mode for the mode.
|
||||
pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
|
||||
return openWriteMode(allocator, path, os.File.default_mode);
|
||||
pub fn openWrite(path: []const u8) OpenError!File {
|
||||
return openWriteMode(path, os.File.default_mode);
|
||||
}
|
||||
|
||||
/// If the path does not exist it will be created.
|
||||
/// If a file already exists in the destination it will be truncated.
|
||||
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
||||
/// Call close to clean up.
|
||||
pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
|
||||
pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {
|
||||
if (is_posix) {
|
||||
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
|
||||
const fd = try os.posixOpen(path, flags, file_mode);
|
||||
@ -78,16 +76,14 @@ pub const File = struct {
|
||||
|
||||
/// If the path does not exist it will be created.
|
||||
/// If a file already exists in the destination this returns OpenError.PathAlreadyExists
|
||||
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
||||
/// Call close to clean up.
|
||||
pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
|
||||
pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
|
||||
if (is_posix) {
|
||||
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
|
||||
const fd = try os.posixOpen(allocator, path, flags, file_mode);
|
||||
const fd = try os.posixOpen(path, flags, file_mode);
|
||||
return openHandle(fd);
|
||||
} else if (is_windows) {
|
||||
const handle = try os.windowsOpen(
|
||||
allocator,
|
||||
path,
|
||||
windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
|
||||
@ -117,12 +113,13 @@ pub const File = struct {
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
|
||||
const path_with_null = try std.cstr.addNullByte(allocator, path);
|
||||
defer allocator.free(path_with_null);
|
||||
|
||||
pub fn accessC(path: [*]const u8) AccessError!void {
|
||||
if (is_windows) {
|
||||
// this needs to convert to UTF-16LE and call accessW
|
||||
@compileError("TODO support windows");
|
||||
}
|
||||
if (is_posix) {
|
||||
const result = posix.access(path_with_null.ptr, posix.F_OK);
|
||||
const result = posix.access(path, posix.F_OK);
|
||||
const err = posix.getErrno(result);
|
||||
switch (err) {
|
||||
0 => return,
|
||||
@ -141,7 +138,7 @@ pub const File = struct {
|
||||
else => return os.unexpectedErrorPosix(err),
|
||||
}
|
||||
} else if (is_windows) {
|
||||
if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
|
||||
if (os.windows.GetFileAttributesA(path) != os.windows.INVALID_FILE_ATTRIBUTES) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -158,6 +155,21 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn access(path: []const u8) AccessError!void {
|
||||
if (is_windows) {
|
||||
// this needs to convert to UTF-16LE and call accessW
|
||||
@compileError("TODO support windows");
|
||||
}
|
||||
if (is_posix) {
|
||||
var path_with_null: [posix.PATH_MAX]u8 = undefined;
|
||||
if (path.len >= posix.PATH_MAX) return error.NameTooLong;
|
||||
mem.copy(u8, path_with_null[0..], path);
|
||||
path_with_null[path.len] = 0;
|
||||
return accessC(&path_with_null);
|
||||
}
|
||||
@compileError("TODO implement access for this OS");
|
||||
}
|
||||
|
||||
/// Upon success, the stream is in an uninitialized state. To continue using it,
|
||||
/// you must use the open() function.
|
||||
pub fn close(self: *File) void {
|
||||
|
346
std/os/index.zig
346
std/os/index.zig
@ -39,11 +39,14 @@ pub const File = @import("file.zig").File;
|
||||
pub const time = @import("time.zig");
|
||||
|
||||
pub const page_size = 4 * 1024;
|
||||
pub const PATH_MAX = switch (builtin.os) {
|
||||
Os.linux => linux.PATH_MAX,
|
||||
Os.macosx, Os.ios => darwin.PATH_MAX,
|
||||
pub const MAX_PATH_BYTES = switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => posix.PATH_MAX,
|
||||
// Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
|
||||
// If it would require 4 UTF-8 bytes, then there would be a surrogate
|
||||
// pair in the UTF-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.
|
||||
Os.windows => 32767 * 3 + 1,
|
||||
else => @compileError("Unsupported OS"),
|
||||
// https://msdn.microsoft.com/en-us/library/930f87yf.aspx
|
||||
};
|
||||
|
||||
pub const UserInfo = @import("get_user_id.zig").UserInfo;
|
||||
@ -423,7 +426,6 @@ pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, off
|
||||
}
|
||||
|
||||
pub const PosixOpenError = error{
|
||||
OutOfMemory,
|
||||
AccessDenied,
|
||||
FileTooBig,
|
||||
IsDir,
|
||||
@ -444,12 +446,10 @@ pub const PosixOpenError = error{
|
||||
/// Calls POSIX open, keeps trying if it gets interrupted, and translates
|
||||
/// the return value into zig errors.
|
||||
pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
|
||||
var path_with_null: [PATH_MAX]u8 = undefined;
|
||||
if (file_path.len > PATH_MAX - 1)
|
||||
return error.NameTooLong;
|
||||
mem.copy(u8, path_with_null[0..PATH_MAX - 1], file_path);
|
||||
path_with_null[file_path.len] = '\x00';
|
||||
|
||||
var path_with_null: [posix.PATH_MAX]u8 = undefined;
|
||||
if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
|
||||
mem.copy(u8, path_with_null[0..], file_path);
|
||||
path_with_null[file_path.len] = 0;
|
||||
return posixOpenC(&path_with_null, flags, perm);
|
||||
}
|
||||
|
||||
@ -728,43 +728,35 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned
|
||||
}
|
||||
|
||||
/// Caller must free the returned memory.
|
||||
pub fn getCwd(allocator: *Allocator) ![]u8 {
|
||||
pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
|
||||
var buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
return mem.dupe(allocator, u8, try getCwd(&buf));
|
||||
}
|
||||
|
||||
pub const GetCwdError = error{Unexpected};
|
||||
|
||||
/// The result is a slice of out_buffer.
|
||||
pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {
|
||||
switch (builtin.os) {
|
||||
Os.windows => {
|
||||
var buf = try allocator.alloc(u8, 256);
|
||||
errdefer allocator.free(buf);
|
||||
|
||||
while (true) {
|
||||
const result = windows.GetCurrentDirectoryA(@intCast(windows.WORD, buf.len), buf.ptr);
|
||||
|
||||
if (result == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
else => unexpectedErrorWindows(err),
|
||||
};
|
||||
var utf16le_buf: [windows_util.PATH_MAX_UTF16]u16 = undefined;
|
||||
const result = windows.GetCurrentDirectoryW(utf16le_buf.len, &utf16le_buf);
|
||||
if (result == 0) {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
else => return unexpectedErrorWindows(err),
|
||||
}
|
||||
|
||||
if (result > buf.len) {
|
||||
buf = try allocator.realloc(u8, buf, result);
|
||||
continue;
|
||||
}
|
||||
|
||||
return allocator.shrink(u8, buf, result);
|
||||
}
|
||||
assert(result <= buf.len);
|
||||
const utf16le_slice = utf16le_buf[0..result];
|
||||
return std.unicode.utf16leToUtf8(out_buffer, utf16le_buf);
|
||||
},
|
||||
else => {
|
||||
var buf = try allocator.alloc(u8, 1024);
|
||||
errdefer allocator.free(buf);
|
||||
while (true) {
|
||||
const err = posix.getErrno(posix.getcwd(buf.ptr, buf.len));
|
||||
if (err == posix.ERANGE) {
|
||||
buf = try allocator.realloc(u8, buf, buf.len * 2);
|
||||
continue;
|
||||
} else if (err > 0) {
|
||||
return unexpectedErrorPosix(err);
|
||||
}
|
||||
|
||||
return allocator.shrink(u8, buf, cstr.len(buf.ptr));
|
||||
const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len));
|
||||
switch (err) {
|
||||
0 => return cstr.toSlice(out_buffer),
|
||||
posix.ERANGE => unreachable,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -899,56 +891,45 @@ pub const DeleteFileError = error{
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn deleteFile(allocator: *Allocator, file_path: []const u8) DeleteFileError!void {
|
||||
pub fn deleteFile(file_path: []const u8) DeleteFileError!void {
|
||||
if (builtin.os == Os.windows) {
|
||||
return deleteFileWindows(allocator, file_path);
|
||||
return deleteFileWindows(file_path);
|
||||
} else {
|
||||
return deleteFilePosix(allocator, file_path);
|
||||
return deleteFilePosix(file_path);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deleteFileWindows(allocator: *Allocator, file_path: []const u8) !void {
|
||||
const buf = try allocator.alloc(u8, file_path.len + 1);
|
||||
defer allocator.free(buf);
|
||||
pub fn deleteFileWindows(file_path: []const u8) !void {
|
||||
@compileError("TODO rewrite with DeleteFileW and no allocator");
|
||||
}
|
||||
|
||||
mem.copy(u8, buf, file_path);
|
||||
buf[file_path.len] = 0;
|
||||
|
||||
if (windows.DeleteFileA(buf.ptr) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
|
||||
windows.ERROR.ACCESS_DENIED => error.AccessDenied,
|
||||
windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
|
||||
else => unexpectedErrorWindows(err),
|
||||
};
|
||||
pub fn deleteFilePosixC(file_path: [*]const u8) !void {
|
||||
const err = posix.getErrno(posix.unlink(file_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.EIO => return error.FileSystem,
|
||||
posix.EISDIR => return error.IsDir,
|
||||
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.EROFS => return error.ReadOnlyFileSystem,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deleteFilePosix(allocator: *Allocator, file_path: []const u8) !void {
|
||||
const buf = try allocator.alloc(u8, file_path.len + 1);
|
||||
defer allocator.free(buf);
|
||||
|
||||
mem.copy(u8, buf, file_path);
|
||||
buf[file_path.len] = 0;
|
||||
|
||||
const err = posix.getErrno(posix.unlink(buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EBUSY => error.FileBusy,
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EIO => error.FileSystem,
|
||||
posix.EISDIR => error.IsDir,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.EROFS => error.ReadOnlyFileSystem,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
}
|
||||
pub fn deleteFilePosix(file_path: []const u8) !void {
|
||||
var path_with_null: [posix.PATH_MAX]u8 = undefined;
|
||||
if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
|
||||
mem.copy(u8, path_with_null[0..], file_path);
|
||||
path_with_null[file_path.len] = 0;
|
||||
return deleteFilePosixC(&path_with_null);
|
||||
}
|
||||
|
||||
/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
|
||||
@ -956,6 +937,7 @@ pub fn deleteFilePosix(allocator: *Allocator, file_path: []const u8) !void {
|
||||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
/// in the same directory as dest_path.
|
||||
/// Destination file will have the same mode as the source file.
|
||||
/// TODO investigate if this can work with no allocator
|
||||
pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []const u8) !void {
|
||||
var in_file = try os.File.openRead(source_path);
|
||||
defer in_file.close();
|
||||
@ -978,6 +960,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con
|
||||
/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
|
||||
/// merged and readily available,
|
||||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
/// TODO investigate if this can work with no allocator
|
||||
pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
|
||||
var in_file = try os.File.openRead(source_path);
|
||||
defer in_file.close();
|
||||
@ -996,6 +979,7 @@ pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: [
|
||||
}
|
||||
|
||||
pub const AtomicFile = struct {
|
||||
/// TODO investigate if we can make this work with no allocator
|
||||
allocator: *Allocator,
|
||||
file: os.File,
|
||||
tmp_path: []u8,
|
||||
@ -1023,7 +1007,7 @@ pub const AtomicFile = struct {
|
||||
try getRandomBytes(rand_buf[0..]);
|
||||
b64_fs_encoder.encode(tmp_path[dirname_component_len..], rand_buf);
|
||||
|
||||
const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
|
||||
const file = os.File.openWriteNoClobber(tmp_path, mode) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
// TODO zig should figure out that this error set does not include PathAlreadyExists since
|
||||
// it is handled in the above switch
|
||||
@ -1059,56 +1043,59 @@ pub const AtomicFile = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn rename(allocator: *Allocator, old_path: []const u8, new_path: []const u8) !void {
|
||||
const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2);
|
||||
defer allocator.free(full_buf);
|
||||
|
||||
const old_buf = full_buf;
|
||||
mem.copy(u8, old_buf, old_path);
|
||||
old_buf[old_path.len] = 0;
|
||||
|
||||
const new_buf = full_buf[old_path.len + 1 ..];
|
||||
mem.copy(u8, new_buf, new_path);
|
||||
new_buf[new_path.len] = 0;
|
||||
|
||||
pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void {
|
||||
if (is_windows) {
|
||||
const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
|
||||
if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
else => unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
@compileError("TODO implement for windows");
|
||||
} else {
|
||||
const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EBUSY => error.FileBusy,
|
||||
posix.EDQUOT => error.DiskQuota,
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EISDIR => error.IsDir,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.EMLINK => error.LinkQuotaExceeded,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOSPC => error.NoSpaceLeft,
|
||||
posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists,
|
||||
posix.EROFS => error.ReadOnlyFileSystem,
|
||||
posix.EXDEV => error.RenameAcrossMountPoints,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
const err = posix.getErrno(posix.rename(old_path, new_path));
|
||||
switch (err) {
|
||||
0 => return,
|
||||
posix.EACCES => return error.AccessDenied,
|
||||
posix.EPERM => return error.AccessDenied,
|
||||
posix.EBUSY => return error.FileBusy,
|
||||
posix.EDQUOT => return error.DiskQuota,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EISDIR => return error.IsDir,
|
||||
posix.ELOOP => return error.SymLinkLoop,
|
||||
posix.EMLINK => return error.LinkQuotaExceeded,
|
||||
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.EEXIST => return error.PathAlreadyExists,
|
||||
posix.ENOTEMPTY => return error.PathAlreadyExists,
|
||||
posix.EROFS => return error.ReadOnlyFileSystem,
|
||||
posix.EXDEV => return error.RenameAcrossMountPoints,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn makeDir(allocator: *Allocator, dir_path: []const u8) !void {
|
||||
pub fn rename(old_path: []const u8, new_path: []const u8) !void {
|
||||
if (is_windows) {
|
||||
return makeDirWindows(allocator, dir_path);
|
||||
@compileError("TODO rewrite with MoveFileExW and no allocator");
|
||||
} else {
|
||||
return makeDirPosix(allocator, dir_path);
|
||||
var old_path_with_null: [posix.PATH_MAX]u8 = undefined;
|
||||
if (old_path.len >= posix.PATH_MAX) return error.NameTooLong;
|
||||
mem.copy(u8, old_path_with_null[0..], old_path);
|
||||
old_path_with_null[old_path.len] = 0;
|
||||
|
||||
var new_path_with_null: [posix.PATH_MAX]u8 = undefined;
|
||||
if (new_path.len >= posix.PATH_MAX) return error.NameTooLong;
|
||||
mem.copy(u8, new_path_with_null[0..], new_path);
|
||||
new_path_with_null[new_path.len] = 0;
|
||||
|
||||
return renameC(&old_path_with_null, &new_path_with_null);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn makeDir(dir_path: []const u8) !void {
|
||||
if (is_windows) {
|
||||
return makeDirWindows(dir_path);
|
||||
} else {
|
||||
return makeDirPosix(dir_path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1126,30 +1113,35 @@ pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn makeDirPosix(allocator: *Allocator, dir_path: []const u8) !void {
|
||||
const path_buf = try cstr.addNullByte(allocator, dir_path);
|
||||
defer allocator.free(path_buf);
|
||||
|
||||
pub fn makeDirPosixC(dir_path: [*]const u8) !void {
|
||||
const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EDQUOT => error.DiskQuota,
|
||||
posix.EEXIST => error.PathAlreadyExists,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.EMLINK => error.LinkQuotaExceeded,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOSPC => error.NoSpaceLeft,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
posix.EROFS => error.ReadOnlyFileSystem,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
switch (err) {
|
||||
0 => return,
|
||||
posix.EACCES => return error.AccessDenied,
|
||||
posix.EPERM => return error.AccessDenied,
|
||||
posix.EDQUOT => return error.DiskQuota,
|
||||
posix.EEXIST => return error.PathAlreadyExists,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.ELOOP => return error.SymLinkLoop,
|
||||
posix.EMLINK => return error.LinkQuotaExceeded,
|
||||
posix.ENAMETOOLONG => return error.NameTooLong,
|
||||
posix.ENOENT => return error.FileNotFound,
|
||||
posix.ENOMEM => return error.SystemResources,
|
||||
posix.ENOSPC => return error.NoSpaceLeft,
|
||||
posix.ENOTDIR => return error.NotDir,
|
||||
posix.EROFS => return error.ReadOnlyFileSystem,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn makeDirPosix(dir_path: []const u8) !void {
|
||||
var path_with_null: [posix.PATH_MAX]u8 = undefined;
|
||||
if (dir_path.len >= posix.PATH_MAX) return error.NameTooLong;
|
||||
mem.copy(u8, path_with_null[0..], dir_path);
|
||||
path_with_null[dir_path.len] = 0;
|
||||
return makeDirPosixC(&path_with_null);
|
||||
}
|
||||
|
||||
/// Calls makeDir recursively to make an entire path. Returns success if the path
|
||||
/// already exists and is a directory.
|
||||
pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
|
||||
@ -1409,6 +1401,7 @@ pub const Dir = struct {
|
||||
},
|
||||
Os.macosx, Os.ios => Handle{
|
||||
.fd = try posixOpen(
|
||||
allocator,
|
||||
dir_path,
|
||||
posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
|
||||
0,
|
||||
@ -1420,6 +1413,7 @@ pub const Dir = struct {
|
||||
},
|
||||
Os.linux => Handle{
|
||||
.fd = try posixOpen(
|
||||
allocator,
|
||||
dir_path,
|
||||
posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
|
||||
0,
|
||||
@ -1616,39 +1610,35 @@ pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void {
|
||||
}
|
||||
|
||||
/// Read value of a symbolic link.
|
||||
pub fn readLink(allocator: *Allocator, file_path: []const u8) ![]u8 {
|
||||
var path_with_null: [PATH_MAX]u8 = undefined;
|
||||
if (file_path.len > PATH_MAX - 1)
|
||||
return error.NameTooLong;
|
||||
mem.copy(u8, path_with_null[0..PATH_MAX - 1], file_path);
|
||||
path_with_null[file_path.len] = '\x00';
|
||||
|
||||
var result_buf = try allocator.alloc(u8, 1024);
|
||||
errdefer allocator.free(result_buf);
|
||||
while (true) {
|
||||
const ret_val = posix.readlink(&path_with_null, result_buf.ptr, result_buf.len);
|
||||
const err = posix.getErrno(ret_val);
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES => error.AccessDenied,
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EIO => error.FileSystem,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
}
|
||||
if (ret_val == result_buf.len) {
|
||||
result_buf = try allocator.realloc(u8, result_buf, result_buf.len * 2);
|
||||
continue;
|
||||
}
|
||||
return allocator.shrink(u8, result_buf, ret_val);
|
||||
/// The return value is a slice of out_buffer.
|
||||
pub fn readLinkC(pathname: [*]const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {
|
||||
const rc = posix.readlink(pathname, out_buffer, out_buffer.len);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return out_buffer[0..rc],
|
||||
posix.EACCES => error.AccessDenied,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EIO => return error.FileSystem,
|
||||
posix.ELOOP => return error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX
|
||||
posix.ENOENT => return error.FileNotFound,
|
||||
posix.ENOMEM => return error.SystemResources,
|
||||
posix.ENOTDIR => return error.NotDir,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read value of a symbolic link.
|
||||
/// The return value is a slice of out_buffer.
|
||||
pub fn readLink(file_path: []const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {
|
||||
var path_with_null: [posix.PATH_MAX]u8 = undefined;
|
||||
if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
|
||||
mem.copy(u8, path_with_null[0..], file_path);
|
||||
path_with_null[file_path.len] = 0;
|
||||
return readLinkC(&path_with_null, out_buffer);
|
||||
}
|
||||
|
||||
pub fn posix_setuid(uid: u32) !void {
|
||||
const err = posix.getErrno(posix.setuid(uid));
|
||||
if (err == 0) return;
|
||||
@ -2035,13 +2025,13 @@ pub fn openSelfExe() !os.File {
|
||||
const proc_file_path = "/proc/self/exe";
|
||||
var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined;
|
||||
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
|
||||
return os.File.openRead(proc_file_path);
|
||||
return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
|
||||
},
|
||||
Os.macosx, Os.ios => {
|
||||
var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
|
||||
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
|
||||
const self_exe_path = try selfExePath(&fixed_allocator.allocator);
|
||||
return os.File.openRead(self_exe_path);
|
||||
return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
|
@ -573,7 +573,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
|
||||
result_index += 1;
|
||||
}
|
||||
|
||||
return result[0..result_index];
|
||||
return allocator.shrink(u8, result, result_index);
|
||||
}
|
||||
|
||||
test "os.path.resolve" {
|
||||
@ -1077,6 +1077,7 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
|
||||
/// Expands all symbolic links and resolves references to `.`, `..`, and
|
||||
/// extra `/` characters in ::pathname.
|
||||
/// Caller must deallocate result.
|
||||
/// TODO rename this to realAlloc and provide real with no allocator. See #1392
|
||||
pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 {
|
||||
switch (builtin.os) {
|
||||
Os.windows => {
|
||||
@ -1166,7 +1167,7 @@ pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 {
|
||||
return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));
|
||||
},
|
||||
Os.linux => {
|
||||
const fd = try os.posixOpen(pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
|
||||
const fd = try os.posixOpen(allocator, pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
|
||||
defer os.close(fd);
|
||||
|
||||
var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
|
||||
|
@ -1,6 +1,5 @@
|
||||
use @import("index.zig");
|
||||
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
|
||||
@ -74,7 +73,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?[*]CHAR) DWORD;
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: WORD, lpBuffer: ?[*]WCHAR) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD;
|
||||
@ -107,7 +107,6 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
|
||||
dwFlags: DWORD,
|
||||
) DWORD;
|
||||
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
|
||||
@ -194,7 +193,6 @@ pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
|
||||
|
||||
|
||||
pub const FILE_NOTIFY_INFORMATION = extern struct {
|
||||
NextEntryOffset: DWORD,
|
||||
Action: DWORD,
|
||||
@ -208,7 +206,7 @@ pub const FILE_ACTION_MODIFIED = 0x00000003;
|
||||
pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
|
||||
pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
|
||||
|
||||
pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn(DWORD, DWORD, *OVERLAPPED) void;
|
||||
pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn (DWORD, DWORD, *OVERLAPPED) void;
|
||||
|
||||
pub const FILE_LIST_DIRECTORY = 1;
|
||||
|
||||
|
@ -7,6 +7,8 @@ const mem = std.mem;
|
||||
const BufMap = std.BufMap;
|
||||
const cstr = std.cstr;
|
||||
|
||||
pub const PATH_MAX_UTF16 = 32767;
|
||||
|
||||
pub const WaitError = error{
|
||||
WaitAbandoned,
|
||||
WaitTimeOut,
|
||||
@ -90,36 +92,17 @@ pub const OpenError = error{
|
||||
AccessDenied,
|
||||
PipeBusy,
|
||||
Unexpected,
|
||||
OutOfMemory,
|
||||
};
|
||||
|
||||
/// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
||||
pub fn windowsOpen(
|
||||
allocator: *mem.Allocator,
|
||||
file_path: []const u8,
|
||||
desired_access: windows.DWORD,
|
||||
share_mode: windows.DWORD,
|
||||
creation_disposition: windows.DWORD,
|
||||
flags_and_attrs: windows.DWORD,
|
||||
) OpenError!windows.HANDLE {
|
||||
const path_with_null = try cstr.addNullByte(allocator, file_path);
|
||||
defer allocator.free(path_with_null);
|
||||
|
||||
const result = windows.CreateFileA(path_with_null.ptr, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
|
||||
|
||||
if (result == windows.INVALID_HANDLE_VALUE) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation,
|
||||
windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists,
|
||||
windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound,
|
||||
windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied,
|
||||
windows.ERROR.PIPE_BUSY => OpenError.PipeBusy,
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
@compileError("TODO rewrite with CreateFileW and no allocator");
|
||||
}
|
||||
|
||||
/// Caller must free result.
|
||||
@ -238,7 +221,7 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_
|
||||
}
|
||||
}
|
||||
|
||||
pub const WindowsWaitResult = enum{
|
||||
pub const WindowsWaitResult = enum {
|
||||
Normal,
|
||||
Aborted,
|
||||
Cancelled,
|
||||
@ -254,7 +237,7 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t
|
||||
if (std.debug.runtime_safety) {
|
||||
std.debug.panic("unexpected error: {}\n", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
return WindowsWaitResult.Normal;
|
||||
|
@ -218,7 +218,6 @@ const Utf8Iterator = struct {
|
||||
}
|
||||
|
||||
const cp_len = utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable;
|
||||
|
||||
it.i += cp_len;
|
||||
return it.bytes[it.i - cp_len .. it.i];
|
||||
}
|
||||
@ -236,6 +235,34 @@ const Utf8Iterator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Utf16LeIterator = struct {
|
||||
bytes: []const u8,
|
||||
i: usize,
|
||||
|
||||
pub fn init(s: []const u16) Utf16LeIterator {
|
||||
return Utf16LeIterator{
|
||||
.bytes = @sliceToBytes(s),
|
||||
.i = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn nextCodepoint(it: *Utf16LeIterator) !?u32 {
|
||||
const c0: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);
|
||||
if (c0 & ~u32(0x03ff) == 0xd800) {
|
||||
// surrogate pair
|
||||
it.i += 2;
|
||||
if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf;
|
||||
const c1: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);
|
||||
if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
|
||||
return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
|
||||
} else if (c0 & ~u32(0x03ff) == 0xdc00) {
|
||||
return error.UnexpectedSecondSurrogateHalf;
|
||||
} else {
|
||||
return c0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test "utf8 encode" {
|
||||
comptime testUtf8Encode() catch unreachable;
|
||||
try testUtf8Encode();
|
||||
@ -446,42 +473,34 @@ fn testDecode(bytes: []const u8) !u32 {
|
||||
return utf8Decode(bytes);
|
||||
}
|
||||
|
||||
// TODO: make this API on top of a non-allocating Utf16LeView
|
||||
pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
|
||||
/// Caller must free returned memory.
|
||||
pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
|
||||
var result = std.ArrayList(u8).init(allocator);
|
||||
// optimistically guess that it will all be ascii.
|
||||
try result.ensureCapacity(utf16le.len);
|
||||
|
||||
const utf16le_as_bytes = @sliceToBytes(utf16le);
|
||||
var i: usize = 0;
|
||||
var out_index: usize = 0;
|
||||
while (i < utf16le_as_bytes.len) : (i += 2) {
|
||||
// decode
|
||||
const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
|
||||
var codepoint: u32 = undefined;
|
||||
if (c0 & ~u32(0x03ff) == 0xd800) {
|
||||
// surrogate pair
|
||||
i += 2;
|
||||
if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf;
|
||||
const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
|
||||
if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
|
||||
codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
|
||||
} else if (c0 & ~u32(0x03ff) == 0xdc00) {
|
||||
return error.UnexpectedSecondSurrogateHalf;
|
||||
} else {
|
||||
codepoint = c0;
|
||||
}
|
||||
|
||||
// encode
|
||||
var it = Utf16LeIterator.init(utf16le);
|
||||
while (try it.nextCodepoint()) |codepoint| {
|
||||
const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
|
||||
try result.resize(result.len + utf8_len);
|
||||
_ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable;
|
||||
assert((utf8Encode(codepoint, result.items[out_index..]) catch unreachable) == utf8_len);
|
||||
out_index += utf8_len;
|
||||
}
|
||||
|
||||
return result.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !void {
|
||||
var out_index: usize = 0;
|
||||
var it = Utf16LeIterator.init(utf16le);
|
||||
while (try it.nextCodepoint()) |codepoint| {
|
||||
const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
|
||||
try result.resize(result.len + utf8_len);
|
||||
assert((utf8Encode(codepoint, result.items[out_index..]) catch unreachable) == utf8_len);
|
||||
out_index += utf8_len;
|
||||
}
|
||||
}
|
||||
|
||||
test "utf16leToUtf8" {
|
||||
var utf16le: [2]u16 = undefined;
|
||||
const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);
|
||||
|
Loading…
x
Reference in New Issue
Block a user