diff options
| author | hryx <codroid@gmail.com> | 2019-05-27 17:24:21 -0700 |
|---|---|---|
| committer | hryx <codroid@gmail.com> | 2019-05-27 17:24:21 -0700 |
| commit | e1f3eec9cc05535b3f3b81f2fb7cd65dd4d1e841 (patch) | |
| tree | 5f408ed68a686491eaf759f9cbba02beac829b38 /std | |
| parent | 2aa1c5da5dded6b1b346c3a1b57443f2c459ebe9 (diff) | |
| parent | 3fccc0747903f0726d6cc8ee73832cb62f1304bb (diff) | |
| download | zig-e1f3eec9cc05535b3f3b81f2fb7cd65dd4d1e841.tar.gz zig-e1f3eec9cc05535b3f3b81f2fb7cd65dd4d1e841.zip | |
Merge branch 'master' into translate-c-userland
Diffstat (limited to 'std')
84 files changed, 11526 insertions, 11421 deletions
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 431a96e64b..bf5700c51e 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -186,13 +186,13 @@ test "std.atomic.Queue" { } } } else { - var putters: [put_thread_count]*std.os.Thread = undefined; + var putters: [put_thread_count]*std.Thread = undefined; for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); + t.* = try std.Thread.spawn(&context, startPuts); } - var getters: [put_thread_count]*std.os.Thread = undefined; + var getters: [put_thread_count]*std.Thread = undefined; for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); + t.* = try std.Thread.spawn(&context, startGets); } for (putters) |t| @@ -220,7 +220,7 @@ fn startPuts(ctx: *Context) u8 { var put_count: usize = puts_per_thread; var r = std.rand.DefaultPrng.init(0xdeadbeef); while (put_count != 0) : (put_count -= 1) { - std.os.time.sleep(1); // let the os scheduler be our fuzz + std.time.sleep(1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); const node = ctx.allocator.create(Queue(i32).Node) catch unreachable; node.* = Queue(i32).Node{ @@ -239,7 +239,7 @@ fn startGets(ctx: *Context) u8 { const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; while (ctx.queue.get()) |node| { - std.os.time.sleep(1); // let the os scheduler be our fuzz + std.time.sleep(1); // let the os scheduler be our fuzz _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); } diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 8ae6c997aa..e9d070ac7b 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -120,13 +120,13 @@ test "std.atomic.stack" { } } } else { - var putters: [put_thread_count]*std.os.Thread = undefined; + var putters: [put_thread_count]*std.Thread = undefined; for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); + t.* = try std.Thread.spawn(&context, startPuts); } - var getters: [put_thread_count]*std.os.Thread = undefined; + var getters: [put_thread_count]*std.Thread = undefined; for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); + t.* = try std.Thread.spawn(&context, startGets); } for (putters) |t| @@ -154,7 +154,7 @@ fn startPuts(ctx: *Context) u8 { var put_count: usize = puts_per_thread; var r = std.rand.DefaultPrng.init(0xdeadbeef); while (put_count != 0) : (put_count -= 1) { - std.os.time.sleep(1); // let the os scheduler be our fuzz + std.time.sleep(1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); const node = ctx.allocator.create(Stack(i32).Node) catch unreachable; node.* = Stack(i32).Node{ @@ -172,7 +172,7 @@ fn startGets(ctx: *Context) u8 { const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; while (ctx.stack.pop()) |node| { - std.os.time.sleep(1); // let the os scheduler be our fuzz + std.time.sleep(1); // let the os scheduler be our fuzz _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); } diff --git a/std/buffer.zig b/std/buffer.zig index 32228af558..bc6aa254da 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -139,15 +139,13 @@ pub const Buffer = struct { }; test "simple Buffer" { - const cstr = @import("cstr.zig"); - var buf = try Buffer.init(debug.global_allocator, ""); testing.expect(buf.len() == 0); try buf.append("hello"); try buf.append(" "); try buf.append("world"); testing.expect(buf.eql("hello world")); - testing.expect(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst())); + testing.expect(mem.eql(u8, mem.toSliceConst(u8, buf.toSliceConst().ptr), buf.toSliceConst())); var buf2 = try Buffer.initFromBuffer(buf); testing.expect(buf.eql(buf2.toSliceConst())); diff --git a/std/build.zig b/std/build.zig index ff64c6cb93..d427521251 100644 --- a/std/build.zig +++ b/std/build.zig @@ -1,6 +1,7 @@ const std = @import("std.zig"); const builtin = @import("builtin"); const io = std.io; +const fs = std.fs; const mem = std.mem; const debug = std.debug; const assert = debug.assert; @@ -8,12 +9,11 @@ const warn = std.debug.warn; const ArrayList = std.ArrayList; const HashMap = std.HashMap; const Allocator = mem.Allocator; -const os = std.os; -const StdIo = os.ChildProcess.StdIo; -const Term = os.ChildProcess.Term; +const process = std.process; const BufSet = std.BufSet; const BufMap = std.BufMap; const fmt_lib = std.fmt; +const File = std.fs.File; pub const FmtStep = @import("build/fmt.zig").FmtStep; @@ -95,11 +95,11 @@ pub const Builder = struct { pub fn init(allocator: *Allocator, zig_exe: []const u8, build_root: []const u8, cache_root: []const u8) Builder { const env_map = allocator.create(BufMap) catch unreachable; - env_map.* = os.getEnvMap(allocator) catch unreachable; + env_map.* = process.getEnvMap(allocator) catch unreachable; var self = Builder{ .zig_exe = zig_exe, .build_root = build_root, - .cache_root = os.path.relative(allocator, build_root, cache_root) catch unreachable, + .cache_root = fs.path.relative(allocator, build_root, cache_root) catch unreachable, .verbose = false, .verbose_tokenize = false, .verbose_ast = false, @@ -153,8 +153,8 @@ pub const Builder = struct { pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void { self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default - self.lib_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "lib" }) catch unreachable; - self.exe_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "bin" }) catch unreachable; + self.lib_dir = fs.path.join(self.allocator, [][]const u8{ self.prefix, "lib" }) catch unreachable; + self.exe_dir = fs.path.join(self.allocator, [][]const u8{ self.prefix, "bin" }) catch unreachable; } pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { @@ -286,7 +286,7 @@ pub const Builder = struct { if (self.verbose) { warn("rm {}\n", installed_file); } - os.deleteFile(installed_file) catch {}; + fs.deleteFile(installed_file) catch {}; } // TODO remove empty directories @@ -325,7 +325,7 @@ pub const Builder = struct { fn detectNativeSystemPaths(self: *Builder) void { var is_nixos = false; - if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { + if (process.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { is_nixos = true; var it = mem.tokenize(nix_cflags_compile, " "); while (true) { @@ -344,7 +344,7 @@ pub const Builder = struct { } else |err| { assert(err == error.EnvironmentVariableNotFound); } - if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| { + if (process.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| { is_nixos = true; var it = mem.tokenize(nix_ldflags, " "); while (true) { @@ -368,7 +368,7 @@ pub const Builder = struct { } if (is_nixos) return; switch (builtin.os) { - builtin.Os.windows => {}, + .windows => {}, else => { const triple = (CrossTarget{ .arch = builtin.arch, @@ -601,7 +601,7 @@ pub const Builder = struct { printCmd(cwd, argv); } - const child = os.ChildProcess.init(argv, self.allocator) catch unreachable; + const child = std.ChildProcess.init(argv, self.allocator) catch unreachable; defer child.deinit(); child.cwd = cwd; @@ -613,7 +613,7 @@ pub const Builder = struct { }; switch (term) { - Term.Exited => |code| { + .Exited => |code| { if (code != 0) { warn("The following command exited with error code {}:\n", code); printCmd(cwd, argv); @@ -630,7 +630,7 @@ pub const Builder = struct { } pub fn makePath(self: *Builder, path: []const u8) !void { - os.makePath(self.allocator, self.pathFromRoot(path)) catch |err| { + fs.makePath(self.allocator, self.pathFromRoot(path)) catch |err| { warn("Unable to create path {}: {}\n", path, @errorName(err)); return err; }; @@ -651,7 +651,7 @@ pub const Builder = struct { ///::dest_rel_path is relative to prefix path or it can be an absolute path pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { - const full_dest_path = os.path.resolve( + const full_dest_path = fs.path.resolve( self.allocator, [][]const u8{ self.prefix, dest_rel_path }, ) catch unreachable; @@ -668,28 +668,28 @@ pub const Builder = struct { } fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { - return self.copyFileMode(source_path, dest_path, os.File.default_mode); + return self.copyFileMode(source_path, dest_path, File.default_mode); } - fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.File.Mode) !void { + fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { if (self.verbose) { warn("cp {} {}\n", source_path, dest_path); } - const dirname = os.path.dirname(dest_path) orelse "."; + const dirname = fs.path.dirname(dest_path) orelse "."; const abs_source_path = self.pathFromRoot(source_path); - os.makePath(self.allocator, dirname) catch |err| { + fs.makePath(self.allocator, dirname) catch |err| { warn("Unable to create path {}: {}\n", dirname, @errorName(err)); return err; }; - os.copyFileMode(abs_source_path, dest_path, mode) catch |err| { + fs.copyFileMode(abs_source_path, dest_path, mode) catch |err| { warn("Unable to copy {} to {}: {}\n", abs_source_path, dest_path, @errorName(err)); return err; }; } fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { - return os.path.resolve(self.allocator, [][]const u8{ self.build_root, rel_path }) catch unreachable; + return fs.path.resolve(self.allocator, [][]const u8{ self.build_root, rel_path }) catch unreachable; } pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 { @@ -701,11 +701,11 @@ pub const Builder = struct { const exe_extension = (Target{ .Native = {} }).exeFileExt(); for (self.search_prefixes.toSliceConst()) |search_prefix| { for (names) |name| { - if (os.path.isAbsolute(name)) { + if (fs.path.isAbsolute(name)) { return name; } - const full_path = try os.path.join(self.allocator, [][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) }); - if (os.path.real(self.allocator, full_path)) |real_path| { + const full_path = try fs.path.join(self.allocator, [][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) }); + if (fs.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { continue; @@ -714,13 +714,13 @@ pub const Builder = struct { } if (self.env_map.get("PATH")) |PATH| { for (names) |name| { - if (os.path.isAbsolute(name)) { + if (fs.path.isAbsolute(name)) { return name; } - var it = mem.tokenize(PATH, []u8{os.path.delimiter}); + var it = mem.tokenize(PATH, []u8{fs.path.delimiter}); while (it.next()) |path| { - const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); - if (os.path.real(self.allocator, full_path)) |real_path| { + const full_path = try fs.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); + if (fs.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { continue; @@ -729,12 +729,12 @@ pub const Builder = struct { } } for (names) |name| { - if (os.path.isAbsolute(name)) { + if (fs.path.isAbsolute(name)) { return name; } for (paths) |path| { - const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); - if (os.path.real(self.allocator, full_path)) |real_path| { + const full_path = try fs.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); + if (fs.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { continue; @@ -748,12 +748,12 @@ pub const Builder = struct { assert(argv.len != 0); const max_output_size = 100 * 1024; - const child = try os.ChildProcess.init(argv, self.allocator); + const child = try std.ChildProcess.init(argv, self.allocator); defer child.deinit(); - child.stdin_behavior = os.ChildProcess.StdIo.Ignore; - child.stdout_behavior = os.ChildProcess.StdIo.Pipe; - child.stderr_behavior = os.ChildProcess.StdIo.Inherit; + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Inherit; try child.spawn(); @@ -765,7 +765,7 @@ pub const Builder = struct { const term = child.wait() catch |err| std.debug.panic("unable to spawn {}: {}", argv[0], err); switch (term) { - os.ChildProcess.Term.Exited => |code| { + .Exited => |code| { if (code != 0) { warn("The following command exited with error code {}:\n", code); printCmd(null, argv); @@ -845,8 +845,8 @@ pub const Target = union(enum) { } } - pub fn oFileExt(self: *const Target) []const u8 { - const abi = switch (self.*) { + pub fn oFileExt(self: Target) []const u8 { + const abi = switch (self) { Target.Native => builtin.abi, Target.Cross => |t| t.abi, }; @@ -856,49 +856,49 @@ pub const Target = union(enum) { }; } - pub fn exeFileExt(self: *const Target) []const u8 { + pub fn exeFileExt(self: Target) []const u8 { return switch (self.getOs()) { - builtin.Os.windows => ".exe", + .windows => ".exe", else => "", }; } - pub fn libFileExt(self: *const Target) []const u8 { + pub fn libFileExt(self: Target) []const u8 { return switch (self.getOs()) { - builtin.Os.windows => ".lib", + .windows => ".lib", else => ".a", }; } - pub fn getOs(self: *const Target) builtin.Os { - return switch (self.*) { + pub fn getOs(self: Target) builtin.Os { + return switch (self) { Target.Native => builtin.os, Target.Cross => |t| t.os, }; } - pub fn isDarwin(self: *const Target) bool { + pub fn isDarwin(self: Target) bool { return switch (self.getOs()) { - builtin.Os.ios, builtin.Os.macosx => true, + .ios, .macosx, .watchos, .tvos => true, else => false, }; } - pub fn isWindows(self: *const Target) bool { + pub fn isWindows(self: Target) bool { return switch (self.getOs()) { - builtin.Os.windows => true, + .windows => true, else => false, }; } - pub fn isFreeBSD(self: *const Target) bool { + pub fn isFreeBSD(self: Target) bool { return switch (self.getOs()) { - builtin.Os.freebsd => true, + .freebsd => true, else => false, }; } - pub fn wantSharedLibSymLinks(self: *const Target) bool { + pub fn wantSharedLibSymLinks(self: Target) bool { return !self.isWindows(); } }; @@ -1064,19 +1064,19 @@ pub const LibExeObjStep = struct { fn computeOutFileNames(self: *LibExeObjStep) void { switch (self.kind) { - Kind.Obj => { + .Obj => { self.out_filename = self.builder.fmt("{}{}", self.name, self.target.oFileExt()); }, - Kind.Exe => { + .Exe => { self.out_filename = self.builder.fmt("{}{}", self.name, self.target.exeFileExt()); }, - Kind.Test => { + .Test => { self.out_filename = self.builder.fmt("test{}", self.target.exeFileExt()); }, - Kind.Lib => { + .Lib => { if (!self.is_dynamic) { switch (self.target.getOs()) { - builtin.Os.windows => { + .windows => { self.out_filename = self.builder.fmt("{}.lib", self.name); }, else => { @@ -1086,13 +1086,13 @@ pub const LibExeObjStep = struct { self.out_lib_filename = self.out_filename; } else { switch (self.target.getOs()) { - builtin.Os.ios, builtin.Os.macosx => { + .ios, .macosx => { self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", self.name, self.version.major, self.version.minor, self.version.patch); self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", self.name, self.version.major); self.name_only_filename = self.builder.fmt("lib{}.dylib", self.name); self.out_lib_filename = self.out_filename; }, - builtin.Os.windows => { + .windows => { self.out_filename = self.builder.fmt("{}.dll", self.name); self.out_lib_filename = self.builder.fmt("{}.lib", self.name); }, @@ -1227,7 +1227,7 @@ pub const LibExeObjStep = struct { /// the make step, from a step that has declared a dependency on this one. /// To run an executable built with zig build, use `run`, or create an install step and invoke it. pub fn getOutputPath(self: *LibExeObjStep) []const u8 { - return os.path.join( + return fs.path.join( self.builder.allocator, [][]const u8{ self.output_dir.?, self.out_filename }, ) catch unreachable; @@ -1237,7 +1237,7 @@ pub const LibExeObjStep = struct { /// the make step, from a step that has declared a dependency on this one. pub fn getOutputLibPath(self: *LibExeObjStep) []const u8 { assert(self.kind == Kind.Lib); - return os.path.join( + return fs.path.join( self.builder.allocator, [][]const u8{ self.output_dir.?, self.out_lib_filename }, ) catch unreachable; @@ -1248,7 +1248,7 @@ pub const LibExeObjStep = struct { pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { assert(self.kind != Kind.Exe); assert(!self.disable_gen_h); - return os.path.join( + return fs.path.join( self.builder.allocator, [][]const u8{ self.output_dir.?, self.out_h_filename }, ) catch unreachable; @@ -1364,7 +1364,7 @@ pub const LibExeObjStep = struct { try zig_args.append("--library"); try zig_args.append(full_path_lib); - if (os.path.dirname(full_path_lib)) |dirname| { + if (fs.path.dirname(full_path_lib)) |dirname| { try zig_args.append("-rpath"); try zig_args.append(dirname); } @@ -1390,7 +1390,7 @@ pub const LibExeObjStep = struct { } if (self.build_options_contents.len() > 0) { - const build_options_file = try os.path.join( + const build_options_file = try fs.path.join( builder.allocator, [][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", self.name) }, ); @@ -1502,7 +1502,7 @@ pub const LibExeObjStep = struct { IncludeDir.OtherStep => |other| { const h_path = other.getOutputHPath(); try zig_args.append("-isystem"); - try zig_args.append(os.path.dirname(h_path).?); + try zig_args.append(fs.path.dirname(h_path).?); }, } } @@ -1575,7 +1575,7 @@ pub const LibExeObjStep = struct { const output_path_nl = try builder.exec(zig_args.toSliceConst()); const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); - self.output_dir = os.path.dirname(output_path).?; + self.output_dir = fs.path.dirname(output_path).?; } if (self.kind == Kind.Lib and self.is_dynamic and self.target.wantSharedLibSymLinks()) { @@ -1636,20 +1636,20 @@ pub const RunStep = struct { } pub fn addPathDir(self: *RunStep, search_path: []const u8) void { - const PATH = if (builtin.os == builtin.Os.windows) "Path" else "PATH"; + const PATH = if (std.os.windows.is_the_target) "Path" else "PATH"; const env_map = self.getEnvMap(); const prev_path = env_map.get(PATH) orelse { env_map.set(PATH, search_path) catch unreachable; return; }; - const new_path = self.builder.fmt("{}" ++ [1]u8{os.path.delimiter} ++ "{}", prev_path, search_path); + const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", prev_path, search_path); env_map.set(PATH, new_path) catch unreachable; } pub fn getEnvMap(self: *RunStep) *BufMap { return self.env_map orelse { const env_map = self.builder.allocator.create(BufMap) catch unreachable; - env_map.* = os.getEnvMap(self.builder.allocator) catch unreachable; + env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable; self.env_map = env_map; return env_map; }; @@ -1687,7 +1687,7 @@ pub const RunStep = struct { switch (link_object) { LibExeObjStep.LinkObject.OtherStep => |other| { if (other.target.isWindows() and other.isDynamicLibrary()) { - self.addPathDir(os.path.dirname(other.getOutputPath()).?); + self.addPathDir(fs.path.dirname(other.getOutputPath()).?); self.addPathForDynLibs(other); } }, @@ -1717,7 +1717,7 @@ const InstallArtifactStep = struct { .builder = builder, .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), .artifact = artifact, - .dest_file = os.path.join( + .dest_file = fs.path.join( builder.allocator, [][]const u8{ dest_dir, artifact.out_filename }, ) catch unreachable, @@ -1725,11 +1725,11 @@ const InstallArtifactStep = struct { self.step.dependOn(&artifact.step); builder.pushInstalledFile(self.dest_file); if (self.artifact.kind == LibExeObjStep.Kind.Lib and self.artifact.is_dynamic) { - builder.pushInstalledFile(os.path.join( + builder.pushInstalledFile(fs.path.join( builder.allocator, [][]const u8{ builder.lib_dir, artifact.major_only_filename }, ) catch unreachable); - builder.pushInstalledFile(os.path.join( + builder.pushInstalledFile(fs.path.join( builder.allocator, [][]const u8{ builder.lib_dir, artifact.name_only_filename }, ) catch unreachable); @@ -1742,12 +1742,12 @@ const InstallArtifactStep = struct { const builder = self.builder; const mode = switch (builtin.os) { - builtin.Os.windows => {}, + .windows => {}, else => switch (self.artifact.kind) { - LibExeObjStep.Kind.Obj => unreachable, - LibExeObjStep.Kind.Test => unreachable, - LibExeObjStep.Kind.Exe => u32(0o755), - LibExeObjStep.Kind.Lib => if (!self.artifact.is_dynamic) u32(0o666) else u32(0o755), + .Obj => unreachable, + .Test => unreachable, + .Exe => u32(0o755), + .Lib => if (!self.artifact.is_dynamic) u32(0o666) else u32(0o755), }, }; try builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode); @@ -1796,8 +1796,8 @@ pub const WriteFileStep = struct { fn make(step: *Step) !void { const self = @fieldParentPtr(WriteFileStep, "step", step); const full_path = self.builder.pathFromRoot(self.file_path); - const full_path_dir = os.path.dirname(full_path) orelse "."; - os.makePath(self.builder.allocator, full_path_dir) catch |err| { + const full_path_dir = fs.path.dirname(full_path) orelse "."; + fs.makePath(self.builder.allocator, full_path_dir) catch |err| { warn("unable to make path {}: {}\n", full_path_dir, @errorName(err)); return err; }; @@ -1844,7 +1844,7 @@ pub const RemoveDirStep = struct { const self = @fieldParentPtr(RemoveDirStep, "step", step); const full_path = self.builder.pathFromRoot(self.dir_path); - os.deleteTree(self.builder.allocator, full_path) catch |err| { + fs.deleteTree(self.builder.allocator, full_path) catch |err| { warn("Unable to remove {}: {}\n", full_path, @errorName(err)); return err; }; @@ -1886,23 +1886,23 @@ pub const Step = struct { }; fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { - const out_dir = os.path.dirname(output_path) orelse "."; - const out_basename = os.path.basename(output_path); + const out_dir = fs.path.dirname(output_path) orelse "."; + const out_basename = fs.path.basename(output_path); // sym link for libfoo.so.1 to libfoo.so.1.2.3 - const major_only_path = os.path.join( + const major_only_path = fs.path.join( allocator, [][]const u8{ out_dir, filename_major_only }, ) catch unreachable; - os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { + fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { warn("Unable to symlink {} -> {}\n", major_only_path, out_basename); return err; }; // sym link for libfoo.so to libfoo.so.1 - const name_only_path = os.path.join( + const name_only_path = fs.path.join( allocator, [][]const u8{ out_dir, filename_name_only }, ) catch unreachable; - os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { + fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only); return err; }; @@ -1,18 +1,28 @@ const builtin = @import("builtin"); -const Os = builtin.Os; +const std = @import("std"); +const page_size = std.mem.page_size; + +pub use @import("os/bits.zig"); pub use switch (builtin.os) { - Os.linux => @import("c/linux.zig"), - Os.windows => @import("c/windows.zig"), - Os.macosx, Os.ios => @import("c/darwin.zig"), - Os.freebsd => @import("c/freebsd.zig"), - Os.netbsd => @import("c/netbsd.zig"), + .linux => @import("c/linux.zig"), + .windows => @import("c/windows.zig"), + .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"), + .freebsd => @import("c/freebsd.zig"), + .netbsd => @import("c/netbsd.zig"), else => struct {}, }; +pub fn getErrno(rc: var) u16 { + if (rc == -1) { + return @intCast(u16, _errno().*); + } else { + return 0; + } +} + // TODO https://github.com/ziglang/zig/issues/265 on this whole file -pub const FILE = @OpaqueType(); pub extern "c" fn fopen(filename: [*]const u8, modes: [*]const u8) ?*FILE; pub extern "c" fn fclose(stream: *FILE) c_int; pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; @@ -20,33 +30,38 @@ pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stre pub extern "c" fn abort() noreturn; pub extern "c" fn exit(code: c_int) noreturn; -pub extern "c" fn isatty(fd: c_int) c_int; -pub extern "c" fn close(fd: c_int) c_int; -pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int; -pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; -pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; -pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; +pub extern "c" fn isatty(fd: fd_t) c_int; +pub extern "c" fn close(fd: fd_t) c_int; +pub extern "c" fn @"close$NOCANCEL"(fd: fd_t) c_int; +pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int; +pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int; +pub extern "c" fn lseek(fd: fd_t, offset: isize, whence: c_int) isize; +pub extern "c" fn open(path: [*]const u8, oflag: c_uint, ...) c_int; pub extern "c" fn raise(sig: c_int) c_int; -pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; -pub extern "c" fn pread(fd: c_int, buf: *c_void, nbyte: usize, offset: u64) isize; +pub extern "c" fn read(fd: fd_t, buf: [*]u8, nbyte: usize) isize; +pub extern "c" fn pread(fd: fd_t, buf: [*]u8, nbyte: usize, offset: u64) isize; +pub extern "c" fn preadv(fd: c_int, iov: [*]const iovec, iovcnt: c_uint, offset: usize) isize; +pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: usize) isize; pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; -pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; -pub extern "c" fn pwrite(fd: c_int, buf: *const c_void, nbyte: usize, offset: u64) isize; -pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; -pub extern "c" fn munmap(addr: ?*c_void, len: usize) c_int; +pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize; +pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: u64) isize; +pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: isize) *c_void; +pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int; +pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int; pub extern "c" fn unlink(path: [*]const u8) c_int; pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; -pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; +pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int; pub extern "c" fn fork() c_int; pub extern "c" fn access(path: [*]const u8, mode: c_uint) c_int; -pub extern "c" fn pipe(fds: *[2]c_int) c_int; +pub extern "c" fn pipe(fds: *[2]fd_t) c_int; +pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; pub extern "c" fn mkdir(path: [*]const u8, mode: c_uint) c_int; pub extern "c" fn symlink(existing: [*]const u8, new: [*]const u8) c_int; pub extern "c" fn rename(old: [*]const u8, new: [*]const u8) c_int; pub extern "c" fn chdir(path: [*]const u8) c_int; pub extern "c" fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) c_int; -pub extern "c" fn dup(fd: c_int) c_int; -pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; +pub extern "c" fn dup(fd: fd_t) c_int; +pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize; pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8; pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; @@ -56,6 +71,25 @@ pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; pub extern "c" fn rmdir(path: [*]const u8) c_int; +pub extern "c" fn getenv(name: [*]const u8) ?[*]u8; +pub extern "c" fn sysctl(name: [*]const c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; +pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; +pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; + +pub extern "c" fn bind(socket: fd_t, address: ?*const sockaddr, address_len: socklen_t) c_int; +pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; +pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int; +pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int; +pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int; +pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int; +pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int; +pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int; +pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; +pub extern "c" fn openat(fd: c_int, path: [*]const u8, flags: c_int) c_int; +pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; +pub extern "c" fn setuid(uid: c_uint) c_int; +pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int; +pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int; pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; pub extern "c" fn malloc(usize) ?*c_void; @@ -63,11 +97,19 @@ pub extern "c" fn realloc(?*c_void, usize) ?*c_void; pub extern "c" fn free(*c_void) void; pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; -pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; -pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; -pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; -pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; -pub extern "pthread" fn pthread_self() pthread_t; -pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; +pub extern "c" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; +pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) c_int; +pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; +pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; +pub extern "c" fn pthread_self() pthread_t; +pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; -pub const pthread_t = *@OpaqueType(); +pub extern "c" fn kqueue() c_int; +pub extern "c" fn kevent( + kq: c_int, + changelist: [*]const Kevent, + nchanges: c_int, + eventlist: [*]Kevent, + nevents: c_int, + timeout: ?*const timespec, +) c_int; diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 0e92e8d411..e45a158f68 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -1,24 +1,19 @@ -const macho = @import("../macho.zig"); +const std = @import("../std.zig"); +const assert = std.debug.assert; +const builtin = @import("builtin"); +const macho = std.macho; + +use @import("../os/bits.zig"); extern "c" fn __error() *c_int; pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int; pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header; -pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize; +pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize; pub extern "c" fn mach_absolute_time() u64; pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void; -pub extern "c" fn kqueue() c_int; -pub extern "c" fn kevent( - kq: c_int, - changelist: [*]const Kevent, - nchanges: c_int, - eventlist: [*]Kevent, - nevents: c_int, - timeout: ?*const timespec, -) c_int; - pub extern "c" fn kevent64( kq: c_int, changelist: [*]const kevent64_s, @@ -29,13 +24,6 @@ pub extern "c" fn kevent64( timeout: ?*const timespec, ) c_int; -pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; -pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; -pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; - -pub extern "c" fn bind(socket: c_int, address: ?*const sockaddr, address_len: socklen_t) c_int; -pub extern "c" fn socket(domain: c_int, type: c_int, protocol: c_int) c_int; - const mach_hdr = if (@sizeOf(usize) == 8) mach_header_64 else mach_header; /// The value of the link editor defined symbol _MH_EXECUTE_SYM is the address @@ -48,170 +36,21 @@ const mach_hdr = if (@sizeOf(usize) == 8) mach_header_64 else mach_header; /// export a weak symbol here, to be overridden by the real one. pub extern "c" var _mh_execute_header: mach_hdr = undefined; comptime { - @export("__mh_execute_header", _mh_execute_header, @import("builtin").GlobalLinkage.Weak); + if (std.os.darwin.is_the_target) { + @export("__mh_execute_header", _mh_execute_header, .Weak); + } } pub const mach_header_64 = macho.mach_header_64; pub const mach_header = macho.mach_header; -pub use @import("../os/darwin/errno.zig"); - pub const _errno = __error; -pub const in_port_t = u16; -pub const sa_family_t = u8; -pub const socklen_t = u32; -pub const sockaddr = extern union { - in: sockaddr_in, - in6: sockaddr_in6, -}; -pub const sockaddr_in = extern struct { - len: u8, - family: sa_family_t, - port: in_port_t, - addr: u32, - zero: [8]u8, -}; -pub const sockaddr_in6 = extern struct { - len: u8, - family: sa_family_t, - port: in_port_t, - flowinfo: u32, - addr: [16]u8, - scope_id: u32, -}; - -pub const timeval = extern struct { - tv_sec: c_long, - tv_usec: i32, -}; - -pub const timezone = extern struct { - tz_minuteswest: i32, - tz_dsttime: i32, -}; - -pub const mach_timebase_info_data = extern struct { - numer: u32, - denom: u32, -}; - -/// Renamed to Stat to not conflict with the stat function. -pub const Stat = extern struct { - dev: i32, - mode: u16, - nlink: u16, - ino: u64, - uid: u32, - gid: u32, - rdev: i32, - atime: usize, - atimensec: usize, - mtime: usize, - mtimensec: usize, - ctime: usize, - ctimensec: usize, - birthtime: usize, - birthtimensec: usize, - size: i64, - blocks: i64, - blksize: i32, - flags: u32, - gen: u32, - lspare: i32, - qspare: [2]i64, -}; +pub extern "c" fn mach_host_self() mach_port_t; +pub extern "c" fn clock_get_time(clock_serv: clock_serv_t, cur_time: *mach_timespec_t) kern_return_t; +pub extern "c" fn host_get_clock_service(host: host_t, clock_id: clock_id_t, clock_serv: ?[*]clock_serv_t) kern_return_t; +pub extern "c" fn mach_port_deallocate(task: ipc_space_t, name: mach_port_name_t) kern_return_t; -pub const timespec = extern struct { - tv_sec: isize, - tv_nsec: isize, -}; - -pub const sigset_t = u32; - -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name. -pub const Sigaction = extern struct { - handler: extern fn (c_int) void, - sa_mask: sigset_t, - sa_flags: c_int, -}; - -pub const dirent = extern struct { - d_ino: usize, - d_seekoff: usize, - d_reclen: u16, - d_namlen: u16, - d_type: u8, - d_name: u8, // field address is address of first byte of name -}; - -pub const pthread_attr_t = extern struct { - __sig: c_long, - __opaque: [56]u8, -}; - -/// Renamed from `kevent` to `Kevent` to avoid conflict with function name. -pub const Kevent = extern struct { - ident: usize, - filter: i16, - flags: u16, - fflags: u32, - data: isize, - udata: usize, -}; - -// sys/types.h on macos uses #pragma pack(4) so these checks are -// to make sure the struct is laid out the same. These values were -// produced from C code using the offsetof macro. -const std = @import("../std.zig"); -const assert = std.debug.assert; - -comptime { - assert(@byteOffsetOf(Kevent, "ident") == 0); - assert(@byteOffsetOf(Kevent, "filter") == 8); - assert(@byteOffsetOf(Kevent, "flags") == 10); - assert(@byteOffsetOf(Kevent, "fflags") == 12); - assert(@byteOffsetOf(Kevent, "data") == 16); - assert(@byteOffsetOf(Kevent, "udata") == 24); -} - -pub const kevent64_s = extern struct { - ident: u64, - filter: i16, - flags: u16, - fflags: u32, - data: i64, - udata: u64, - ext: [2]u64, -}; - -pub const mach_port_t = c_uint; -pub const clock_serv_t = mach_port_t; -pub const clock_res_t = c_int; -pub const mach_port_name_t = natural_t; -pub const natural_t = c_uint; -pub const mach_timespec_t = extern struct { - tv_sec: c_uint, - tv_nsec: clock_res_t, -}; -pub const kern_return_t = c_int; -pub const host_t = mach_port_t; -pub const CALENDAR_CLOCK = 1; - -pub extern fn mach_host_self() mach_port_t; -pub extern fn clock_get_time(clock_serv: clock_serv_t, cur_time: *mach_timespec_t) kern_return_t; -pub extern fn host_get_clock_service(host: host_t, clock_id: clock_id_t, clock_serv: ?[*]clock_serv_t) kern_return_t; -pub extern fn mach_port_deallocate(task: ipc_space_t, name: mach_port_name_t) kern_return_t; - -// sys/types.h on macos uses #pragma pack() so these checks are -// to make sure the struct is laid out the same. These values were -// produced from C code using the offsetof macro. -comptime { - assert(@byteOffsetOf(kevent64_s, "ident") == 0); - assert(@byteOffsetOf(kevent64_s, "filter") == 8); - assert(@byteOffsetOf(kevent64_s, "flags") == 10); - assert(@byteOffsetOf(kevent64_s, "fflags") == 12); - assert(@byteOffsetOf(kevent64_s, "data") == 16); - assert(@byteOffsetOf(kevent64_s, "udata") == 24); - assert(@byteOffsetOf(kevent64_s, "ext") == 32); +pub fn sigaddset(set: *sigset_t, signo: u5) void { + set.* |= u32(1) << (signo - 1); } diff --git a/std/c/freebsd.zig b/std/c/freebsd.zig index 1f8e314333..70f3aeb9ca 100644 --- a/std/c/freebsd.zig +++ b/std/c/freebsd.zig @@ -1,154 +1,4 @@ extern "c" fn __error() *c_int; pub const _errno = __error; -pub extern "c" fn kqueue() c_int; -pub extern "c" fn kevent( - kq: c_int, - changelist: [*]const Kevent, - nchanges: c_int, - eventlist: [*]Kevent, - nevents: c_int, - timeout: ?*const timespec, -) c_int; -pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; -pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; -pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; -pub extern "c" fn getdirentries(fd: c_int, buf_ptr: [*]u8, nbytes: usize, basep: *i64) usize; pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; -pub extern "c" fn pipe2(arg0: *[2]c_int, arg1: u32) c_int; -pub extern "c" fn preadv(fd: c_int, iov: *const c_void, iovcnt: c_int, offset: usize) isize; -pub extern "c" fn pwritev(fd: c_int, iov: *const c_void, iovcnt: c_int, offset: usize) isize; -pub extern "c" fn openat(fd: c_int, path: ?[*]const u8, flags: c_int) c_int; -pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; -pub extern "c" fn setuid(uid: c_uint) c_int; -pub extern "c" fn kill(pid: c_int, sig: c_int) c_int; -pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int; -pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int; - -/// Renamed from `kevent` to `Kevent` to avoid conflict with function name. -pub const Kevent = extern struct { - ident: usize, - filter: i16, - flags: u16, - fflags: u32, - data: i64, - udata: usize, - // TODO ext -}; - -pub const pthread_attr_t = extern struct { - __size: [56]u8, - __align: c_long, -}; - -pub const msghdr = extern struct { - /// optional address - msg_name: ?*sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - /// optional address - msg_name: ?*const sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec_const, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - -pub const Stat = extern struct { - dev: u64, - ino: u64, - nlink: usize, - - mode: u16, - __pad0: u16, - uid: u32, - gid: u32, - __pad1: u32, - rdev: u64, - - atim: timespec, - mtim: timespec, - ctim: timespec, - birthtim: timespec, - - size: i64, - blocks: i64, - blksize: isize, - flags: u32, - gen: u64, - __spare: [10]u64, -}; - -pub const timespec = extern struct { - tv_sec: isize, - tv_nsec: isize, -}; - -pub const dirent = extern struct { - d_fileno: usize, - d_off: i64, - d_reclen: u16, - d_type: u8, - d_pad0: u8, - d_namlen: u16, - d_pad1: u16, - d_name: [256]u8, -}; - -pub const in_port_t = u16; -pub const sa_family_t = u16; - -pub const sockaddr = extern union { - in: sockaddr_in, - in6: sockaddr_in6, -}; - -pub const sockaddr_in = extern struct { - len: u8, - family: sa_family_t, - port: in_port_t, - addr: [16]u8, - zero: [8]u8, -}; - -pub const sockaddr_in6 = extern struct { - len: u8, - family: sa_family_t, - port: in_port_t, - flowinfo: u32, - addr: [16]u8, - scope_id: u32, -}; diff --git a/std/c/linux.zig b/std/c/linux.zig index 48e359f361..9e028728c7 100644 --- a/std/c/linux.zig +++ b/std/c/linux.zig @@ -1,17 +1,27 @@ -const linux = @import("../os/linux.zig"); -pub use @import("../os/linux/errno.zig"); +const std = @import("../std.zig"); +use std.c; -pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int; extern "c" fn __errno_location() *c_int; pub const _errno = __errno_location; -pub const pthread_attr_t = extern struct { - __size: [56]u8, - __align: c_long, -}; +pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int; +pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int; +pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int; +pub extern "c" fn epoll_ctl(epfd: fd_t, op: c_uint, fd: fd_t, event: *epoll_event) c_int; +pub extern "c" fn epoll_create1(flags: c_uint) c_int; +pub extern "c" fn epoll_wait(epfd: fd_t, events: [*]epoll_event, maxevents: c_uint, timeout: c_int) c_int; +pub extern "c" fn epoll_pwait( + epfd: fd_t, + events: [*]epoll_event, + maxevents: c_int, + timeout: c_int, + sigmask: *const sigset_t, +) c_int; +pub extern "c" fn inotify_init1(flags: c_uint) c_int; +pub extern "c" fn inotify_add_watch(fd: fd_t, pathname: [*]const u8, mask: u32) c_int; /// See std.elf for constants for this -pub extern fn getauxval(__type: c_ulong) c_ulong; +pub extern "c" fn getauxval(__type: c_ulong) c_ulong; -pub const dl_iterate_phdr_callback = extern fn (info: *linux.dl_phdr_info, size: usize, data: ?*c_void) c_int; -pub extern fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; +pub const dl_iterate_phdr_callback = extern fn (info: *dl_phdr_info, size: usize, data: ?*c_void) c_int; +pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; diff --git a/std/c/netbsd.zig b/std/c/netbsd.zig index ef3da91bea..c677e1500b 100644 --- a/std/c/netbsd.zig +++ b/std/c/netbsd.zig @@ -1,150 +1,4 @@ extern "c" fn __errno() *c_int; pub const _errno = __errno; -pub extern "c" fn kqueue() c_int; -pub extern "c" fn kevent( - kq: c_int, - changelist: [*]const Kevent, - nchanges: c_int, - eventlist: [*]Kevent, - nevents: c_int, - timeout: ?*const timespec, -) c_int; -pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; -pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; -pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; -pub extern "c" fn getdirentries(fd: c_int, buf_ptr: [*]u8, nbytes: usize, basep: *i64) usize; pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; -pub extern "c" fn pipe2(arg0: *[2]c_int, arg1: u32) c_int; -pub extern "c" fn preadv(fd: c_int, iov: *const c_void, iovcnt: c_int, offset: usize) isize; -pub extern "c" fn pwritev(fd: c_int, iov: *const c_void, iovcnt: c_int, offset: usize) isize; -pub extern "c" fn openat(fd: c_int, path: ?[*]const u8, flags: c_int) c_int; -pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; -pub extern "c" fn setuid(uid: c_uint) c_int; -pub extern "c" fn kill(pid: c_int, sig: c_int) c_int; -pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int; -pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int; - -/// Renamed from `kevent` to `Kevent` to avoid conflict with function name. -pub const Kevent = extern struct { - ident: usize, - filter: i32, - flags: u32, - fflags: u32, - data: i64, - udata: usize, -}; - -pub const pthread_attr_t = extern struct { - pta_magic: u32, - pta_flags: c_int, - pta_private: *c_void, -}; - -pub const msghdr = extern struct { - /// optional address - msg_name: ?*sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - /// optional address - msg_name: ?*const sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec_const, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - -pub const Stat = extern struct { - dev: u64, - mode: u32, - ino: u64, - nlink: usize, - - uid: u32, - gid: u32, - rdev: u64, - - atim: timespec, - mtim: timespec, - ctim: timespec, - birthtim: timespec, - - size: i64, - blocks: i64, - blksize: isize, - flags: u32, - gen: u32, - __spare: [2]u32, -}; - -pub const timespec = extern struct { - tv_sec: i64, - tv_nsec: isize, -}; - -pub const dirent = extern struct { - d_fileno: u64, - d_reclen: u16, - d_namlen: u16, - d_type: u8, - d_off: i64, - d_name: [512]u8, -}; - -pub const in_port_t = u16; -pub const sa_family_t = u8; - -pub const sockaddr = extern union { - in: sockaddr_in, - in6: sockaddr_in6, -}; - -pub const sockaddr_in = extern struct { - len: u8, - family: sa_family_t, - port: in_port_t, - addr: u32, - zero: [8]u8, -}; - -pub const sockaddr_in6 = extern struct { - len: u8, - family: sa_family_t, - port: in_port_t, - flowinfo: u32, - addr: [16]u8, - scope_id: u32, -}; diff --git a/std/os/child_process.zig b/std/child_process.zig index 3269e39c7a..1268f72c37 100644 --- a/std/os/child_process.zig +++ b/std/child_process.zig @@ -1,9 +1,11 @@ -const std = @import("../std.zig"); +const std = @import("std.zig"); const cstr = std.cstr; const unicode = std.unicode; const io = std.io; +const fs = std.fs; const os = std.os; -const posix = os.posix; +const process = std.process; +const File = std.fs.File; const windows = os.windows; const mem = std.mem; const debug = std.debug; @@ -12,21 +14,18 @@ const Buffer = std.Buffer; const builtin = @import("builtin"); const Os = builtin.Os; const LinkedList = std.LinkedList; -const windows_util = @import("windows/util.zig"); const maxInt = std.math.maxInt; -const is_windows = builtin.os == Os.windows; - pub const ChildProcess = struct { - pub pid: if (is_windows) void else i32, - pub handle: if (is_windows) windows.HANDLE else void, - pub thread_handle: if (is_windows) windows.HANDLE else void, + pub pid: if (os.windows.is_the_target) void else i32, + pub handle: if (os.windows.is_the_target) windows.HANDLE else void, + pub thread_handle: if (os.windows.is_the_target) windows.HANDLE else void, pub allocator: *mem.Allocator, - pub stdin: ?os.File, - pub stdout: ?os.File, - pub stderr: ?os.File, + pub stdin: ?File, + pub stdout: ?File, + pub stderr: ?File, pub term: ?(SpawnError!Term), @@ -40,41 +39,25 @@ pub const ChildProcess = struct { pub stderr_behavior: StdIo, /// Set to change the user id when spawning the child process. - pub uid: if (is_windows) void else ?u32, + pub uid: if (os.windows.is_the_target) void else ?u32, /// Set to change the group id when spawning the child process. - pub gid: if (is_windows) void else ?u32, + pub gid: if (os.windows.is_the_target) void else ?u32, /// Set to change the current working directory when spawning the child process. pub cwd: ?[]const u8, - err_pipe: if (is_windows) void else [2]i32, - llnode: if (is_windows) void else LinkedList(*ChildProcess).Node, - - pub const SpawnError = error{ - ProcessFdQuotaExceeded, - Unexpected, - NotDir, - SystemResources, - FileNotFound, - NameTooLong, - SymLinkLoop, - FileSystem, - OutOfMemory, - AccessDenied, - PermissionDenied, - InvalidUserId, - ResourceLimitReached, - InvalidExe, - IsDir, - FileBusy, - }; + err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t, + llnode: if (os.windows.is_the_target) void else LinkedList(*ChildProcess).Node, + + pub const SpawnError = error{OutOfMemory} || os.ExecveError || os.SetIdError || + os.ChangeCurDirError || windows.CreateProcessError; pub const Term = union(enum) { - Exited: i32, - Signal: i32, - Stopped: i32, - Unknown: i32, + Exited: u32, + Signal: u32, + Stopped: u32, + Unknown: u32, }; pub const StdIo = enum { @@ -99,10 +82,8 @@ pub const ChildProcess = struct { .term = null, .env_map = null, .cwd = null, - .uid = if (is_windows) {} else - null, - .gid = if (is_windows) {} else - null, + .uid = if (os.windows.is_the_target) {} else null, + .gid = if (os.windows.is_the_target) {} else null, .stdin = null, .stdout = null, .stderr = null, @@ -122,7 +103,7 @@ pub const ChildProcess = struct { /// On success must call `kill` or `wait`. pub fn spawn(self: *ChildProcess) !void { - if (is_windows) { + if (os.windows.is_the_target) { return self.spawnWindows(); } else { return self.spawnPosix(); @@ -136,7 +117,7 @@ pub const ChildProcess = struct { /// Forcibly terminates child process and then cleans up all resources. pub fn kill(self: *ChildProcess) !Term { - if (is_windows) { + if (os.windows.is_the_target) { return self.killWindows(1); } else { return self.killPosix(); @@ -149,12 +130,7 @@ pub const ChildProcess = struct { return term; } - if (!windows.TerminateProcess(self.handle, exit_code)) { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - } + try windows.TerminateProcess(self.handle, exit_code); try self.waitUnwrappedWindows(); return self.term.?; } @@ -164,23 +140,14 @@ pub const ChildProcess = struct { self.cleanupStreams(); return term; } - const ret = posix.kill(self.pid, posix.SIGTERM); - const err = posix.getErrno(ret); - if (err > 0) { - return switch (err) { - posix.EINVAL => unreachable, - posix.EPERM => error.PermissionDenied, - posix.ESRCH => error.ProcessNotFound, - else => os.unexpectedErrorPosix(err), - }; - } + try os.kill(self.pid, os.SIGTERM); self.waitUnwrapped(); return self.term.?; } /// Blocks until child process terminates and then cleans up all resources. pub fn wait(self: *ChildProcess) !Term { - if (is_windows) { + if (os.windows.is_the_target) { return self.waitWindows(); } else { return self.waitPosix(); @@ -188,7 +155,7 @@ pub const ChildProcess = struct { } pub const ExecResult = struct { - term: os.ChildProcess.Term, + term: Term, stdout: []u8, stderr: []u8, }; @@ -250,14 +217,14 @@ pub const ChildProcess = struct { } fn waitUnwrappedWindows(self: *ChildProcess) !void { - const result = os.windowsWaitSingle(self.handle, windows.INFINITE); + const result = windows.WaitForSingleObject(self.handle, windows.INFINITE); self.term = (SpawnError!Term)(x: { var exit_code: windows.DWORD = undefined; - if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) { + if (windows.kernel32.GetExitCodeProcess(self.handle, &exit_code) == 0) { break :x Term{ .Unknown = 0 }; } else { - break :x Term{ .Exited = @bitCast(i32, exit_code) }; + break :x Term{ .Exited = exit_code }; } }); @@ -268,22 +235,12 @@ pub const ChildProcess = struct { } fn waitUnwrapped(self: *ChildProcess) void { - var status: i32 = undefined; - while (true) { - const err = posix.getErrno(posix.waitpid(self.pid, &status, 0)); - if (err > 0) { - switch (err) { - posix.EINTR => continue, - else => unreachable, - } - } - self.cleanupStreams(); - self.handleWaitResult(status); - return; - } + const status = os.waitpid(self.pid, 0); + self.cleanupStreams(); + self.handleWaitResult(status); } - fn handleWaitResult(self: *ChildProcess, status: i32) void { + fn handleWaitResult(self: *ChildProcess, status: u32) void { self.term = self.cleanupAfterWait(status); } @@ -302,7 +259,7 @@ pub const ChildProcess = struct { } } - fn cleanupAfterWait(self: *ChildProcess, status: i32) !Term { + fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term { defer { os.close(self.err_pipe[0]); os.close(self.err_pipe[1]); @@ -324,35 +281,35 @@ pub const ChildProcess = struct { return statusToTerm(status); } - fn statusToTerm(status: i32) Term { - return if (posix.WIFEXITED(status)) - Term{ .Exited = posix.WEXITSTATUS(status) } - else if (posix.WIFSIGNALED(status)) - Term{ .Signal = posix.WTERMSIG(status) } - else if (posix.WIFSTOPPED(status)) - Term{ .Stopped = posix.WSTOPSIG(status) } + fn statusToTerm(status: u32) Term { + return if (os.WIFEXITED(status)) + Term{ .Exited = os.WEXITSTATUS(status) } + else if (os.WIFSIGNALED(status)) + Term{ .Signal = os.WTERMSIG(status) } + else if (os.WIFSTOPPED(status)) + Term{ .Stopped = os.WSTOPSIG(status) } else Term{ .Unknown = status }; } fn spawnPosix(self: *ChildProcess) !void { - const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try makePipe() else undefined; + const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe() else undefined; errdefer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); }; - const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try makePipe() else undefined; + const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try os.pipe() else undefined; errdefer if (self.stdout_behavior == StdIo.Pipe) { destroyPipe(stdout_pipe); }; - const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try makePipe() else undefined; + const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try os.pipe() else undefined; errdefer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); }; const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); - const dev_null_fd = if (any_ignore) try os.posixOpenC(c"/dev/null", posix.O_RDWR, 0) else undefined; + const dev_null_fd = if (any_ignore) try os.openC(c"/dev/null", os.O_RDWR, 0) else undefined; defer { if (any_ignore) os.close(dev_null_fd); } @@ -364,7 +321,7 @@ pub const ChildProcess = struct { break :x env_map; } else x: { we_own_env_map = true; - env_map_owned = try os.getEnvMap(self.allocator); + env_map_owned = try process.getEnvMap(self.allocator); break :x &env_map_owned; }; defer { @@ -373,65 +330,58 @@ pub const ChildProcess = struct { // This pipe is used to communicate errors between the time of fork // and execve from the child process to the parent process. - const err_pipe = try makePipe(); + const err_pipe = try os.pipe(); errdefer destroyPipe(err_pipe); - const pid_result = posix.fork(); - const pid_err = posix.getErrno(pid_result); - if (pid_err > 0) { - return switch (pid_err) { - posix.EAGAIN, posix.ENOMEM, posix.ENOSYS => error.SystemResources, - else => os.unexpectedErrorPosix(pid_err), - }; - } + const pid_result = try os.fork(); if (pid_result == 0) { // we are the child - setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); - setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); - setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); + setUpChildIo(self.stdin_behavior, stdin_pipe[0], os.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); + setUpChildIo(self.stdout_behavior, stdout_pipe[1], os.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); + setUpChildIo(self.stderr_behavior, stderr_pipe[1], os.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); - if (self.stdin_behavior == StdIo.Pipe) { + if (self.stdin_behavior == .Pipe) { os.close(stdin_pipe[0]); os.close(stdin_pipe[1]); } - if (self.stdout_behavior == StdIo.Pipe) { + if (self.stdout_behavior == .Pipe) { os.close(stdout_pipe[0]); os.close(stdout_pipe[1]); } - if (self.stderr_behavior == StdIo.Pipe) { + if (self.stderr_behavior == .Pipe) { os.close(stderr_pipe[0]); os.close(stderr_pipe[1]); } if (self.cwd) |cwd| { - os.changeCurDir(cwd) catch |err| forkChildErrReport(err_pipe[1], err); + os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err); } if (self.gid) |gid| { - os.posix_setregid(gid, gid) catch |err| forkChildErrReport(err_pipe[1], err); + os.setregid(gid, gid) catch |err| forkChildErrReport(err_pipe[1], err); } if (self.uid) |uid| { - os.posix_setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err); + os.setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err); } - os.posixExecve(self.argv, env_map, self.allocator) catch |err| forkChildErrReport(err_pipe[1], err); + os.execve(self.allocator, self.argv, env_map) catch |err| forkChildErrReport(err_pipe[1], err); } // we are the parent const pid = @intCast(i32, pid_result); if (self.stdin_behavior == StdIo.Pipe) { - self.stdin = os.File.openHandle(stdin_pipe[1]); + self.stdin = File.openHandle(stdin_pipe[1]); } else { self.stdin = null; } if (self.stdout_behavior == StdIo.Pipe) { - self.stdout = os.File.openHandle(stdout_pipe[0]); + self.stdout = File.openHandle(stdout_pipe[0]); } else { self.stdout = null; } if (self.stderr_behavior == StdIo.Pipe) { - self.stderr = os.File.openHandle(stderr_pipe[0]); + self.stderr = File.openHandle(stderr_pipe[0]); } else { self.stderr = null; } @@ -462,7 +412,7 @@ pub const ChildProcess = struct { const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); const nul_handle = if (any_ignore) blk: { - break :blk try os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL); + break :blk try windows.CreateFile("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null); } else blk: { break :blk undefined; }; @@ -470,7 +420,7 @@ pub const ChildProcess = struct { if (any_ignore) os.close(nul_handle); } if (any_ignore) { - try windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0); + try windows.SetHandleInformation(nul_handle, windows.HANDLE_FLAG_INHERIT, 0); } var g_hChildStd_IN_Rd: ?windows.HANDLE = null; @@ -483,7 +433,7 @@ pub const ChildProcess = struct { g_hChildStd_IN_Rd = nul_handle; }, StdIo.Inherit => { - g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE); + g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch null; }, StdIo.Close => { g_hChildStd_IN_Rd = null; @@ -503,7 +453,7 @@ pub const ChildProcess = struct { g_hChildStd_OUT_Wr = nul_handle; }, StdIo.Inherit => { - g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE); + g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch null; }, StdIo.Close => { g_hChildStd_OUT_Wr = null; @@ -523,7 +473,7 @@ pub const ChildProcess = struct { g_hChildStd_ERR_Wr = nul_handle; }, StdIo.Inherit => { - g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE); + g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null; }, StdIo.Close => { g_hChildStd_ERR_Wr = null; @@ -565,7 +515,7 @@ pub const ChildProcess = struct { defer if (cwd_w) |cwd| self.allocator.free(cwd); const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; - const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null; + const maybe_envp_buf = if (self.env_map) |env_map| try createWindowsEnvBlock(self.allocator, env_map) else null; defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; @@ -573,7 +523,7 @@ pub const ChildProcess = struct { // to match posix semantics const app_name = x: { if (self.cwd) |cwd| { - const resolved = try os.path.resolve(self.allocator, [][]const u8{ cwd, self.argv[0] }); + const resolved = try fs.path.resolve(self.allocator, [][]const u8{ cwd, self.argv[0] }); defer self.allocator.free(resolved); break :x try cstr.addNullByte(self.allocator, resolved); } else { @@ -591,12 +541,12 @@ pub const ChildProcess = struct { windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| { if (no_path_err != error.FileNotFound) return no_path_err; - const PATH = try os.getEnvVarOwned(self.allocator, "PATH"); + const PATH = try process.getEnvVarOwned(self.allocator, "PATH"); defer self.allocator.free(PATH); var it = mem.tokenize(PATH, ";"); while (it.next()) |search_path| { - const joined_path = try os.path.join(self.allocator, [][]const u8{ search_path, app_name }); + const joined_path = try fs.path.join(self.allocator, [][]const u8{ search_path, app_name }); defer self.allocator.free(joined_path); const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path); @@ -616,17 +566,17 @@ pub const ChildProcess = struct { }; if (g_hChildStd_IN_Wr) |h| { - self.stdin = os.File.openHandle(h); + self.stdin = File.openHandle(h); } else { self.stdin = null; } if (g_hChildStd_OUT_Rd) |h| { - self.stdout = os.File.openHandle(h); + self.stdout = File.openHandle(h); } else { self.stdout = null; } if (g_hChildStd_ERR_Rd) |h| { - self.stderr = os.File.openHandle(h); + self.stderr = File.openHandle(h); } else { self.stderr = null; } @@ -648,10 +598,10 @@ pub const ChildProcess = struct { fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { switch (stdio) { - StdIo.Pipe => try os.posixDup2(pipe_fd, std_fileno), + StdIo.Pipe => try os.dup2(pipe_fd, std_fileno), StdIo.Close => os.close(std_fileno), StdIo.Inherit => {}, - StdIo.Ignore => try os.posixDup2(dev_null_fd, std_fileno), + StdIo.Ignore => try os.dup2(dev_null_fd, std_fileno), } } }; @@ -674,7 +624,7 @@ fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, c // However this would imply that programs compiled with -DUNICODE could not pass // environment variables to programs that were not, which seems unlikely. // More investigation is needed. - if (windows.CreateProcessW( + return windows.CreateProcessW( app_name, cmd_line, null, @@ -685,16 +635,7 @@ fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, c cwd_ptr, lpStartupInfo, lpProcessInformation, - ) == 0) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - windows.ERROR.INVALID_PARAMETER => unreachable, - windows.ERROR.INVALID_NAME => return error.InvalidName, - else => return os.unexpectedErrorWindows(err), - } - } + ); } /// Caller must dealloc. @@ -740,61 +681,27 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { if (wr) |h| os.close(h); } -// TODO: workaround for bug where the `const` from `&const` is dropped when the type is -// a namespace field lookup -const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; - -fn windowsMakePipe(rd: *windows.HANDLE, wr: *windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { - if (windows.CreatePipe(rd, wr, sattr, 0) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - } -} - -fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) !void { - if (windows.SetHandleInformation(h, mask, flags) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - } -} - -fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { +fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; - try windowsMakePipe(&rd_h, &wr_h, sattr); + try windows.CreatePipe(&rd_h, &wr_h, sattr); errdefer windowsDestroyPipe(rd_h, wr_h); - try windowsSetHandleInfo(wr_h, windows.HANDLE_FLAG_INHERIT, 0); + try windows.SetHandleInformation(wr_h, windows.HANDLE_FLAG_INHERIT, 0); rd.* = rd_h; wr.* = wr_h; } -fn windowsMakePipeOut(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { +fn windowsMakePipeOut(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; - try windowsMakePipe(&rd_h, &wr_h, sattr); + try windows.CreatePipe(&rd_h, &wr_h, sattr); errdefer windowsDestroyPipe(rd_h, wr_h); - try windowsSetHandleInfo(rd_h, windows.HANDLE_FLAG_INHERIT, 0); + try windows.SetHandleInformation(rd_h, windows.HANDLE_FLAG_INHERIT, 0); rd.* = rd_h; wr.* = wr_h; } -fn makePipe() ![2]i32 { - var fds: [2]i32 = undefined; - const err = posix.getErrno(posix.pipe(&fds)); - if (err > 0) { - return switch (err) { - posix.EMFILE, posix.ENFILE => error.SystemResources, - else => os.unexpectedErrorPosix(err), - }; - } - return fds; -} - -fn destroyPipe(pipe: [2]i32) void { +fn destroyPipe(pipe: [2]os.fd_t) void { os.close(pipe[0]); os.close(pipe[1]); } @@ -803,17 +710,54 @@ fn destroyPipe(pipe: [2]i32) void { // Then the child exits. fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { writeIntFd(fd, ErrInt(@errorToInt(err))) catch {}; - posix.exit(1); + os.exit(1); } const ErrInt = @IntType(false, @sizeOf(anyerror) * 8); fn writeIntFd(fd: i32, value: ErrInt) !void { - const stream = &os.File.openHandle(fd).outStream().stream; + const stream = &File.openHandle(fd).outStream().stream; stream.writeIntNative(ErrInt, value) catch return error.SystemResources; } fn readIntFd(fd: i32) !ErrInt { - const stream = &os.File.openHandle(fd).inStream().stream; + const stream = &File.openHandle(fd).inStream().stream; return stream.readIntNative(ErrInt) catch return error.SystemResources; } + +/// Caller must free result. +pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 { + // count bytes needed + const max_chars_needed = x: { + var max_chars_needed: usize = 4; // 4 for the final 4 null bytes + var it = env_map.iterator(); + while (it.next()) |pair| { + // +1 for '=' + // +1 for null byte + max_chars_needed += pair.key.len + pair.value.len + 2; + } + break :x max_chars_needed; + }; + const result = try allocator.alloc(u16, max_chars_needed); + errdefer allocator.free(result); + + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| { + i += try unicode.utf8ToUtf16Le(result[i..], pair.key); + result[i] = '='; + i += 1; + i += try unicode.utf8ToUtf16Le(result[i..], pair.value); + result[i] = 0; + i += 1; + } + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + return allocator.shrink(result, i); +} diff --git a/std/coff.zig b/std/coff.zig index c31d2d8233..87f3f089de 100644 --- a/std/coff.zig +++ b/std/coff.zig @@ -3,6 +3,7 @@ const std = @import("std.zig"); const io = std.io; const mem = std.mem; const os = std.os; +const File = std.fs.File; const ArrayList = std.ArrayList; @@ -28,7 +29,7 @@ pub const CoffError = error{ }; pub const Coff = struct { - in_file: os.File, + in_file: File, allocator: *mem.Allocator, coff_header: CoffHeader, @@ -77,7 +78,7 @@ pub const Coff = struct { try self.loadOptionalHeader(&file_stream); } - fn loadOptionalHeader(self: *Coff, file_stream: *os.File.InStream) !void { + fn loadOptionalHeader(self: *Coff, file_stream: *File.InStream) !void { const in = &file_stream.stream; self.pe_header.magic = try in.readIntLittle(u16); // For now we're only interested in finding the reference to the .pdb, @@ -91,7 +92,7 @@ pub const Coff = struct { } else return error.InvalidPEMagic; - try self.in_file.seekForward(skip_size); + try self.in_file.seekBy(skip_size); const number_of_rva_and_sizes = try in.readIntLittle(u32); if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) diff --git a/std/crypto.zig b/std/crypto.zig index 2429441fc7..2b57de9e60 100644 --- a/std/crypto.zig +++ b/std/crypto.zig @@ -32,6 +32,9 @@ pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; pub const X25519 = @import("crypto/x25519.zig").X25519; +const std = @import("std.zig"); +pub const randomBytes = std.os.getrandom; + test "crypto" { _ = @import("crypto/blake2.zig"); _ = @import("crypto/chacha20.zig"); diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index 73a2a86124..aee06571a0 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -1,6 +1,6 @@ const builtin = @import("builtin"); const std = @import("std"); -const time = std.os.time; +const time = std.time; const Timer = time.Timer; const crypto = @import("../crypto.zig"); diff --git a/std/cstr.zig b/std/cstr.zig index 49d6373732..dd28e50449 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -9,11 +9,6 @@ pub const line_sep = switch (builtin.os) { else => "\n", }; -/// Deprecated, use mem.len -pub fn len(ptr: [*]const u8) usize { - return mem.len(u8, ptr); -} - pub fn cmp(a: [*]const u8, b: [*]const u8) i8 { var index: usize = 0; while (a[index] == b[index] and a[index] != 0) : (index += 1) {} @@ -26,16 +21,6 @@ pub fn cmp(a: [*]const u8, b: [*]const u8) i8 { } } -/// Deprecated, use mem.toSliceConst -pub fn toSliceConst(str: [*]const u8) []const u8 { - return mem.toSliceConst(u8, str); -} - -/// Deprecated, use mem.toSlice -pub fn toSlice(str: [*]u8) []u8 { - return mem.toSlice(u8, str); -} - test "cstr fns" { comptime testCStrFnsImpl(); testCStrFnsImpl(); @@ -43,7 +28,7 @@ test "cstr fns" { fn testCStrFnsImpl() void { testing.expect(cmp(c"aoeu", c"aoez") == -1); - testing.expect(len(c"123456789") == 9); + testing.expect(mem.len(u8, c"123456789") == 9); } /// Returns a mutable slice with 1 more byte of length which is a null byte. diff --git a/std/debug.zig b/std/debug.zig index 45abfda88e..e2f4a67755 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -3,15 +3,18 @@ const math = std.math; const mem = std.mem; const io = std.io; const os = std.os; +const fs = std.fs; +const process = std.process; const elf = std.elf; const DW = std.dwarf; const macho = std.macho; const coff = std.coff; const pdb = std.pdb; -const windows = os.windows; const ArrayList = std.ArrayList; const builtin = @import("builtin"); const maxInt = std.math.maxInt; +const File = std.fs.File; +const windows = std.os.windows; const leb = @import("debug/leb128.zig"); @@ -19,8 +22,8 @@ pub const FailingAllocator = @import("debug/failing_allocator.zig").FailingAlloc pub const failing_allocator = &FailingAllocator.init(global_allocator, 0).allocator; pub const runtime_safety = switch (builtin.mode) { - builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true, - builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, }; const Module = struct { @@ -36,10 +39,10 @@ const Module = struct { /// Tries to write to stderr, unbuffered, and ignores any error returned. /// Does not append a newline. -var stderr_file: os.File = undefined; -var stderr_file_out_stream: os.File.OutStream = undefined; +var stderr_file: File = undefined; +var stderr_file_out_stream: File.OutStream = undefined; -var stderr_stream: ?*io.OutStream(os.File.WriteError) = null; +var stderr_stream: ?*io.OutStream(File.WriteError) = null; var stderr_mutex = std.Mutex.init(); pub fn warn(comptime fmt: []const u8, args: ...) void { const held = stderr_mutex.acquire(); @@ -48,7 +51,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void { stderr.print(fmt, args) catch return; } -pub fn getStderrStream() !*io.OutStream(os.File.WriteError) { +pub fn getStderrStream() !*io.OutStream(File.WriteError) { if (stderr_stream) |st| { return st; } else { @@ -75,13 +78,17 @@ pub fn getSelfDebugInfo() !*DebugInfo { fn wantTtyColor() bool { var bytes: [128]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; - return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); + return if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); } /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. /// TODO multithreaded awareness pub fn dumpCurrentStackTrace(start_addr: ?usize) void { const stderr = getStderrStream() catch return; + if (os.wasi.is_the_target) { + stderr.print("Unable to dump stack trace: unimplemented on WASI\n") catch return; + return; + } const debug_info = getSelfDebugInfo() catch |err| { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; return; @@ -99,47 +106,44 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { /// chopping off the irrelevant frames and shifting so that the returned addresses pointer /// equals the passed in addresses pointer. pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void { - switch (builtin.os) { - builtin.Os.windows => { - const addrs = stack_trace.instruction_addresses; - const u32_addrs_len = @intCast(u32, addrs.len); - const first_addr = first_address orelse { - stack_trace.index = windows.RtlCaptureStackBackTrace( - 0, - u32_addrs_len, - @ptrCast(**c_void, addrs.ptr), - null, - ); - return; - }; - var addr_buf_stack: [32]usize = undefined; - const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; - const n = windows.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**c_void, addr_buf.ptr), null); - const first_index = for (addr_buf[0..n]) |addr, i| { - if (addr == first_addr) { - break i; - } - } else { - stack_trace.index = 0; + if (windows.is_the_target) { + const addrs = stack_trace.instruction_addresses; + const u32_addrs_len = @intCast(u32, addrs.len); + const first_addr = first_address orelse { + stack_trace.index = windows.ntdll.RtlCaptureStackBackTrace( + 0, + u32_addrs_len, + @ptrCast(**c_void, addrs.ptr), + null, + ); + return; + }; + var addr_buf_stack: [32]usize = undefined; + const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; + const n = windows.ntdll.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**c_void, addr_buf.ptr), null); + const first_index = for (addr_buf[0..n]) |addr, i| { + if (addr == first_addr) { + break i; + } + } else { + stack_trace.index = 0; + return; + }; + const slice = addr_buf[first_index..n]; + // We use a for loop here because slice and addrs may alias. + for (slice) |addr, i| { + addrs[i] = addr; + } + stack_trace.index = slice.len; + } else { + var it = StackIterator.init(first_address); + for (stack_trace.instruction_addresses) |*addr, i| { + addr.* = it.next() orelse { + stack_trace.index = i; return; }; - const slice = addr_buf[first_index..n]; - // We use a for loop here because slice and addrs may alias. - for (slice) |addr, i| { - addrs[i] = addr; - } - stack_trace.index = slice.len; - }, - else => { - var it = StackIterator.init(first_address); - for (stack_trace.instruction_addresses) |*addr, i| { - addr.* = it.next() orelse { - stack_trace.index = i; - return; - }; - } - stack_trace.index = stack_trace.instruction_addresses.len; - }, + } + stack_trace.index = stack_trace.instruction_addresses.len; } } @@ -147,6 +151,10 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace /// TODO multithreaded awareness pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { const stderr = getStderrStream() catch return; + if (os.wasi.is_the_target) { + stderr.print("Unable to dump stack trace: unimplemented on WASI\n") catch return; + return; + } const debug_info = getSelfDebugInfo() catch |err| { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; return; @@ -259,9 +267,8 @@ pub const StackIterator = struct { }; pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { - switch (builtin.os) { - builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr), - else => {}, + if (windows.is_the_target) { + return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr); } var it = StackIterator.init(start_addr); while (it.next()) |return_address| { @@ -276,7 +283,7 @@ pub fn writeCurrentStackTraceWindows( start_addr: ?usize, ) !void { var addr_buf: [1024]usize = undefined; - const n = windows.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**c_void, &addr_buf), null); + const n = windows.ntdll.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**c_void, &addr_buf), null); const addrs = addr_buf[0..n]; var start_i: usize = if (start_addr) |saddr| blk: { for (addrs) |addr, i| { @@ -290,17 +297,18 @@ pub fn writeCurrentStackTraceWindows( } pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { - switch (builtin.os) { - builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), - builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), - else => return error.UnsupportedOperatingSystem, + if (windows.is_the_target) { + return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color); } + if (os.darwin.is_the_target) { + return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color); + } + return printSourceAtAddressPosix(debug_info, out_stream, address, tty_color); } fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void { const allocator = getDebugInfoAllocator(); - const base_address = os.getBaseAddress(); + const base_address = process.getBaseAddress(); const relative_address = relocated_address - base_address; var coff_section: *coff.Section = undefined; @@ -330,7 +338,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres const mod = &di.modules[mod_index]; try populateModule(di, mod); - const obj_basename = os.path.basename(mod.obj_file_name); + const obj_basename = fs.path.basename(mod.obj_file_name); var symbol_i: usize = 0; const symbol_name = while (symbol_i != mod.symbols.len) { @@ -510,7 +518,7 @@ const TtyColor = enum { /// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt fn setTtyColor(tty_color: TtyColor) void { - if (os.supportsAnsiEscapeCodes(stderr_file.handle)) { + if (stderr_file.supportsAnsiEscapeCodes()) { switch (tty_color) { TtyColor.Red => { stderr_file.write(RED) catch return; @@ -540,29 +548,29 @@ fn setTtyColor(tty_color: TtyColor) void { S.init_attrs = true; var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; // TODO handle error - _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info); + _ = windows.kernel32.GetConsoleScreenBufferInfo(stderr_file.handle, &info); S.attrs = info.wAttributes; } // TODO handle errors switch (tty_color) { TtyColor.Red => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY) catch {}; }, TtyColor.Green => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY) catch {}; }, TtyColor.Cyan => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; }, TtyColor.White, TtyColor.Bold => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; }, TtyColor.Dim => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY) catch {}; }, TtyColor.Reset => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs) catch {}; }, } } @@ -633,7 +641,7 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach } fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { - const base_addr = std.os.getBaseAddress(); + const base_addr = process.getBaseAddress(); const adjusted_addr = 0x100000000 + (address - base_addr); const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { @@ -648,7 +656,7 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt const symbol_name = mem.toSliceConst(u8, di.strings.ptr + symbol.nlist.n_strx); const compile_unit_name = if (symbol.ofile) |ofile| blk: { const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); - break :blk os.path.basename(ofile_path); + break :blk fs.path.basename(ofile_path); } else "???"; if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { defer line_info.deinit(); @@ -715,7 +723,7 @@ pub fn printSourceAtAddressDwarf( } } -pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { +pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs); } @@ -775,16 +783,17 @@ pub const OpenSelfDebugInfoError = error{ }; pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { - switch (builtin.os) { - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return openSelfDebugInfoLinux(allocator), - builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), - builtin.Os.windows => return openSelfDebugInfoWindows(allocator), - else => return error.UnsupportedOperatingSystem, + if (windows.is_the_target) { + return openSelfDebugInfoWindows(allocator); + } + if (os.darwin.is_the_target) { + return openSelfDebugInfoMacOs(allocator); } + return openSelfDebugInfoPosix(allocator); } fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { - const self_file = try os.openSelfExe(); + const self_file = try fs.openSelfExe(); defer self_file.close(); const coff_obj = try allocator.create(coff.Coff); @@ -811,7 +820,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { const len = try di.coff.getPdbPath(path_buf[0..]); const raw_path = path_buf[0..len]; - const path = try os.path.resolve(allocator, [][]const u8{raw_path}); + const path = try fs.path.resolve(allocator, [][]const u8{raw_path}); try di.pdb.openFile(di.coff, path); @@ -893,7 +902,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { if (this_record_len % 4 != 0) { const round_to_next_4 = (this_record_len | 0x3) + 1; const march_forward_bytes = round_to_next_4 - this_record_len; - try dbi.seekForward(march_forward_bytes); + try dbi.seekBy(@intCast(isize, march_forward_bytes)); this_record_len += march_forward_bytes; } @@ -1001,29 +1010,27 @@ pub fn openElfDebugInfo( return di; } -fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DwarfInfo { +fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo { const S = struct { - var self_exe_file: os.File = undefined; + var self_exe_file: File = undefined; var self_exe_mmap_seekable: io.SliceSeekableInStream = undefined; }; - S.self_exe_file = try os.openSelfExe(); + S.self_exe_file = try fs.openSelfExe(); errdefer S.self_exe_file.close(); - const self_exe_mmap_len = try S.self_exe_file.getEndPos(); - const self_exe_mmap = os.posix.mmap( + const self_exe_mmap_len = mem.alignForward(try S.self_exe_file.getEndPos(), mem.page_size); + const self_exe_mmap = try os.mmap( null, self_exe_mmap_len, - os.posix.PROT_READ, - os.posix.MAP_SHARED, + os.PROT_READ, + os.MAP_SHARED, S.self_exe_file.handle, 0, ); - if (self_exe_mmap == os.posix.MAP_FAILED) return error.OutOfMemory; - errdefer assert(os.posix.munmap(self_exe_mmap, self_exe_mmap_len) == 0); + errdefer os.munmap(self_exe_mmap); - const file_mmap_slice = @intToPtr([*]const u8, self_exe_mmap)[0..self_exe_mmap_len]; - S.self_exe_mmap_seekable = io.SliceSeekableInStream.init(file_mmap_slice); + S.self_exe_mmap_seekable = io.SliceSeekableInStream.init(self_exe_mmap); return openElfDebugInfo( allocator, @@ -1112,11 +1119,11 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { } fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { - var f = try os.File.openRead(line_info.file_name); + var f = try File.openRead(line_info.file_name); defer f.close(); // TODO fstat and make sure that the file has the correct size - var buf: [os.page_size]u8 = undefined; + var buf: [mem.page_size]u8 = undefined; var line: usize = 1; var column: usize = 1; var abs_index: usize = 0; @@ -1195,7 +1202,7 @@ pub const DwarfInfo = struct { }; pub const DebugInfo = switch (builtin.os) { - builtin.Os.macosx, builtin.Os.ios => struct { + .macosx, .ios, .watchos, .tvos => struct { symbols: []const MachoSymbol, strings: []const u8, ofiles: OFileTable, @@ -1211,13 +1218,13 @@ pub const DebugInfo = switch (builtin.os) { return self.ofiles.allocator; } }, - builtin.Os.uefi, builtin.Os.windows => struct { + .uefi, .windows => struct { pdb: pdb.Pdb, coff: *coff.Coff, sect_contribs: []pdb.SectionContribEntry, modules: []Module, }, - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => DwarfInfo, + .linux, .freebsd, .netbsd => DwarfInfo, else => @compileError("Unsupported OS"), }; @@ -1411,7 +1418,7 @@ const LineNumberProgram = struct { return error.InvalidDebugInfo; } else self.include_dirs[file_entry.dir_index]; - const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name }); + const file_name = try fs.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name }); errdefer self.file_entries.allocator.free(file_name); return LineInfo{ .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0, @@ -1938,7 +1945,7 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr }, else => { const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; - try di.dwarf_seekable_stream.seekForward(fwd_amt); + try di.dwarf_seekable_stream.seekBy(fwd_amt); }, } } else if (opcode >= opcode_base) { @@ -1990,7 +1997,7 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr else => { if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; const len_bytes = standard_opcode_lengths[opcode - 1]; - try di.dwarf_seekable_stream.seekForward(len_bytes); + try di.dwarf_seekable_stream.seekBy(len_bytes); }, } } diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig index 57122fd69c..3ae3b4c66a 100644 --- a/std/dynamic_library.zig +++ b/std/dynamic_library.zig @@ -1,21 +1,17 @@ const builtin = @import("builtin"); -const Os = builtin.Os; const std = @import("std.zig"); const mem = std.mem; -const cstr = std.cstr; const os = std.os; const assert = std.debug.assert; const testing = std.testing; const elf = std.elf; -const linux = os.linux; -const windows = os.windows; -const win_util = @import("os/windows/util.zig"); +const windows = std.os.windows; const maxInt = std.math.maxInt; pub const DynLib = switch (builtin.os) { - Os.linux => LinuxDynLib, - Os.windows => WindowsDynLib, + .linux => LinuxDynLib, + .windows => WindowsDynLib, else => void, }; @@ -105,39 +101,35 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { pub const LinuxDynLib = struct { elf_lib: ElfLib, fd: i32, - map_addr: usize, - map_size: usize, + memory: []align(mem.page_size) u8, /// Trusts the file pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib { - const fd = try std.os.posixOpen(path, 0, linux.O_RDONLY | linux.O_CLOEXEC); - errdefer std.os.close(fd); + const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC); + errdefer os.close(fd); - const size = @intCast(usize, (try std.os.posixFStat(fd)).size); + const size = @intCast(usize, (try os.fstat(fd)).size); - const addr = linux.mmap( + const bytes = try os.mmap( null, size, - linux.PROT_READ | linux.PROT_EXEC, - linux.MAP_PRIVATE | linux.MAP_LOCKED, + os.PROT_READ | os.PROT_EXEC, + os.MAP_PRIVATE | os.MAP_LOCKED, fd, 0, ); - errdefer _ = linux.munmap(addr, size); - - const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size]; + errdefer os.munmap(bytes); return DynLib{ .elf_lib = try ElfLib.init(bytes), .fd = fd, - .map_addr = addr, - .map_size = size, + .memory = bytes, }; } pub fn close(self: *DynLib) void { - _ = linux.munmap(self.map_addr, self.map_size); - std.os.close(self.fd); + os.munmap(self.memory); + os.close(self.fd); self.* = undefined; } @@ -149,7 +141,7 @@ pub const LinuxDynLib = struct { pub const ElfLib = struct { strings: [*]u8, syms: [*]elf.Sym, - hashtab: [*]linux.Elf_Symndx, + hashtab: [*]os.Elf_Symndx, versym: ?[*]u16, verdef: ?*elf.Verdef, base: usize, @@ -184,7 +176,7 @@ pub const ElfLib = struct { var maybe_strings: ?[*]u8 = null; var maybe_syms: ?[*]elf.Sym = null; - var maybe_hashtab: ?[*]linux.Elf_Symndx = null; + var maybe_hashtab: ?[*]os.Elf_Symndx = null; var maybe_versym: ?[*]u16 = null; var maybe_verdef: ?*elf.Verdef = null; @@ -195,7 +187,7 @@ pub const ElfLib = struct { switch (dynv[i]) { elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p), elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p), - elf.DT_HASH => maybe_hashtab = @intToPtr([*]linux.Elf_Symndx, p), + elf.DT_HASH => maybe_hashtab = @intToPtr([*]os.Elf_Symndx, p), elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p), elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p), else => {}, @@ -225,7 +217,7 @@ pub const ElfLib = struct { if (0 == (u32(1) << @intCast(u5, self.syms[i].st_info & 0xf) & OK_TYPES)) continue; if (0 == (u32(1) << @intCast(u5, self.syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == self.syms[i].st_shndx) continue; - if (!mem.eql(u8, name, cstr.toSliceConst(self.strings + self.syms[i].st_name))) continue; + if (!mem.eql(u8, name, mem.toSliceConst(u8, self.strings + self.syms[i].st_name))) continue; if (maybe_versym) |versym| { if (!checkver(self.verdef.?, versym[i], vername, self.strings)) continue; @@ -248,7 +240,7 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [ def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); } const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); - return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name)); + return mem.eql(u8, vername, mem.toSliceConst(u8, strings + aux.vda_name)); } pub const WindowsDynLib = struct { @@ -256,36 +248,28 @@ pub const WindowsDynLib = struct { dll: windows.HMODULE, pub fn open(allocator: *mem.Allocator, path: []const u8) !WindowsDynLib { - const wpath = try win_util.sliceToPrefixedFileW(path); + const wpath = try windows.sliceToPrefixedFileW(path); return WindowsDynLib{ .allocator = allocator, - .dll = windows.LoadLibraryW(&wpath) orelse { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound, - else => return os.unexpectedErrorWindows(err), - } - }, + .dll = try windows.LoadLibraryW(&wpath), }; } pub fn close(self: *WindowsDynLib) void { - assert(windows.FreeLibrary(self.dll) != 0); + windows.FreeLibrary(self.dll); self.* = undefined; } pub fn lookup(self: *WindowsDynLib, name: []const u8) ?usize { - return @ptrToInt(windows.GetProcAddress(self.dll, name.ptr)); + return @ptrToInt(windows.kernel32.GetProcAddress(self.dll, name.ptr)); } }; test "dynamic_library" { const libname = switch (builtin.os) { - Os.linux => "invalid_so.so", - Os.windows => "invalid_dll.dll", + .linux => "invalid_so.so", + .windows => "invalid_dll.dll", else => return, }; diff --git a/std/elf.zig b/std/elf.zig index 39617d3cf4..c605a177a5 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -6,6 +6,7 @@ const math = std.math; const mem = std.mem; const debug = std.debug; const InStream = std.stream.InStream; +const File = std.fs.File; pub const AT_NULL = 0; pub const AT_IGNORE = 1; @@ -367,7 +368,7 @@ pub const Elf = struct { string_section: *SectionHeader, section_headers: []SectionHeader, allocator: *mem.Allocator, - prealloc_file: os.File, + prealloc_file: File, /// Call close when done. pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void { @@ -375,7 +376,7 @@ pub const Elf = struct { } /// Call close when done. - pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: os.File) !void { + pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: File) !void { @compileError("TODO implement"); } @@ -410,7 +411,7 @@ pub const Elf = struct { if (version_byte != 1) return error.InvalidFormat; // skip over padding - try seekable_stream.seekForward(9); + try seekable_stream.seekBy(9); elf.file_type = switch (try in.readInt(u16, elf.endian)) { 1 => FileType.Relocatable, @@ -447,7 +448,7 @@ pub const Elf = struct { } // skip over flags - try seekable_stream.seekForward(4); + try seekable_stream.seekBy(4); const header_size = try in.readInt(u16, elf.endian); if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) { diff --git a/std/event/fs.zig b/std/event/fs.zig index 9756d15a76..55731a607c 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -5,9 +5,10 @@ const assert = std.debug.assert; const testing = std.testing; const os = std.os; const mem = std.mem; -const posix = os.posix; const windows = os.windows; const Loop = event.Loop; +const fd_t = os.fd_t; +const File = std.fs.File; pub const RequestNode = std.atomic.Queue(Request).Node; @@ -30,53 +31,53 @@ pub const Request = struct { End, // special - means the fs thread should exit pub const PWriteV = struct { - fd: os.FileHandle, - iov: []const os.posix.iovec_const, + fd: fd_t, + iov: []const os.iovec_const, offset: usize, result: Error!void, - pub const Error = os.PosixWriteError; + pub const Error = os.WriteError; }; pub const PReadV = struct { - fd: os.FileHandle, - iov: []const os.posix.iovec, + fd: fd_t, + iov: []const os.iovec, offset: usize, result: Error!usize, - pub const Error = os.PosixReadError; + pub const Error = os.ReadError; }; pub const Open = struct { /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265 path: []const u8, flags: u32, - mode: os.File.Mode, - result: Error!os.FileHandle, + mode: File.Mode, + result: Error!fd_t, - pub const Error = os.File.OpenError; + pub const Error = File.OpenError; }; pub const WriteFile = struct { /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265 path: []const u8, contents: []const u8, - mode: os.File.Mode, + mode: File.Mode, result: Error!void, - pub const Error = os.File.OpenError || os.File.WriteError; + pub const Error = File.OpenError || File.WriteError; }; pub const Close = struct { - fd: os.FileHandle, + fd: fd_t, }; }; }; -pub const PWriteVError = error{OutOfMemory} || os.File.WriteError; +pub const PWriteVError = error{OutOfMemory} || File.WriteError; /// data - just the inner references - must live until pwritev promise completes. -pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) PWriteVError!void { +pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void { // workaround for https://github.com/ziglang/zig/issues/1194 suspend { resume @handle(); @@ -87,11 +88,11 @@ pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, o builtin.Os.freebsd, builtin.Os.netbsd, => { - const iovecs = try loop.allocator.alloc(os.posix.iovec_const, data.len); + const iovecs = try loop.allocator.alloc(os.iovec_const, data.len); defer loop.allocator.free(iovecs); for (data) |buf, i| { - iovecs[i] = os.posix.iovec_const{ + iovecs[i] = os.iovec_const{ .iov_base = buf.ptr, .iov_len = buf.len, }; @@ -109,7 +110,7 @@ pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, o } /// data must outlive the returned promise -pub async fn pwritevWindows(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) os.WindowsWriteError!void { +pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void { if (data.len == 0) return; if (data.len == 1) return await (async pwriteWindows(loop, fd, data[0], offset) catch unreachable); @@ -121,7 +122,7 @@ pub async fn pwritevWindows(loop: *Loop, fd: os.FileHandle, data: []const []cons } } -pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, offset: u64) os.WindowsWriteError!void { +pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void { // workaround for https://github.com/ziglang/zig/issues/1194 suspend { resume @handle(); @@ -146,33 +147,32 @@ pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, off errdefer loop.finishOneEvent(); errdefer { - _ = windows.CancelIoEx(fd, &resume_node.base.overlapped); + _ = windows.kernel32.CancelIoEx(fd, &resume_node.base.overlapped); } suspend { - _ = windows.WriteFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &resume_node.base.overlapped); + _ = windows.kernel32.WriteFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &resume_node.base.overlapped); } var bytes_transferred: windows.DWORD = undefined; - if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { - const err = windows.GetLastError(); - return switch (err) { + if (windows.kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { + switch (windows.kernel32.GetLastError()) { windows.ERROR.IO_PENDING => unreachable, - windows.ERROR.INVALID_USER_BUFFER => error.SystemResources, - windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources, - windows.ERROR.OPERATION_ABORTED => error.OperationAborted, - windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources, - windows.ERROR.BROKEN_PIPE => error.BrokenPipe, - else => os.unexpectedErrorWindows(err), - }; + windows.ERROR.INVALID_USER_BUFFER => return error.SystemResources, + windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources, + windows.ERROR.OPERATION_ABORTED => return error.OperationAborted, + windows.ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources, + windows.ERROR.BROKEN_PIPE => return error.BrokenPipe, + else => |err| return windows.unexpectedError(err), + } } } /// iovecs must live until pwritev promise completes. pub async fn pwritevPosix( loop: *Loop, - fd: os.FileHandle, - iovecs: []const posix.iovec_const, + fd: fd_t, + iovecs: []const os.iovec_const, offset: usize, -) os.PosixWriteError!void { +) os.WriteError!void { // workaround for https://github.com/ziglang/zig/issues/1194 suspend { resume @handle(); @@ -209,10 +209,10 @@ pub async fn pwritevPosix( return req_node.data.msg.PWriteV.result; } -pub const PReadVError = error{OutOfMemory} || os.File.ReadError; +pub const PReadVError = error{OutOfMemory} || File.ReadError; /// data - just the inner references - must live until preadv promise completes. -pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: usize) PReadVError!usize { +pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize { // workaround for https://github.com/ziglang/zig/issues/1194 suspend { resume @handle(); @@ -225,11 +225,11 @@ pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: builtin.Os.freebsd, builtin.Os.netbsd, => { - const iovecs = try loop.allocator.alloc(os.posix.iovec, data.len); + const iovecs = try loop.allocator.alloc(os.iovec, data.len); defer loop.allocator.free(iovecs); for (data) |buf, i| { - iovecs[i] = os.posix.iovec{ + iovecs[i] = os.iovec{ .iov_base = buf.ptr, .iov_len = buf.len, }; @@ -247,7 +247,7 @@ pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: } /// data must outlive the returned promise -pub async fn preadvWindows(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: u64) os.WindowsReadError!usize { +pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize { assert(data.len != 0); if (data.len == 1) return await (async preadWindows(loop, fd, data[0], offset) catch unreachable); @@ -271,7 +271,7 @@ pub async fn preadvWindows(loop: *Loop, fd: os.FileHandle, data: []const []u8, o } } -pub async fn preadWindows(loop: *Loop, fd: os.FileHandle, data: []u8, offset: u64) !usize { +pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize { // workaround for https://github.com/ziglang/zig/issues/1194 suspend { resume @handle(); @@ -291,25 +291,24 @@ pub async fn preadWindows(loop: *Loop, fd: os.FileHandle, data: []u8, offset: u6 }, }; // TODO only call create io completion port once per fd - _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined); + _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined) catch undefined; loop.beginOneEvent(); errdefer loop.finishOneEvent(); errdefer { - _ = windows.CancelIoEx(fd, &resume_node.base.overlapped); + _ = windows.kernel32.CancelIoEx(fd, &resume_node.base.overlapped); } suspend { - _ = windows.ReadFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &resume_node.base.overlapped); + _ = windows.kernel32.ReadFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &resume_node.base.overlapped); } var bytes_transferred: windows.DWORD = undefined; - if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { - const err = windows.GetLastError(); - switch (err) { + if (windows.kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { + switch (windows.kernel32.GetLastError()) { windows.ERROR.IO_PENDING => unreachable, windows.ERROR.OPERATION_ABORTED => return error.OperationAborted, windows.ERROR.BROKEN_PIPE => return error.BrokenPipe, windows.ERROR.HANDLE_EOF => return usize(bytes_transferred), - else => return os.unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } } return usize(bytes_transferred); @@ -318,10 +317,10 @@ pub async fn preadWindows(loop: *Loop, fd: os.FileHandle, data: []u8, offset: u6 /// iovecs must live until preadv promise completes pub async fn preadvPosix( loop: *Loop, - fd: os.FileHandle, - iovecs: []const posix.iovec, + fd: fd_t, + iovecs: []const os.iovec, offset: usize, -) os.PosixReadError!usize { +) os.ReadError!usize { // workaround for https://github.com/ziglang/zig/issues/1194 suspend { resume @handle(); @@ -362,8 +361,8 @@ pub async fn openPosix( loop: *Loop, path: []const u8, flags: u32, - mode: os.File.Mode, -) os.File.OpenError!os.FileHandle { + mode: File.Mode, +) File.OpenError!fd_t { // workaround for https://github.com/ziglang/zig/issues/1194 suspend { resume @handle(); @@ -402,19 +401,21 @@ pub async fn openPosix( return req_node.data.msg.Open.result; } -pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle { +pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t { switch (builtin.os) { builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => { - const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC; - return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable); + const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC; + return await (async openPosix(loop, path, flags, File.default_mode) catch unreachable); }, - builtin.Os.windows => return os.windowsOpen( + builtin.Os.windows => return windows.CreateFile( path, windows.GENERIC_READ, windows.FILE_SHARE_READ, + null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED, + null, ), else => @compileError("Unsupported OS"), @@ -423,27 +424,29 @@ pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHa /// Creates if does not exist. Truncates the file if it exists. /// Uses the default mode. -pub async fn openWrite(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle { - return await (async openWriteMode(loop, path, os.File.default_mode) catch unreachable); +pub async fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t { + return await (async openWriteMode(loop, path, File.default_mode) catch unreachable); } /// Creates if does not exist. Truncates the file if it exists. -pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os.File.OpenError!os.FileHandle { +pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t { switch (builtin.os) { builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd, => { - const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; - return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable); + const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC; + return await (async openPosix(loop, path, flags, File.default_mode) catch unreachable); }, - builtin.Os.windows => return os.windowsOpen( + builtin.Os.windows => return windows.CreateFile( path, windows.GENERIC_WRITE, windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, + null, windows.CREATE_ALWAYS, windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED, + null, ), else => @compileError("Unsupported OS"), } @@ -453,20 +456,22 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os pub async fn openReadWrite( loop: *Loop, path: []const u8, - mode: os.File.Mode, -) os.File.OpenError!os.FileHandle { + mode: File.Mode, +) File.OpenError!fd_t { switch (builtin.os) { builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => { - const flags = posix.O_LARGEFILE | posix.O_RDWR | posix.O_CREAT | posix.O_CLOEXEC; + const flags = os.O_LARGEFILE | os.O_RDWR | os.O_CREAT | os.O_CLOEXEC; return await (async openPosix(loop, path, flags, mode) catch unreachable); }, - builtin.Os.windows => return os.windowsOpen( + builtin.Os.windows => return windows.CreateFile( path, windows.GENERIC_WRITE | windows.GENERIC_READ, windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, + null, windows.OPEN_ALWAYS, windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED, + null, ), else => @compileError("Unsupported OS"), @@ -487,7 +492,7 @@ pub const CloseOperation = struct { builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => OsDataPosix, builtin.Os.windows => struct { - handle: ?os.FileHandle, + handle: ?fd_t, }, else => @compileError("Unsupported OS"), @@ -551,7 +556,7 @@ pub const CloseOperation = struct { } } - pub fn setHandle(self: *CloseOperation, handle: os.FileHandle) void { + pub fn setHandle(self: *CloseOperation, handle: fd_t) void { switch (builtin.os) { builtin.Os.linux, builtin.Os.macosx, @@ -585,7 +590,7 @@ pub const CloseOperation = struct { } } - pub fn getHandle(self: *CloseOperation) os.FileHandle { + pub fn getHandle(self: *CloseOperation) fd_t { switch (builtin.os) { builtin.Os.linux, builtin.Os.macosx, @@ -606,11 +611,11 @@ pub const CloseOperation = struct { /// contents must remain alive until writeFile completes. /// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate pub async fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void { - return await (async writeFileMode(loop, path, contents, os.File.default_mode) catch unreachable); + return await (async writeFileMode(loop, path, contents, File.default_mode) catch unreachable); } /// contents must remain alive until writeFile completes. -pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void { +pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { switch (builtin.os) { builtin.Os.linux, builtin.Os.macosx, @@ -623,19 +628,21 @@ pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, } async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void { - const handle = try os.windowsOpen( + const handle = try windows.CreateFile( path, windows.GENERIC_WRITE, windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, + null, windows.CREATE_ALWAYS, windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED, + null, ); defer os.close(handle); try await (async pwriteWindows(loop, handle, contents, 0) catch unreachable); } -async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void { +async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { // workaround for https://github.com/ziglang/zig/issues/1194 suspend { resume @handle(); @@ -689,7 +696,7 @@ pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 defer list.deinit(); while (true) { - try list.ensureCapacity(list.len + os.page_size); + try list.ensureCapacity(list.len + mem.page_size); const buf = list.items[list.len..]; const buf_array = [][]u8{buf}; const amt = try await (async preadv(loop, fd, buf_array, list.len) catch unreachable); @@ -787,7 +794,7 @@ pub fn Watch(comptime V: type) type { switch (builtin.os) { builtin.Os.linux => { - const inotify_fd = try os.linuxINotifyInit1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC); + const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC); errdefer os.close(inotify_fd); var result: *Self = undefined; @@ -880,7 +887,7 @@ pub fn Watch(comptime V: type) type { } async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V { - const resolved_path = try os.path.resolve(self.channel.loop.allocator, [][]const u8{file_path}); + const resolved_path = try std.fs.path.resolve(self.channel.loop.allocator, [][]const u8{file_path}); var resolved_path_consumed = false; defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path); @@ -888,10 +895,7 @@ pub fn Watch(comptime V: type) type { var close_op_consumed = false; defer if (!close_op_consumed) close_op.finish(); - const flags = switch (builtin.os) { - builtin.Os.macosx => posix.O_SYMLINK | posix.O_EVTONLY, - else => 0, - }; + const flags = if (os.darwin.is_the_target) os.O_SYMLINK | os.O_EVTONLY else 0; const mode = 0; const fd = try await (async openPosix(self.channel.loop, resolved_path, flags, mode) catch unreachable); close_op.setHandle(fd); @@ -943,16 +947,16 @@ pub fn Watch(comptime V: type) type { while (true) { if (await (async self.channel.loop.bsdWaitKev( @intCast(usize, close_op.getHandle()), - posix.EVFILT_VNODE, - posix.NOTE_WRITE | posix.NOTE_DELETE, + os.EVFILT_VNODE, + os.NOTE_WRITE | os.NOTE_DELETE, ) catch unreachable)) |kev| { // TODO handle EV_ERROR - if (kev.fflags & posix.NOTE_DELETE != 0) { + if (kev.fflags & os.NOTE_DELETE != 0) { await (async self.channel.put(Self.Event{ .id = Event.Id.Delete, .data = value_copy, }) catch unreachable); - } else if (kev.fflags & posix.NOTE_WRITE != 0) { + } else if (kev.fflags & os.NOTE_WRITE != 0) { await (async self.channel.put(Self.Event{ .id = Event.Id.CloseWrite, .data = value_copy, @@ -961,6 +965,7 @@ pub fn Watch(comptime V: type) type { } else |err| switch (err) { error.EventNotFound => unreachable, error.ProcessNotFound => unreachable, + error.Overflow => unreachable, error.AccessDenied, error.SystemResources => |casted_err| { await (async self.channel.put(casted_err) catch unreachable); }, @@ -971,17 +976,17 @@ pub fn Watch(comptime V: type) type { async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V { const value_copy = value; - const dirname = os.path.dirname(file_path) orelse "."; + const dirname = std.fs.path.dirname(file_path) orelse "."; const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname); var dirname_with_null_consumed = false; defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null); - const basename = os.path.basename(file_path); + const basename = std.fs.path.basename(file_path); const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename); var basename_with_null_consumed = false; defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null); - const wd = try os.linuxINotifyAddWatchC( + const wd = try os.inotify_add_watchC( self.os_data.inotify_fd, dirname_with_null.ptr, os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK, @@ -1017,7 +1022,7 @@ pub fn Watch(comptime V: type) type { const value_copy = value; // TODO we might need to convert dirname and basename to canonical file paths ("short"?) - const dirname = try std.mem.dupe(self.channel.loop.allocator, u8, os.path.dirname(file_path) orelse "."); + const dirname = try std.mem.dupe(self.channel.loop.allocator, u8, std.fs.path.dirname(file_path) orelse "."); var dirname_consumed = false; defer if (!dirname_consumed) self.channel.loop.allocator.free(dirname); @@ -1025,13 +1030,13 @@ pub fn Watch(comptime V: type) type { defer self.channel.loop.allocator.free(dirname_utf16le); // TODO https://github.com/ziglang/zig/issues/265 - const basename = os.path.basename(file_path); + const basename = std.fs.path.basename(file_path); const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename); var basename_utf16le_null_consumed = false; defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null); const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1]; - const dir_handle = windows.CreateFileW( + const dir_handle = try windows.CreateFileW( dirname_utf16le.ptr, windows.FILE_LIST_DIRECTORY, windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE, @@ -1040,16 +1045,8 @@ pub fn Watch(comptime V: type) type { windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED, null, ); - if (dir_handle == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - else => return os.unexpectedErrorWindows(err), - } - } var dir_handle_consumed = false; - defer if (!dir_handle_consumed) os.close(dir_handle); + defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle); const held = await (async self.os_data.table_lock.acquire() catch unreachable); defer held.release(); @@ -1128,7 +1125,7 @@ pub fn Watch(comptime V: type) type { var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined; // TODO handle this error not in the channel but in the setup - _ = os.windowsCreateIoCompletionPort( + _ = windows.CreateIoCompletionPort( dir_handle, self.channel.loop.os_data.io_port, undefined, @@ -1144,10 +1141,10 @@ pub fn Watch(comptime V: type) type { self.channel.loop.beginOneEvent(); errdefer self.channel.loop.finishOneEvent(); errdefer { - _ = windows.CancelIoEx(dir_handle, &resume_node.base.overlapped); + _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped); } suspend { - _ = windows.ReadDirectoryChangesW( + _ = windows.kernel32.ReadDirectoryChangesW( dir_handle, &event_buf, @intCast(windows.DWORD, event_buf.len), @@ -1163,10 +1160,9 @@ pub fn Watch(comptime V: type) type { } } var bytes_transferred: windows.DWORD = undefined; - if (windows.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { - const errno = windows.GetLastError(); - const err = switch (errno) { - else => os.unexpectedErrorWindows(errno), + if (windows.kernel32.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { + const err = switch (windows.kernel32.GetLastError()) { + else => |err| windows.unexpectedError(err), }; await (async self.channel.put(err) catch unreachable); } else { @@ -1261,7 +1257,7 @@ pub fn Watch(comptime V: type) type { ev = @ptrCast(*os.linux.inotify_event, ptr); if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) { const basename_ptr = ptr + @sizeOf(os.linux.inotify_event); - const basename_with_null = basename_ptr[0 .. std.cstr.len(basename_ptr) + 1]; + const basename_with_null = basename_ptr[0 .. std.mem.len(u8, basename_ptr) + 1]; const user_value = blk: { const held = await (async watch.os_data.table_lock.acquire() catch unreachable); defer held.release(); @@ -1340,7 +1336,7 @@ async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { } async fn testFsWatch(loop: *Loop) !void { - const file_path = try os.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" }); + const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" }); defer loop.allocator.free(file_path); const contents = @@ -1366,7 +1362,7 @@ async fn testFsWatch(loop: *Loop) !void { defer if (!ev_consumed) cancel ev; // overwrite line 2 - const fd = try await try async openReadWrite(loop, file_path, os.File.default_mode); + const fd = try await try async openReadWrite(loop, file_path, File.default_mode); { defer os.close(fd); @@ -1388,15 +1384,15 @@ async fn testFsWatch(loop: *Loop) !void { } pub const OutStream = struct { - fd: os.FileHandle, + fd: fd_t, stream: Stream, loop: *Loop, offset: usize, - pub const Error = os.File.WriteError; + pub const Error = File.WriteError; pub const Stream = event.io.OutStream(Error); - pub fn init(loop: *Loop, fd: os.FileHandle, offset: usize) OutStream { + pub fn init(loop: *Loop, fd: fd_t, offset: usize) OutStream { return OutStream{ .fd = fd, .loop = loop, @@ -1414,7 +1410,7 @@ pub const OutStream = struct { }; pub const InStream = struct { - fd: os.FileHandle, + fd: fd_t, stream: Stream, loop: *Loop, offset: usize, @@ -1422,7 +1418,7 @@ pub const InStream = struct { pub const Error = PReadVError; // TODO make this not have OutOfMemory pub const Stream = event.io.InStream(Error); - pub fn init(loop: *Loop, fd: os.FileHandle, offset: usize) InStream { + pub fn init(loop: *Loop, fd: fd_t, offset: usize) InStream { return InStream{ .fd = fd, .loop = loop, diff --git a/std/event/group.zig b/std/event/group.zig index 455d1bd60c..143efd76c3 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -155,7 +155,7 @@ async fn testGroup(loop: *Loop) void { } async fn sleepALittle(count: *usize) void { - std.os.time.sleep(1 * std.os.time.millisecond); + std.time.sleep(1 * std.time.millisecond); _ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); } diff --git a/std/event/loop.zig b/std/event/loop.zig index 76b1f6455b..61732d78f5 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -7,9 +7,9 @@ const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; const fs = std.event.fs; const os = std.os; -const posix = os.posix; const windows = os.windows; const maxInt = std.math.maxInt; +const Thread = std.Thread; pub const Loop = struct { allocator: *mem.Allocator, @@ -17,7 +17,7 @@ pub const Loop = struct { os_data: OsData, final_resume_node: ResumeNode, pending_event_count: usize, - extra_threads: []*os.Thread, + extra_threads: []*Thread, // pre-allocated eventfds. all permanently active. // this is how we send promises to be resumed on other threads. @@ -32,7 +32,7 @@ pub const Loop = struct { overlapped: Overlapped, pub const overlapped_init = switch (builtin.os) { - builtin.Os.windows => windows.OVERLAPPED{ + .windows => windows.OVERLAPPED{ .Internal = 0, .InternalHigh = 0, .Offset = 0, @@ -50,13 +50,13 @@ pub const Loop = struct { }; pub const EventFd = switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => KEventFd, - builtin.Os.linux => struct { + .macosx, .freebsd, .netbsd => KEventFd, + .linux => struct { base: ResumeNode, epoll_op: u32, eventfd: i32, }, - builtin.Os.windows => struct { + .windows => struct { base: ResumeNode, completion_key: usize, }, @@ -65,15 +65,15 @@ pub const Loop = struct { const KEventFd = struct { base: ResumeNode, - kevent: posix.Kevent, + kevent: os.Kevent, }; pub const Basic = switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => KEventBasic, - builtin.Os.linux => struct { + .macosx, .freebsd, .netbsd => KEventBasic, + .linux => struct { base: ResumeNode, }, - builtin.Os.windows => struct { + .windows => struct { base: ResumeNode, }, else => @compileError("unsupported OS"), @@ -81,7 +81,7 @@ pub const Loop = struct { const KEventBasic = struct { base: ResumeNode, - kev: posix.Kevent, + kev: os.Kevent, }; }; @@ -99,7 +99,7 @@ pub const Loop = struct { /// have the correct pointer value. pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode"); - const core_count = try os.cpuCount(allocator); + const core_count = try Thread.cpuCount(); return self.initInternal(allocator, core_count); } @@ -127,7 +127,7 @@ pub const Loop = struct { ); errdefer self.allocator.free(self.eventfd_resume_nodes); - self.extra_threads = try self.allocator.alloc(*os.Thread, extra_thread_count); + self.extra_threads = try self.allocator.alloc(*Thread, extra_thread_count); errdefer self.allocator.free(self.extra_threads); try self.initOsData(extra_thread_count); @@ -139,15 +139,15 @@ pub const Loop = struct { self.allocator.free(self.extra_threads); } - const InitOsDataError = os.LinuxEpollCreateError || mem.Allocator.Error || os.LinuxEventFdError || - os.SpawnThreadError || os.LinuxEpollCtlError || os.BsdKEventError || - os.WindowsCreateIoCompletionPortError; + const InitOsDataError = os.EpollCreateError || mem.Allocator.Error || os.EventFdError || + Thread.SpawnError || os.EpollCtlError || os.KEventError || + windows.CreateIoCompletionPortError; const wakeup_bytes = []u8{0x1} ** 8; fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { switch (builtin.os) { - builtin.Os.linux => { + .linux => { self.os_data.fs_queue = std.atomic.Queue(fs.Request).init(); self.os_data.fs_queue_item = 0; // we need another thread for the file system because Linux does not have an async @@ -172,32 +172,32 @@ pub const Loop = struct { .handle = undefined, .overlapped = ResumeNode.overlapped_init, }, - .eventfd = try os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), - .epoll_op = posix.EPOLL_CTL_ADD, + .eventfd = try os.eventfd(1, os.EFD_CLOEXEC | os.EFD_NONBLOCK), + .epoll_op = os.EPOLL_CTL_ADD, }, .next = undefined, }; self.available_eventfd_resume_nodes.push(eventfd_node); } - self.os_data.epollfd = try os.linuxEpollCreate(posix.EPOLL_CLOEXEC); + self.os_data.epollfd = try os.epoll_create1(os.EPOLL_CLOEXEC); errdefer os.close(self.os_data.epollfd); - self.os_data.final_eventfd = try os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); + self.os_data.final_eventfd = try os.eventfd(0, os.EFD_CLOEXEC | os.EFD_NONBLOCK); errdefer os.close(self.os_data.final_eventfd); - self.os_data.final_eventfd_event = posix.epoll_event{ - .events = posix.EPOLLIN, - .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, + self.os_data.final_eventfd_event = os.epoll_event{ + .events = os.EPOLLIN, + .data = os.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, }; - try os.linuxEpollCtl( + try os.epoll_ctl( self.os_data.epollfd, - posix.EPOLL_CTL_ADD, + os.EPOLL_CTL_ADD, self.os_data.final_eventfd, &self.os_data.final_eventfd_event, ); - self.os_data.fs_thread = try os.spawnThread(self, posixFsRun); + self.os_data.fs_thread = try Thread.spawn(self, posixFsRun); errdefer { self.posixFsRequest(&self.os_data.fs_end_request); self.os_data.fs_thread.wait(); @@ -211,21 +211,21 @@ pub const Loop = struct { var extra_thread_index: usize = 0; errdefer { // writing 8 bytes to an eventfd cannot fail - os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; while (extra_thread_index != 0) { extra_thread_index -= 1; self.extra_threads[extra_thread_index].wait(); } } while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { - self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun); + self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun); } }, - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { - self.os_data.kqfd = try os.bsdKQueue(); + .macosx, .freebsd, .netbsd => { + self.os_data.kqfd = try os.kqueue(); errdefer os.close(self.os_data.kqfd); - self.os_data.fs_kqfd = try os.bsdKQueue(); + self.os_data.fs_kqfd = try os.kqueue(); errdefer os.close(self.os_data.fs_kqfd); self.os_data.fs_queue = std.atomic.Queue(fs.Request).init(); @@ -240,7 +240,7 @@ pub const Loop = struct { }, }; - const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; + const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; for (self.eventfd_resume_nodes) |*eventfd_node, i| { eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ @@ -251,10 +251,10 @@ pub const Loop = struct { .overlapped = ResumeNode.overlapped_init, }, // this one is for sending events - .kevent = posix.Kevent{ + .kevent = os.Kevent{ .ident = i, - .filter = posix.EVFILT_USER, - .flags = posix.EV_CLEAR | posix.EV_ADD | posix.EV_DISABLE, + .filter = os.EVFILT_USER, + .flags = os.EV_CLEAR | os.EV_ADD | os.EV_DISABLE, .fflags = 0, .data = 0, .udata = @ptrToInt(&eventfd_node.data.base), @@ -263,46 +263,46 @@ pub const Loop = struct { .next = undefined, }; self.available_eventfd_resume_nodes.push(eventfd_node); - const kevent_array = (*const [1]posix.Kevent)(&eventfd_node.data.kevent); - _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null); - eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE; - eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER; + const kevent_array = (*const [1]os.Kevent)(&eventfd_node.data.kevent); + _ = try os.kevent(self.os_data.kqfd, kevent_array, empty_kevs, null); + eventfd_node.data.kevent.flags = os.EV_CLEAR | os.EV_ENABLE; + eventfd_node.data.kevent.fflags = os.NOTE_TRIGGER; } // Pre-add so that we cannot get error.SystemResources // later when we try to activate it. - self.os_data.final_kevent = posix.Kevent{ + self.os_data.final_kevent = os.Kevent{ .ident = extra_thread_count, - .filter = posix.EVFILT_USER, - .flags = posix.EV_ADD | posix.EV_DISABLE, + .filter = os.EVFILT_USER, + .flags = os.EV_ADD | os.EV_DISABLE, .fflags = 0, .data = 0, .udata = @ptrToInt(&self.final_resume_node), }; - const final_kev_arr = (*const [1]posix.Kevent)(&self.os_data.final_kevent); - _ = try os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null); - self.os_data.final_kevent.flags = posix.EV_ENABLE; - self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER; + const final_kev_arr = (*const [1]os.Kevent)(&self.os_data.final_kevent); + _ = try os.kevent(self.os_data.kqfd, final_kev_arr, empty_kevs, null); + self.os_data.final_kevent.flags = os.EV_ENABLE; + self.os_data.final_kevent.fflags = os.NOTE_TRIGGER; - self.os_data.fs_kevent_wake = posix.Kevent{ + self.os_data.fs_kevent_wake = os.Kevent{ .ident = 0, - .filter = posix.EVFILT_USER, - .flags = posix.EV_ADD | posix.EV_ENABLE, - .fflags = posix.NOTE_TRIGGER, + .filter = os.EVFILT_USER, + .flags = os.EV_ADD | os.EV_ENABLE, + .fflags = os.NOTE_TRIGGER, .data = 0, .udata = undefined, }; - self.os_data.fs_kevent_wait = posix.Kevent{ + self.os_data.fs_kevent_wait = os.Kevent{ .ident = 0, - .filter = posix.EVFILT_USER, - .flags = posix.EV_ADD | posix.EV_CLEAR, + .filter = os.EVFILT_USER, + .flags = os.EV_ADD | os.EV_CLEAR, .fflags = 0, .data = 0, .udata = undefined, }; - self.os_data.fs_thread = try os.spawnThread(self, posixFsRun); + self.os_data.fs_thread = try Thread.spawn(self, posixFsRun); errdefer { self.posixFsRequest(&self.os_data.fs_end_request); self.os_data.fs_thread.wait(); @@ -315,24 +315,24 @@ pub const Loop = struct { var extra_thread_index: usize = 0; errdefer { - _ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable; + _ = os.kevent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable; while (extra_thread_index != 0) { extra_thread_index -= 1; self.extra_threads[extra_thread_index].wait(); } } while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { - self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun); + self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun); } }, - builtin.Os.windows => { - self.os_data.io_port = try os.windowsCreateIoCompletionPort( + .windows => { + self.os_data.io_port = try windows.CreateIoCompletionPort( windows.INVALID_HANDLE_VALUE, null, undefined, maxInt(windows.DWORD), ); - errdefer os.close(self.os_data.io_port); + errdefer windows.CloseHandle(self.os_data.io_port); for (self.eventfd_resume_nodes) |*eventfd_node, i| { eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ @@ -361,7 +361,7 @@ pub const Loop = struct { while (i < extra_thread_index) : (i += 1) { while (true) { const overlapped = &self.final_resume_node.overlapped; - os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue; + windows.PostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue; break; } } @@ -371,7 +371,7 @@ pub const Loop = struct { } } while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { - self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun); + self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun); } }, else => {}, @@ -380,18 +380,18 @@ pub const Loop = struct { fn deinitOsData(self: *Loop) void { switch (builtin.os) { - builtin.Os.linux => { + .linux => { os.close(self.os_data.final_eventfd); while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd); os.close(self.os_data.epollfd); self.allocator.free(self.eventfd_resume_nodes); }, - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { + .macosx, .freebsd, .netbsd => { os.close(self.os_data.kqfd); os.close(self.os_data.fs_kqfd); }, - builtin.Os.windows => { - os.close(self.os_data.io_port); + .windows => { + windows.CloseHandle(self.os_data.io_port); }, else => {}, } @@ -400,28 +400,28 @@ pub const Loop = struct { /// resume_node must live longer than the promise that it holds a reference to. /// flags must contain EPOLLET pub fn linuxAddFd(self: *Loop, fd: i32, resume_node: *ResumeNode, flags: u32) !void { - assert(flags & posix.EPOLLET == posix.EPOLLET); + assert(flags & os.EPOLLET == os.EPOLLET); self.beginOneEvent(); errdefer self.finishOneEvent(); try self.linuxModFd( fd, - posix.EPOLL_CTL_ADD, + os.EPOLL_CTL_ADD, flags, resume_node, ); } pub fn linuxModFd(self: *Loop, fd: i32, op: u32, flags: u32, resume_node: *ResumeNode) !void { - assert(flags & posix.EPOLLET == posix.EPOLLET); + assert(flags & os.EPOLLET == os.EPOLLET); var ev = os.linux.epoll_event{ .events = flags, .data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, }; - try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev); + try os.epoll_ctl(self.os_data.epollfd, op, fd, &ev); } pub fn linuxRemoveFd(self: *Loop, fd: i32) void { - os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; + os.epoll_ctl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; self.finishOneEvent(); } @@ -440,7 +440,7 @@ pub const Loop = struct { } } - pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !posix.Kevent { + pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent { // TODO #1194 suspend { resume @handle(); @@ -464,31 +464,31 @@ pub const Loop = struct { pub fn bsdAddKev(self: *Loop, resume_node: *ResumeNode.Basic, ident: usize, filter: i16, fflags: u32) !void { self.beginOneEvent(); errdefer self.finishOneEvent(); - var kev = posix.Kevent{ + var kev = os.Kevent{ .ident = ident, .filter = filter, - .flags = posix.EV_ADD | posix.EV_ENABLE | posix.EV_CLEAR, + .flags = os.EV_ADD | os.EV_ENABLE | os.EV_CLEAR, .fflags = fflags, .data = 0, .udata = @ptrToInt(&resume_node.base), }; - const kevent_array = (*const [1]posix.Kevent)(&kev); - const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; - _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null); + const kevent_array = (*const [1]os.Kevent)(&kev); + const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + _ = try os.kevent(self.os_data.kqfd, kevent_array, empty_kevs, null); } pub fn bsdRemoveKev(self: *Loop, ident: usize, filter: i16) void { - var kev = posix.Kevent{ + var kev = os.Kevent{ .ident = ident, .filter = filter, - .flags = posix.EV_DELETE, + .flags = os.EV_DELETE, .fflags = 0, .data = 0, .udata = 0, }; - const kevent_array = (*const [1]posix.Kevent)(&kev); - const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; - _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch undefined; + const kevent_array = (*const [1]os.Kevent)(&kev); + const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + _ = os.kevent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch undefined; self.finishOneEvent(); } @@ -501,18 +501,18 @@ pub const Loop = struct { const eventfd_node = &resume_stack_node.data; eventfd_node.base.handle = next_tick_node.data; switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { - const kevent_array = (*const [1]posix.Kevent)(&eventfd_node.kevent); - const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; - _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch { + .macosx, .freebsd, .netbsd => { + const kevent_array = (*const [1]os.Kevent)(&eventfd_node.kevent); + const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + _ = os.kevent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch { self.next_tick_queue.unget(next_tick_node); self.available_eventfd_resume_nodes.push(resume_stack_node); return; }; }, - builtin.Os.linux => { + .linux => { // the pending count is already accounted for - const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT | + const epoll_events = os.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT | os.linux.EPOLLET; self.linuxModFd( eventfd_node.eventfd, @@ -525,8 +525,8 @@ pub const Loop = struct { return; }; }, - builtin.Os.windows => { - os.windowsPostQueuedCompletionStatus( + .windows => { + windows.PostQueuedCompletionStatus( self.os_data.io_port, undefined, undefined, @@ -623,26 +623,26 @@ pub const Loop = struct { if (prev == 1) { // cause all the threads to stop switch (builtin.os) { - builtin.Os.linux => { + .linux => { self.posixFsRequest(&self.os_data.fs_end_request); // writing 8 bytes to an eventfd cannot fail - os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; return; }, - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { + .macosx, .freebsd, .netbsd => { self.posixFsRequest(&self.os_data.fs_end_request); - const final_kevent = (*const [1]posix.Kevent)(&self.os_data.final_kevent); - const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; + const final_kevent = (*const [1]os.Kevent)(&self.os_data.final_kevent); + const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; // cannot fail because we already added it and this just enables it - _ = os.bsdKEvent(self.os_data.kqfd, final_kevent, empty_kevs, null) catch unreachable; + _ = os.kevent(self.os_data.kqfd, final_kevent, empty_kevs, null) catch unreachable; return; }, - builtin.Os.windows => { + .windows => { var i: usize = 0; while (i < self.extra_threads.len + 1) : (i += 1) { while (true) { const overlapped = &self.final_resume_node.overlapped; - os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue; + windows.PostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue; break; } } @@ -663,10 +663,10 @@ pub const Loop = struct { } switch (builtin.os) { - builtin.Os.linux => { + .linux => { // only process 1 event so we don't steal from other threads var events: [1]os.linux.epoll_event = undefined; - const count = os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); + const count = os.epoll_wait(self.os_data.epollfd, events[0..], -1); for (events[0..count]) |ev| { const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); const handle = resume_node.handle; @@ -676,7 +676,7 @@ pub const Loop = struct { ResumeNode.Id.Stop => return, ResumeNode.Id.EventFd => { const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); - event_fd_node.epoll_op = posix.EPOLL_CTL_MOD; + event_fd_node.epoll_op = os.EPOLL_CTL_MOD; const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); self.available_eventfd_resume_nodes.push(stack_node); }, @@ -687,10 +687,10 @@ pub const Loop = struct { } } }, - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { - var eventlist: [1]posix.Kevent = undefined; - const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; - const count = os.bsdKEvent(self.os_data.kqfd, empty_kevs, eventlist[0..], null) catch unreachable; + .macosx, .freebsd, .netbsd => { + var eventlist: [1]os.Kevent = undefined; + const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + const count = os.kevent(self.os_data.kqfd, empty_kevs, eventlist[0..], null) catch unreachable; for (eventlist[0..count]) |ev| { const resume_node = @intToPtr(*ResumeNode, ev.udata); const handle = resume_node.handle; @@ -713,16 +713,16 @@ pub const Loop = struct { } } }, - builtin.Os.windows => { + .windows => { var completion_key: usize = undefined; const overlapped = while (true) { var nbytes: windows.DWORD = undefined; var overlapped: ?*windows.OVERLAPPED = undefined; - switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { - os.WindowsWaitResult.Aborted => return, - os.WindowsWaitResult.Normal => {}, - os.WindowsWaitResult.EOF => {}, - os.WindowsWaitResult.Cancelled => continue, + switch (windows.GetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { + .Aborted => return, + .Normal => {}, + .EOF => {}, + .Cancelled => continue, } if (overlapped) |o| break o; } else unreachable; // TODO else unreachable should not be necessary @@ -751,16 +751,16 @@ pub const Loop = struct { self.os_data.fs_queue.put(request_node); switch (builtin.os) { builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { - const fs_kevs = (*const [1]posix.Kevent)(&self.os_data.fs_kevent_wake); - const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; - _ = os.bsdKEvent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable; + const fs_kevs = (*const [1]os.Kevent)(&self.os_data.fs_kevent_wake); + const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable; }, builtin.Os.linux => { _ = @atomicRmw(i32, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); const rc = os.linux.futex_wake(&self.os_data.fs_queue_item, os.linux.FUTEX_WAKE, 1); switch (os.linux.getErrno(rc)) { 0 => {}, - posix.EINVAL => unreachable, + os.EINVAL => unreachable, else => unreachable, } }, @@ -783,24 +783,24 @@ pub const Loop = struct { switch (node.data.msg) { @TagType(fs.Request.Msg).End => return, @TagType(fs.Request.Msg).PWriteV => |*msg| { - msg.result = os.posix_pwritev(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset); + msg.result = os.pwritev(msg.fd, msg.iov, msg.offset); }, @TagType(fs.Request.Msg).PReadV => |*msg| { - msg.result = os.posix_preadv(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset); + msg.result = os.preadv(msg.fd, msg.iov, msg.offset); }, @TagType(fs.Request.Msg).Open => |*msg| { - msg.result = os.posixOpenC(msg.path.ptr, msg.flags, msg.mode); + msg.result = os.openC(msg.path.ptr, msg.flags, msg.mode); }, @TagType(fs.Request.Msg).Close => |*msg| os.close(msg.fd), @TagType(fs.Request.Msg).WriteFile => |*msg| blk: { - const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | - posix.O_CLOEXEC | posix.O_TRUNC; - const fd = os.posixOpenC(msg.path.ptr, flags, msg.mode) catch |err| { + const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | + os.O_CLOEXEC | os.O_TRUNC; + const fd = os.openC(msg.path.ptr, flags, msg.mode) catch |err| { msg.result = err; break :blk; }; defer os.close(fd); - msg.result = os.posixWrite(fd, msg.contents); + msg.result = os.write(fd, msg.contents); }, } switch (node.data.finish) { @@ -816,14 +816,14 @@ pub const Loop = struct { builtin.Os.linux => { const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null); switch (os.linux.getErrno(rc)) { - 0, posix.EINTR, posix.EAGAIN => continue, + 0, os.EINTR, os.EAGAIN => continue, else => unreachable, } }, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { - const fs_kevs = (*const [1]posix.Kevent)(&self.os_data.fs_kevent_wait); - var out_kevs: [1]posix.Kevent = undefined; - _ = os.bsdKEvent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable; + const fs_kevs = (*const [1]os.Kevent)(&self.os_data.fs_kevent_wait); + var out_kevs: [1]os.Kevent = undefined; + _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable; }, else => @compileError("Unsupported OS"), } @@ -831,9 +831,9 @@ pub const Loop = struct { } const OsData = switch (builtin.os) { - builtin.Os.linux => LinuxOsData, - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => KEventData, - builtin.Os.windows => struct { + .linux => LinuxOsData, + .macosx, .freebsd, .netbsd => KEventData, + .windows => struct { io_port: windows.HANDLE, extra_thread_count: usize, }, @@ -842,10 +842,10 @@ pub const Loop = struct { const KEventData = struct { kqfd: i32, - final_kevent: posix.Kevent, - fs_kevent_wake: posix.Kevent, - fs_kevent_wait: posix.Kevent, - fs_thread: *os.Thread, + final_kevent: os.Kevent, + fs_kevent_wake: os.Kevent, + fs_kevent_wait: os.Kevent, + fs_thread: *Thread, fs_kqfd: i32, fs_queue: std.atomic.Queue(fs.Request), fs_end_request: fs.RequestNode, @@ -855,7 +855,7 @@ pub const Loop = struct { epollfd: i32, final_eventfd: i32, final_eventfd_event: os.linux.epoll_event, - fs_thread: *os.Thread, + fs_thread: *Thread, fs_queue_item: i32, fs_queue: std.atomic.Queue(fs.Request), fs_end_request: fs.RequestNode, diff --git a/std/event/net.zig b/std/event/net.zig index 687c119920..f4398196e3 100644 --- a/std/event/net.zig +++ b/std/event/net.zig @@ -4,11 +4,12 @@ const testing = std.testing; const event = std.event; const mem = std.mem; const os = std.os; -const posix = os.posix; const Loop = std.event.Loop; +const File = std.fs.File; +const fd_t = os.fd_t; pub const Server = struct { - handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, os.File) void, + handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, File) void, loop: *Loop, sockfd: ?i32, @@ -40,30 +41,33 @@ pub const Server = struct { pub fn listen( self: *Server, address: *const std.net.Address, - handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, os.File) void, + handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, File) void, ) !void { self.handleRequestFn = handleRequestFn; - const sockfd = try os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); + const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp); errdefer os.close(sockfd); self.sockfd = sockfd; - try os.posixBind(sockfd, &address.os_addr); - try os.posixListen(sockfd, posix.SOMAXCONN); - self.listen_address = std.net.Address.initPosix(try os.posixGetSockName(sockfd)); + try os.bind(sockfd, &address.os_addr); + try os.listen(sockfd, os.SOMAXCONN); + self.listen_address = std.net.Address.initPosix(try os.getsockname(sockfd)); self.accept_coro = try async<self.loop.allocator> Server.handler(self); errdefer cancel self.accept_coro.?; self.listen_resume_node.handle = self.accept_coro.?; - try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET); + try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); errdefer self.loop.removeFd(sockfd); } /// Stop listening pub fn close(self: *Server) void { self.loop.linuxRemoveFd(self.sockfd.?); - os.close(self.sockfd.?); + if (self.sockfd) |fd| { + os.close(fd); + self.sockfd = null; + } } pub fn deinit(self: *Server) void { @@ -75,13 +79,13 @@ pub const Server = struct { while (true) { var accepted_addr: std.net.Address = undefined; // TODO just inline the following function here and don't expose it as posixAsyncAccept - if (os.posixAsyncAccept(self.sockfd.?, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { + if (os.accept4_async(self.sockfd.?, &accepted_addr.os_addr, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC)) |accepted_fd| { if (accepted_fd == -1) { // would block suspend; // we will get resumed by epoll_wait in the event loop continue; } - var socket = os.File.openHandle(accepted_fd); + var socket = File.openHandle(accepted_fd); _ = async<self.loop.allocator> self.handleRequestFn(self, &accepted_addr, socket) catch |err| switch (err) { error.OutOfMemory => { socket.close(); @@ -89,14 +93,7 @@ pub const Server = struct { }, }; } else |err| switch (err) { - error.ProcessFdQuotaExceeded => { - errdefer os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); - suspend { - self.waiting_for_emfile_node = PromiseNode.init(@handle()); - os.emfile_promise_queue.append(&self.waiting_for_emfile_node); - } - continue; - }, + error.ProcessFdQuotaExceeded => @panic("TODO handle this error"), error.ConnectionAborted => continue, error.FileDescriptorNotASocket => unreachable, @@ -111,24 +108,24 @@ pub const Server = struct { }; pub async fn connectUnixSocket(loop: *Loop, path: []const u8) !i32 { - const sockfd = try os.posixSocket( - posix.AF_UNIX, - posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, + const sockfd = try os.socket( + os.AF_UNIX, + os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, 0, ); errdefer os.close(sockfd); - var sock_addr = posix.sockaddr_un{ - .family = posix.AF_UNIX, + var sock_addr = os.sockaddr_un{ + .family = os.AF_UNIX, .path = undefined, }; if (path.len > @typeOf(sock_addr.path).len) return error.NameTooLong; mem.copy(u8, sock_addr.path[0..], path); - const size = @intCast(u32, @sizeOf(posix.sa_family_t) + path.len); - try os.posixConnectAsync(sockfd, &sock_addr, size); - try await try async loop.linuxWaitFd(sockfd, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET); - try os.posixGetSockOptConnectError(sockfd); + const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len); + try os.connect_async(sockfd, &sock_addr, size); + try await try async loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); + try os.getsockoptError(sockfd); return sockfd; } @@ -146,51 +143,49 @@ pub const ReadError = error{ }; /// returns number of bytes read. 0 means EOF. -pub async fn read(loop: *std.event.Loop, fd: os.FileHandle, buffer: []u8) ReadError!usize { - const iov = posix.iovec{ +pub async fn read(loop: *std.event.Loop, fd: fd_t, buffer: []u8) ReadError!usize { + const iov = os.iovec{ .iov_base = buffer.ptr, .iov_len = buffer.len, }; - const iovs: *const [1]posix.iovec = &iov; + const iovs: *const [1]os.iovec = &iov; return await (async readvPosix(loop, fd, iovs, 1) catch unreachable); } pub const WriteError = error{}; -pub async fn write(loop: *std.event.Loop, fd: os.FileHandle, buffer: []const u8) WriteError!void { - const iov = posix.iovec_const{ +pub async fn write(loop: *std.event.Loop, fd: fd_t, buffer: []const u8) WriteError!void { + const iov = os.iovec_const{ .iov_base = buffer.ptr, .iov_len = buffer.len, }; - const iovs: *const [1]posix.iovec_const = &iov; + const iovs: *const [1]os.iovec_const = &iov; return await (async writevPosix(loop, fd, iovs, 1) catch unreachable); } -pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const posix.iovec_const, count: usize) !void { +pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const os.iovec_const, count: usize) !void { while (true) { switch (builtin.os) { - builtin.Os.macosx, builtin.Os.linux => { - const rc = posix.writev(fd, iov, count); - const err = posix.getErrno(rc); - switch (err) { + .macosx, .linux => { + switch (os.errno(os.system.writev(fd, iov, count))) { 0 => return, - posix.EINTR => continue, - posix.ESPIPE => unreachable, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => { - try await (async loop.linuxWaitFd(fd, posix.EPOLLET | posix.EPOLLOUT) catch unreachable); + os.EINTR => continue, + os.ESPIPE => unreachable, + os.EINVAL => unreachable, + os.EFAULT => unreachable, + os.EAGAIN => { + try await (async loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT) catch unreachable); continue; }, - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => unreachable, - posix.EFBIG => unreachable, - posix.EIO => return error.InputOutput, - posix.ENOSPC => unreachable, - posix.EPERM => return error.AccessDenied, - posix.EPIPE => unreachable, - else => return os.unexpectedErrorPosix(err), + os.EBADF => unreachable, // always a race condition + os.EDESTADDRREQ => unreachable, // connect was never called + os.EDQUOT => unreachable, + os.EFBIG => unreachable, + os.EIO => return error.InputOutput, + os.ENOSPC => unreachable, + os.EPERM => return error.AccessDenied, + os.EPIPE => unreachable, + else => |err| return os.unexpectedErrno(err), } }, else => @compileError("Unsupported OS"), @@ -199,27 +194,26 @@ pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const posix.iovec_const, } /// returns number of bytes read. 0 means EOF. -pub async fn readvPosix(loop: *std.event.Loop, fd: i32, iov: [*]posix.iovec, count: usize) !usize { +pub async fn readvPosix(loop: *std.event.Loop, fd: i32, iov: [*]os.iovec, count: usize) !usize { while (true) { switch (builtin.os) { builtin.Os.linux, builtin.Os.freebsd, builtin.Os.macosx => { - const rc = posix.readv(fd, iov, count); - const err = posix.getErrno(rc); - switch (err) { + const rc = os.system.readv(fd, iov, count); + switch (os.errno(rc)) { 0 => return rc, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => { - try await (async loop.linuxWaitFd(fd, posix.EPOLLET | posix.EPOLLIN) catch unreachable); + os.EINTR => continue, + os.EINVAL => unreachable, + os.EFAULT => unreachable, + os.EAGAIN => { + try await (async loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN) catch unreachable); continue; }, - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => unreachable, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return os.unexpectedErrorPosix(err), + os.EBADF => unreachable, // always a race condition + os.EIO => return error.InputOutput, + os.EISDIR => unreachable, + os.ENOBUFS => return error.SystemResources, + os.ENOMEM => return error.SystemResources, + else => |err| return os.unexpectedErrno(err), } }, else => @compileError("Unsupported OS"), @@ -227,12 +221,12 @@ pub async fn readvPosix(loop: *std.event.Loop, fd: i32, iov: [*]posix.iovec, cou } } -pub async fn writev(loop: *Loop, fd: os.FileHandle, data: []const []const u8) !void { - const iovecs = try loop.allocator.alloc(os.posix.iovec_const, data.len); +pub async fn writev(loop: *Loop, fd: fd_t, data: []const []const u8) !void { + const iovecs = try loop.allocator.alloc(os.iovec_const, data.len); defer loop.allocator.free(iovecs); for (data) |buf, i| { - iovecs[i] = os.posix.iovec_const{ + iovecs[i] = os.iovec_const{ .iov_base = buf.ptr, .iov_len = buf.len, }; @@ -241,12 +235,12 @@ pub async fn writev(loop: *Loop, fd: os.FileHandle, data: []const []const u8) !v return await (async writevPosix(loop, fd, iovecs.ptr, data.len) catch unreachable); } -pub async fn readv(loop: *Loop, fd: os.FileHandle, data: []const []u8) !usize { - const iovecs = try loop.allocator.alloc(os.posix.iovec, data.len); +pub async fn readv(loop: *Loop, fd: fd_t, data: []const []u8) !usize { + const iovecs = try loop.allocator.alloc(os.iovec, data.len); defer loop.allocator.free(iovecs); for (data) |buf, i| { - iovecs[i] = os.posix.iovec{ + iovecs[i] = os.iovec{ .iov_base = buf.ptr, .iov_len = buf.len, }; @@ -255,17 +249,17 @@ pub async fn readv(loop: *Loop, fd: os.FileHandle, data: []const []u8) !usize { return await (async readvPosix(loop, fd, iovecs.ptr, data.len) catch unreachable); } -pub async fn connect(loop: *Loop, _address: *const std.net.Address) !os.File { +pub async fn connect(loop: *Loop, _address: *const std.net.Address) !File { var address = _address.*; // TODO https://github.com/ziglang/zig/issues/1592 - const sockfd = try os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); + const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp); errdefer os.close(sockfd); - try os.posixConnectAsync(sockfd, &address.os_addr, @sizeOf(posix.sockaddr_in)); - try await try async loop.linuxWaitFd(sockfd, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET); - try os.posixGetSockOptConnectError(sockfd); + try os.connect_async(sockfd, &address.os_addr, @sizeOf(os.sockaddr_in)); + try await try async loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); + try os.getsockoptError(sockfd); - return os.File.openHandle(sockfd); + return File.openHandle(sockfd); } test "listen on a port, send bytes, receive bytes" { @@ -281,7 +275,7 @@ test "listen on a port, send bytes, receive bytes" { tcp_server: Server, const Self = @This(); - async<*mem.Allocator> fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: os.File) void { + async<*mem.Allocator> fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: File) void { const self = @fieldParentPtr(Self, "tcp_server", tcp_server); var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592 defer socket.close(); @@ -294,7 +288,7 @@ test "listen on a port, send bytes, receive bytes" { cancel @handle(); } } - async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: os.File) !void { + async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: File) !void { const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/1592 var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592 @@ -331,14 +325,14 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv } pub const OutStream = struct { - fd: os.FileHandle, + fd: fd_t, stream: Stream, loop: *Loop, pub const Error = WriteError; pub const Stream = event.io.OutStream(Error); - pub fn init(loop: *Loop, fd: os.FileHandle) OutStream { + pub fn init(loop: *Loop, fd: fd_t) OutStream { return OutStream{ .fd = fd, .loop = loop, @@ -353,14 +347,14 @@ pub const OutStream = struct { }; pub const InStream = struct { - fd: os.FileHandle, + fd: fd_t, stream: Stream, loop: *Loop, pub const Error = ReadError; pub const Stream = event.io.InStream(Error); - pub fn init(loop: *Loop, fd: os.FileHandle) InStream { + pub fn init(loop: *Loop, fd: fd_t) InStream { return InStream{ .fd = fd, .loop = loop, diff --git a/std/event/rwlock.zig b/std/event/rwlock.zig index 76b364fedc..00f3c0bc60 100644 --- a/std/event/rwlock.zig +++ b/std/event/rwlock.zig @@ -271,7 +271,7 @@ async fn writeRunner(lock: *RwLock) void { var i: usize = 0; while (i < shared_test_data.len) : (i += 1) { - std.os.time.sleep(100 * std.os.time.microsecond); + std.time.sleep(100 * std.time.microsecond); const lock_promise = async lock.acquireWrite() catch @panic("out of memory"); const handle = await lock_promise; defer handle.release(); @@ -286,7 +286,7 @@ async fn writeRunner(lock: *RwLock) void { async fn readRunner(lock: *RwLock) void { suspend; // resumed by onNextTick - std.os.time.sleep(1); + std.time.sleep(1); var i: usize = 0; while (i < shared_test_data.len) : (i += 1) { diff --git a/std/fmt.zig b/std/fmt.zig index 27a8abba6e..74c36f7086 100644 --- a/std/fmt.zig +++ b/std/fmt.zig @@ -226,7 +226,7 @@ pub fn formatType( builtin.TypeInfo.Pointer.Size.Many => { if (ptr_info.child == u8) { if (fmt.len > 0 and fmt[0] == 's') { - const len = std.cstr.len(value); + const len = mem.len(u8, value); return formatText(value[0..len], fmt, context, Errors, output); } } diff --git a/std/fs.zig b/std/fs.zig new file mode 100644 index 0000000000..6d5874d7c0 --- /dev/null +++ b/std/fs.zig @@ -0,0 +1,808 @@ +const builtin = @import("builtin"); +const std = @import("std.zig"); +const os = std.os; +const mem = std.mem; +const base64 = std.base64; +const crypto = std.crypto; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +pub const path = @import("fs/path.zig"); +pub const File = @import("fs/file.zig").File; + +pub const symLink = os.symlink; +pub const symLinkC = os.symlinkC; +pub const deleteFile = os.unlink; +pub const deleteFileC = os.unlinkC; +pub const rename = os.rename; +pub const renameC = os.renameC; +pub const renameW = os.renameW; +pub const realpath = os.realpath; +pub const realpathC = os.realpathC; +pub const realpathW = os.realpathW; + +pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir; +pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError; + +/// This represents the maximum size of a UTF-8 encoded file path. +/// All file system operations which return a path are guaranteed to +/// fit into a UTF-8 encoded array of this length. +/// path being too long if it is this 0long +pub const MAX_PATH_BYTES = switch (builtin.os) { + .linux, .macosx, .ios, .freebsd, .netbsd => os.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. + .windows => os.windows.PATH_MAX_WIDE * 3 + 1, + else => @compileError("Unsupported OS"), +}; + +// here we replace the standard +/ with -_ so that it can be used in a file name +const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); + +/// TODO remove the allocator requirement from this API +pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { + if (symLink(existing_path, new_path)) { + return; + } else |err| switch (err) { + error.PathAlreadyExists => {}, + else => return err, // TODO zig should know this set does not include PathAlreadyExists + } + + const dirname = path.dirname(new_path) orelse "."; + + var rand_buf: [12]u8 = undefined; + const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); + defer allocator.free(tmp_path); + mem.copy(u8, tmp_path[0..], dirname); + tmp_path[dirname.len] = path.sep; + while (true) { + try crypto.randomBytes(rand_buf[0..]); + b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); + + if (symLink(existing_path, tmp_path)) { + return rename(tmp_path, new_path); + } else |err| switch (err) { + error.PathAlreadyExists => continue, + else => return err, // TODO zig should know this set does not include PathAlreadyExists + } + } +} + +/// 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 +/// in the same directory as dest_path. +/// Destination file will have the same mode as the source file. +pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { + var in_file = try File.openRead(source_path); + defer in_file.close(); + + const mode = try in_file.mode(); + const in_stream = &in_file.inStream().stream; + + var atomic_file = try AtomicFile.init(dest_path, mode); + defer atomic_file.deinit(); + + var buf: [mem.page_size]u8 = undefined; + while (true) { + const amt = try in_stream.readFull(buf[0..]); + try atomic_file.file.write(buf[0..amt]); + if (amt != buf.len) { + return atomic_file.finish(); + } + } +} + +/// 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 +pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { + var in_file = try File.openRead(source_path); + defer in_file.close(); + + var atomic_file = try AtomicFile.init(dest_path, mode); + defer atomic_file.deinit(); + + var buf: [mem.page_size]u8 = undefined; + while (true) { + const amt = try in_file.read(buf[0..]); + try atomic_file.file.write(buf[0..amt]); + if (amt != buf.len) { + return atomic_file.finish(); + } + } +} + +pub const AtomicFile = struct { + file: File, + tmp_path_buf: [MAX_PATH_BYTES]u8, + dest_path: []const u8, + finished: bool, + + const InitError = File.OpenError; + + /// dest_path must remain valid for the lifetime of AtomicFile + /// call finish to atomically replace dest_path with contents + /// TODO once we have null terminated pointers, use the + /// openWriteNoClobberN function + pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { + const dirname = path.dirname(dest_path); + var rand_buf: [12]u8 = undefined; + const dirname_component_len = if (dirname) |d| d.len + 1 else 0; + const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len); + const tmp_path_len = dirname_component_len + encoded_rand_len; + var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined; + if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong; + + if (dirname) |dir| { + mem.copy(u8, tmp_path_buf[0..], dir); + tmp_path_buf[dir.len] = path.sep; + } + + tmp_path_buf[tmp_path_len] = 0; + + while (true) { + try crypto.randomBytes(rand_buf[0..]); + b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); + + const file = File.openWriteNoClobberC(&tmp_path_buf, 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 + else => return err, + }; + + return AtomicFile{ + .file = file, + .tmp_path_buf = tmp_path_buf, + .dest_path = dest_path, + .finished = false, + }; + } + } + + /// always call deinit, even after successful finish() + pub fn deinit(self: *AtomicFile) void { + if (!self.finished) { + self.file.close(); + deleteFileC(&self.tmp_path_buf) catch {}; + self.finished = true; + } + } + + pub fn finish(self: *AtomicFile) !void { + assert(!self.finished); + self.file.close(); + self.finished = true; + if (os.windows.is_the_target) { + const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path); + const tmp_path_w = try os.windows.cStrToPrefixedFileW(&self.tmp_path_buf); + return os.renameW(&tmp_path_w, &dest_path_w); + } + const dest_path_c = try os.toPosixPath(self.dest_path); + return os.renameC(&self.tmp_path_buf, &dest_path_c); + } +}; + +const default_new_dir_mode = 0o755; + +/// Create a new directory. +pub fn makeDir(dir_path: []const u8) !void { + return os.mkdir(dir_path, default_new_dir_mode); +} + +/// Same as `makeDir` except the parameter is a null-terminated UTF8-encoded string. +pub fn makeDirC(dir_path: [*]const u8) !void { + return os.mkdirC(dir_path, default_new_dir_mode); +} + +/// Same as `makeDir` except the parameter is a null-terminated UTF16LE-encoded string. +pub fn makeDirW(dir_path: [*]const u16) !void { + return os.mkdirW(dir_path, default_new_dir_mode); +} + +/// Calls makeDir recursively to make an entire path. Returns success if the path +/// already exists and is a directory. +/// This function is not atomic, and if it returns an error, the file system may +/// have been modified regardless. +/// TODO determine if we can remove the allocator requirement from this function +pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { + const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); + defer allocator.free(resolved_path); + + var end_index: usize = resolved_path.len; + while (true) { + makeDir(resolved_path[0..end_index]) catch |err| switch (err) { + error.PathAlreadyExists => { + // TODO stat the file and return an error if it's not a directory + // this is important because otherwise a dangling symlink + // could cause an infinite loop + if (end_index == resolved_path.len) return; + }, + error.FileNotFound => { + // march end_index backward until next path component + while (true) { + end_index -= 1; + if (path.isSep(resolved_path[end_index])) break; + } + continue; + }, + else => return err, + }; + if (end_index == resolved_path.len) return; + // march end_index forward until next path component + while (true) { + end_index += 1; + if (end_index == resolved_path.len or path.isSep(resolved_path[end_index])) break; + } + } +} + +/// Returns `error.DirNotEmpty` if the directory is not empty. +/// To delete a directory recursively, see `deleteTree`. +pub fn deleteDir(dir_path: []const u8) !void { + return os.rmdir(dir_path); +} + +/// Same as `deleteDir` except the parameter is a null-terminated UTF8-encoded string. +pub fn deleteDirC(dir_path: [*]const u8) !void { + return os.rmdirC(dir_path); +} + +/// Same as `deleteDir` except the parameter is a null-terminated UTF16LE-encoded string. +pub fn deleteDirW(dir_path: [*]const u16) !void { + return os.rmdirW(dir_path); +} + +/// Whether ::full_path describes a symlink, file, or directory, this function +/// removes it. If it cannot be removed because it is a non-empty directory, +/// this function recursively removes its entries and then tries again. +const DeleteTreeError = error{ + OutOfMemory, + AccessDenied, + FileTooBig, + IsDir, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + SystemResources, + NoSpaceLeft, + PathAlreadyExists, + ReadOnlyFileSystem, + NotDir, + FileNotFound, + FileSystem, + FileBusy, + DirNotEmpty, + DeviceBusy, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, + + /// On Windows, file paths cannot contain these characters: + /// '/', '*', '?', '"', '<', '>', '|' + BadPathName, + + Unexpected, +}; + +/// TODO determine if we can remove the allocator requirement +pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { + start_over: while (true) { + var got_access_denied = false; + // First, try deleting the item as a file. This way we don't follow sym links. + if (deleteFile(full_path)) { + return; + } else |err| switch (err) { + error.FileNotFound => return, + error.IsDir => {}, + error.AccessDenied => got_access_denied = true, + + error.InvalidUtf8, + error.SymLinkLoop, + error.NameTooLong, + error.SystemResources, + error.ReadOnlyFileSystem, + error.NotDir, + error.FileSystem, + error.FileBusy, + error.BadPathName, + error.Unexpected, + => return err, + } + { + var dir = Dir.open(allocator, full_path) catch |err| switch (err) { + error.NotDir => { + if (got_access_denied) { + return error.AccessDenied; + } + continue :start_over; + }, + + error.OutOfMemory, + error.AccessDenied, + error.FileTooBig, + error.IsDir, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.NameTooLong, + error.SystemFdQuotaExceeded, + error.NoDevice, + error.FileNotFound, + error.SystemResources, + error.NoSpaceLeft, + error.PathAlreadyExists, + error.Unexpected, + error.InvalidUtf8, + error.BadPathName, + error.DeviceBusy, + => return err, + }; + defer dir.close(); + + var full_entry_buf = std.ArrayList(u8).init(allocator); + defer full_entry_buf.deinit(); + + while (try dir.next()) |entry| { + try full_entry_buf.resize(full_path.len + entry.name.len + 1); + const full_entry_path = full_entry_buf.toSlice(); + mem.copy(u8, full_entry_path, full_path); + full_entry_path[full_path.len] = path.sep; + mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); + + try deleteTree(allocator, full_entry_path); + } + } + return deleteDir(full_path); + } +} + +pub const Dir = struct { + handle: Handle, + allocator: *Allocator, + + pub const Handle = switch (builtin.os) { + .macosx, .ios, .freebsd, .netbsd => struct { + fd: i32, + seek: i64, + buf: []u8, + index: usize, + end_index: usize, + }, + .linux => struct { + fd: i32, + buf: []u8, + index: usize, + end_index: usize, + }, + .windows => struct { + handle: os.windows.HANDLE, + find_file_data: os.windows.WIN32_FIND_DATAW, + first: bool, + name_data: [256]u8, + }, + else => @compileError("unimplemented"), + }; + + pub const Entry = struct { + name: []const u8, + kind: Kind, + + pub const Kind = enum { + BlockDevice, + CharacterDevice, + Directory, + NamedPipe, + SymLink, + File, + UnixDomainSocket, + Whiteout, + Unknown, + }; + }; + + pub const OpenError = error{ + FileNotFound, + NotDir, + AccessDenied, + FileTooBig, + IsDir, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + SystemResources, + NoSpaceLeft, + PathAlreadyExists, + OutOfMemory, + InvalidUtf8, + BadPathName, + DeviceBusy, + + Unexpected, + }; + + /// TODO remove the allocator requirement from this API + pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir { + return Dir{ + .allocator = allocator, + .handle = switch (builtin.os) { + .windows => blk: { + var find_file_data: os.windows.WIN32_FIND_DATAW = undefined; + const handle = try os.windows.FindFirstFile(dir_path, &find_file_data); + break :blk Handle{ + .handle = handle, + .find_file_data = find_file_data, // TODO guaranteed copy elision + .first = true, + .name_data = undefined, + }; + }, + .macosx, .ios, .freebsd, .netbsd => Handle{ + .fd = try os.open(dir_path, os.O_RDONLY | os.O_NONBLOCK | os.O_DIRECTORY | os.O_CLOEXEC, 0), + .seek = 0, + .index = 0, + .end_index = 0, + .buf = []u8{}, + }, + .linux => Handle{ + .fd = try os.open(dir_path, os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC, 0), + .index = 0, + .end_index = 0, + .buf = []u8{}, + }, + else => @compileError("unimplemented"), + }, + }; + } + + pub fn close(self: *Dir) void { + if (os.windows.is_the_target) { + return os.windows.FindClose(self.handle.handle); + } + self.allocator.free(self.handle.buf); + os.close(self.handle.fd); + } + + /// Memory such as file names referenced in this returned entry becomes invalid + /// with subsequent calls to next, as well as when this `Dir` is deinitialized. + pub fn next(self: *Dir) !?Entry { + switch (builtin.os) { + .linux => return self.nextLinux(), + .macosx, .ios => return self.nextDarwin(), + .windows => return self.nextWindows(), + .freebsd => return self.nextBsd(), + .netbsd => return self.nextBsd(), + else => @compileError("unimplemented"), + } + } + + fn nextDarwin(self: *Dir) !?Entry { + start_over: while (true) { + if (self.handle.index >= self.handle.end_index) { + if (self.handle.buf.len == 0) { + self.handle.buf = try self.allocator.alloc(u8, mem.page_size); + } + + while (true) { + const rc = os.system.__getdirentries64( + self.handle.fd, + self.handle.buf.ptr, + self.handle.buf.len, + &self.handle.seek, + ); + if (rc == 0) return null; + if (rc < 0) { + switch (os.errno(rc)) { + os.EBADF => unreachable, + os.EFAULT => unreachable, + os.ENOTDIR => unreachable, + os.EINVAL => { + self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); + continue; + }, + else => |err| return os.unexpectedErrno(err), + } + } + self.handle.index = 0; + self.handle.end_index = @intCast(usize, rc); + break; + } + } + const darwin_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]); + const next_index = self.handle.index + darwin_entry.d_reclen; + self.handle.index = next_index; + + const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; + + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (darwin_entry.d_type) { + os.DT_BLK => Entry.Kind.BlockDevice, + os.DT_CHR => Entry.Kind.CharacterDevice, + os.DT_DIR => Entry.Kind.Directory, + os.DT_FIFO => Entry.Kind.NamedPipe, + os.DT_LNK => Entry.Kind.SymLink, + os.DT_REG => Entry.Kind.File, + os.DT_SOCK => Entry.Kind.UnixDomainSocket, + os.DT_WHT => Entry.Kind.Whiteout, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } + } + + fn nextWindows(self: *Dir) !?Entry { + while (true) { + if (self.handle.first) { + self.handle.first = false; + } else { + if (!try os.windows.FindNextFile(self.handle.handle, &self.handle.find_file_data)) + return null; + } + const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); + if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' })) + continue; + // Trust that Windows gives us valid UTF-16LE + const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; + const name_utf8 = self.handle.name_data[0..name_utf8_len]; + const kind = blk: { + const attrs = self.handle.find_file_data.dwFileAttributes; + if (attrs & os.windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; + if (attrs & os.windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; + if (attrs & os.windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File; + break :blk Entry.Kind.Unknown; + }; + return Entry{ + .name = name_utf8, + .kind = kind, + }; + } + } + + fn nextLinux(self: *Dir) !?Entry { + start_over: while (true) { + if (self.handle.index >= self.handle.end_index) { + if (self.handle.buf.len == 0) { + self.handle.buf = try self.allocator.alloc(u8, mem.page_size); + } + + while (true) { + const rc = os.linux.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len); + switch (os.linux.getErrno(rc)) { + 0 => {}, + os.EBADF => unreachable, + os.EFAULT => unreachable, + os.ENOTDIR => unreachable, + os.EINVAL => { + self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); + continue; + }, + else => |err| return os.unexpectedErrno(err), + } + if (rc == 0) return null; + self.handle.index = 0; + self.handle.end_index = rc; + break; + } + } + const linux_entry = @ptrCast(*align(1) os.dirent64, &self.handle.buf[self.handle.index]); + const next_index = self.handle.index + linux_entry.d_reclen; + self.handle.index = next_index; + + const name = mem.toSlice(u8, @ptrCast([*]u8, &linux_entry.d_name)); + + // skip . and .. entries + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (linux_entry.d_type) { + os.DT_BLK => Entry.Kind.BlockDevice, + os.DT_CHR => Entry.Kind.CharacterDevice, + os.DT_DIR => Entry.Kind.Directory, + os.DT_FIFO => Entry.Kind.NamedPipe, + os.DT_LNK => Entry.Kind.SymLink, + os.DT_REG => Entry.Kind.File, + os.DT_SOCK => Entry.Kind.UnixDomainSocket, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } + } + + fn nextBsd(self: *Dir) !?Entry { + start_over: while (true) { + if (self.handle.index >= self.handle.end_index) { + if (self.handle.buf.len == 0) { + self.handle.buf = try self.allocator.alloc(u8, mem.page_size); + } + + while (true) { + const rc = os.system.getdirentries( + self.handle.fd, + self.handle.buf.ptr, + self.handle.buf.len, + &self.handle.seek, + ); + switch (os.errno(rc)) { + 0 => {}, + os.EBADF => unreachable, + os.EFAULT => unreachable, + os.ENOTDIR => unreachable, + os.EINVAL => { + self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); + continue; + }, + else => |err| return os.unexpectedErrno(err), + } + if (rc == 0) return null; + self.handle.index = 0; + self.handle.end_index = @intCast(usize, rc); + break; + } + } + const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]); + const next_index = self.handle.index + freebsd_entry.d_reclen; + self.handle.index = next_index; + + const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; + + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (freebsd_entry.d_type) { + os.DT_BLK => Entry.Kind.BlockDevice, + os.DT_CHR => Entry.Kind.CharacterDevice, + os.DT_DIR => Entry.Kind.Directory, + os.DT_FIFO => Entry.Kind.NamedPipe, + os.DT_LNK => Entry.Kind.SymLink, + os.DT_REG => Entry.Kind.File, + os.DT_SOCK => Entry.Kind.UnixDomainSocket, + os.DT_WHT => Entry.Kind.Whiteout, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } + } +}; + +/// Read value of a symbolic link. +/// The return value is a slice of buffer, from index `0`. +pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 { + return os.readlink(pathname, buffer); +} + +/// Same as `readLink`, except the `pathname` parameter is null-terminated. +pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 { + return os.readlinkC(pathname, buffer); +} + +pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError; + +pub fn openSelfExe() OpenSelfExeError!File { + if (os.linux.is_the_target) { + return File.openReadC(c"/proc/self/exe"); + } + if (os.windows.is_the_target) { + var buf: [os.windows.PATH_MAX_WIDE]u16 = undefined; + const wide_slice = try selfExePathW(&buf); + return File.openReadW(wide_slice.ptr); + } + var buf: [MAX_PATH_BYTES]u8 = undefined; + const self_exe_path = try selfExePath(&buf); + buf[self_exe_path.len] = 0; + return File.openReadC(self_exe_path.ptr); +} + +test "openSelfExe" { + switch (builtin.os) { + .linux, .macosx, .ios, .windows, .freebsd => (try openSelfExe()).close(), + else => return error.SkipZigTest, // Unsupported OS. + } +} + +pub const SelfExePathError = os.ReadLinkError || os.SysCtlError; + +/// Get the path to the current executable. +/// 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 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: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { + if (os.darwin.is_the_target) { + var u32_len: u32 = out_buffer.len; + const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len); + if (rc != 0) return error.NameTooLong; + return mem.toSlice(u8, out_buffer); + } + switch (builtin.os) { + .linux => return os.readlinkC(c"/proc/self/exe", out_buffer), + .freebsd => { + var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 }; + var out_len: usize = out_buffer.len; + try os.sysctl(&mib, out_buffer, &out_len, null, 0); + // TODO could this slice from 0 to out_len instead? + return mem.toSlice(u8, out_buffer); + }, + .netbsd => { + var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME }; + var out_len: usize = out_buffer.len; + try os.sysctl(&mib, out_buffer, &out_len, null, 0); + // TODO could this slice from 0 to out_len instead? + return mem.toSlice(u8, out_buffer); + }, + .windows => { + var utf16le_buf: [os.windows.PATH_MAX_WIDE]u16 = undefined; + const utf16le_slice = try selfExePathW(&utf16le_buf); + // Trust that Windows gives us valid UTF-16LE. + const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; + return out_buffer[0..end_index]; + }, + else => @compileError("std.fs.selfExePath not supported for this target"), + } +} + +/// Same as `selfExePath` except the result is UTF16LE-encoded. +pub fn selfExePathW(out_buffer: *[os.windows.PATH_MAX_WIDE]u16) SelfExePathError![]u16 { + return os.windows.GetModuleFileNameW(null, out_buffer, out_buffer.len); +} + +/// `selfExeDirPath` except allocates the result on the heap. +/// Caller owns returned memory. +pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { + var buf: [MAX_PATH_BYTES]u8 = undefined; + return mem.dupe(allocator, u8, try selfExeDirPath(&buf)); +} + +/// Get the directory path that contains the current executable. +/// Returned value is a slice of out_buffer. +pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 { + if (os.linux.is_the_target) { + // If the currently executing binary has been deleted, + // the file path looks something like `/a/b/c/exe (deleted)` + // This path cannot be opened, but it's valid for determining the directory + // the executable was in when it was run. + const full_exe_path = try os.readlinkC(c"/proc/self/exe", out_buffer); + // Assume that /proc/self/exe has an absolute path, and therefore dirname + // will not return null. + return path.dirname(full_exe_path).?; + } + 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. +pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 { + var buf: [MAX_PATH_BYTES]u8 = undefined; + return mem.dupe(allocator, u8, try os.realpath(pathname, &buf)); +} + +test "" { + _ = @import("fs/path.zig"); + _ = @import("fs/file.zig"); + _ = @import("fs/get_app_data_dir.zig"); +} diff --git a/std/fs/file.zig b/std/fs/file.zig new file mode 100644 index 0000000000..589927b823 --- /dev/null +++ b/std/fs/file.zig @@ -0,0 +1,319 @@ +const std = @import("../std.zig"); +const builtin = @import("builtin"); +const os = std.os; +const io = std.io; +const mem = std.mem; +const math = std.math; +const assert = std.debug.assert; +const windows = os.windows; +const Os = builtin.Os; +const maxInt = std.math.maxInt; + +pub const File = struct { + /// The OS-specific file descriptor or file handle. + handle: os.fd_t, + + pub const Mode = switch (builtin.os) { + Os.windows => void, + else => u32, + }; + + pub const default_mode = switch (builtin.os) { + Os.windows => {}, + else => 0o666, + }; + + pub const OpenError = windows.CreateFileError || os.OpenError; + + /// Call close to clean up. + pub fn openRead(path: []const u8) OpenError!File { + if (windows.is_the_target) { + const path_w = try windows.sliceToPrefixedFileW(path); + return openReadW(&path_w); + } + const path_c = try os.toPosixPath(path); + return openReadC(&path_c); + } + + /// `openRead` except with a null terminated path + pub fn openReadC(path: [*]const u8) OpenError!File { + if (windows.is_the_target) { + const path_w = try windows.cStrToPrefixedFileW(path); + return openReadW(&path_w); + } + const flags = os.O_LARGEFILE | os.O_RDONLY; + const fd = try os.openC(path, flags, 0); + return openHandle(fd); + } + + /// `openRead` except with a null terminated UTF16LE encoded path + pub fn openReadW(path_w: [*]const u16) OpenError!File { + const handle = try windows.CreateFileW( + path_w, + windows.GENERIC_READ, + windows.FILE_SHARE_READ, + null, + windows.OPEN_EXISTING, + windows.FILE_ATTRIBUTE_NORMAL, + null, + ); + return openHandle(handle); + } + + /// Calls `openWriteMode` with `default_mode` for the mode. + pub fn openWrite(path: []const u8) OpenError!File { + return openWriteMode(path, default_mode); + } + + /// If the path does not exist it will be created. + /// If a file already exists in the destination it will be truncated. + /// Call close to clean up. + pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File { + if (windows.is_the_target) { + const path_w = try windows.sliceToPrefixedFileW(path); + return openWriteModeW(&path_w, file_mode); + } + const path_c = try os.toPosixPath(path); + return openWriteModeC(&path_c, file_mode); + } + + /// Same as `openWriteMode` except `path` is null-terminated. + pub fn openWriteModeC(path: [*]const u8, file_mode: Mode) OpenError!File { + if (windows.is_the_target) { + const path_w = try windows.cStrToPrefixedFileW(path); + return openWriteModeW(&path_w, file_mode); + } + const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC; + const fd = try os.openC(path, flags, file_mode); + return openHandle(fd); + } + + /// Same as `openWriteMode` except `path` is null-terminated and UTF16LE encoded + pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File { + const handle = try windows.CreateFileW( + path_w, + windows.GENERIC_WRITE, + windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, + null, + windows.CREATE_ALWAYS, + windows.FILE_ATTRIBUTE_NORMAL, + null, + ); + return openHandle(handle); + } + + /// If the path does not exist it will be created. + /// If a file already exists in the destination this returns OpenError.PathAlreadyExists + /// Call close to clean up. + pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File { + if (windows.is_the_target) { + const path_w = try windows.sliceToPrefixedFileW(path); + return openWriteNoClobberW(&path_w, file_mode); + } + const path_c = try os.toPosixPath(path); + return openWriteNoClobberC(&path_c, file_mode); + } + + pub fn openWriteNoClobberC(path: [*]const u8, file_mode: Mode) OpenError!File { + if (windows.is_the_target) { + const path_w = try windows.cStrToPrefixedFileW(path); + return openWriteNoClobberW(&path_w, file_mode); + } + const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_EXCL; + const fd = try os.openC(path, flags, file_mode); + return openHandle(fd); + } + + pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File { + const handle = try windows.CreateFileW( + path_w, + windows.GENERIC_WRITE, + windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, + null, + windows.CREATE_NEW, + windows.FILE_ATTRIBUTE_NORMAL, + null, + ); + return openHandle(handle); + } + + pub fn openHandle(handle: os.fd_t) File { + return File{ .handle = handle }; + } + + /// Test for the existence of `path`. + /// `path` is UTF8-encoded. + /// In general it is recommended to avoid 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. + pub fn access(path: []const u8) !void { + return os.access(path, os.F_OK); + } + + /// Same as `access` except the parameter is null-terminated. + pub fn accessC(path: [*]const u8) !void { + return os.accessC(path, os.F_OK); + } + + /// Same as `access` except the parameter is null-terminated UTF16LE-encoded. + pub fn accessW(path: [*]const u16) !void { + return os.accessW(path, os.F_OK); + } + + /// 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 { + return os.close(self.handle); + } + + /// Test whether the file refers to a terminal. + /// See also `supportsAnsiEscapeCodes`. + pub fn isTty(self: File) bool { + return os.isatty(self.handle); + } + + /// Test whether ANSI escape codes will be treated as such. + pub fn supportsAnsiEscapeCodes(self: File) bool { + if (windows.is_the_target) { + return os.isCygwinPty(self.handle); + } + return self.isTty(); + } + + pub const SeekError = os.SeekError; + + /// Repositions read/write file offset relative to the current offset. + pub fn seekBy(self: File, offset: i64) SeekError!void { + return os.lseek_CUR(self.handle, offset); + } + + /// Repositions read/write file offset relative to the end. + pub fn seekFromEnd(self: File, offset: i64) SeekError!void { + return os.lseek_END(self.handle, offset); + } + + /// Repositions read/write file offset relative to the beginning. + pub fn seekTo(self: File, offset: u64) SeekError!void { + return os.lseek_SET(self.handle, offset); + } + + pub const GetPosError = os.SeekError || os.FStatError; + + pub fn getPos(self: File) GetPosError!u64 { + return os.lseek_CUR_get(self.handle); + } + + pub fn getEndPos(self: File) GetPosError!u64 { + if (windows.is_the_target) { + return windows.GetFileSizeEx(self.handle); + } + const stat = try os.fstat(self.handle); + return @bitCast(u64, stat.size); + } + + pub const ModeError = os.FStatError; + + pub fn mode(self: File) ModeError!Mode { + if (windows.is_the_target) { + return {}; + } + const stat = try os.fstat(self.handle); + // TODO: we should be able to cast u16 to ModeError!u32, making this + // explicit cast not necessary + return Mode(stat.mode); + } + + pub const ReadError = os.ReadError; + + pub fn read(self: File, buffer: []u8) ReadError!usize { + return os.read(self.handle, buffer); + } + + pub const WriteError = os.WriteError; + + pub fn write(self: File, bytes: []const u8) WriteError!void { + return os.write(self.handle, bytes); + } + + pub fn inStream(file: File) InStream { + return InStream{ + .file = file, + .stream = InStream.Stream{ .readFn = InStream.readFn }, + }; + } + + pub fn outStream(file: File) OutStream { + return OutStream{ + .file = file, + .stream = OutStream.Stream{ .writeFn = OutStream.writeFn }, + }; + } + + pub fn seekableStream(file: File) SeekableStream { + return SeekableStream{ + .file = file, + .stream = SeekableStream.Stream{ + .seekToFn = SeekableStream.seekToFn, + .seekByFn = SeekableStream.seekByFn, + .getPosFn = SeekableStream.getPosFn, + .getEndPosFn = SeekableStream.getEndPosFn, + }, + }; + } + + /// Implementation of io.InStream trait for File + pub const InStream = struct { + file: File, + stream: Stream, + + pub const Error = ReadError; + pub const Stream = io.InStream(Error); + + fn readFn(in_stream: *Stream, buffer: []u8) Error!usize { + const self = @fieldParentPtr(InStream, "stream", in_stream); + return self.file.read(buffer); + } + }; + + /// Implementation of io.OutStream trait for File + pub const OutStream = struct { + file: File, + stream: Stream, + + pub const Error = WriteError; + pub const Stream = io.OutStream(Error); + + fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void { + const self = @fieldParentPtr(OutStream, "stream", out_stream); + return self.file.write(bytes); + } + }; + + /// Implementation of io.SeekableStream trait for File + pub const SeekableStream = struct { + file: File, + stream: Stream, + + pub const Stream = io.SeekableStream(SeekError, GetPosError); + + pub fn seekToFn(seekable_stream: *Stream, pos: u64) SeekError!void { + const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); + return self.file.seekTo(pos); + } + + pub fn seekByFn(seekable_stream: *Stream, amt: i64) SeekError!void { + const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); + return self.file.seekBy(amt); + } + + pub fn getEndPosFn(seekable_stream: *Stream) GetPosError!u64 { + const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); + return self.file.getEndPos(); + } + + pub fn getPosFn(seekable_stream: *Stream) GetPosError!u64 { + const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); + return self.file.getPos(); + } + }; +}; diff --git a/std/os/get_app_data_dir.zig b/std/fs/get_app_data_dir.zig index e69c03edb9..87703592c1 100644 --- a/std/os/get_app_data_dir.zig +++ b/std/fs/get_app_data_dir.zig @@ -2,6 +2,7 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); const unicode = std.unicode; const mem = std.mem; +const fs = std.fs; const os = std.os; pub const GetAppDataDirError = error{ @@ -13,16 +14,16 @@ pub const GetAppDataDirError = error{ /// TODO determine if we can remove the allocator requirement pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { switch (builtin.os) { - builtin.Os.windows => { + .windows => { var dir_path_ptr: [*]u16 = undefined; - switch (os.windows.SHGetKnownFolderPath( + switch (os.windows.shell32.SHGetKnownFolderPath( &os.windows.FOLDERID_LocalAppData, os.windows.KF_FLAG_CREATE, null, &dir_path_ptr, )) { os.windows.S_OK => { - defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); + defer os.windows.ole32.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); const global_dir = unicode.utf16leToUtf8Alloc(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) { error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, @@ -30,25 +31,25 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD error.OutOfMemory => return error.OutOfMemory, }; defer allocator.free(global_dir); - return os.path.join(allocator, [][]const u8{ global_dir, appname }); + return fs.path.join(allocator, [][]const u8{ global_dir, appname }); }, os.windows.E_OUTOFMEMORY => return error.OutOfMemory, else => return error.AppDataDirUnavailable, } }, - builtin.Os.macosx => { - const home_dir = os.getEnvPosix("HOME") orelse { + .macosx => { + const home_dir = os.getenv("HOME") orelse { // TODO look in /etc/passwd return error.AppDataDirUnavailable; }; - return os.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname }); + return fs.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname }); }, - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => { - const home_dir = os.getEnvPosix("HOME") orelse { + .linux, .freebsd, .netbsd => { + const home_dir = os.getenv("HOME") orelse { // TODO look in /etc/passwd return error.AppDataDirUnavailable; }; - return os.path.join(allocator, [][]const u8{ home_dir, ".local", "share", appname }); + return fs.path.join(allocator, [][]const u8{ home_dir, ".local", "share", appname }); }, else => @compileError("Unsupported OS"), } @@ -60,7 +61,7 @@ fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { return ptr[0..index]; } -test "std.os.getAppDataDir" { +test "getAppDataDir" { var buf: [512]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; diff --git a/std/os/path.zig b/std/fs/path.zig index fa8bb282eb..7b95a3e4d2 100644 --- a/std/os/path.zig +++ b/std/fs/path.zig @@ -1,33 +1,28 @@ -const std = @import("../std.zig"); const builtin = @import("builtin"); -const Os = builtin.Os; +const std = @import("../std.zig"); const debug = std.debug; const assert = debug.assert; const testing = std.testing; const mem = std.mem; const fmt = std.fmt; const Allocator = mem.Allocator; -const os = std.os; const math = std.math; -const posix = os.posix; -const windows = os.windows; -const cstr = std.cstr; -const windows_util = @import("windows/util.zig"); +const windows = std.os.windows; +const fs = std.fs; +const process = std.process; pub const sep_windows = '\\'; pub const sep_posix = '/'; -pub const sep = if (is_windows) sep_windows else sep_posix; +pub const sep = if (windows.is_the_target) sep_windows else sep_posix; pub const sep_str = [1]u8{sep}; pub const delimiter_windows = ';'; pub const delimiter_posix = ':'; -pub const delimiter = if (is_windows) delimiter_windows else delimiter_posix; - -const is_windows = builtin.os == builtin.Os.windows; +pub const delimiter = if (windows.is_the_target) delimiter_windows else delimiter_posix; pub fn isSep(byte: u8) bool { - if (is_windows) { + if (windows.is_the_target) { return byte == '/' or byte == '\\'; } else { return byte == '/'; @@ -77,7 +72,7 @@ fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u return buf; } -pub const join = if (is_windows) joinWindows else joinPosix; +pub const join = if (windows.is_the_target) joinWindows else joinPosix; /// Naively combines a series of paths with the native path seperator. /// Allocates memory for the result, which must be freed by the caller. @@ -105,7 +100,7 @@ fn testJoinPosix(paths: []const []const u8, expected: []const u8) void { testing.expectEqualSlices(u8, expected, actual); } -test "os.path.join" { +test "join" { testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); testJoinWindows([][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c"); @@ -134,7 +129,7 @@ test "os.path.join" { } pub fn isAbsolute(path: []const u8) bool { - if (is_windows) { + if (windows.is_the_target) { return isAbsoluteWindows(path); } else { return isAbsolutePosix(path); @@ -164,7 +159,7 @@ pub fn isAbsolutePosix(path: []const u8) bool { return path[0] == sep_posix; } -test "os.path.isAbsoluteWindows" { +test "isAbsoluteWindows" { testIsAbsoluteWindows("/", true); testIsAbsoluteWindows("//", true); testIsAbsoluteWindows("//server", true); @@ -186,7 +181,7 @@ test "os.path.isAbsoluteWindows" { testIsAbsoluteWindows("/usr/local", true); } -test "os.path.isAbsolutePosix" { +test "isAbsolutePosix" { testIsAbsolutePosix("/home/foo", true); testIsAbsolutePosix("/home/foo/..", true); testIsAbsolutePosix("bar/", false); @@ -279,7 +274,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { return relative_path; } -test "os.path.windowsParsePath" { +test "windowsParsePath" { { const parsed = windowsParsePath("//a/b"); testing.expect(parsed.is_abs); @@ -313,7 +308,7 @@ test "os.path.windowsParsePath" { } pub fn diskDesignator(path: []const u8) []const u8 { - if (is_windows) { + if (windows.is_the_target) { return diskDesignatorWindows(path); } else { return ""; @@ -378,7 +373,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { - if (is_windows) { + if (windows.is_the_target) { return resolveWindows(allocator, paths); } else { return resolvePosix(allocator, paths); @@ -395,8 +390,8 @@ pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { - assert(is_windows); // resolveWindows called on non windows can't use getCwd - return os.getCwdAlloc(allocator); + assert(windows.is_the_target); // resolveWindows called on non windows can't use getCwd + return process.getCwdAlloc(allocator); } // determine which disk designator we will result with, if any @@ -490,8 +485,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { result_disk_designator = result[0..result_index]; }, WindowsPath.Kind.None => { - assert(is_windows); // resolveWindows called on non windows can't use getCwd - const cwd = try os.getCwdAlloc(allocator); + assert(windows.is_the_target); // resolveWindows called on non windows can't use getCwd + const cwd = try process.getCwdAlloc(allocator); defer allocator.free(cwd); const parsed_cwd = windowsParsePath(cwd); result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1); @@ -505,9 +500,9 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { }, } } else { - assert(is_windows); // resolveWindows called on non windows can't use getCwd + assert(windows.is_the_target); // resolveWindows called on non windows can't use getCwd // TODO call get cwd for the result_disk_designator instead of the global one - const cwd = try os.getCwdAlloc(allocator); + const cwd = try process.getCwdAlloc(allocator); defer allocator.free(cwd); result = try allocator.alloc(u8, max_size + cwd.len + 1); @@ -576,8 +571,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { - assert(!is_windows); // resolvePosix called on windows can't use getCwd - return os.getCwdAlloc(allocator); + assert(!windows.is_the_target); // resolvePosix called on windows can't use getCwd + return process.getCwdAlloc(allocator); } var first_index: usize = 0; @@ -598,8 +593,8 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (have_abs) { result = try allocator.alloc(u8, max_size); } else { - assert(!is_windows); // resolvePosix called on windows can't use getCwd - const cwd = try os.getCwdAlloc(allocator); + assert(!windows.is_the_target); // resolvePosix called on windows can't use getCwd + const cwd = try process.getCwdAlloc(allocator); defer allocator.free(cwd); result = try allocator.alloc(u8, max_size + cwd.len + 1); mem.copy(u8, result, cwd); @@ -637,9 +632,9 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { return allocator.shrink(result, result_index); } -test "os.path.resolve" { - const cwd = try os.getCwdAlloc(debug.global_allocator); - if (is_windows) { +test "resolve" { + const cwd = try process.getCwdAlloc(debug.global_allocator); + if (windows.is_the_target) { if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { cwd[0] = asciiUpper(cwd[0]); } @@ -650,9 +645,9 @@ test "os.path.resolve" { } } -test "os.path.resolveWindows" { - if (is_windows) { - const cwd = try os.getCwdAlloc(debug.global_allocator); +test "resolveWindows" { + if (windows.is_the_target) { + const cwd = try process.getCwdAlloc(debug.global_allocator); const parsed_cwd = windowsParsePath(cwd); { const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }); @@ -693,7 +688,7 @@ test "os.path.resolveWindows" { testing.expect(mem.eql(u8, testResolveWindows([][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }), "C:\\foo\\tmp.3\\cycles\\root.js")); } -test "os.path.resolvePosix" { +test "resolvePosix" { testing.expect(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b", "c" }), "/a/b/c")); testing.expect(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b", "c", "//d", "e///" }), "/d/e")); testing.expect(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b/c", "..", "../" }), "/a")); @@ -717,7 +712,7 @@ fn testResolvePosix(paths: []const []const u8) []u8 { /// If the path is a file in the current directory (no directory component) /// then returns null pub fn dirname(path: []const u8) ?[]const u8 { - if (is_windows) { + if (windows.is_the_target) { return dirnameWindows(path); } else { return dirnamePosix(path); @@ -784,7 +779,7 @@ pub fn dirnamePosix(path: []const u8) ?[]const u8 { return path[0..end_index]; } -test "os.path.dirnamePosix" { +test "dirnamePosix" { testDirnamePosix("/a/b/c", "/a/b"); testDirnamePosix("/a/b/c///", "/a/b"); testDirnamePosix("/a", "/"); @@ -796,7 +791,7 @@ test "os.path.dirnamePosix" { testDirnamePosix("a//", null); } -test "os.path.dirnameWindows" { +test "dirnameWindows" { testDirnameWindows("c:\\", "c:\\"); testDirnameWindows("c:\\foo", "c:\\"); testDirnameWindows("c:\\foo\\", "c:\\"); @@ -849,7 +844,7 @@ fn testDirnameWindows(input: []const u8, expected_output: ?[]const u8) void { } pub fn basename(path: []const u8) []const u8 { - if (is_windows) { + if (windows.is_the_target) { return basenameWindows(path); } else { return basenamePosix(path); @@ -909,7 +904,7 @@ pub fn basenameWindows(path: []const u8) []const u8 { return path[start_index + 1 .. end_index]; } -test "os.path.basename" { +test "basename" { testBasename("", ""); testBasename("/", ""); testBasename("/dir/basename.ext", "basename.ext"); @@ -965,7 +960,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void { /// string is returned. /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. pub fn relative(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { - if (is_windows) { + if (windows.is_the_target) { return relativeWindows(allocator, from, to); } else { return relativePosix(allocator, from, to); @@ -1090,7 +1085,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![ return []u8{}; } -test "os.path.relative" { +test "relative" { testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); @@ -1139,150 +1134,3 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons const result = relativeWindows(debug.global_allocator, from, to) catch unreachable; testing.expectEqualSlices(u8, expected_output, result); } - -pub const RealError = error{ - FileNotFound, - AccessDenied, - NameTooLong, - NotSupported, - NotDir, - SymLinkLoop, - InputOutput, - FileTooBig, - IsDir, - ProcessFdQuotaExceeded, - SystemFdQuotaExceeded, - NoDevice, - SystemResources, - NoSpaceLeft, - FileSystem, - BadPathName, - DeviceBusy, - - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, - - /// TODO remove this possibility - PathAlreadyExists, - - /// TODO remove this possibility - Unexpected, -}; - -/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. -/// Otherwise use `real` or `realC`. -pub fn realW(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u16) RealError![]u8 { - const h_file = windows.CreateFileW( - pathname, - windows.GENERIC_READ, - windows.FILE_SHARE_READ, - null, - windows.OPEN_EXISTING, - windows.FILE_ATTRIBUTE_NORMAL, - null, - ); - if (h_file == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.ACCESS_DENIED => return error.AccessDenied, - windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, - else => return os.unexpectedErrorWindows(err), - } - } - defer os.close(h_file); - var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; - const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast - const result = windows.GetFinalPathNameByHandleW(h_file, &utf16le_buf, casted_len, windows.VOLUME_NAME_DOS); - assert(result <= utf16le_buf.len); - if (result == 0) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources, - windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, - windows.ERROR.INVALID_PARAMETER => unreachable, - else => return os.unexpectedErrorWindows(err), - } - } - const utf16le_slice = utf16le_buf[0..result]; - - // windows returns \\?\ prepended to the path - // we strip it because nobody wants \\?\ prepended to their path - const prefix = []u16{ '\\', '\\', '?', '\\' }; - const start_index = if (mem.startsWith(u16, utf16le_slice, prefix)) prefix.len else 0; - - // Trust that Windows gives us valid UTF-16LE. - const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice[start_index..]) catch unreachable; - return out_buffer[0..end_index]; -} - -/// See `real` -/// Use this when you have a null terminated pointer path. -pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealError![]u8 { - switch (builtin.os) { - Os.windows => { - const pathname_w = try windows_util.cStrToPrefixedFileW(pathname); - return realW(out_buffer, pathname_w); - }, - Os.freebsd, Os.netbsd, Os.macosx, Os.ios => { - // TODO instead of calling the libc function here, port the implementation to Zig - const err = posix.getErrno(posix.realpath(pathname, out_buffer)); - switch (err) { - 0 => return mem.toSlice(u8, out_buffer), - posix.EINVAL => unreachable, - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EACCES => return error.AccessDenied, - posix.ENOENT => return error.FileNotFound, - posix.ENOTSUP => return error.NotSupported, - posix.ENOTDIR => return error.NotDir, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ELOOP => return error.SymLinkLoop, - posix.EIO => return error.InputOutput, - else => return os.unexpectedErrorPosix(err), - } - }, - Os.linux => { - const fd = try os.posixOpenC(pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0); - defer os.close(fd); - - var buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; - const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable; - - return os.readLinkC(out_buffer, proc_path.ptr); - }, - else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)), - } -} - -/// Return the canonicalized absolute pathname. -/// Expands all symbolic links and resolves references to `.`, `..`, and -/// extra `/` characters in ::pathname. -/// The return value is a slice of out_buffer, and not necessarily from the beginning. -pub fn real(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: []const u8) RealError![]u8 { - switch (builtin.os) { - Os.windows => { - const pathname_w = try windows_util.sliceToPrefixedFileW(pathname); - return realW(out_buffer, &pathname_w); - }, - Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => { - const pathname_c = try os.toPosixPath(pathname); - return realC(out_buffer, &pathname_c); - }, - else => @compileError("Unsupported OS"), - } -} - -/// `real`, except caller must free the returned memory. -pub fn realAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 { - var buf: [os.MAX_PATH_BYTES]u8 = undefined; - return mem.dupe(allocator, u8, try real(&buf, pathname)); -} - -test "os.path.real" { - // at least call it so it gets compiled - var buf: [os.MAX_PATH_BYTES]u8 = undefined; - testing.expectError(error.FileNotFound, real(&buf, "definitely_bogus_does_not_exist1234")); -} diff --git a/std/heap.zig b/std/heap.zig index 3bbb35e65d..7d7774f453 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -5,7 +5,6 @@ const testing = std.testing; const mem = std.mem; const os = std.os; const builtin = @import("builtin"); -const Os = builtin.Os; const c = std.c; const maxInt = std.math.maxInt; @@ -51,201 +50,195 @@ pub const DirectAllocator = struct { if (n == 0) return (([*]u8)(undefined))[0..0]; - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const p = os.posix; - const alloc_size = if (alignment <= os.page_size) n else n + alignment; - const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); - if (addr == p.MAP_FAILED) return error.OutOfMemory; - if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n]; - - const aligned_addr = mem.alignForward(addr, alignment); - - // Unmap the extra bytes that were only requested in order to guarantee - // that the range of memory we were provided had a proper alignment in - // it somewhere. The extra bytes could be at the beginning, or end, or both. - const unused_start_len = aligned_addr - addr; - if (unused_start_len != 0) { - const err = p.munmap(addr, unused_start_len); - assert(p.getErrno(err) == 0); - } - const aligned_end_addr = std.mem.alignForward(aligned_addr + n, os.page_size); - const unused_end_len = addr + alloc_size - aligned_end_addr; - if (unused_end_len != 0) { - const err = p.munmap(aligned_end_addr, unused_end_len); - assert(p.getErrno(err) == 0); - } + if (os.windows.is_the_target) { + const w = os.windows; + + // Although officially it's at least aligned to page boundary, + // Windows is known to reserve pages on a 64K boundary. It's + // even more likely that the requested alignment is <= 64K than + // 4K, so we're just allocating blindly and hoping for the best. + // see https://devblogs.microsoft.com/oldnewthing/?p=42223 + const addr = w.VirtualAlloc( + null, + n, + w.MEM_COMMIT | w.MEM_RESERVE, + w.PAGE_READWRITE, + ) catch return error.OutOfMemory; + + // If the allocation is sufficiently aligned, use it. + if (@ptrToInt(addr) & (alignment - 1) == 0) { + return @ptrCast([*]u8, addr)[0..n]; + } - return @intToPtr([*]u8, aligned_addr)[0..n]; - }, - .windows => { - const w = os.windows; - - // Although officially it's at least aligned to page boundary, - // Windows is known to reserve pages on a 64K boundary. It's - // even more likely that the requested alignment is <= 64K than - // 4K, so we're just allocating blindly and hoping for the best. - // see https://devblogs.microsoft.com/oldnewthing/?p=42223 - const addr = w.VirtualAlloc( + // If it wasn't, actually do an explicitely aligned allocation. + w.VirtualFree(addr, 0, w.MEM_RELEASE); + const alloc_size = n + alignment; + + const final_addr = while (true) { + // Reserve a range of memory large enough to find a sufficiently + // aligned address. + const reserved_addr = w.VirtualAlloc( null, + alloc_size, + w.MEM_RESERVE, + w.PAGE_NOACCESS, + ) catch return error.OutOfMemory; + const aligned_addr = mem.alignForward(@ptrToInt(reserved_addr), alignment); + + // Release the reserved pages (not actually used). + w.VirtualFree(reserved_addr, 0, w.MEM_RELEASE); + + // At this point, it is possible that another thread has + // obtained some memory space that will cause the next + // VirtualAlloc call to fail. To handle this, we will retry + // until it succeeds. + const ptr = w.VirtualAlloc( + @intToPtr(*c_void, aligned_addr), n, w.MEM_COMMIT | w.MEM_RESERVE, w.PAGE_READWRITE, - ) orelse return error.OutOfMemory; + ) catch continue; - // If the allocation is sufficiently aligned, use it. - if (@ptrToInt(addr) & (alignment - 1) == 0) { - return @ptrCast([*]u8, addr)[0..n]; - } + return @ptrCast([*]u8, ptr)[0..n]; + }; - // If it wasn't, actually do an explicitely aligned allocation. - if (w.VirtualFree(addr, 0, w.MEM_RELEASE) == 0) unreachable; - const alloc_size = n + alignment; - - const final_addr = while (true) { - // Reserve a range of memory large enough to find a sufficiently - // aligned address. - const reserved_addr = w.VirtualAlloc( - null, - alloc_size, - w.MEM_RESERVE, - w.PAGE_NOACCESS, - ) orelse return error.OutOfMemory; - const aligned_addr = mem.alignForward(@ptrToInt(reserved_addr), alignment); - - // Release the reserved pages (not actually used). - if (w.VirtualFree(reserved_addr, 0, w.MEM_RELEASE) == 0) unreachable; - - // At this point, it is possible that another thread has - // obtained some memory space that will cause the next - // VirtualAlloc call to fail. To handle this, we will retry - // until it succeeds. - if (w.VirtualAlloc( - @intToPtr(*c_void, aligned_addr), - n, - w.MEM_COMMIT | w.MEM_RESERVE, - w.PAGE_READWRITE, - )) |ptr| break ptr; - } else unreachable; // TODO else unreachable should not be necessary - - return @ptrCast([*]u8, final_addr)[0..n]; - }, - else => @compileError("Unsupported OS"), + return @ptrCast([*]u8, final_addr)[0..n]; } + + const alloc_size = if (alignment <= mem.page_size) n else n + alignment; + const slice = os.mmap( + null, + mem.alignForward(alloc_size, mem.page_size), + os.PROT_READ | os.PROT_WRITE, + os.MAP_PRIVATE | os.MAP_ANONYMOUS, + -1, + 0, + ) catch return error.OutOfMemory; + if (alloc_size == n) return slice[0..n]; + + const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment); + + // Unmap the extra bytes that were only requested in order to guarantee + // that the range of memory we were provided had a proper alignment in + // it somewhere. The extra bytes could be at the beginning, or end, or both. + const unused_start_len = aligned_addr - @ptrToInt(slice.ptr); + if (unused_start_len != 0) { + os.munmap(slice[0..unused_start_len]); + } + const aligned_end_addr = mem.alignForward(aligned_addr + n, mem.page_size); + const unused_end_len = @ptrToInt(slice.ptr) + slice.len - aligned_end_addr; + if (unused_end_len != 0) { + os.munmap(@intToPtr([*]align(mem.page_size) u8, aligned_end_addr)[0..unused_end_len]); + } + + return @intToPtr([*]u8, aligned_addr)[0..n]; } - fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { + fn shrink(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { + const old_mem = @alignCast(mem.page_size, old_mem_unaligned); + if (os.windows.is_the_target) { + const w = os.windows; + if (new_size == 0) { + // From the docs: + // "If the dwFreeType parameter is MEM_RELEASE, this parameter + // must be 0 (zero). The function frees the entire region that + // is reserved in the initial allocation call to VirtualAlloc." + // So we can only use MEM_RELEASE when actually releasing the + // whole allocation. + w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE); + } else { const base_addr = @ptrToInt(old_mem.ptr); const old_addr_end = base_addr + old_mem.len; const new_addr_end = base_addr + new_size; - const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size); + const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size); if (old_addr_end > new_addr_end_rounded) { - _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded); - } - return old_mem[0..new_size]; - }, - .windows => { - const w = os.windows; - if (new_size == 0) { - // From the docs: - // "If the dwFreeType parameter is MEM_RELEASE, this parameter - // must be 0 (zero). The function frees the entire region that - // is reserved in the initial allocation call to VirtualAlloc." - // So we can only use MEM_RELEASE when actually releasing the - // whole allocation. - if (w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE) == 0) unreachable; - } else { - const base_addr = @ptrToInt(old_mem.ptr); - const old_addr_end = base_addr + old_mem.len; - const new_addr_end = base_addr + new_size; - const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size); - if (old_addr_end > new_addr_end_rounded) { - // For shrinking that is not releasing, we will only - // decommit the pages not needed anymore. - if (w.VirtualFree( - @intToPtr(*c_void, new_addr_end_rounded), - old_addr_end - new_addr_end_rounded, - w.MEM_DECOMMIT, - ) == 0) unreachable; - } + // For shrinking that is not releasing, we will only + // decommit the pages not needed anymore. + w.VirtualFree( + @intToPtr(*c_void, new_addr_end_rounded), + old_addr_end - new_addr_end_rounded, + w.MEM_DECOMMIT, + ); } - return old_mem[0..new_size]; - }, - else => @compileError("Unsupported OS"), + } + return old_mem[0..new_size]; + } + const base_addr = @ptrToInt(old_mem.ptr); + const old_addr_end = base_addr + old_mem.len; + const new_addr_end = base_addr + new_size; + const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size); + if (old_addr_end > new_addr_end_rounded) { + const ptr = @intToPtr([*]align(mem.page_size) u8, new_addr_end_rounded); + os.munmap(ptr[0 .. old_addr_end - new_addr_end_rounded]); } + return old_mem[0..new_size]; } - fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - if (new_size <= old_mem.len and new_align <= old_align) { - return shrink(allocator, old_mem, old_align, new_size, new_align); - } - const result = try alloc(allocator, new_size, new_align); - if (old_mem.len != 0) { - @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); - _ = os.posix.munmap(@ptrToInt(old_mem.ptr), old_mem.len); - } - return result; - }, - .windows => { - if (old_mem.len == 0) { - return alloc(allocator, new_size, new_align); - } - - if (new_size <= old_mem.len and new_align <= old_align) { - return shrink(allocator, old_mem, old_align, new_size, new_align); - } - - const w = os.windows; - const base_addr = @ptrToInt(old_mem.ptr); + fn realloc(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { + const old_mem = @alignCast(mem.page_size, old_mem_unaligned); + if (os.windows.is_the_target) { + if (old_mem.len == 0) { + return alloc(allocator, new_size, new_align); + } - if (new_align > old_align and base_addr & (new_align - 1) != 0) { - // Current allocation doesn't satisfy the new alignment. - // For now we'll do a new one no matter what, but maybe - // there is something smarter to do instead. - const result = try alloc(allocator, new_size, new_align); - assert(old_mem.len != 0); - @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); - if (w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE) == 0) unreachable; + if (new_size <= old_mem.len and new_align <= old_align) { + return shrink(allocator, old_mem, old_align, new_size, new_align); + } - return result; - } + const w = os.windows; + const base_addr = @ptrToInt(old_mem.ptr); - const old_addr_end = base_addr + old_mem.len; - const old_addr_end_rounded = mem.alignForward(old_addr_end, os.page_size); - const new_addr_end = base_addr + new_size; - const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size); - if (new_addr_end_rounded == old_addr_end_rounded) { - // The reallocation fits in the already allocated pages. - return @ptrCast([*]u8, old_mem.ptr)[0..new_size]; - } - assert(new_addr_end_rounded > old_addr_end_rounded); + if (new_align > old_align and base_addr & (new_align - 1) != 0) { + // Current allocation doesn't satisfy the new alignment. + // For now we'll do a new one no matter what, but maybe + // there is something smarter to do instead. + const result = try alloc(allocator, new_size, new_align); + assert(old_mem.len != 0); + @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); + w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE); - // We need to commit new pages. - const additional_size = new_addr_end - old_addr_end_rounded; - const realloc_addr = w.VirtualAlloc( - @intToPtr(*c_void, old_addr_end_rounded), - additional_size, - w.MEM_COMMIT | w.MEM_RESERVE, - w.PAGE_READWRITE, - ) orelse { - // Committing new pages at the end of the existing allocation - // failed, we need to try a new one. - const new_alloc_mem = try alloc(allocator, new_size, new_align); - @memcpy(new_alloc_mem.ptr, old_mem.ptr, old_mem.len); - if (w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE) == 0) unreachable; - - return new_alloc_mem; - }; + return result; + } - assert(@ptrToInt(realloc_addr) == old_addr_end_rounded); + const old_addr_end = base_addr + old_mem.len; + const old_addr_end_rounded = mem.alignForward(old_addr_end, mem.page_size); + const new_addr_end = base_addr + new_size; + const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size); + if (new_addr_end_rounded == old_addr_end_rounded) { + // The reallocation fits in the already allocated pages. return @ptrCast([*]u8, old_mem.ptr)[0..new_size]; - }, - else => @compileError("Unsupported OS"), + } + assert(new_addr_end_rounded > old_addr_end_rounded); + + // We need to commit new pages. + const additional_size = new_addr_end - old_addr_end_rounded; + const realloc_addr = w.kernel32.VirtualAlloc( + @intToPtr(*c_void, old_addr_end_rounded), + additional_size, + w.MEM_COMMIT | w.MEM_RESERVE, + w.PAGE_READWRITE, + ) orelse { + // Committing new pages at the end of the existing allocation + // failed, we need to try a new one. + const new_alloc_mem = try alloc(allocator, new_size, new_align); + @memcpy(new_alloc_mem.ptr, old_mem.ptr, old_mem.len); + w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE); + + return new_alloc_mem; + }; + + assert(@ptrToInt(realloc_addr) == old_addr_end_rounded); + return @ptrCast([*]u8, old_mem.ptr)[0..new_size]; + } + if (new_size <= old_mem.len and new_align <= old_align) { + return shrink(allocator, old_mem, old_align, new_size, new_align); } + const result = try alloc(allocator, new_size, new_align); + if (old_mem.len != 0) { + @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); + os.munmap(old_mem); + } + return result; } }; @@ -268,7 +261,7 @@ pub const HeapAllocator = switch (builtin.os) { pub fn deinit(self: *HeapAllocator) void { if (self.heap_handle) |heap_handle| { - _ = os.windows.HeapDestroy(heap_handle); + os.windows.HeapDestroy(heap_handle); } } @@ -281,12 +274,12 @@ pub const HeapAllocator = switch (builtin.os) { const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst); const heap_handle = optional_heap_handle orelse blk: { const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0; - const hh = os.windows.HeapCreate(options, amt, 0) orelse return error.OutOfMemory; + const hh = os.windows.kernel32.HeapCreate(options, amt, 0) orelse return error.OutOfMemory; const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh; - _ = os.windows.HeapDestroy(hh); + os.windows.HeapDestroy(hh); break :blk other_hh.?; // can't be null because of the cmpxchg }; - const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; + const ptr = os.windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; const root_addr = @ptrToInt(ptr); const adjusted_addr = mem.alignForward(root_addr, alignment); const record_addr = adjusted_addr + n; @@ -316,12 +309,12 @@ pub const HeapAllocator = switch (builtin.os) { const old_ptr = @intToPtr(*c_void, root_addr); if (new_size == 0) { - if (os.windows.HeapFree(self.heap_handle.?, 0, old_ptr) == 0) unreachable; + os.windows.HeapFree(self.heap_handle.?, 0, old_ptr); return old_mem[0..0]; } const amt = new_size + new_align + @sizeOf(usize); - const new_ptr = os.windows.HeapReAlloc( + const new_ptr = os.windows.kernel32.HeapReAlloc( self.heap_handle.?, 0, old_ptr, @@ -386,7 +379,7 @@ pub const ArenaAllocator = struct { var len = prev_len; while (true) { len += len / 2; - len += os.page_size - @rem(len, os.page_size); + len += mem.page_size - @rem(len, mem.page_size); if (len >= actual_min_size) break; } const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len); @@ -532,11 +525,11 @@ const WasmAllocator = struct { const adjusted_index = self.end_index + (adjusted_addr - addr); const new_end_index = adjusted_index + size; - if (new_end_index > self.num_pages * os.page_size) { - const required_memory = new_end_index - (self.num_pages * os.page_size); + if (new_end_index > self.num_pages * mem.page_size) { + const required_memory = new_end_index - (self.num_pages * mem.page_size); - var num_pages: usize = required_memory / os.page_size; - if (required_memory % os.page_size != 0) { + var num_pages: usize = required_memory / mem.page_size; + if (required_memory % mem.page_size != 0) { num_pages += 1; } @@ -565,14 +558,14 @@ const WasmAllocator = struct { // Initialize start_ptr at the first realloc if (self.num_pages == 0) { - self.start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * os.page_size); + self.start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * mem.page_size); } if (is_last_item(allocator, old_mem, new_align)) { const start_index = self.end_index - old_mem.len; const new_end_index = start_index + new_size; - if (new_end_index > self.num_pages * os.page_size) { + if (new_end_index > self.num_pages * mem.page_size) { _ = try alloc(allocator, new_end_index - self.end_index, new_align); } const result = self.start_ptr[start_index..new_end_index]; @@ -888,10 +881,10 @@ fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !voi fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void { //Maybe a platform's page_size is actually the same as or // very near usize? - if (os.page_size << 2 > maxInt(usize)) return; + if (mem.page_size << 2 > maxInt(usize)) return; const USizeShift = @IntType(false, std.math.log2(usize.bit_count)); - const large_align = u29(os.page_size << 2); + const large_align = u29(mem.page_size << 2); var align_mask: usize = undefined; _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(u29, large_align)), &align_mask); @@ -918,7 +911,7 @@ fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!voi var debug_buffer: [1000]u8 = undefined; const debug_allocator = &FixedBufferAllocator.init(&debug_buffer).allocator; - const alloc_size = os.page_size * 2 + 50; + const alloc_size = mem.page_size * 2 + 50; var slice = try allocator.alignedAlloc(u8, 16, alloc_size); defer allocator.free(slice); @@ -927,7 +920,7 @@ fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!voi // which is 16 pages, hence the 32. This test may require to increase // the size of the allocations feeding the `allocator` parameter if they // fail, because of this high over-alignment we want to have. - while (@ptrToInt(slice.ptr) == mem.alignForward(@ptrToInt(slice.ptr), os.page_size * 32)) { + while (@ptrToInt(slice.ptr) == mem.alignForward(@ptrToInt(slice.ptr), mem.page_size * 32)) { try stuff_to_free.append(slice); slice = try allocator.alignedAlloc(u8, 16, alloc_size); } @@ -938,7 +931,7 @@ fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!voi slice[60] = 0x34; // realloc to a smaller size but with a larger alignment - slice = try allocator.alignedRealloc(slice, os.page_size * 32, alloc_size / 2); + slice = try allocator.alignedRealloc(slice, mem.page_size * 32, alloc_size / 2); testing.expect(slice[0] == 0x12); testing.expect(slice[60] == 0x34); } diff --git a/std/io.zig b/std/io.zig index 5e1aaf5da3..258961fdfc 100644 --- a/std/io.zig +++ b/std/io.zig @@ -1,38 +1,44 @@ const std = @import("std.zig"); const builtin = @import("builtin"); -const Os = builtin.Os; const c = std.c; const math = std.math; const debug = std.debug; const assert = debug.assert; const os = std.os; +const fs = std.fs; const mem = std.mem; const meta = std.meta; const trait = meta.trait; const Buffer = std.Buffer; const fmt = std.fmt; -const File = std.os.File; +const File = std.fs.File; const testing = std.testing; -const is_posix = builtin.os != builtin.Os.windows; -const is_windows = builtin.os == builtin.Os.windows; +pub const GetStdIoError = os.windows.GetStdHandleError; -const GetStdIoErrs = os.WindowsGetStdHandleErrs; - -pub fn getStdErr() GetStdIoErrs!File { - const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_ERROR_HANDLE) else if (is_posix) os.posix.STDERR_FILENO else unreachable; - return File.openHandle(handle); +pub fn getStdOut() GetStdIoError!File { + if (os.windows.is_the_target) { + const handle = try os.windows.GetStdHandle(os.windows.STD_OUTPUT_HANDLE); + return File.openHandle(handle); + } + return File.openHandle(os.STDOUT_FILENO); } -pub fn getStdOut() GetStdIoErrs!File { - const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_OUTPUT_HANDLE) else if (is_posix) os.posix.STDOUT_FILENO else unreachable; - return File.openHandle(handle); +pub fn getStdErr() GetStdIoError!File { + if (os.windows.is_the_target) { + const handle = try os.windows.GetStdHandle(os.windows.STD_ERROR_HANDLE); + return File.openHandle(handle); + } + return File.openHandle(os.STDERR_FILENO); } -pub fn getStdIn() GetStdIoErrs!File { - const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_INPUT_HANDLE) else if (is_posix) os.posix.STDIN_FILENO else unreachable; - return File.openHandle(handle); +pub fn getStdIn() GetStdIoError!File { + if (os.windows.is_the_target) { + const handle = try os.windows.GetStdHandle(os.windows.STD_INPUT_HANDLE); + return File.openHandle(handle); + } + return File.openHandle(os.STDIN_FILENO); } pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; @@ -66,7 +72,7 @@ pub fn InStream(comptime ReadError: type) type { return; } - const new_buf_size = math.min(max_size, actual_buf_len + os.page_size); + const new_buf_size = math.min(max_size, actual_buf_len + mem.page_size); if (new_buf_size == actual_buf_len) return error.StreamTooLong; try buffer.resize(new_buf_size); } @@ -301,7 +307,7 @@ pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptim } pub fn BufferedInStream(comptime Error: type) type { - return BufferedInStreamCustom(os.page_size, Error); + return BufferedInStreamCustom(mem.page_size, Error); } pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type { @@ -774,7 +780,7 @@ test "io.CountingOutStream" { } pub fn BufferedOutStream(comptime Error: type) type { - return BufferedOutStreamCustom(os.page_size, Error); + return BufferedOutStreamCustom(mem.page_size, Error); } pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type { @@ -979,9 +985,9 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { } pub const BufferedAtomicFile = struct { - atomic_file: os.AtomicFile, - file_stream: os.File.OutStream, - buffered_stream: BufferedOutStream(os.File.WriteError), + atomic_file: fs.AtomicFile, + file_stream: File.OutStream, + buffered_stream: BufferedOutStream(File.WriteError), allocator: *mem.Allocator, pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile { @@ -995,11 +1001,11 @@ pub const BufferedAtomicFile = struct { }; errdefer allocator.destroy(self); - self.atomic_file = try os.AtomicFile.init(dest_path, os.File.default_mode); + self.atomic_file = try fs.AtomicFile.init(dest_path, File.default_mode); errdefer self.atomic_file.deinit(); self.file_stream = self.atomic_file.file.outStream(); - self.buffered_stream = BufferedOutStream(os.File.WriteError).init(&self.file_stream.stream); + self.buffered_stream = BufferedOutStream(File.WriteError).init(&self.file_stream.stream); return self; } @@ -1014,7 +1020,7 @@ pub const BufferedAtomicFile = struct { try self.atomic_file.finish(); } - pub fn stream(self: *BufferedAtomicFile) *OutStream(os.File.WriteError) { + pub fn stream(self: *BufferedAtomicFile) *OutStream(File.WriteError) { return &self.buffered_stream.stream; } }; diff --git a/std/io/c_out_stream.zig b/std/io/c_out_stream.zig index c66b342f1e..8b341e6937 100644 --- a/std/io/c_out_stream.zig +++ b/std/io/c_out_stream.zig @@ -1,13 +1,13 @@ const std = @import("../std.zig"); +const os = std.os; const OutStream = std.io.OutStream; const builtin = @import("builtin"); -const posix = std.os.posix; -/// TODO make std.os.FILE use *FILE when linking libc and this just becomes -/// std.io.FileOutStream because std.os.File.write would do this when linking +/// TODO make a proposal to make `std.fs.File` use *FILE when linking libc and this just becomes +/// std.io.FileOutStream because std.fs.File.write would do this when linking /// libc. pub const COutStream = struct { - pub const Error = std.os.File.WriteError; + pub const Error = std.fs.File.WriteError; pub const Stream = OutStream(Error); stream: Stream, @@ -24,25 +24,20 @@ pub const COutStream = struct { const self = @fieldParentPtr(COutStream, "stream", out_stream); const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, self.c_file); if (amt_written == bytes.len) return; - // TODO errno on windows. should we have a posix layer for windows? - if (builtin.os == .windows) { - return error.InputOutput; - } - const errno = std.c._errno().*; - switch (errno) { + switch (std.c._errno().*) { 0 => unreachable, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // this is a blocking API - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return error.DiskQuota, - posix.EFBIG => return error.FileTooBig, - posix.EIO => return error.InputOutput, - posix.ENOSPC => return error.NoSpaceLeft, - posix.EPERM => return error.AccessDenied, - posix.EPIPE => return error.BrokenPipe, - else => return std.os.unexpectedErrorPosix(@intCast(usize, errno)), + os.EINVAL => unreachable, + os.EFAULT => unreachable, + os.EAGAIN => unreachable, // this is a blocking API + os.EBADF => unreachable, // always a race condition + os.EDESTADDRREQ => unreachable, // connect was never called + os.EDQUOT => return error.DiskQuota, + os.EFBIG => return error.FileTooBig, + os.EIO => return error.InputOutput, + os.ENOSPC => return error.NoSpaceLeft, + os.EPERM => return error.AccessDenied, + os.EPIPE => return error.BrokenPipe, + else => |err| return os.unexpectedErrno(@intCast(usize, err)), } } }; diff --git a/std/io/seekable_stream.zig b/std/io/seekable_stream.zig index 392e135302..86f76d8c14 100644 --- a/std/io/seekable_stream.zig +++ b/std/io/seekable_stream.zig @@ -8,7 +8,7 @@ pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType pub const GetSeekPosError = GetSeekPosErrorType; seekToFn: fn (self: *Self, pos: u64) SeekError!void, - seekForwardFn: fn (self: *Self, pos: i64) SeekError!void, + seekByFn: fn (self: *Self, pos: i64) SeekError!void, getPosFn: fn (self: *Self) GetSeekPosError!u64, getEndPosFn: fn (self: *Self) GetSeekPosError!u64, @@ -17,8 +17,8 @@ pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType return self.seekToFn(self, pos); } - pub fn seekForward(self: *Self, amt: i64) SeekError!void { - return self.seekForwardFn(self, amt); + pub fn seekBy(self: *Self, amt: i64) SeekError!void { + return self.seekByFn(self, amt); } pub fn getEndPos(self: *Self) GetSeekPosError!u64 { @@ -52,7 +52,7 @@ pub const SliceSeekableInStream = struct { .stream = Stream{ .readFn = readFn }, .seekable_stream = SeekableInStream{ .seekToFn = seekToFn, - .seekForwardFn = seekForwardFn, + .seekByFn = seekByFn, .getEndPosFn = getEndPosFn, .getPosFn = getPosFn, }, @@ -77,7 +77,7 @@ pub const SliceSeekableInStream = struct { self.pos = usize_pos; } - fn seekForwardFn(in_stream: *SeekableInStream, amt: i64) SeekError!void { + fn seekByFn(in_stream: *SeekableInStream, amt: i64) SeekError!void { const self = @fieldParentPtr(Self, "seekable_stream", in_stream); if (amt < 0) { diff --git a/std/io/test.zig b/std/io/test.zig index 07a3c0e8dd..fc3b0f8902 100644 --- a/std/io/test.zig +++ b/std/io/test.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("../std.zig"); const io = std.io; const meta = std.meta; @@ -6,8 +7,8 @@ const DefaultPrng = std.rand.DefaultPrng; const expect = std.testing.expect; const expectError = std.testing.expectError; const mem = std.mem; -const os = std.os; -const builtin = @import("builtin"); +const fs = std.fs; +const File = std.fs.File; test "write a file, read it, then delete it" { var raw_bytes: [200 * 1024]u8 = undefined; @@ -18,11 +19,11 @@ 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(tmp_file_name); + var file = try File.openWrite(tmp_file_name); defer file.close(); var file_out_stream = file.outStream(); - var buf_stream = io.BufferedOutStream(os.File.WriteError).init(&file_out_stream.stream); + var buf_stream = io.BufferedOutStream(File.WriteError).init(&file_out_stream.stream); const st = &buf_stream.stream; try st.print("begin"); try st.write(data[0..]); @@ -32,15 +33,15 @@ test "write a file, read it, then delete it" { { // make sure openWriteNoClobber doesn't harm the file - if (os.File.openWriteNoClobber(tmp_file_name, os.File.default_mode)) |file| { + if (File.openWriteNoClobber(tmp_file_name, File.default_mode)) |file| { unreachable; } else |err| { - std.debug.assert(err == os.File.OpenError.PathAlreadyExists); + std.debug.assert(err == File.OpenError.PathAlreadyExists); } } { - var file = try os.File.openRead(tmp_file_name); + var file = try File.openRead(tmp_file_name); defer file.close(); const file_size = try file.getEndPos(); @@ -48,7 +49,7 @@ test "write a file, read it, then delete it" { expect(file_size == expected_file_size); var file_in_stream = file.inStream(); - var buf_stream = io.BufferedInStream(os.File.ReadError).init(&file_in_stream.stream); + var buf_stream = io.BufferedInStream(File.ReadError).init(&file_in_stream.stream); const st = &buf_stream.stream; const contents = try st.readAllAlloc(allocator, 2 * 1024); defer allocator.free(contents); @@ -57,7 +58,7 @@ test "write a file, read it, then delete it" { expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); expect(mem.eql(u8, contents[contents.len - "end".len ..], "end")); } - try os.deleteFile(tmp_file_name); + try fs.deleteFile(tmp_file_name); } test "BufferOutStream" { @@ -273,12 +274,12 @@ test "BitOutStream" { test "BitStreams with File Stream" { const tmp_file_name = "temp_test_file.txt"; { - var file = try os.File.openWrite(tmp_file_name); + var file = try File.openWrite(tmp_file_name); defer file.close(); var file_out = file.outStream(); var file_out_stream = &file_out.stream; - const OutError = os.File.WriteError; + const OutError = File.WriteError; var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream); try bit_stream.writeBits(u2(1), 1); @@ -290,12 +291,12 @@ test "BitStreams with File Stream" { try bit_stream.flushBits(); } { - var file = try os.File.openRead(tmp_file_name); + var file = try File.openRead(tmp_file_name); defer file.close(); var file_in = file.inStream(); var file_in_stream = &file_in.stream; - const InError = os.File.ReadError; + const InError = File.ReadError; var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream); var out_bits: usize = undefined; @@ -315,7 +316,7 @@ test "BitStreams with File Stream" { expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); } - try os.deleteFile(tmp_file_name); + try fs.deleteFile(tmp_file_name); } fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { @@ -595,7 +596,7 @@ test "c out stream" { const filename = c"tmp_io_test_file.txt"; const out_file = std.c.fopen(filename, c"w") orelse return error.UnableToOpenTestFile; - defer std.os.deleteFileC(filename) catch {}; + defer fs.deleteFileC(filename) catch {}; const out_stream = &io.COutStream.init(out_file).stream; try out_stream.print("hi: {}\n", i32(123)); diff --git a/std/mem.zig b/std/mem.zig index 64fe270eda..17dec85a5b 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -8,6 +8,11 @@ const meta = std.meta; const trait = meta.trait; const testing = std.testing; +pub const page_size = switch (builtin.arch) { + .wasm32, .wasm64 => 64 * 1024, + else => 4 * 1024, +}; + pub const Allocator = struct { pub const Error = error{OutOfMemory}; @@ -580,14 +585,14 @@ pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) test "comptime read/write int" { comptime { var bytes: [2]u8 = undefined; - std.mem.writeIntLittle(u16, &bytes, 0x1234); - const result = std.mem.readIntBig(u16, &bytes); + writeIntLittle(u16, &bytes, 0x1234); + const result = readIntBig(u16, &bytes); testing.expect(result == 0x3412); } comptime { var bytes: [2]u8 = undefined; - std.mem.writeIntBig(u16, &bytes, 0x1234); - const result = std.mem.readIntLittle(u16, &bytes); + writeIntBig(u16, &bytes, 0x1234); + const result = readIntLittle(u16, &bytes); testing.expect(result == 0x3412); } } @@ -1048,7 +1053,7 @@ fn testReadIntImpl() void { } } -test "std.mem.writeIntSlice" { +test "writeIntSlice" { testWriteIntImpl(); comptime testWriteIntImpl(); } @@ -1179,7 +1184,7 @@ pub fn reverse(comptime T: type, items: []T) void { } } -test "std.mem.reverse" { +test "reverse" { var arr = []i32{ 5, 3, @@ -1206,7 +1211,7 @@ pub fn rotate(comptime T: type, items: []T, amount: usize) void { reverse(T, items); } -test "std.mem.rotate" { +test "rotate" { var arr = []i32{ 5, 3, @@ -1291,14 +1296,14 @@ pub fn asBytes(ptr: var) AsBytesReturnType(@typeOf(ptr)) { return @ptrCast(AsBytesReturnType(P), ptr); } -test "std.mem.asBytes" { +test "asBytes" { const deadbeef = u32(0xDEADBEEF); const deadbeef_bytes = switch (builtin.endian) { builtin.Endian.Big => "\xDE\xAD\xBE\xEF", builtin.Endian.Little => "\xEF\xBE\xAD\xDE", }; - testing.expect(std.mem.eql(u8, asBytes(&deadbeef), deadbeef_bytes)); + testing.expect(eql(u8, asBytes(&deadbeef), deadbeef_bytes)); var codeface = u32(0xC0DEFACE); for (asBytes(&codeface).*) |*b| @@ -1318,7 +1323,7 @@ test "std.mem.asBytes" { .c = 0xDE, .d = 0xA1, }; - testing.expect(std.mem.eql(u8, asBytes(&inst), "\xBE\xEF\xDE\xA1")); + testing.expect(eql(u8, asBytes(&inst), "\xBE\xEF\xDE\xA1")); } ///Given any value, returns a copy of its bytes in an array. @@ -1326,17 +1331,17 @@ pub fn toBytes(value: var) [@sizeOf(@typeOf(value))]u8 { return asBytes(&value).*; } -test "std.mem.toBytes" { +test "toBytes" { var my_bytes = toBytes(u32(0x12345678)); switch (builtin.endian) { - builtin.Endian.Big => testing.expect(std.mem.eql(u8, my_bytes, "\x12\x34\x56\x78")), - builtin.Endian.Little => testing.expect(std.mem.eql(u8, my_bytes, "\x78\x56\x34\x12")), + builtin.Endian.Big => testing.expect(eql(u8, my_bytes, "\x12\x34\x56\x78")), + builtin.Endian.Little => testing.expect(eql(u8, my_bytes, "\x78\x56\x34\x12")), } my_bytes[0] = '\x99'; switch (builtin.endian) { - builtin.Endian.Big => testing.expect(std.mem.eql(u8, my_bytes, "\x99\x34\x56\x78")), - builtin.Endian.Little => testing.expect(std.mem.eql(u8, my_bytes, "\x99\x56\x34\x12")), + builtin.Endian.Big => testing.expect(eql(u8, my_bytes, "\x99\x34\x56\x78")), + builtin.Endian.Little => testing.expect(eql(u8, my_bytes, "\x99\x56\x34\x12")), } } @@ -1358,7 +1363,7 @@ pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @typ return @ptrCast(BytesAsValueReturnType(T, @typeOf(bytes)), bytes); } -test "std.mem.bytesAsValue" { +test "bytesAsValue" { const deadbeef = u32(0xDEADBEEF); const deadbeef_bytes = switch (builtin.endian) { builtin.Endian.Big => "\xDE\xAD\xBE\xEF", @@ -1400,7 +1405,7 @@ test "std.mem.bytesAsValue" { pub fn bytesToValue(comptime T: type, bytes: var) T { return bytesAsValue(T, &bytes).*; } -test "std.mem.bytesToValue" { +test "bytesToValue" { const deadbeef_bytes = switch (builtin.endian) { builtin.Endian.Big => "\xDE\xAD\xBE\xEF", builtin.Endian.Little => "\xEF\xBE\xAD\xDE", @@ -1425,25 +1430,25 @@ pub fn subArrayPtr(ptr: var, comptime start: usize, comptime length: usize) SubA return @ptrCast(ReturnType, &ptr[start]); } -test "std.mem.subArrayPtr" { +test "subArrayPtr" { const a1 = "abcdef"; const sub1 = subArrayPtr(&a1, 2, 3); - testing.expect(std.mem.eql(u8, sub1.*, "cde")); + testing.expect(eql(u8, sub1.*, "cde")); var a2 = "abcdef"; var sub2 = subArrayPtr(&a2, 2, 3); - testing.expect(std.mem.eql(u8, sub2, "cde")); + testing.expect(eql(u8, sub2, "cde")); sub2[1] = 'X'; - testing.expect(std.mem.eql(u8, a2, "abcXef")); + testing.expect(eql(u8, a2, "abcXef")); } /// Round an address up to the nearest aligned address pub fn alignForward(addr: usize, alignment: usize) usize { - return (addr + alignment - 1) & ~(alignment - 1); + return alignBackward(addr + (alignment - 1), alignment); } -test "std.mem.alignForward" { +test "alignForward" { testing.expect(alignForward(1, 1) == 1); testing.expect(alignForward(2, 1) == 2); testing.expect(alignForward(1, 2) == 2); @@ -1457,3 +1462,30 @@ test "std.mem.alignForward" { testing.expect(alignForward(16, 8) == 16); testing.expect(alignForward(17, 8) == 24); } + +pub fn alignBackward(addr: usize, alignment: usize) usize { + // 000010000 // example addr + // 000001111 // subtract 1 + // 111110000 // binary not + return addr & ~(alignment - 1); +} + +pub fn isAligned(addr: usize, alignment: usize) bool { + return alignBackward(addr, alignment) == addr; +} + +test "isAligned" { + testing.expect(isAligned(0, 4)); + testing.expect(isAligned(1, 1)); + testing.expect(isAligned(2, 1)); + testing.expect(isAligned(2, 2)); + testing.expect(!isAligned(2, 4)); + testing.expect(isAligned(3, 1)); + testing.expect(!isAligned(3, 2)); + testing.expect(!isAligned(3, 4)); + testing.expect(isAligned(4, 4)); + testing.expect(isAligned(4, 2)); + testing.expect(isAligned(4, 1)); + testing.expect(!isAligned(4, 8)); + testing.expect(!isAligned(4, 16)); +} diff --git a/std/mutex.zig b/std/mutex.zig index 2b3ac4e366..6b8e586ea8 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -152,9 +152,9 @@ test "std.Mutex" { testing.expect(context.data == TestContext.incr_count); } else { const thread_count = 10; - var threads: [thread_count]*std.os.Thread = undefined; + var threads: [thread_count]*std.Thread = undefined; for (threads) |*t| { - t.* = try std.os.spawnThread(&context, worker); + t.* = try std.Thread.spawn(&context, worker); } for (threads) |t| t.wait(); diff --git a/std/net.zig b/std/net.zig index bb292efd83..b426759701 100644 --- a/std/net.zig +++ b/std/net.zig @@ -2,8 +2,8 @@ const std = @import("std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const net = @This(); -const posix = std.os.posix; const mem = std.mem; +const os = std.os; pub const TmpWinAddr = struct { family: u8, @@ -12,7 +12,7 @@ pub const TmpWinAddr = struct { pub const OsAddress = switch (builtin.os) { builtin.Os.windows => TmpWinAddr, - else => posix.sockaddr, + else => os.sockaddr, }; pub const Address = struct { @@ -20,9 +20,9 @@ pub const Address = struct { pub fn initIp4(ip4: u32, _port: u16) Address { return Address{ - .os_addr = posix.sockaddr{ - .in = posix.sockaddr_in{ - .family = posix.AF_INET, + .os_addr = os.sockaddr{ + .in = os.sockaddr_in{ + .family = os.AF_INET, .port = mem.nativeToBig(u16, _port), .addr = ip4, .zero = []u8{0} ** 8, @@ -33,10 +33,10 @@ pub const Address = struct { pub fn initIp6(ip6: *const Ip6Addr, _port: u16) Address { return Address{ - .family = posix.AF_INET6, - .os_addr = posix.sockaddr{ - .in6 = posix.sockaddr_in6{ - .family = posix.AF_INET6, + .family = os.AF_INET6, + .os_addr = os.sockaddr{ + .in6 = os.sockaddr_in6{ + .family = os.AF_INET6, .port = mem.nativeToBig(u16, _port), .flowinfo = 0, .addr = ip6.addr, @@ -50,18 +50,18 @@ pub const Address = struct { return mem.bigToNative(u16, self.os_addr.in.port); } - pub fn initPosix(addr: posix.sockaddr) Address { + pub fn initPosix(addr: os.sockaddr) Address { return Address{ .os_addr = addr }; } pub fn format(self: *const Address, out_stream: var) !void { switch (self.os_addr.in.family) { - posix.AF_INET => { + os.AF_INET => { const native_endian_port = mem.bigToNative(u16, self.os_addr.in.port); const bytes = ([]const u8)((*self.os_addr.in.addr)[0..1]); try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port); }, - posix.AF_INET6 => { + os.AF_INET6 => { const native_endian_port = mem.bigToNative(u16, self.os_addr.in6.port); try out_stream.print("[TODO render ip6 address]:{}", native_endian_port); }, diff --git a/std/os.zig b/std/os.zig index 9b452e89ae..be2be92f7d 100644 --- a/std/os.zig +++ b/std/os.zig @@ -1,495 +1,484 @@ +// This file contains thin wrappers around OS-specific APIs, with these +// specific goals in mind: +// * Convert "errno"-style error codes into Zig errors. +// * When null-terminated byte buffers are required, provide APIs which accept +// slices as well as APIs which accept null-terminated byte buffers. Same goes +// for UTF-16LE encoding. +// * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide +// cross platform abstracting. +// * When there exists a corresponding libc function and linking libc, the libc +// implementation is used. Exceptions are made for known buggy areas of libc. +// On Linux libc can be side-stepped by using `std.os.linux` directly. +// * For Windows, this file represents the API that libc would provide for +// Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`. +// Note: The Zig standard library does not support POSIX thread cancellation, and +// in general EINTR is handled by trying again. + const std = @import("std.zig"); const builtin = @import("builtin"); -const Os = builtin.Os; -const is_windows = builtin.os == Os.windows; -const is_posix = switch (builtin.os) { - builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => true, - else => false, -}; -const os = @This(); +const assert = std.debug.assert; +const math = std.math; +const mem = std.mem; +const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES; comptime { - assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir -} - -test "std.os" { - _ = @import("os/child_process.zig"); - _ = @import("os/darwin.zig"); - _ = @import("os/darwin/errno.zig"); - _ = @import("os/get_user_id.zig"); - _ = @import("os/linux.zig"); - _ = @import("os/path.zig"); - _ = @import("os/test.zig"); - _ = @import("os/time.zig"); - _ = @import("os/windows.zig"); - _ = @import("os/uefi.zig"); - _ = @import("os/wasi.zig"); - _ = @import("os/get_app_data_dir.zig"); + assert(@import("std") == std); // std lib tests require --override-std-dir } -pub const windows = @import("os/windows.zig"); pub const darwin = @import("os/darwin.zig"); -pub const linux = @import("os/linux.zig"); pub const freebsd = @import("os/freebsd.zig"); +pub const linux = @import("os/linux.zig"); pub const netbsd = @import("os/netbsd.zig"); -pub const zen = @import("os/zen.zig"); pub const uefi = @import("os/uefi.zig"); pub const wasi = @import("os/wasi.zig"); +pub const windows = @import("os/windows.zig"); +pub const zen = @import("os/zen.zig"); -pub const posix = switch (builtin.os) { - Os.linux => linux, - Os.macosx, Os.ios => darwin, - Os.freebsd => freebsd, - Os.netbsd => netbsd, - Os.zen => zen, - Os.wasi => wasi, - else => @compileError("Unsupported OS"), -}; - -pub const net = @import("net.zig"); - -pub const ChildProcess = @import("os/child_process.zig").ChildProcess; -pub const path = @import("os/path.zig"); -pub const File = @import("os/file.zig").File; -pub const time = @import("os/time.zig"); - -pub const page_size = switch (builtin.arch) { - .wasm32, .wasm64 => 64 * 1024, - else => 4 * 1024, -}; - -pub const MAX_PATH_BYTES = switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => 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 => windows_util.PATH_MAX_WIDE * 3 + 1, - else => @compileError("Unsupported OS"), +/// When linking libc, this is the C API. Otherwise, it is the OS-specific system interface. +pub const system = if (builtin.link_libc) std.c else switch (builtin.os) { + .macosx, .ios, .watchos, .tvos => darwin, + .freebsd => freebsd, + .linux => linux, + .netbsd => netbsd, + .wasi => wasi, + .windows => windows, + .zen => zen, + else => struct {}, }; -pub const UserInfo = @import("os/get_user_id.zig").UserInfo; -pub const getUserInfo = @import("os/get_user_id.zig").getUserInfo; - -const windows_util = @import("os/windows/util.zig"); -pub const windowsWaitSingle = windows_util.windowsWaitSingle; -pub const windowsWrite = windows_util.windowsWrite; -pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; -pub const windowsOpen = windows_util.windowsOpen; -pub const windowsOpenW = windows_util.windowsOpenW; -pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; - -pub const WindowsCreateIoCompletionPortError = windows_util.WindowsCreateIoCompletionPortError; -pub const windowsCreateIoCompletionPort = windows_util.windowsCreateIoCompletionPort; - -pub const WindowsPostQueuedCompletionStatusError = windows_util.WindowsPostQueuedCompletionStatusError; -pub const windowsPostQueuedCompletionStatus = windows_util.windowsPostQueuedCompletionStatus; - -pub const WindowsWaitResult = windows_util.WindowsWaitResult; -pub const windowsGetQueuedCompletionStatus = windows_util.windowsGetQueuedCompletionStatus; - -pub const WindowsWaitError = windows_util.WaitError; -pub const WindowsOpenError = windows_util.OpenError; -pub const WindowsWriteError = windows_util.WriteError; -pub const WindowsReadError = windows_util.ReadError; - -pub const FileHandle = if (is_windows) windows.HANDLE else i32; - -pub const getAppDataDir = @import("os/get_app_data_dir.zig").getAppDataDir; -pub const GetAppDataDirError = @import("os/get_app_data_dir.zig").GetAppDataDirError; +pub use @import("os/bits.zig"); -const debug = std.debug; -const assert = debug.assert; -const testing = std.testing; +/// See also `getenv`. Populated by startup code before main(). +pub var environ: [][*]u8 = undefined; -const c = std.c; +/// Populated by startup code before main(). +/// Not available on Windows. See `std.process.args` +/// for obtaining the process arguments. +pub var argv: [][*]u8 = undefined; -const mem = std.mem; -const Allocator = mem.Allocator; +/// To obtain errno, call this function with the return value of the +/// system function call. For some systems this will obtain the value directly +/// from the return code; for others it will use a thread-local errno variable. +/// Therefore, this function only returns a well-defined value when it is called +/// directly after the system function call which one wants to learn the errno +/// value of. +pub const errno = system.getErrno; -const BufMap = std.BufMap; -const cstr = std.cstr; +/// Closes the file descriptor. +/// This function is not capable of returning any indication of failure. An +/// application which wants to ensure writes have succeeded before closing +/// must call `fsync` before `close`. +/// Note: The Zig standard library does not support POSIX thread cancellation. +pub fn close(fd: fd_t) void { + if (windows.is_the_target) { + return windows.CloseHandle(fd); + } + if (wasi.is_the_target) { + _ = wasi.fd_close(fd); + } + if (darwin.is_the_target) { + // This avoids the EINTR problem. + switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) { + EBADF => unreachable, // Always a race condition. + else => return, + } + } + switch (errno(system.close(fd))) { + EBADF => unreachable, // Always a race condition. + EINTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425 + else => return, + } +} -const io = std.io; -const base64 = std.base64; -const ArrayList = std.ArrayList; -const Buffer = std.Buffer; -const math = std.math; +pub const GetRandomError = OpenError; -/// Fills `buf` with random bytes. If linking against libc, this calls the +/// Obtain a series of random bytes. These bytes can be used to seed user-space +/// random number generators or for cryptographic purposes. +/// When linking against libc, this calls the /// appropriate OS-specific library call. Otherwise it uses the zig standard /// library implementation. -pub fn getRandomBytes(buf: []u8) !void { - switch (builtin.os) { - Os.linux => while (true) { - // TODO check libc version and potentially call c.getrandom. - // See #397 - const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); - switch (errno) { +pub fn getrandom(buf: []u8) GetRandomError!void { + if (windows.is_the_target) { + return windows.RtlGenRandom(buf); + } + if (linux.is_the_target) { + while (true) { + // Bypass libc because it's missing on even relatively new versions. + switch (linux.getErrno(linux.getrandom(buf.ptr, buf.len, 0))) { 0 => return, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EINTR => continue, - posix.ENOSYS => return getRandomBytesDevURandom(buf), - else => return unexpectedErrorPosix(errno), - } - }, - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => return getRandomBytesDevURandom(buf), - Os.windows => { - // Call RtlGenRandom() instead of CryptGetRandom() on Windows - // https://github.com/rust-lang-nursery/rand/issues/111 - // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 - if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => unexpectedErrorWindows(err), - }; - } - }, - Os.wasi => { - const random_get_result = os.wasi.random_get(buf.ptr, buf.len); - if (random_get_result != os.wasi.ESUCCESS) { - return error.Unknown; - } - }, - Os.zen => { - const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 }; - var i: usize = 0; - while (i < buf.len) : (i += 1) { - if (i > randomness.len) return error.Unknown; - buf[i] = randomness[i]; + EINVAL => unreachable, + EFAULT => unreachable, + EINTR => continue, + ENOSYS => return getRandomBytesDevURandom(buf), + else => |err| return unexpectedErrno(err), } - }, - else => @compileError("Unsupported OS"), + } + } + if (wasi.is_the_target) { + switch (wasi.random_get(buf.ptr, buf.len)) { + 0 => return, + else => |err| return unexpectedErrno(err), + } } + return getRandomBytesDevURandom(buf); } fn getRandomBytesDevURandom(buf: []u8) !void { - const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); + const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0); defer close(fd); - const stream = &File.openHandle(fd).inStream().stream; - stream.readNoEof(buf) catch |err| switch (err) { - error.EndOfStream => unreachable, - error.OperationAborted => unreachable, - error.BrokenPipe => unreachable, - error.Unexpected => return error.Unexpected, - error.InputOutput => return error.Unexpected, - error.SystemResources => return error.Unexpected, - error.IsDir => unreachable, - }; + const stream = &std.fs.File.openHandle(fd).inStream().stream; + stream.readNoEof(buf) catch return error.Unexpected; } -test "os.getRandomBytes" { - var buf_a: [50]u8 = undefined; - var buf_b: [50]u8 = undefined; - // Call Twice - try getRandomBytes(buf_a[0..]); - try getRandomBytes(buf_b[0..]); - - // Check if random (not 100% conclusive) - testing.expect(!mem.eql(u8, buf_a, buf_b)); -} - -/// Raises a signal in the current kernel thread, ending its execution. +/// Causes abnormal process termination. /// If linking against libc, this calls the abort() libc function. Otherwise -/// it uses the zig standard library implementation. +/// it raises SIGABRT followed by SIGKILL and finally lo pub fn abort() noreturn { @setCold(true); if (builtin.link_libc) { - c.abort(); + system.abort(); } - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - _ = posix.raise(posix.SIGABRT); - _ = posix.raise(posix.SIGKILL); - while (true) {} - }, - Os.windows => { - if (builtin.mode == builtin.Mode.Debug) { - @breakpoint(); - } - windows.ExitProcess(3); - }, - Os.wasi => { - _ = wasi.proc_raise(wasi.SIGABRT); - // TODO: Is SIGKILL even necessary? - _ = wasi.proc_raise(wasi.SIGKILL); - while (true) {} - }, - Os.uefi => { - // TODO there's gotta be a better thing to do here than loop forever - while (true) {} - }, - else => @compileError("Unsupported OS"), + if (windows.is_the_target) { + if (builtin.mode == .Debug) { + @breakpoint(); + } + windows.kernel32.ExitProcess(3); } + if (builtin.os == .uefi) { + // TODO there must be a better thing to do here than loop forever + while (true) {} + } + + raise(SIGABRT) catch {}; + + // TODO the rest of the implementation of abort() from musl libc here + + raise(SIGKILL) catch {}; + exit(127); } -/// Exits the program cleanly with the specified status code. -pub fn exit(status: u8) noreturn { - @setCold(true); +pub const RaiseError = error{Unexpected}; + +pub fn raise(sig: u8) RaiseError!void { if (builtin.link_libc) { - c.exit(status); - } - switch (builtin.os) { - Os.linux => { - if (builtin.single_threaded) { - linux.exit(status); - } else { - linux.exit_group(status); - } - }, - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - posix.exit(status); - }, - Os.windows => { - windows.ExitProcess(status); - }, - Os.wasi => { - wasi.proc_exit(status); - }, - else => @compileError("Unsupported OS"), + switch (errno(system.raise(sig))) { + 0 => return, + else => |err| return unexpectedErrno(err), + } } -} -/// When a file descriptor is closed on linux, it pops the first -/// node from this queue and resumes it. -/// Async functions which get the EMFILE error code can suspend, -/// putting their coroutine handle into this list. -/// TODO make this an atomic linked list -pub var emfile_promise_queue = std.LinkedList(promise).init(); + if (wasi.is_the_target) { + switch (wasi.proc_raise(SIGABRT)) { + 0 => return, + else => |err| return unexpectedErrno(err), + } + } -/// Closes the file handle. Keeps trying if it gets interrupted by a signal. -pub fn close(handle: FileHandle) void { - if (is_windows) { - windows_util.windowsClose(handle); - } else { - while (true) { - const err = posix.getErrno(posix.close(handle)); - switch (err) { - posix.EINTR => continue, - else => { - if (emfile_promise_queue.popFirst()) |p| resume p.data; - return; - }, - } + if (linux.is_the_target) { + var set: linux.sigset_t = undefined; + linux.blockAppSignals(&set); + const tid = linux.syscall0(linux.SYS_gettid); + const rc = linux.syscall2(linux.SYS_tkill, tid, sig); + linux.restoreSignals(&set); + switch (errno(rc)) { + 0 => return, + else => |err| return unexpectedErrno(err), } } + + @compileError("std.os.raise unimplemented for this target"); } -pub const PosixReadError = error{ +pub const KillError = error{ + PermissionDenied, + Unexpected, +}; + +pub fn kill(pid: pid_t, sig: u8) KillError!void { + switch (errno(system.kill(pid, sig))) { + 0 => return, + EINVAL => unreachable, // invalid signal + EPERM => return error.PermissionDenied, + ESRCH => unreachable, // always a race condition + else => |err| return unexpectedErrno(err), + } +} + +/// Exits the program cleanly with the specified status code. +pub fn exit(status: u8) noreturn { + if (builtin.link_libc) { + system.exit(status); + } + if (windows.is_the_target) { + windows.kernel32.ExitProcess(status); + } + if (wasi.is_the_target) { + wasi.proc_exit(status); + } + if (linux.is_the_target and !builtin.single_threaded) { + linux.exit_group(status); + } + system.exit(status); +} + +pub const ReadError = error{ InputOutput, SystemResources, IsDir, + OperationAborted, + BrokenPipe, Unexpected, }; /// Returns the number of bytes that were read, which can be less than /// buf.len. If 0 bytes were read, that means EOF. -pub fn posixRead(fd: i32, buf: []u8) PosixReadError!usize { +/// This function is for blocking file descriptors only. For non-blocking, see +/// `readAsync`. +pub fn read(fd: fd_t, buf: []u8) ReadError!usize { + if (windows.is_the_target) { + return windows.ReadFile(fd, buf); + } + + if (wasi.is_the_target and !builtin.link_libc) { + const iovs = [1]iovec{iovec{ + .iov_base = buf.ptr, + .iov_len = buf.len, + }}; + + var nread: usize = undefined; + switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) { + 0 => return nread, + else => |err| return unexpectedErrno(err), + } + } + // Linux can return EINVAL when read amount is > 0x7ffff000 // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274 + // TODO audit this. Shawn Landden says that this is not actually true. + // if this logic should stay, move it to std.os.linux const max_buf_len = 0x7ffff000; var index: usize = 0; while (index < buf.len) { const want_to_read = math.min(buf.len - index, usize(max_buf_len)); - const rc = posix.read(fd, buf.ptr + index, want_to_read); - const err = posix.getErrno(rc); - switch (err) { + const rc = system.read(fd, buf.ptr + index, want_to_read); + switch (errno(rc)) { 0 => { - index += rc; - if (rc == want_to_read) continue; + const amt_read = @intCast(usize, rc); + index += amt_read; + if (amt_read == want_to_read) continue; // Read returned less than buf.len. return index; }, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => return error.IsDir, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), + EINTR => continue, + EINVAL => unreachable, + EFAULT => unreachable, + EAGAIN => unreachable, // This function is for blocking reads. + EBADF => unreachable, // Always a race condition. + EIO => return error.InputOutput, + EISDIR => return error.IsDir, + ENOBUFS => return error.SystemResources, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), } } return index; } /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. -pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) PosixReadError!usize { - switch (builtin.os) { - builtin.Os.macosx => { - // Darwin does not have preadv but it does have pread. - var off: usize = 0; - var iov_i: usize = 0; - var inner_off: usize = 0; - while (true) { - const v = iov[iov_i]; - const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); - const err = darwin.getErrno(rc); - switch (err) { - 0 => { - off += rc; - inner_off += rc; - if (inner_off == v.iov_len) { - iov_i += 1; - inner_off = 0; - if (iov_i == count) { - return off; - } - } - if (rc == 0) return off; // EOF - continue; - }, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.ESPIPE => unreachable, // fd is not seekable - posix.EAGAIN => unreachable, // this function is not for non blocking - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => return error.IsDir, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), - } - } - }, - builtin.Os.linux, builtin.Os.freebsd, Os.netbsd => while (true) { - const rc = posix.preadv(fd, iov, count, offset); - const err = posix.getErrno(rc); +/// This function is for blocking file descriptors only. For non-blocking, see +/// `preadvAsync`. +pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { + if (darwin.is_the_target) { + // Darwin does not have preadv but it does have pread. + var off: usize = 0; + var iov_i: usize = 0; + var inner_off: usize = 0; + while (true) { + const v = iov[iov_i]; + const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); + const err = darwin.getErrno(rc); switch (err) { - 0 => return rc, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // don't call this function for non blocking - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => return error.IsDir, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), + 0 => { + const amt_read = @bitCast(usize, rc); + off += amt_read; + inner_off += amt_read; + if (inner_off == v.iov_len) { + iov_i += 1; + inner_off = 0; + if (iov_i == iov.len) { + return off; + } + } + if (rc == 0) return off; // EOF + continue; + }, + EINTR => continue, + EINVAL => unreachable, + EFAULT => unreachable, + ESPIPE => unreachable, // fd is not seekable + EAGAIN => unreachable, // This function is for blocking reads. + EBADF => unreachable, // always a race condition + EIO => return error.InputOutput, + EISDIR => return error.IsDir, + ENOBUFS => return error.SystemResources, + ENOMEM => return error.SystemResources, + else => return unexpectedErrno(err), } - }, - else => @compileError("Unsupported OS"), + } + } + while (true) { + // TODO handle the case when iov_len is too large and get rid of this @intCast + const rc = system.preadv(fd, iov.ptr, @intCast(u32, iov.len), offset); + switch (errno(rc)) { + 0 => return @bitCast(usize, rc), + EINTR => continue, + EINVAL => unreachable, + EFAULT => unreachable, + EAGAIN => unreachable, // This function is for blocking reads. + EBADF => unreachable, // always a race condition + EIO => return error.InputOutput, + EISDIR => return error.IsDir, + ENOBUFS => return error.SystemResources, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } } } -pub const PosixWriteError = error{ +pub const WriteError = error{ DiskQuota, FileTooBig, InputOutput, NoSpaceLeft, AccessDenied, BrokenPipe, - - /// See https://github.com/ziglang/zig/issues/1396 + SystemResources, + OperationAborted, Unexpected, }; -/// Calls POSIX write, and keeps trying if it gets interrupted. -pub fn posixWrite(fd: i32, bytes: []const u8) PosixWriteError!void { +/// Write to a file descriptor. Keeps trying if it gets interrupted. +/// This function is for blocking file descriptors only. For non-blocking, see +/// `writeAsync`. +pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { + if (windows.is_the_target) { + return windows.WriteFile(fd, bytes); + } + + if (wasi.is_the_target and !builtin.link_libc) { + const ciovs = [1]iovec_const{iovec_const{ + .iov_base = bytes.ptr, + .iov_len = bytes.len, + }}; + var nwritten: usize = undefined; + switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) { + 0 => return, + else => |err| return unexpectedErrno(err), + } + } + // Linux can return EINVAL when write amount is > 0x7ffff000 // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856 + // TODO audit this. Shawn Landden says that this is not actually true. + // if this logic should stay, move it to std.os.linux const max_bytes_len = 0x7ffff000; var index: usize = 0; while (index < bytes.len) { const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); - const rc = posix.write(fd, bytes.ptr + index, amt_to_write); - const write_err = posix.getErrno(rc); - switch (write_err) { + const rc = system.write(fd, bytes.ptr + index, amt_to_write); + switch (errno(rc)) { 0 => { - index += rc; + index += @intCast(usize, rc); continue; }, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // use posixAsyncWrite for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return PosixWriteError.DiskQuota, - posix.EFBIG => return PosixWriteError.FileTooBig, - posix.EIO => return PosixWriteError.InputOutput, - posix.ENOSPC => return PosixWriteError.NoSpaceLeft, - posix.EPERM => return PosixWriteError.AccessDenied, - posix.EPIPE => return PosixWriteError.BrokenPipe, - else => return unexpectedErrorPosix(write_err), + EINTR => continue, + EINVAL => unreachable, + EFAULT => unreachable, + EAGAIN => unreachable, // This function is for blocking writes. + EBADF => unreachable, // Always a race condition. + EDESTADDRREQ => unreachable, // `connect` was never called. + EDQUOT => return error.DiskQuota, + EFBIG => return error.FileTooBig, + EIO => return error.InputOutput, + ENOSPC => return error.NoSpaceLeft, + EPERM => return error.AccessDenied, + EPIPE => return error.BrokenPipe, + else => |err| return unexpectedErrno(err), + } + } +} + +/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted. +/// This function is for blocking file descriptors only. For non-blocking, see +/// `pwritevAsync`. +pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void { + if (darwin.is_the_target) { + // Darwin does not have pwritev but it does have pwrite. + var off: usize = 0; + var iov_i: usize = 0; + var inner_off: usize = 0; + while (true) { + const v = iov[iov_i]; + const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); + const err = darwin.getErrno(rc); + switch (err) { + 0 => { + const amt_written = @bitCast(usize, rc); + off += amt_written; + inner_off += amt_written; + if (inner_off == v.iov_len) { + iov_i += 1; + inner_off = 0; + if (iov_i == iov.len) { + return; + } + } + continue; + }, + EINTR => continue, + ESPIPE => unreachable, // `fd` is not seekable. + EINVAL => unreachable, + EFAULT => unreachable, + EAGAIN => unreachable, // This function is for blocking writes. + EBADF => unreachable, // Always a race condition. + EDESTADDRREQ => unreachable, // `connect` was never called. + EDQUOT => return error.DiskQuota, + EFBIG => return error.FileTooBig, + EIO => return error.InputOutput, + ENOSPC => return error.NoSpaceLeft, + EPERM => return error.AccessDenied, + EPIPE => return error.BrokenPipe, + else => return unexpectedErrno(err), + } } } -} -pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void { - switch (builtin.os) { - builtin.Os.macosx => { - // Darwin does not have pwritev but it does have pwrite. - var off: usize = 0; - var iov_i: usize = 0; - var inner_off: usize = 0; - while (true) { - const v = iov[iov_i]; - const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); - const err = darwin.getErrno(rc); - switch (err) { - 0 => { - off += rc; - inner_off += rc; - if (inner_off == v.iov_len) { - iov_i += 1; - inner_off = 0; - if (iov_i == count) { - return; - } - } - continue; - }, - posix.EINTR => continue, - posix.ESPIPE => unreachable, // fd is not seekable - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return PosixWriteError.DiskQuota, - posix.EFBIG => return PosixWriteError.FileTooBig, - posix.EIO => return PosixWriteError.InputOutput, - posix.ENOSPC => return PosixWriteError.NoSpaceLeft, - posix.EPERM => return PosixWriteError.AccessDenied, - posix.EPIPE => return PosixWriteError.BrokenPipe, - else => return unexpectedErrorPosix(err), - } - } - }, - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => while (true) { - const rc = posix.pwritev(fd, iov, count, offset); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return PosixWriteError.DiskQuota, - posix.EFBIG => return PosixWriteError.FileTooBig, - posix.EIO => return PosixWriteError.InputOutput, - posix.ENOSPC => return PosixWriteError.NoSpaceLeft, - posix.EPERM => return PosixWriteError.AccessDenied, - posix.EPIPE => return PosixWriteError.BrokenPipe, - else => return unexpectedErrorPosix(err), - } - }, - else => @compileError("Unsupported OS"), + while (true) { + // TODO handle the case when iov_len is too large and get rid of this @intCast + const rc = system.pwritev(fd, iov.ptr, @intCast(u32, iov.len), offset); + switch (errno(rc)) { + 0 => return, + EINTR => continue, + EINVAL => unreachable, + EFAULT => unreachable, + EAGAIN => unreachable, // This function is for blocking writes. + EBADF => unreachable, // Always a race condition. + EDESTADDRREQ => unreachable, // `connect` was never called. + EDQUOT => return error.DiskQuota, + EFBIG => return error.FileTooBig, + EIO => return error.InputOutput, + ENOSPC => return error.NoSpaceLeft, + EPERM => return error.AccessDenied, + EPIPE => return error.BrokenPipe, + else => |err| return unexpectedErrno(err), + } } } -pub const PosixOpenError = error{ +pub const OpenError = error{ AccessDenied, FileTooBig, IsDir, @@ -504,106 +493,60 @@ pub const PosixOpenError = error{ NotDir, PathAlreadyExists, DeviceBusy, - - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -/// ::file_path needs to be copied in memory to add a null terminating byte. -/// 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 { +/// Open and possibly create a file. Keeps trying if it gets interrupted. +/// `file_path` needs to be copied in memory to add a null terminating byte. +/// See also `openC`. +pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t { const file_path_c = try toPosixPath(file_path); - return posixOpenC(&file_path_c, flags, perm); + return openC(&file_path_c, flags, perm); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { +/// Open and possibly create a file. Keeps trying if it gets interrupted. +/// See also `open`. +/// TODO https://github.com/ziglang/zig/issues/265 +pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t { while (true) { - const result = posix.open(file_path, flags, perm); - const err = posix.getErrno(result); - if (err > 0) { - switch (err) { - posix.EINTR => continue, - - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EACCES => return PosixOpenError.AccessDenied, - posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, - posix.EISDIR => return PosixOpenError.IsDir, - posix.ELOOP => return PosixOpenError.SymLinkLoop, - posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, - posix.ENAMETOOLONG => return PosixOpenError.NameTooLong, - posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded, - posix.ENODEV => return PosixOpenError.NoDevice, - posix.ENOENT => return PosixOpenError.FileNotFound, - posix.ENOMEM => return PosixOpenError.SystemResources, - posix.ENOSPC => return PosixOpenError.NoSpaceLeft, - posix.ENOTDIR => return PosixOpenError.NotDir, - posix.EPERM => return PosixOpenError.AccessDenied, - posix.EEXIST => return PosixOpenError.PathAlreadyExists, - posix.EBUSY => return PosixOpenError.DeviceBusy, - else => return unexpectedErrorPosix(err), - } - } - return @intCast(i32, result); - } -} - -/// Used to convert a slice to a null terminated slice on the stack. -/// TODO well defined copy elision -pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 { - var path_with_null: [posix.PATH_MAX]u8 = undefined; - if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; - mem.copy(u8, path_with_null[0..], file_path); - path_with_null[file_path.len] = 0; - return path_with_null; -} - -pub fn posixDup2(old_fd: i32, new_fd: i32) !void { + const rc = system.open(file_path, flags, perm); + switch (errno(rc)) { + 0 => return @intCast(fd_t, rc), + EINTR => continue, + + EFAULT => unreachable, + EINVAL => unreachable, + EACCES => return error.AccessDenied, + EFBIG => return error.FileTooBig, + EOVERFLOW => return error.FileTooBig, + EISDIR => return error.IsDir, + ELOOP => return error.SymLinkLoop, + EMFILE => return error.ProcessFdQuotaExceeded, + ENAMETOOLONG => return error.NameTooLong, + ENFILE => return error.SystemFdQuotaExceeded, + ENODEV => return error.NoDevice, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOSPC => return error.NoSpaceLeft, + ENOTDIR => return error.NotDir, + EPERM => return error.AccessDenied, + EEXIST => return error.PathAlreadyExists, + EBUSY => return error.DeviceBusy, + else => |err| return unexpectedErrno(err), + } + } +} + +pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void { while (true) { - const err = posix.getErrno(posix.dup2(old_fd, new_fd)); - if (err > 0) { - return switch (err) { - posix.EBUSY, posix.EINTR => continue, - posix.EMFILE => error.ProcessFdQuotaExceeded, - posix.EINVAL => unreachable, - else => unexpectedErrorPosix(err), - }; - } - return; - } -} - -pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 { - const envp_count = env_map.count(); - const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); - mem.set(?[*]u8, envp_buf, null); - errdefer freeNullDelimitedEnvMap(allocator, envp_buf); - { - var it = env_map.iterator(); - var i: usize = 0; - while (it.next()) |pair| : (i += 1) { - const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); - @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); - env_buf[pair.key.len] = '='; - @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); - env_buf[env_buf.len - 1] = 0; - - envp_buf[i] = env_buf.ptr; + switch (errno(system.dup2(old_fd, new_fd))) { + 0 => return, + EBUSY, EINTR => continue, + EMFILE => return error.ProcessFdQuotaExceeded, + EINVAL => unreachable, + else => |err| return unexpectedErrno(err), } - assert(i == envp_count); - } - assert(envp_buf[envp_count] == null); - return envp_buf; -} - -pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { - for (envp_buf) |env| { - const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; - allocator.free(env_buf); } - allocator.free(envp_buf); } /// This function must allocate memory to add a null terminating bytes on path and each arg. @@ -611,34 +554,35 @@ pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { /// pointers after the args and after the environment variables. /// `argv[0]` is the executable path. /// This function also uses the PATH environment variable to get the full path to the executable. -pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { - const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1); +/// TODO provide execveC which does not take an allocator +pub fn execve(allocator: *mem.Allocator, argv_slice: []const []const u8, env_map: *const std.BufMap) !void { + const argv_buf = try allocator.alloc(?[*]u8, argv_slice.len + 1); mem.set(?[*]u8, argv_buf, null); defer { for (argv_buf) |arg| { - const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; + const arg_buf = if (arg) |ptr| mem.toSlice(u8, ptr) else break; allocator.free(arg_buf); } allocator.free(argv_buf); } - for (argv) |arg, i| { + for (argv_slice) |arg, i| { const arg_buf = try allocator.alloc(u8, arg.len + 1); @memcpy(arg_buf.ptr, arg.ptr, arg.len); arg_buf[arg.len] = 0; argv_buf[i] = arg_buf.ptr; } - argv_buf[argv.len] = null; + argv_buf[argv_slice.len] = null; const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); defer freeNullDelimitedEnvMap(allocator, envp_buf); - const exe_path = argv[0]; + const exe_path = argv_slice[0]; if (mem.indexOfScalar(u8, exe_path, '/') != null) { - return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); + return execveErrnoToErr(errno(system.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); } - const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; + const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; // PATH.len because it is >= the largest search_path // +1 for the / to join the search path and exe_path // +1 for the null terminating byte @@ -652,21 +596,52 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: path_buf[search_path.len] = '/'; mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); path_buf[search_path.len + exe_path.len + 1] = 0; - err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); + err = errno(system.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); assert(err > 0); - if (err == posix.EACCES) { + if (err == EACCES) { seen_eacces = true; - } else if (err != posix.ENOENT) { - return posixExecveErrnoToErr(err); + } else if (err != ENOENT) { + return execveErrnoToErr(err); } } if (seen_eacces) { - err = posix.EACCES; + err = EACCES; } - return posixExecveErrnoToErr(err); + return execveErrnoToErr(err); } -pub const PosixExecveError = error{ +pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![]?[*]u8 { + const envp_count = env_map.count(); + const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); + mem.set(?[*]u8, envp_buf, null); + errdefer freeNullDelimitedEnvMap(allocator, envp_buf); + { + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| : (i += 1) { + const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); + @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); + env_buf[pair.key.len] = '='; + @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); + env_buf[env_buf.len - 1] = 0; + + envp_buf[i] = env_buf.ptr; + } + assert(i == envp_count); + } + assert(envp_buf[envp_count] == null); + return envp_buf; +} + +pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*]u8) void { + for (envp_buf) |env| { + const env_buf = if (env) |ptr| ptr[0 .. mem.len(u8, ptr) + 1] else break; + allocator.free(env_buf); + } + allocator.free(envp_buf); +} + +pub const ExecveError = error{ SystemResources, AccessDenied, InvalidExe, @@ -675,157 +650,41 @@ pub const PosixExecveError = error{ FileNotFound, NotDir, FileBusy, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NameTooLong, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -fn posixExecveErrnoToErr(err: usize) PosixExecveError { +fn execveErrnoToErr(err: usize) ExecveError { assert(err > 0); switch (err) { - posix.EFAULT => unreachable, - posix.E2BIG => return error.SystemResources, - posix.EMFILE => return error.SystemResources, - posix.ENAMETOOLONG => return error.SystemResources, - posix.ENFILE => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EINVAL => return error.InvalidExe, - posix.ENOEXEC => return error.InvalidExe, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.FileSystem, - posix.EISDIR => return error.IsDir, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ETXTBSY => return error.FileBusy, - else => return unexpectedErrorPosix(err), - } -} - -pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; -pub var posix_environ_raw: [][*]u8 = undefined; - -/// See std.elf for the constants. -pub fn linuxGetAuxVal(index: usize) usize { - if (builtin.link_libc) { - return usize(std.c.getauxval(index)); - } else if (linux_elf_aux_maybe) |auxv| { - var i: usize = 0; - while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { - if (auxv[i].a_type == index) - return auxv[i].a_un.a_val; - } - } - return 0; -} - -pub fn getBaseAddress() usize { - switch (builtin.os) { - builtin.Os.linux => { - const base = linuxGetAuxVal(std.elf.AT_BASE); - if (base != 0) { - return base; - } - const phdr = linuxGetAuxVal(std.elf.AT_PHDR); - return phdr - @sizeOf(std.elf.Ehdr); - }, - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { - return @ptrToInt(&std.c._mh_execute_header); - }, - builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), - else => @compileError("Unsupported OS"), - } -} - -/// Caller must free result when done. -/// TODO make this go through libc when we have it -pub fn getEnvMap(allocator: *Allocator) !BufMap { - var result = BufMap.init(allocator); - errdefer result.deinit(); - - if (is_windows) { - const ptr = windows.GetEnvironmentStringsW() orelse return error.OutOfMemory; - defer assert(windows.FreeEnvironmentStringsW(ptr) != 0); - - var i: usize = 0; - while (true) { - if (ptr[i] == 0) return result; - - const key_start = i; - - while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} - const key_w = ptr[key_start..i]; - const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); - errdefer allocator.free(key); - - if (ptr[i] == '=') i += 1; - - const value_start = i; - while (ptr[i] != 0) : (i += 1) {} - const value_w = ptr[value_start..i]; - const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); - errdefer allocator.free(value); - - i += 1; // skip over null byte - - try result.setMove(key, value); - } - } else if (builtin.os == Os.wasi) { - var environ_count: usize = undefined; - var environ_buf_size: usize = undefined; - - const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size); - if (environ_sizes_get_ret != os.wasi.ESUCCESS) { - return unexpectedErrorPosix(environ_sizes_get_ret); - } - - // TODO: Verify that the documentation is incorrect - // https://github.com/WebAssembly/WASI/issues/27 - var environ = try allocator.alloc(?[*]u8, environ_count + 1); - defer allocator.free(environ); - var environ_buf = try std.heap.wasm_allocator.alloc(u8, environ_buf_size); - defer allocator.free(environ_buf); - - const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr); - if (environ_get_ret != os.wasi.ESUCCESS) { - return unexpectedErrorPosix(environ_get_ret); - } - - for (environ) |env| { - if (env) |ptr| { - const pair = mem.toSlice(u8, ptr); - var parts = mem.separate(pair, "="); - const key = parts.next().?; - const value = parts.next().?; - try result.set(key, value); - } - } - return result; - } else { - for (posix_environ_raw) |ptr| { - var line_i: usize = 0; - while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} - const key = ptr[0..line_i]; - - var end_i: usize = line_i; - while (ptr[end_i] != 0) : (end_i += 1) {} - const value = ptr[line_i + 1 .. end_i]; - - try result.set(key, value); - } - return result; - } -} - -test "os.getEnvMap" { - var env = try getEnvMap(std.debug.global_allocator); - defer env.deinit(); -} - + EFAULT => unreachable, + E2BIG => return error.SystemResources, + EMFILE => return error.ProcessFdQuotaExceeded, + ENAMETOOLONG => return error.NameTooLong, + ENFILE => return error.SystemFdQuotaExceeded, + ENOMEM => return error.SystemResources, + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EINVAL => return error.InvalidExe, + ENOEXEC => return error.InvalidExe, + EIO => return error.FileSystem, + ELOOP => return error.FileSystem, + EISDIR => return error.IsDir, + ENOENT => return error.FileNotFound, + ENOTDIR => return error.NotDir, + ETXTBSY => return error.FileBusy, + else => return unexpectedErrno(err), + } +} + +/// Get an environment variable. +/// See also `getenvC`. /// TODO make this go through libc when we have it -pub fn getEnvPosix(key: []const u8) ?[]const u8 { - for (posix_environ_raw) |ptr| { +pub fn getenv(key: []const u8) ?[]const u8 { + for (environ) |ptr| { var line_i: usize = 0; while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} const this_key = ptr[0..line_i]; @@ -840,221 +699,134 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { return null; } -pub const GetEnvVarOwnedError = error{ - OutOfMemory, - EnvironmentVariableNotFound, - - /// See https://github.com/ziglang/zig/issues/1774 - InvalidUtf8, -}; - -/// Caller must free returned memory. -/// TODO make this go through libc when we have it -pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { - if (is_windows) { - const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); - defer allocator.free(key_with_null); - - var buf = try allocator.alloc(u16, 256); - defer allocator.free(buf); - - while (true) { - const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; - const result = windows.GetEnvironmentVariableW(key_with_null.ptr, buf.ptr, windows_buf_len); - - if (result == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound, - else => { - unexpectedErrorWindows(err) catch {}; - return error.EnvironmentVariableNotFound; - }, - }; - } - - if (result > buf.len) { - buf = try allocator.realloc(buf, result); - continue; - } - - return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) { - error.DanglingSurrogateHalf => return error.InvalidUtf8, - error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, - error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, - error.OutOfMemory => return error.OutOfMemory, - }; - } - } else { - const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound; - return mem.dupe(allocator, u8, result); - } -} - -test "os.getEnvVarOwned" { - var ga = debug.global_allocator; - testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); -} - -/// Caller must free the returned memory. -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 utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; - const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast - const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast - const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); - if (result == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } - assert(result <= utf16le_buf.len); - const utf16le_slice = utf16le_buf[0..result]; - // Trust that Windows gives us valid UTF-16LE. - const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; - return out_buffer[0..end_index]; - }, - else => { - 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), - } - }, - } -} - -test "os.getCwd" { - // at least call it so it gets compiled - _ = getCwdAlloc(debug.global_allocator) catch undefined; - var buf: [MAX_PATH_BYTES]u8 = undefined; - _ = getCwd(&buf) catch undefined; -} - -pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; - -/// TODO add a symLinkC variant -pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void { - if (is_windows) { - return symLinkWindows(existing_path, new_path); - } else { - return symLinkPosix(existing_path, new_path); +/// Get an environment variable with a null-terminated name. +/// See also `getenv`. +/// TODO https://github.com/ziglang/zig/issues/265 +pub fn getenvC(key: [*]const u8) ?[]const u8 { + if (builtin.link_libc) { + const value = system.getenv(key) orelse return null; + return mem.toSliceConst(u8, value); } + return getenv(mem.toSliceConst(u8, key)); } -pub const WindowsSymLinkError = error{ +pub const GetCwdError = error{ NameTooLong, - InvalidUtf8, - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 + CurrentWorkingDirectoryUnlinked, Unexpected, }; -pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void { - if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } +/// The result is a slice of out_buffer, indexed from 0. +pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { + if (windows.is_the_target) { + return windows.GetCurrentDirectory(out_buffer); } -} -pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { - const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path); - const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); - return symLinkW(&existing_path_w, &new_path_w); + const err = if (builtin.link_libc) blk: { + break :blk if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*; + } else blk: { + break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len)); + }; + switch (err) { + 0 => return mem.toSlice(u8, out_buffer.ptr), + EFAULT => unreachable, + EINVAL => unreachable, + ENOENT => return error.CurrentWorkingDirectoryUnlinked, + ERANGE => return error.NameTooLong, + else => return unexpectedErrno(@intCast(usize, err)), + } } -pub const PosixSymLinkError = error{ +pub const SymLinkError = error{ AccessDenied, DiskQuota, PathAlreadyExists, FileSystem, SymLinkLoop, - NameTooLong, FileNotFound, SystemResources, NoSpaceLeft, ReadOnlyFileSystem, NotDir, - - /// See https://github.com/ziglang/zig/issues/1396 + NameTooLong, + InvalidUtf8, + BadPathName, Unexpected, }; -pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void { - const err = posix.getErrno(posix.symlink(existing_path, new_path)); - switch (err) { - 0 => return, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EDQUOT => return error.DiskQuota, - posix.EEXIST => return error.PathAlreadyExists, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.NoSpaceLeft, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } -} - -pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { - const existing_path_c = try toPosixPath(existing_path); - const new_path_c = try toPosixPath(new_path); - return symLinkPosixC(&existing_path_c, &new_path_c); -} - -// here we replace the standard +/ with -_ so that it can be used in a file name -const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); - -/// TODO remove the allocator requirement from this API -pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { - if (symLink(existing_path, new_path)) { - return; - } else |err| switch (err) { - error.PathAlreadyExists => {}, - else => return err, // TODO zig should know this set does not include PathAlreadyExists - } - - const dirname = os.path.dirname(new_path) orelse "."; - - var rand_buf: [12]u8 = undefined; - const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); - defer allocator.free(tmp_path); - mem.copy(u8, tmp_path[0..], dirname); - tmp_path[dirname.len] = os.path.sep; - while (true) { - try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); - - if (symLink(existing_path, tmp_path)) { - return rename(tmp_path, new_path); - } else |err| switch (err) { - error.PathAlreadyExists => continue, - else => return err, // TODO zig should know this set does not include PathAlreadyExists - } +/// 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 `symlinkC` and `symlinkW`. +pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { + if (windows.is_the_target) { + const target_path_w = try windows.sliceToPrefixedFileW(target_path); + const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); + return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); + } else { + const target_path_c = try toPosixPath(target_path); + const sym_link_path_c = try toPosixPath(sym_link_path); + return symlinkC(&target_path_c, &sym_link_path_c); } } -pub const DeleteFileError = error{ +/// This is the same as `symlink` except the parameters are null-terminated pointers. +/// See also `symlink`. +pub fn symlinkC(target_path: [*]const u8, sym_link_path: [*]const u8) SymLinkError!void { + if (windows.is_the_target) { + const target_path_w = try windows.cStrToPrefixedFileW(target_path); + const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); + return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); + } + switch (errno(system.symlink(target_path, sym_link_path))) { + 0 => return, + EFAULT => unreachable, + EINVAL => unreachable, + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EDQUOT => return error.DiskQuota, + EEXIST => return error.PathAlreadyExists, + EIO => return error.FileSystem, + ELOOP => return error.SymLinkLoop, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOTDIR => return error.NotDir, + ENOMEM => return error.SystemResources, + ENOSPC => return error.NoSpaceLeft, + EROFS => return error.ReadOnlyFileSystem, + else => |err| return unexpectedErrno(err), + } +} + +pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { + const target_path_c = try toPosixPath(target_path); + const sym_link_path_c = try toPosixPath(sym_link_path); + return symlinkatC(target_path_c, newdirfd, sym_link_path_c); +} + +pub fn symlinkatC(target_path: [*]const u8, newdirfd: fd_t, sym_link_path: [*]const u8) SymLinkError!void { + switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) { + 0 => return, + EFAULT => unreachable, + EINVAL => unreachable, + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EDQUOT => return error.DiskQuota, + EEXIST => return error.PathAlreadyExists, + EIO => return error.FileSystem, + ELOOP => return error.SymLinkLoop, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOTDIR => return error.NotDir, + ENOMEM => return error.SystemResources, + ENOSPC => return error.NoSpaceLeft, + EROFS => return error.ReadOnlyFileSystem, + else => |err| return unexpectedErrno(err), + } +} + +pub const UnlinkError = error{ FileNotFound, AccessDenied, FileBusy, @@ -1065,6 +837,7 @@ pub const DeleteFileError = error{ NotDir, SystemResources, ReadOnlyFileSystem, + Unexpected, /// On Windows, file paths must be valid Unicode. InvalidUtf8, @@ -1072,225 +845,70 @@ pub const DeleteFileError = error{ /// On Windows, file paths cannot contain these characters: /// '/', '*', '?', '"', '<', '>', '|' BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, }; -pub fn deleteFile(file_path: []const u8) DeleteFileError!void { - if (builtin.os == Os.windows) { - const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); - return deleteFileW(&file_path_w); +/// Delete a name and possibly the file it refers to. +/// See also `unlinkC`. +pub fn unlink(file_path: []const u8) UnlinkError!void { + if (windows.is_the_target) { + const file_path_w = try windows.sliceToPrefixedFileW(file_path); + return windows.DeleteFileW(&file_path_w); } else { const file_path_c = try toPosixPath(file_path); - return deleteFileC(&file_path_c); + return unlinkC(&file_path_c); } } -pub fn deleteFileW(file_path: [*]const u16) DeleteFileError!void { - if (windows.DeleteFileW(file_path) == 0) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.ACCESS_DENIED => return error.AccessDenied, - windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, - windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, - else => return unexpectedErrorWindows(err), - } - } -} - -pub fn deleteFileC(file_path: [*]const u8) DeleteFileError!void { - if (is_windows) { - const file_path_w = try windows_util.cStrToPrefixedFileW(file_path); - return deleteFileW(&file_path_w); - } else { - 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), - } - } -} - -/// 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 -/// in the same directory as dest_path. -/// Destination file will have the same mode as the source file. -pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { - var in_file = try os.File.openRead(source_path); - defer in_file.close(); - - const mode = try in_file.mode(); - const in_stream = &in_file.inStream().stream; - - var atomic_file = try AtomicFile.init(dest_path, mode); - defer atomic_file.deinit(); - - var buf: [page_size]u8 = undefined; - while (true) { - const amt = try in_stream.readFull(buf[0..]); - try atomic_file.file.write(buf[0..amt]); - if (amt != buf.len) { - return atomic_file.finish(); - } - } -} - -/// 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 -pub fn copyFileMode(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(); - - var atomic_file = try AtomicFile.init(dest_path, mode); - defer atomic_file.deinit(); - - var buf: [page_size]u8 = undefined; - while (true) { - const amt = try in_file.read(buf[0..]); - try atomic_file.file.write(buf[0..amt]); - if (amt != buf.len) { - return atomic_file.finish(); - } - } -} - -pub const AtomicFile = struct { - file: os.File, - tmp_path_buf: [MAX_PATH_BYTES]u8, - dest_path: []const u8, - finished: bool, - - const InitError = os.File.OpenError; - - /// dest_path must remain valid for the lifetime of AtomicFile - /// call finish to atomically replace dest_path with contents - /// TODO once we have null terminated pointers, use the - /// openWriteNoClobberN function - pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { - const dirname = os.path.dirname(dest_path); - var rand_buf: [12]u8 = undefined; - const dirname_component_len = if (dirname) |d| d.len + 1 else 0; - const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len); - const tmp_path_len = dirname_component_len + encoded_rand_len; - var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined; - if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong; - - if (dirname) |dir| { - mem.copy(u8, tmp_path_buf[0..], dir); - tmp_path_buf[dir.len] = os.path.sep; - } - - tmp_path_buf[tmp_path_len] = 0; - - while (true) { - try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); - - const file = os.File.openWriteNoClobberC(&tmp_path_buf, 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 - else => return err, - }; - - return AtomicFile{ - .file = file, - .tmp_path_buf = tmp_path_buf, - .dest_path = dest_path, - .finished = false, - }; - } - } - - /// always call deinit, even after successful finish() - pub fn deinit(self: *AtomicFile) void { - if (!self.finished) { - self.file.close(); - deleteFileC(&self.tmp_path_buf) catch {}; - self.finished = true; - } - } - - pub fn finish(self: *AtomicFile) !void { - assert(!self.finished); - self.file.close(); - self.finished = true; - if (is_posix) { - const dest_path_c = try toPosixPath(self.dest_path); - return renameC(&self.tmp_path_buf, &dest_path_c); - } else if (is_windows) { - const dest_path_w = try windows_util.sliceToPrefixedFileW(self.dest_path); - const tmp_path_w = try windows_util.cStrToPrefixedFileW(&self.tmp_path_buf); - return renameW(&tmp_path_w, &dest_path_w); - } else { - @compileError("Unsupported OS"); - } +/// Same as `unlink` except the parameter is a null terminated UTF8-encoded string. +pub fn unlinkC(file_path: [*]const u8) UnlinkError!void { + if (windows.is_the_target) { + const file_path_w = try windows.cStrToPrefixedFileW(file_path); + return windows.DeleteFileW(&file_path_w); } + switch (errno(system.unlink(file_path))) { + 0 => return, + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EBUSY => return error.FileBusy, + EFAULT => unreachable, + EINVAL => unreachable, + EIO => return error.FileSystem, + EISDIR => return error.IsDir, + ELOOP => return error.SymLinkLoop, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOTDIR => return error.NotDir, + ENOMEM => return error.SystemResources, + EROFS => return error.ReadOnlyFileSystem, + else => |err| return unexpectedErrno(err), + } +} + +const RenameError = error{ + AccessDenied, + FileBusy, + DiskQuota, + IsDir, + SymLinkLoop, + LinkQuotaExceeded, + NameTooLong, + FileNotFound, + NotDir, + SystemResources, + NoSpaceLeft, + PathAlreadyExists, + ReadOnlyFileSystem, + RenameAcrossMountPoints, + InvalidUtf8, + BadPathName, + Unexpected, }; -pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { - if (is_windows) { - const old_path_w = try windows_util.cStrToPrefixedFileW(old_path); - const new_path_w = try windows_util.cStrToPrefixedFileW(new_path); - return renameW(&old_path_w, &new_path_w); - } else { - 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 renameW(old_path: [*]const u16, new_path: [*]const u16) !void { - const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; - if (windows.MoveFileExW(old_path, new_path, flags) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } -} - -pub fn rename(old_path: []const u8, new_path: []const u8) !void { - if (is_windows) { - const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); - const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); +/// Change the name or location of a file. +pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { + if (windows.is_the_target) { + const old_path_w = try windows.sliceToPrefixedFileW(old_path); + const new_path_w = try windows.sliceToPrefixedFileW(new_path); return renameW(&old_path_w, &new_path_w); } else { const old_path_c = try toPosixPath(old_path); @@ -1299,1213 +917,329 @@ pub fn rename(old_path: []const u8, new_path: []const u8) !void { } } -pub fn makeDir(dir_path: []const u8) !void { - if (is_windows) { - return makeDirWindows(dir_path); - } else { - return makeDirPosix(dir_path); - } -} - -pub fn makeDirWindows(dir_path: []const u8) !void { - const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); - - if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists, - windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, - else => unexpectedErrorWindows(err), - }; +/// Same as `rename` except the parameters are null-terminated byte arrays. +pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void { + if (windows.is_the_target) { + const old_path_w = try windows.cStrToPrefixedFileW(old_path); + const new_path_w = try windows.cStrToPrefixedFileW(new_path); + return renameW(&old_path_w, &new_path_w); } -} - -pub fn makeDirPosixC(dir_path: [*]const u8) !void { - const err = posix.getErrno(posix.mkdir(dir_path, 0o755)); - switch (err) { + switch (errno(system.rename(old_path, new_path))) { 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 { - const dir_path_c = try toPosixPath(dir_path); - return makeDirPosixC(&dir_path_c); -} - -/// Calls makeDir recursively to make an entire path. Returns success if the path -/// already exists and is a directory. -/// TODO determine if we can remove the allocator requirement from this function -pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { - const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); - defer allocator.free(resolved_path); - - var end_index: usize = resolved_path.len; - while (true) { - makeDir(resolved_path[0..end_index]) catch |err| switch (err) { - error.PathAlreadyExists => { - // TODO stat the file and return an error if it's not a directory - // this is important because otherwise a dangling symlink - // could cause an infinite loop - if (end_index == resolved_path.len) return; - }, - error.FileNotFound => { - // march end_index backward until next path component - while (true) { - end_index -= 1; - if (os.path.isSep(resolved_path[end_index])) break; - } - continue; - }, - else => return err, - }; - if (end_index == resolved_path.len) return; - // march end_index forward until next path component - while (true) { - end_index += 1; - if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break; - } - } + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EBUSY => return error.FileBusy, + EDQUOT => return error.DiskQuota, + EFAULT => unreachable, + EINVAL => unreachable, + EISDIR => return error.IsDir, + ELOOP => return error.SymLinkLoop, + EMLINK => return error.LinkQuotaExceeded, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOTDIR => return error.NotDir, + ENOMEM => return error.SystemResources, + ENOSPC => return error.NoSpaceLeft, + EEXIST => return error.PathAlreadyExists, + ENOTEMPTY => return error.PathAlreadyExists, + EROFS => return error.ReadOnlyFileSystem, + EXDEV => return error.RenameAcrossMountPoints, + else => |err| return unexpectedErrno(err), + } +} + +/// Same as `rename` except the parameters are null-terminated UTF16LE encoded byte arrays. +/// Assumes target is Windows. +pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) RenameError!void { + const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; + return windows.MoveFileExW(old_path, new_path, flags); } -pub const DeleteDirError = error{ +pub const MakeDirError = error{ AccessDenied, - FileBusy, + DiskQuota, + PathAlreadyExists, SymLinkLoop, + LinkQuotaExceeded, NameTooLong, FileNotFound, SystemResources, + NoSpaceLeft, NotDir, - DirNotEmpty, ReadOnlyFileSystem, InvalidUtf8, BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { - switch (builtin.os) { - Os.windows => { - const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path); - return deleteDirW(&dir_path_w); - }, - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const err = posix.getErrno(posix.rmdir(dir_path)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EBUSY => return error.FileBusy, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOTDIR => return error.NotDir, - posix.EEXIST => return error.DirNotEmpty, - posix.ENOTEMPTY => return error.DirNotEmpty, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } - }, - else => @compileError("unimplemented"), +/// Create a directory. +/// `mode` is ignored on Windows. +pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { + if (windows.is_the_target) { + const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); + return windows.CreateDirectoryW(&dir_path_w, null); + } else { + const dir_path_c = try toPosixPath(dir_path); + return mkdirC(&dir_path_c, mode); } } -pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void { - if (windows.RemoveDirectoryW(dir_path_w) == 0) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, - else => return unexpectedErrorWindows(err), - } +/// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string. +pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void { + if (windows.is_the_target) { + const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); + return windows.CreateDirectoryW(&dir_path_w, null); } -} - -/// Returns ::error.DirNotEmpty if the directory is not empty. -/// To delete a directory recursively, see ::deleteTree -pub fn deleteDir(dir_path: []const u8) DeleteDirError!void { - switch (builtin.os) { - Os.windows => { - const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); - return deleteDirW(&dir_path_w); - }, - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const dir_path_c = try toPosixPath(dir_path); - return deleteDirC(&dir_path_c); - }, - else => @compileError("unimplemented"), + switch (errno(system.mkdir(dir_path, mode))) { + 0 => return, + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EDQUOT => return error.DiskQuota, + EEXIST => return error.PathAlreadyExists, + EFAULT => unreachable, + ELOOP => return error.SymLinkLoop, + EMLINK => return error.LinkQuotaExceeded, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOSPC => return error.NoSpaceLeft, + ENOTDIR => return error.NotDir, + EROFS => return error.ReadOnlyFileSystem, + else => |err| return unexpectedErrno(err), } } -/// Whether ::full_path describes a symlink, file, or directory, this function -/// removes it. If it cannot be removed because it is a non-empty directory, -/// this function recursively removes its entries and then tries again. -const DeleteTreeError = error{ - OutOfMemory, +pub const DeleteDirError = error{ AccessDenied, - FileTooBig, - IsDir, + FileBusy, SymLinkLoop, - ProcessFdQuotaExceeded, NameTooLong, - SystemFdQuotaExceeded, - NoDevice, + FileNotFound, SystemResources, - NoSpaceLeft, - PathAlreadyExists, - ReadOnlyFileSystem, NotDir, - FileNotFound, - FileSystem, - FileBusy, DirNotEmpty, - DeviceBusy, - - /// On Windows, file paths must be valid Unicode. + ReadOnlyFileSystem, InvalidUtf8, - - /// On Windows, file paths cannot contain these characters: - /// '/', '*', '?', '"', '<', '>', '|' BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -/// TODO determine if we can remove the allocator requirement -pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { - start_over: while (true) { - var got_access_denied = false; - // First, try deleting the item as a file. This way we don't follow sym links. - if (deleteFile(full_path)) { - return; - } else |err| switch (err) { - error.FileNotFound => return, - error.IsDir => {}, - error.AccessDenied => got_access_denied = true, - - error.InvalidUtf8, - error.SymLinkLoop, - error.NameTooLong, - error.SystemResources, - error.ReadOnlyFileSystem, - error.NotDir, - error.FileSystem, - error.FileBusy, - error.BadPathName, - error.Unexpected, - => return err, - } - { - var dir = Dir.open(allocator, full_path) catch |err| switch (err) { - error.NotDir => { - if (got_access_denied) { - return error.AccessDenied; - } - continue :start_over; - }, - - error.OutOfMemory, - error.AccessDenied, - error.FileTooBig, - error.IsDir, - error.SymLinkLoop, - error.ProcessFdQuotaExceeded, - error.NameTooLong, - error.SystemFdQuotaExceeded, - error.NoDevice, - error.FileNotFound, - error.SystemResources, - error.NoSpaceLeft, - error.PathAlreadyExists, - error.Unexpected, - error.InvalidUtf8, - error.BadPathName, - error.DeviceBusy, - => return err, - }; - defer dir.close(); - - var full_entry_buf = ArrayList(u8).init(allocator); - defer full_entry_buf.deinit(); - - while (try dir.next()) |entry| { - try full_entry_buf.resize(full_path.len + entry.name.len + 1); - const full_entry_path = full_entry_buf.toSlice(); - mem.copy(u8, full_entry_path, full_path); - full_entry_path[full_path.len] = path.sep; - mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); - - try deleteTree(allocator, full_entry_path); - } - } - return deleteDir(full_path); +/// Deletes an empty directory. +pub fn rmdir(dir_path: []const u8) DeleteDirError!void { + if (windows.is_the_target) { + const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); + return windows.RemoveDirectoryW(&dir_path_w); + } else { + const dir_path_c = try toPosixPath(dir_path); + return rmdirC(&dir_path_c); } } -pub const Dir = struct { - handle: Handle, - allocator: *Allocator, - - pub const Handle = switch (builtin.os) { - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => struct { - fd: i32, - seek: i64, - buf: []u8, - index: usize, - end_index: usize, - }, - Os.linux => struct { - fd: i32, - buf: []u8, - index: usize, - end_index: usize, - }, - Os.windows => struct { - handle: windows.HANDLE, - find_file_data: windows.WIN32_FIND_DATAW, - first: bool, - name_data: [256]u8, - }, - else => @compileError("unimplemented"), - }; - - pub const Entry = struct { - name: []const u8, - kind: Kind, - - pub const Kind = enum { - BlockDevice, - CharacterDevice, - Directory, - NamedPipe, - SymLink, - File, - UnixDomainSocket, - Whiteout, - Unknown, - }; - }; - - pub const OpenError = error{ - FileNotFound, - NotDir, - AccessDenied, - FileTooBig, - IsDir, - SymLinkLoop, - ProcessFdQuotaExceeded, - NameTooLong, - SystemFdQuotaExceeded, - NoDevice, - SystemResources, - NoSpaceLeft, - PathAlreadyExists, - OutOfMemory, - InvalidUtf8, - BadPathName, - DeviceBusy, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, - }; - - /// TODO remove the allocator requirement from this API - pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir { - return Dir{ - .allocator = allocator, - .handle = switch (builtin.os) { - Os.windows => blk: { - var find_file_data: windows.WIN32_FIND_DATAW = undefined; - const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data); - break :blk Handle{ - .handle = handle, - .find_file_data = find_file_data, // TODO guaranteed copy elision - .first = true, - .name_data = undefined, - }; - }, - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => Handle{ - .fd = try posixOpen( - dir_path, - posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, - 0, - ), - .seek = 0, - .index = 0, - .end_index = 0, - .buf = []u8{}, - }, - Os.linux => Handle{ - .fd = try posixOpen( - dir_path, - posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, - 0, - ), - .index = 0, - .end_index = 0, - .buf = []u8{}, - }, - else => @compileError("unimplemented"), - }, - }; - } - - pub fn close(self: *Dir) void { - switch (builtin.os) { - Os.windows => { - _ = windows.FindClose(self.handle.handle); - }, - Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => { - self.allocator.free(self.handle.buf); - os.close(self.handle.fd); - }, - else => @compileError("unimplemented"), - } - } - - /// Memory such as file names referenced in this returned entry becomes invalid - /// with subsequent calls to next, as well as when this `Dir` is deinitialized. - pub fn next(self: *Dir) !?Entry { - switch (builtin.os) { - Os.linux => return self.nextLinux(), - Os.macosx, Os.ios => return self.nextDarwin(), - Os.windows => return self.nextWindows(), - Os.freebsd => return self.nextFreebsd(), - Os.netbsd => return self.nextFreebsd(), - else => @compileError("unimplemented"), - } - } - - fn nextDarwin(self: *Dir) !?Entry { - start_over: while (true) { - if (self.handle.index >= self.handle.end_index) { - if (self.handle.buf.len == 0) { - self.handle.buf = try self.allocator.alloc(u8, page_size); - } - - while (true) { - const result = posix.getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); - const err = posix.getErrno(result); - if (err > 0) { - switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, - posix.EINVAL => { - self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); - continue; - }, - else => return unexpectedErrorPosix(err), - } - } - if (result == 0) return null; - self.handle.index = 0; - self.handle.end_index = result; - break; - } - } - const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); - const next_index = self.handle.index + darwin_entry.d_reclen; - self.handle.index = next_index; - - const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; - - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - continue :start_over; - } - - const entry_kind = switch (darwin_entry.d_type) { - posix.DT_BLK => Entry.Kind.BlockDevice, - posix.DT_CHR => Entry.Kind.CharacterDevice, - posix.DT_DIR => Entry.Kind.Directory, - posix.DT_FIFO => Entry.Kind.NamedPipe, - posix.DT_LNK => Entry.Kind.SymLink, - posix.DT_REG => Entry.Kind.File, - posix.DT_SOCK => Entry.Kind.UnixDomainSocket, - posix.DT_WHT => Entry.Kind.Whiteout, - else => Entry.Kind.Unknown, - }; - return Entry{ - .name = name, - .kind = entry_kind, - }; - } - } - - fn nextWindows(self: *Dir) !?Entry { - while (true) { - if (self.handle.first) { - self.handle.first = false; - } else { - if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data)) - return null; - } - const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); - if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' })) - continue; - // Trust that Windows gives us valid UTF-16LE - const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; - const name_utf8 = self.handle.name_data[0..name_utf8_len]; - const kind = blk: { - const attrs = self.handle.find_file_data.dwFileAttributes; - if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; - if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; - if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File; - break :blk Entry.Kind.Unknown; - }; - return Entry{ - .name = name_utf8, - .kind = kind, - }; - } +/// Same as `rmdir` except the parameter is null-terminated. +pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void { + if (windows.is_the_target) { + const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); + return windows.RemoveDirectoryW(&dir_path_w); } - - fn nextLinux(self: *Dir) !?Entry { - start_over: while (true) { - if (self.handle.index >= self.handle.end_index) { - if (self.handle.buf.len == 0) { - self.handle.buf = try self.allocator.alloc(u8, page_size); - } - - while (true) { - const result = posix.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len); - const err = posix.getErrno(result); - if (err > 0) { - switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, - posix.EINVAL => { - self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); - continue; - }, - else => return unexpectedErrorPosix(err), - } - } - if (result == 0) return null; - self.handle.index = 0; - self.handle.end_index = result; - break; - } - } - const linux_entry = @ptrCast(*align(1) posix.dirent64, &self.handle.buf[self.handle.index]); - const next_index = self.handle.index + linux_entry.d_reclen; - self.handle.index = next_index; - - const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); - - // skip . and .. entries - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - continue :start_over; - } - - const entry_kind = switch (linux_entry.d_type) { - posix.DT_BLK => Entry.Kind.BlockDevice, - posix.DT_CHR => Entry.Kind.CharacterDevice, - posix.DT_DIR => Entry.Kind.Directory, - posix.DT_FIFO => Entry.Kind.NamedPipe, - posix.DT_LNK => Entry.Kind.SymLink, - posix.DT_REG => Entry.Kind.File, - posix.DT_SOCK => Entry.Kind.UnixDomainSocket, - else => Entry.Kind.Unknown, - }; - return Entry{ - .name = name, - .kind = entry_kind, - }; - } - } - - fn nextFreebsd(self: *Dir) !?Entry { - start_over: while (true) { - if (self.handle.index >= self.handle.end_index) { - if (self.handle.buf.len == 0) { - self.handle.buf = try self.allocator.alloc(u8, page_size); - } - - while (true) { - const result = posix.getdirentries(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); - const err = posix.getErrno(result); - if (err > 0) { - switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, - posix.EINVAL => { - self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); - continue; - }, - else => return unexpectedErrorPosix(err), - } - } - if (result == 0) return null; - self.handle.index = 0; - self.handle.end_index = result; - break; - } - } - const freebsd_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); - const next_index = self.handle.index + freebsd_entry.d_reclen; - self.handle.index = next_index; - - const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; - - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - continue :start_over; - } - - const entry_kind = switch (freebsd_entry.d_type) { - posix.DT_BLK => Entry.Kind.BlockDevice, - posix.DT_CHR => Entry.Kind.CharacterDevice, - posix.DT_DIR => Entry.Kind.Directory, - posix.DT_FIFO => Entry.Kind.NamedPipe, - posix.DT_LNK => Entry.Kind.SymLink, - posix.DT_REG => Entry.Kind.File, - posix.DT_SOCK => Entry.Kind.UnixDomainSocket, - posix.DT_WHT => Entry.Kind.Whiteout, - else => Entry.Kind.Unknown, - }; - return Entry{ - .name = name, - .kind = entry_kind, - }; - } - } -}; - -pub fn changeCurDir(dir_path: []const u8) !void { - const dir_path_c = try toPosixPath(dir_path); - const err = posix.getErrno(posix.chdir(&dir_path_c)); - switch (err) { + switch (errno(system.rmdir(dir_path))) { 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EFAULT => unreachable, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOTDIR => return error.NotDir, - else => return unexpectedErrorPosix(err), - } -} - -/// Read value of a symbolic link. -/// The return value is a slice of out_buffer. -pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 { - const rc = posix.readlink(pathname, out_buffer, out_buffer.len); - const err = posix.getErrno(rc); - switch (err) { - 0 => return out_buffer[0..rc], - posix.EACCES => return 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(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 { - const file_path_c = try toPosixPath(file_path); - return readLinkC(out_buffer, &file_path_c); -} - -pub fn posix_setuid(uid: u32) !void { - const err = posix.getErrno(posix.setuid(uid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub fn posix_setreuid(ruid: u32, euid: u32) !void { - const err = posix.getErrno(posix.setreuid(ruid, euid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub fn posix_setgid(gid: u32) !void { - const err = posix.getErrno(posix.setgid(gid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub fn posix_setregid(rgid: u32, egid: u32) !void { - const err = posix.getErrno(posix.setregid(rgid, egid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub const WindowsGetStdHandleErrs = error{ - NoStdHandles, - - /// See https://github.com/ziglang/zig/issues/1396 + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EBUSY => return error.FileBusy, + EFAULT => unreachable, + EINVAL => unreachable, + ELOOP => return error.SymLinkLoop, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOTDIR => return error.NotDir, + EEXIST => return error.DirNotEmpty, + ENOTEMPTY => return error.DirNotEmpty, + EROFS => return error.ReadOnlyFileSystem, + else => |err| return unexpectedErrno(err), + } +} + +pub const ChangeCurDirError = error{ + AccessDenied, + FileSystem, + SymLinkLoop, + NameTooLong, + FileNotFound, + SystemResources, + NotDir, Unexpected, }; -pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE { - if (windows.GetStdHandle(handle_id)) |handle| { - if (handle == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - } - return handle; +/// Changes the current working directory of the calling process. +/// `dir_path` is recommended to be a UTF-8 encoded string. +pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { + if (windows.is_the_target) { + const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); + @compileError("TODO implement chdir for Windows"); } else { - return error.NoStdHandles; + const dir_path_c = try toPosixPath(dir_path); + return chdirC(&dir_path_c); } } -pub const ArgIteratorPosix = struct { - index: usize, - count: usize, - - pub fn init() ArgIteratorPosix { - return ArgIteratorPosix{ - .index = 0, - .count = raw.len, - }; +/// Same as `chdir` except the parameter is null-terminated. +pub fn chdirC(dir_path: [*]const u8) ChangeCurDirError!void { + if (windows.is_the_target) { + const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); + @compileError("TODO implement chdir for Windows"); } - - pub fn next(self: *ArgIteratorPosix) ?[]const u8 { - if (self.index == self.count) return null; - - const s = raw[self.index]; - self.index += 1; - return cstr.toSlice(s); - } - - pub fn skip(self: *ArgIteratorPosix) bool { - if (self.index == self.count) return false; - - self.index += 1; - return true; + switch (errno(system.chdir(dir_path))) { + 0 => return, + EACCES => return error.AccessDenied, + EFAULT => unreachable, + EIO => return error.FileSystem, + ELOOP => return error.SymLinkLoop, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOTDIR => return error.NotDir, + else => |err| return unexpectedErrno(err), } +} - /// This is marked as public but actually it's only meant to be used - /// internally by zig's startup code. - pub var raw: [][*]u8 = undefined; +pub const ReadLinkError = error{ + AccessDenied, + FileSystem, + SymLinkLoop, + NameTooLong, + FileNotFound, + SystemResources, + NotDir, + Unexpected, }; -pub const ArgIteratorWindows = struct { - index: usize, - cmd_line: [*]const u8, - in_quote: bool, - quote_count: usize, - seen_quote_count: usize, - - pub const NextError = error{OutOfMemory}; - - pub fn init() ArgIteratorWindows { - return initWithCmdLine(windows.GetCommandLineA()); - } - - pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows { - return ArgIteratorWindows{ - .index = 0, - .cmd_line = cmd_line, - .in_quote = false, - .quote_count = countQuotes(cmd_line), - .seen_quote_count = 0, - }; - } - - /// You must free the returned memory when done. - pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) { - // march forward over whitespace - while (true) : (self.index += 1) { - const byte = self.cmd_line[self.index]; - switch (byte) { - 0 => return null, - ' ', '\t' => continue, - else => break, - } - } - - return self.internalNext(allocator); - } - - pub fn skip(self: *ArgIteratorWindows) bool { - // march forward over whitespace - while (true) : (self.index += 1) { - const byte = self.cmd_line[self.index]; - switch (byte) { - 0 => return false, - ' ', '\t' => continue, - else => break, - } - } - - var backslash_count: usize = 0; - while (true) : (self.index += 1) { - const byte = self.cmd_line[self.index]; - switch (byte) { - 0 => return true, - '"' => { - const quote_is_real = backslash_count % 2 == 0; - if (quote_is_real) { - self.seen_quote_count += 1; - } - }, - '\\' => { - backslash_count += 1; - }, - ' ', '\t' => { - if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { - return true; - } - backslash_count = 0; - }, - else => { - backslash_count = 0; - continue; - }, - } - } +/// Read value of a symbolic link. +/// The return value is a slice of `out_buffer` from index 0. +pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { + if (windows.is_the_target) { + const file_path_w = try windows.sliceToPrefixedFileW(file_path); + @compileError("TODO implement readlink for Windows"); + } else { + const file_path_c = try toPosixPath(file_path); + return readlinkC(&file_path_c, out_buffer); } +} - fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 { - var buf = try Buffer.initSize(allocator, 0); - defer buf.deinit(); - - var backslash_count: usize = 0; - while (true) : (self.index += 1) { - const byte = self.cmd_line[self.index]; - switch (byte) { - 0 => return buf.toOwnedSlice(), - '"' => { - const quote_is_real = backslash_count % 2 == 0; - try self.emitBackslashes(&buf, backslash_count / 2); - backslash_count = 0; - - if (quote_is_real) { - self.seen_quote_count += 1; - if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) { - try buf.appendByte('"'); - } - } else { - try buf.appendByte('"'); - } - }, - '\\' => { - backslash_count += 1; - }, - ' ', '\t' => { - try self.emitBackslashes(&buf, backslash_count); - backslash_count = 0; - if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { - try buf.appendByte(byte); - } else { - return buf.toOwnedSlice(); - } - }, - else => { - try self.emitBackslashes(&buf, backslash_count); - backslash_count = 0; - try buf.appendByte(byte); - }, - } - } +/// Same as `readlink` except `file_path` is null-terminated. +pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { + if (windows.is_the_target) { + const file_path_w = try windows.cStrToPrefixedFileW(file_path); + @compileError("TODO implement readlink for Windows"); } - - fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void { - var i: usize = 0; - while (i < emit_count) : (i += 1) { - try buf.appendByte('\\'); - } + const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len); + switch (errno(rc)) { + 0 => return out_buffer[0..@bitCast(usize, rc)], + EACCES => return error.AccessDenied, + EFAULT => unreachable, + EINVAL => unreachable, + EIO => return error.FileSystem, + ELOOP => return error.SymLinkLoop, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOTDIR => return error.NotDir, + else => |err| return unexpectedErrno(err), } +} - fn countQuotes(cmd_line: [*]const u8) usize { - var result: usize = 0; - var backslash_count: usize = 0; - var index: usize = 0; - while (true) : (index += 1) { - const byte = cmd_line[index]; - switch (byte) { - 0 => return result, - '\\' => backslash_count += 1, - '"' => { - result += 1 - (backslash_count % 2); - backslash_count = 0; - }, - else => { - backslash_count = 0; - }, - } - } - } +pub const SetIdError = error{ + ResourceLimitReached, + InvalidUserId, + PermissionDenied, + Unexpected, }; -pub const ArgIterator = struct { - const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix; - - inner: InnerType, - - pub fn init() ArgIterator { - if (builtin.os == Os.wasi) { - // TODO: Figure out a compatible interface accomodating WASI - @compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead."); - } - - return ArgIterator{ .inner = InnerType.init() }; - } - - pub const NextError = ArgIteratorWindows.NextError; - - /// You must free the returned memory when done. - pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { - if (builtin.os == Os.windows) { - return self.inner.next(allocator); - } else { - return mem.dupe(allocator, u8, self.inner.next() orelse return null); - } - } - - /// If you only are targeting posix you can call this and not need an allocator. - pub fn nextPosix(self: *ArgIterator) ?[]const u8 { - return self.inner.next(); - } - - /// Parse past 1 argument without capturing it. - /// Returns `true` if skipped an arg, `false` if we are at the end. - pub fn skip(self: *ArgIterator) bool { - return self.inner.skip(); +pub fn setuid(uid: u32) SetIdError!void { + switch (errno(system.setuid(uid))) { + 0 => return, + EAGAIN => return error.ResourceLimitReached, + EINVAL => return error.InvalidUserId, + EPERM => return error.PermissionDenied, + else => |err| return unexpectedErrno(err), } -}; - -pub fn args() ArgIterator { - return ArgIterator.init(); } -/// Caller must call argsFree on result. -pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { - if (builtin.os == Os.wasi) { - var count: usize = undefined; - var buf_size: usize = undefined; - - const args_sizes_get_ret = os.wasi.args_sizes_get(&count, &buf_size); - if (args_sizes_get_ret != os.wasi.ESUCCESS) { - return unexpectedErrorPosix(args_sizes_get_ret); - } - - var argv = try allocator.alloc([*]u8, count); - defer allocator.free(argv); - - var argv_buf = try allocator.alloc(u8, buf_size); - const args_get_ret = os.wasi.args_get(argv.ptr, argv_buf.ptr); - if (args_get_ret != os.wasi.ESUCCESS) { - return unexpectedErrorPosix(args_get_ret); - } - - var result_slice = try allocator.alloc([]u8, count); - - var i: usize = 0; - while (i < count) : (i += 1) { - result_slice[i] = mem.toSlice(u8, argv[i]); - } - - return result_slice; - } - - // TODO refactor to only make 1 allocation. - var it = args(); - var contents = try Buffer.initSize(allocator, 0); - defer contents.deinit(); - - var slice_list = ArrayList(usize).init(allocator); - defer slice_list.deinit(); - - while (it.next(allocator)) |arg_or_err| { - const arg = try arg_or_err; - defer allocator.free(arg); - try contents.append(arg); - try slice_list.append(arg.len); - } - - const contents_slice = contents.toSliceConst(); - const slice_sizes = slice_list.toSliceConst(); - const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len); - const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len); - const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes); - errdefer allocator.free(buf); - - const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]); - const result_contents = buf[slice_list_bytes..]; - mem.copy(u8, result_contents, contents_slice); - - var contents_index: usize = 0; - for (slice_sizes) |len, i| { - const new_index = contents_index + len; - result_slice_list[i] = result_contents[contents_index..new_index]; - contents_index = new_index; +pub fn setreuid(ruid: u32, euid: u32) SetIdError!void { + switch (errno(system.setreuid(ruid, euid))) { + 0 => return, + EAGAIN => return error.ResourceLimitReached, + EINVAL => return error.InvalidUserId, + EPERM => return error.PermissionDenied, + else => |err| return unexpectedErrno(err), } - - return result_slice_list; } -pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { - if (builtin.os == Os.wasi) { - const last_item = args_alloc[args_alloc.len - 1]; - const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated - const first_item_ptr = args_alloc[0].ptr; - const len = last_byte_addr - @ptrToInt(first_item_ptr); - allocator.free(first_item_ptr[0..len]); - - return allocator.free(args_alloc); - } - - var total_bytes: usize = 0; - for (args_alloc) |arg| { - total_bytes += @sizeOf([]u8) + arg.len; +pub fn setgid(gid: u32) SetIdError!void { + switch (errno(system.setgid(gid))) { + 0 => return, + EAGAIN => return error.ResourceLimitReached, + EINVAL => return error.InvalidUserId, + EPERM => return error.PermissionDenied, + else => |err| return unexpectedErrno(err), } - const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes]; - const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); - return allocator.free(aligned_allocated_buf); } -test "windows arg parsing" { - testWindowsCmdLine(c"a b\tc d", [][]const u8{ "a", "b", "c", "d" }); - testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" }); - testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" }); - testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" }); - testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" }); - testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" }); - - testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{ - ".\\..\\zig-cache\\build", - "bin\\zig.exe", - ".\\..", - ".\\..\\zig-cache", - "--help", - }); -} - -fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { - var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); - for (expected_args) |expected_arg| { - const arg = it.next(debug.global_allocator).? catch unreachable; - testing.expectEqualSlices(u8, expected_arg, arg); +pub fn setregid(rgid: u32, egid: u32) SetIdError!void { + switch (errno(system.setregid(rgid, egid))) { + 0 => return, + EAGAIN => return error.ResourceLimitReached, + EINVAL => return error.InvalidUserId, + EPERM => return error.PermissionDenied, + else => |err| return unexpectedErrno(err), } - testing.expect(it.next(debug.global_allocator) == null); } -// TODO make this a build variable that you can set -const unexpected_error_tracing = false; -const UnexpectedError = error{ - /// The Operating System returned an undocumented error code. - Unexpected, -}; +/// Test whether a file descriptor refers to a terminal. +pub fn isatty(handle: fd_t) bool { + if (windows.is_the_target) { + if (isCygwinPty(handle)) + return true; -/// Call this when you made a syscall or something that sets errno -/// and you get an unexpected error. -pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { - if (unexpected_error_tracing) { - debug.warn("unexpected errno: {}\n", errno); - debug.dumpCurrentStackTrace(null); + var out: windows.DWORD = undefined; + return windows.kernel32.GetConsoleMode(handle, &out) != 0; } - return error.Unexpected; -} - -/// Call this when you made a windows DLL call or something that does SetLastError -/// and you get an unexpected error. -pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { - if (unexpected_error_tracing) { - debug.warn("unexpected GetLastError(): {}\n", err); - @breakpoint(); - debug.dumpCurrentStackTrace(null); + if (builtin.link_libc) { + return system.isatty(handle) != 0; } - return error.Unexpected; -} - -pub fn openSelfExe() !os.File { - switch (builtin.os) { - Os.linux => return os.File.openReadC(c"/proc/self/exe"), - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - var buf: [MAX_PATH_BYTES]u8 = undefined; - const self_exe_path = try selfExePath(&buf); - buf[self_exe_path.len] = 0; - return os.File.openReadC(self_exe_path.ptr); - }, - Os.windows => { - var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; - const wide_slice = try selfExePathW(&buf); - return os.File.openReadW(wide_slice.ptr); - }, - else => @compileError("Unsupported OS"), + if (wasi.is_the_target) { + @compileError("TODO implement std.os.isatty for WASI"); } -} - -test "openSelfExe" { - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.windows, Os.freebsd => (try openSelfExe()).close(), - else => return error.SkipZigTest, // Unsupported OS. + if (linux.is_the_target) { + var wsz: linux.winsize = undefined; + return linux.syscall3(linux.SYS_ioctl, @bitCast(usize, isize(handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } + unreachable; } -pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { - const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast - const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len); - assert(rc <= out_buffer.len); - if (rc == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } - return out_buffer[0..rc]; -} - -/// Get the path to the current executable. -/// 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 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: *[MAX_PATH_BYTES]u8) ![]u8 { - switch (builtin.os) { - Os.linux => return readLink(out_buffer, "/proc/self/exe"), - Os.freebsd => { - var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 }; - var out_len: usize = out_buffer.len; - const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); - - if (err == 0) return mem.toSlice(u8, out_buffer); - - return switch (err) { - posix.EFAULT => error.BadAdress, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; - }, - Os.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; - const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); - - if (err == 0) return mem.toSlice(u8, out_buffer); - - return switch (err) { - posix.EFAULT => error.BadAdress, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; - }, - Os.windows => { - var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; - const utf16le_slice = try selfExePathW(&utf16le_buf); - // Trust that Windows gives us valid UTF-16LE. - const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; - return out_buffer[0..end_index]; - }, - Os.macosx, Os.ios => { - var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast - const rc = c._NSGetExecutablePath(out_buffer, &u32_len); - if (rc != 0) return error.NameTooLong; - return mem.toSlice(u8, out_buffer); - }, - else => @compileError("Unsupported OS"), - } -} - -/// `selfExeDirPath` except allocates the result on the heap. -/// Caller owns returned memory. -pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { - var buf: [MAX_PATH_BYTES]u8 = undefined; - return mem.dupe(allocator, u8, try selfExeDirPath(&buf)); -} - -/// Get the directory path that contains the current executable. -/// Returned value is a slice of out_buffer. -pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { - switch (builtin.os) { - Os.linux => { - // If the currently executing binary has been deleted, - // the file path looks something like `/a/b/c/exe (deleted)` - // This path cannot be opened, but it's valid for determining the directory - // the executable was in when it was run. - const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe"); - // Assume that /proc/self/exe has an absolute path, and therefore dirname - // will not return null. - return path.dirname(full_exe_path).?; - }, - Os.windows, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const self_exe_path = try selfExePath(out_buffer); - // Assume that the OS APIs return absolute paths, and therefore dirname - // will not return null. - return path.dirname(self_exe_path).?; - }, - else => @compileError("Unsupported OS"), - } -} +pub fn isCygwinPty(handle: fd_t) bool { + if (!windows.is_the_target) return false; -pub fn isTty(handle: FileHandle) bool { - if (is_windows) { - return windows_util.windowsIsTty(handle); - } else { - if (builtin.link_libc) { - return c.isatty(handle) != 0; - } else { - return posix.isatty(handle); - } - } -} + const size = @sizeOf(windows.FILE_NAME_INFO); + var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH); -pub fn supportsAnsiEscapeCodes(handle: FileHandle) bool { - if (is_windows) { - return windows_util.windowsIsCygwinPty(handle); - } else { - if (builtin.link_libc) { - return c.isatty(handle) != 0; - } else { - return posix.isatty(handle); - } + if (windows.kernel32.GetFileInformationByHandleEx( + handle, + windows.FileNameInfo, + @ptrCast(*c_void, &name_info_bytes), + name_info_bytes.len, + ) == 0) { + return false; } + + const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); + const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; + const name_wide = @bytesToSlice(u16, name_bytes); + return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or + mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null; } -pub const PosixSocketError = error{ +pub const SocketError = error{ /// Permission to create a socket of the specified type and/or /// pro‐tocol is denied. PermissionDenied, @@ -2528,25 +1262,26 @@ pub const PosixSocketError = error{ /// The protocol type or the specified protocol is not supported within this domain. ProtocolNotSupported, + + Unexpected, }; -pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { - const rc = posix.socket(domain, socket_type, protocol); - const err = posix.getErrno(rc); - switch (err) { +pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 { + const rc = system.socket(domain, socket_type, protocol); + switch (errno(rc)) { 0 => return @intCast(i32, rc), - posix.EACCES => return PosixSocketError.PermissionDenied, - posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, - posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, - posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, - posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, - posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, - posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, - else => return unexpectedErrorPosix(err), + EACCES => return error.PermissionDenied, + EAFNOSUPPORT => return error.AddressFamilyNotSupported, + EINVAL => return error.ProtocolFamilyNotAvailable, + EMFILE => return error.ProcessFdQuotaExceeded, + ENFILE => return error.SystemFdQuotaExceeded, + ENOBUFS, ENOMEM => return error.SystemResources, + EPROTONOSUPPORT => return error.ProtocolNotSupported, + else => |err| return unexpectedErrno(err), } } -pub const PosixBindError = error{ +pub const BindError = error{ /// The address is protected, and the user is not the superuser. /// For UNIX domain sockets: Search permission is denied on a component /// of the path prefix. @@ -2580,34 +1315,32 @@ pub const PosixBindError = error{ /// The socket inode would reside on a read-only filesystem. ReadOnlyFileSystem, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -/// addr is `&const T` where T is one of the sockaddr -pub fn posixBind(fd: i32, addr: *const posix.sockaddr) PosixBindError!void { - const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); - const err = posix.getErrno(rc); - switch (err) { +/// addr is `*const T` where T is one of the sockaddr +pub fn bind(fd: i32, addr: *const sockaddr) BindError!void { + const rc = system.bind(fd, addr, @sizeOf(sockaddr)); + switch (errno(rc)) { 0 => return, - posix.EACCES => return PosixBindError.AccessDenied, - posix.EADDRINUSE => return PosixBindError.AddressInUse, - posix.EBADF => unreachable, // always a race condition if this error is returned - posix.EINVAL => unreachable, - posix.ENOTSOCK => unreachable, - posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable, - posix.EFAULT => unreachable, - posix.ELOOP => return PosixBindError.SymLinkLoop, - posix.ENAMETOOLONG => return PosixBindError.NameTooLong, - posix.ENOENT => return PosixBindError.FileNotFound, - posix.ENOMEM => return PosixBindError.SystemResources, - posix.ENOTDIR => return PosixBindError.NotDir, - posix.EROFS => return PosixBindError.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } -} - -const PosixListenError = error{ + EACCES => return error.AccessDenied, + EADDRINUSE => return error.AddressInUse, + EBADF => unreachable, // always a race condition if this error is returned + EINVAL => unreachable, + ENOTSOCK => unreachable, + EADDRNOTAVAIL => return error.AddressNotAvailable, + EFAULT => unreachable, + ELOOP => return error.SymLinkLoop, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOTDIR => return error.NotDir, + EROFS => return error.ReadOnlyFileSystem, + else => |err| return unexpectedErrno(err), + } +} + +const ListenError = error{ /// Another socket is already listening on the same port. /// For Internet domain sockets, the socket referred to by sockfd had not previously /// been bound to an address and, upon attempting to bind it to an ephemeral port, it @@ -2621,24 +1354,22 @@ const PosixListenError = error{ /// The socket is not of a type that supports the listen() operation. OperationNotSupported, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void { - const rc = posix.listen(sockfd, backlog); - const err = posix.getErrno(rc); - switch (err) { +pub fn listen(sockfd: i32, backlog: u32) ListenError!void { + const rc = system.listen(sockfd, backlog); + switch (errno(rc)) { 0 => return, - posix.EADDRINUSE => return PosixListenError.AddressInUse, - posix.EBADF => unreachable, - posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket, - posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported, - else => return unexpectedErrorPosix(err), + EADDRINUSE => return error.AddressInUse, + EBADF => unreachable, + ENOTSOCK => return error.FileDescriptorNotASocket, + EOPNOTSUPP => return error.OperationNotSupported, + else => |err| return unexpectedErrno(err), } } -pub const PosixAcceptError = error{ +pub const AcceptError = error{ ConnectionAborted, /// The per-process limit on the number of open file descriptors has been reached. @@ -2662,66 +1393,66 @@ pub const PosixAcceptError = error{ /// Firewall rules forbid connection. BlockedByFirewall, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { +/// Accept a connection on a socket. `fd` must be opened in blocking mode. +/// See also `accept4_async`. +pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { while (true) { - var sockaddr_size = u32(@sizeOf(posix.sockaddr)); - const rc = posix.accept4(fd, addr, &sockaddr_size, flags); - const err = posix.getErrno(rc); - switch (err) { + var sockaddr_size = u32(@sizeOf(sockaddr)); + const rc = system.accept4(fd, addr, &sockaddr_size, flags); + switch (errno(rc)) { 0 => return @intCast(i32, rc), - posix.EINTR => continue, - else => return unexpectedErrorPosix(err), - - posix.EAGAIN => unreachable, // use posixAsyncAccept for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, - posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, - posix.ENOBUFS => return PosixAcceptError.SystemResources, - posix.ENOMEM => return PosixAcceptError.SystemResources, - posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, - posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, - posix.EPROTO => return PosixAcceptError.ProtocolFailure, - posix.EPERM => return PosixAcceptError.BlockedByFirewall, + EINTR => continue, + else => |err| return unexpectedErrno(err), + + EAGAIN => unreachable, // This function is for blocking only. + EBADF => unreachable, // always a race condition + ECONNABORTED => return error.ConnectionAborted, + EFAULT => unreachable, + EINVAL => unreachable, + EMFILE => return error.ProcessFdQuotaExceeded, + ENFILE => return error.SystemFdQuotaExceeded, + ENOBUFS => return error.SystemResources, + ENOMEM => return error.SystemResources, + ENOTSOCK => return error.FileDescriptorNotASocket, + EOPNOTSUPP => return error.OperationNotSupported, + EPROTO => return error.ProtocolFailure, + EPERM => return error.BlockedByFirewall, } } } +/// This is the same as `accept4` except `fd` is expected to be non-blocking. /// Returns -1 if would block. -pub fn posixAsyncAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { +pub fn accept4_async(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { while (true) { - var sockaddr_size = u32(@sizeOf(posix.sockaddr)); - const rc = posix.accept4(fd, addr, &sockaddr_size, flags); - const err = posix.getErrno(rc); - switch (err) { + var sockaddr_size = u32(@sizeOf(sockaddr)); + const rc = system.accept4(fd, addr, &sockaddr_size, flags); + switch (errno(rc)) { 0 => return @intCast(i32, rc), - posix.EINTR => continue, - else => return unexpectedErrorPosix(err), - - posix.EAGAIN => return -1, - posix.EBADF => unreachable, // always a race condition - posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, - posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, - posix.ENOBUFS => return PosixAcceptError.SystemResources, - posix.ENOMEM => return PosixAcceptError.SystemResources, - posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, - posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, - posix.EPROTO => return PosixAcceptError.ProtocolFailure, - posix.EPERM => return PosixAcceptError.BlockedByFirewall, + EINTR => continue, + else => |err| return unexpectedErrno(err), + + EAGAIN => return -1, + EBADF => unreachable, // always a race condition + ECONNABORTED => return error.ConnectionAborted, + EFAULT => unreachable, + EINVAL => unreachable, + EMFILE => return error.ProcessFdQuotaExceeded, + ENFILE => return error.SystemFdQuotaExceeded, + ENOBUFS => return error.SystemResources, + ENOMEM => return error.SystemResources, + ENOTSOCK => return error.FileDescriptorNotASocket, + EOPNOTSUPP => return error.OperationNotSupported, + EPROTO => return error.ProtocolFailure, + EPERM => return error.BlockedByFirewall, } } } -pub const LinuxEpollCreateError = error{ +pub const EpollCreateError = error{ /// The per-user limit on the number of epoll instances imposed by /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further /// details. @@ -2734,25 +1465,23 @@ pub const LinuxEpollCreateError = error{ /// There was insufficient memory to create the kernel object. SystemResources, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { - const rc = posix.epoll_create1(flags); - const err = posix.getErrno(rc); - switch (err) { +pub fn epoll_create1(flags: u32) EpollCreateError!i32 { + const rc = system.epoll_create1(flags); + switch (errno(rc)) { 0 => return @intCast(i32, rc), - else => return unexpectedErrorPosix(err), + else => |err| return unexpectedErrno(err), - posix.EINVAL => unreachable, - posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded, - posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded, - posix.ENOMEM => return LinuxEpollCreateError.SystemResources, + EINVAL => unreachable, + EMFILE => return error.ProcessFdQuotaExceeded, + ENFILE => return error.SystemFdQuotaExceeded, + ENOMEM => return error.SystemResources, } } -pub const LinuxEpollCtlError = error{ +pub const EpollCtlError = error{ /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered /// with this epoll instance. FileDescriptorAlreadyPresentInSet, @@ -2777,94 +1506,88 @@ pub const LinuxEpollCtlError = error{ /// for example, a regular file or a directory. FileDescriptorIncompatibleWithEpoll, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) LinuxEpollCtlError!void { - const rc = posix.epoll_ctl(epfd, op, fd, event); - const err = posix.getErrno(rc); - switch (err) { +pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: *epoll_event) EpollCtlError!void { + const rc = system.epoll_ctl(epfd, op, fd, event); + switch (errno(rc)) { 0 => return, - else => return unexpectedErrorPosix(err), + else => |err| return unexpectedErrno(err), - posix.EBADF => unreachable, // always a race condition if this happens - posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet, - posix.EINVAL => unreachable, - posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop, - posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered, - posix.ENOMEM => return LinuxEpollCtlError.SystemResources, - posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached, - posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll, + EBADF => unreachable, // always a race condition if this happens + EEXIST => return error.FileDescriptorAlreadyPresentInSet, + EINVAL => unreachable, + ELOOP => return error.OperationCausesCircularLoop, + ENOENT => return error.FileDescriptorNotRegistered, + ENOMEM => return error.SystemResources, + ENOSPC => return error.UserResourceLimitReached, + EPERM => return error.FileDescriptorIncompatibleWithEpoll, } } -pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { +/// Waits for an I/O event on an epoll file descriptor. +/// Returns the number of file descriptors ready for the requested I/O, +/// or zero if no file descriptor became ready during the requested timeout milliseconds. +pub fn epoll_wait(epfd: i32, events: []epoll_event, timeout: i32) usize { while (true) { - const rc = posix.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EINTR => continue, - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, + // TODO get rid of the @intCast + const rc = system.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + EINTR => continue, + EBADF => unreachable, + EFAULT => unreachable, + EINVAL => unreachable, else => unreachable, } } } -pub const LinuxEventFdError = error{ - InvalidFlagValue, +pub const EventFdError = error{ SystemResources, ProcessFdQuotaExceeded, SystemFdQuotaExceeded, - - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 { - const rc = posix.eventfd(initval, flags); - const err = posix.getErrno(rc); - switch (err) { +pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 { + const rc = system.eventfd(initval, flags); + switch (errno(rc)) { 0 => return @intCast(i32, rc), - else => return unexpectedErrorPosix(err), + else => |err| return unexpectedErrno(err), - posix.EINVAL => return LinuxEventFdError.InvalidFlagValue, - posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded, - posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded, - posix.ENODEV => return LinuxEventFdError.SystemResources, - posix.ENOMEM => return LinuxEventFdError.SystemResources, + EINVAL => unreachable, // invalid parameters + EMFILE => return error.ProcessFdQuotaExceeded, + ENFILE => return error.SystemFdQuotaExceeded, + ENODEV => return error.SystemResources, + ENOMEM => return error.SystemResources, } } -pub const PosixGetSockNameError = error{ +pub const GetSockNameError = error{ /// Insufficient resources were available in the system to perform the operation. SystemResources, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr { - var addr: posix.sockaddr = undefined; - var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr); - const rc = posix.getsockname(sockfd, &addr, &addrlen); - const err = posix.getErrno(rc); - switch (err) { +pub fn getsockname(sockfd: i32) GetSockNameError!sockaddr { + var addr: sockaddr = undefined; + var addrlen: socklen_t = @sizeOf(sockaddr); + switch (errno(system.getsockname(sockfd, &addr, &addrlen))) { 0 => return addr, - else => return unexpectedErrorPosix(err), + else => |err| return unexpectedErrno(err), - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ENOTSOCK => unreachable, - posix.ENOBUFS => return PosixGetSockNameError.SystemResources, + EBADF => unreachable, // always a race condition + EFAULT => unreachable, + EINVAL => unreachable, // invalid parameters + ENOTSOCK => unreachable, + ENOBUFS => return error.SystemResources, } } -pub const PosixConnectError = error{ +pub const ConnectError = error{ /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket /// file, or search permission is denied for one of the directories in the path prefix. /// or @@ -2897,632 +1620,828 @@ pub const PosixConnectError = error{ /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. ConnectionTimedOut, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { +/// Initiate a connection on a socket. +/// This is for blocking file descriptors only. +/// For non-blocking, see `connect_async`. +pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void { while (true) { - const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); - const err = posix.getErrno(rc); - switch (err) { + switch (errno(system.connect(sockfd, sock_addr, @sizeOf(sockaddr)))) { 0 => return, - else => return unexpectedErrorPosix(err), - - posix.EACCES => return PosixConnectError.PermissionDenied, - posix.EPERM => return PosixConnectError.PermissionDenied, - posix.EADDRINUSE => return PosixConnectError.AddressInUse, - posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, - posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, - posix.EAGAIN => return PosixConnectError.SystemResources, - posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. - posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, - posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. - posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. - posix.EINTR => continue, - posix.EISCONN => unreachable, // The socket is already connected. - posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, - } + EACCES => return error.PermissionDenied, + EPERM => return error.PermissionDenied, + EADDRINUSE => return error.AddressInUse, + EADDRNOTAVAIL => return error.AddressNotAvailable, + EAFNOSUPPORT => return error.AddressFamilyNotSupported, + EAGAIN => return error.SystemResources, + EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. + EBADF => unreachable, // sockfd is not a valid open file descriptor. + ECONNREFUSED => return error.ConnectionRefused, + EFAULT => unreachable, // The socket structure address is outside the user's address space. + EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. + EINTR => continue, + EISCONN => unreachable, // The socket is already connected. + ENETUNREACH => return error.NetworkUnreachable, + ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. + ETIMEDOUT => return error.ConnectionTimedOut, + else => |err| return unexpectedErrno(err), + } + } +} + +/// Same as `connect` except it is for blocking socket file descriptors. +/// It expects to receive EINPROGRESS`. +pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void { + while (true) { + switch (errno(system.connect(sockfd, sock_addr, @sizeOf(sockaddr)))) { + EINTR => continue, + 0, EINPROGRESS => return, + EACCES => return error.PermissionDenied, + EPERM => return error.PermissionDenied, + EADDRINUSE => return error.AddressInUse, + EADDRNOTAVAIL => return error.AddressNotAvailable, + EAFNOSUPPORT => return error.AddressFamilyNotSupported, + EAGAIN => return error.SystemResources, + EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. + EBADF => unreachable, // sockfd is not a valid open file descriptor. + ECONNREFUSED => return error.ConnectionRefused, + EFAULT => unreachable, // The socket structure address is outside the user's address space. + EISCONN => unreachable, // The socket is already connected. + ENETUNREACH => return error.NetworkUnreachable, + ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. + ETIMEDOUT => return error.ConnectionTimedOut, + else => |err| return unexpectedErrno(err), + } + } +} + +pub fn getsockoptError(sockfd: i32) ConnectError!void { + var err_code: u32 = undefined; + var size: u32 = @sizeOf(u32); + const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size); + assert(size == 4); + switch (errno(rc)) { + 0 => switch (err_code) { + 0 => return, + EACCES => return error.PermissionDenied, + EPERM => return error.PermissionDenied, + EADDRINUSE => return error.AddressInUse, + EADDRNOTAVAIL => return error.AddressNotAvailable, + EAFNOSUPPORT => return error.AddressFamilyNotSupported, + EAGAIN => return error.SystemResources, + EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. + EBADF => unreachable, // sockfd is not a valid open file descriptor. + ECONNREFUSED => return error.ConnectionRefused, + EFAULT => unreachable, // The socket structure address is outside the user's address space. + EISCONN => unreachable, // The socket is already connected. + ENETUNREACH => return error.NetworkUnreachable, + ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. + ETIMEDOUT => return error.ConnectionTimedOut, + else => |err| return unexpectedErrno(err), + }, + EBADF => unreachable, // The argument sockfd is not a valid file descriptor. + EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. + EINVAL => unreachable, + ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. + ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + else => |err| return unexpectedErrno(err), } } -/// Same as posixConnect except it is for blocking socket file descriptors. -/// It expects to receive EINPROGRESS. -pub fn posixConnectAsync(sockfd: i32, sockaddr: *const c_void, len: u32) PosixConnectError!void { +pub fn waitpid(pid: i32, flags: u32) u32 { + // TODO allow implicit pointer cast from *u32 to *c_uint ? + const Status = if (builtin.link_libc) c_uint else u32; + var status: Status = undefined; while (true) { - const rc = posix.connect(sockfd, sockaddr, len); - const err = posix.getErrno(rc); - switch (err) { - 0, posix.EINPROGRESS => return, - else => return unexpectedErrorPosix(err), - - posix.EACCES => return PosixConnectError.PermissionDenied, - posix.EPERM => return PosixConnectError.PermissionDenied, - posix.EADDRINUSE => return PosixConnectError.AddressInUse, - posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, - posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, - posix.EAGAIN => return PosixConnectError.SystemResources, - posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. - posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, - posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. - posix.EINTR => continue, - posix.EISCONN => unreachable, // The socket is already connected. - posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, + switch (errno(system.waitpid(pid, &status, flags))) { + 0 => return @bitCast(u32, status), + EINTR => continue, + ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. + EINVAL => unreachable, // The options argument was invalid + else => unreachable, } } } -pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { - var err_code: i32 = undefined; - var size: u32 = @sizeOf(i32); - const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast([*]u8, &err_code), &size); - assert(size == 4); - const err = posix.getErrno(rc); - switch (err) { - 0 => switch (err_code) { - 0 => return, - else => return unexpectedErrorPosix(err), - - posix.EACCES => return PosixConnectError.PermissionDenied, - posix.EPERM => return PosixConnectError.PermissionDenied, - posix.EADDRINUSE => return PosixConnectError.AddressInUse, - posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, - posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, - posix.EAGAIN => return PosixConnectError.SystemResources, - posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. - posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, - posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. - posix.EISCONN => unreachable, // The socket is already connected. - posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, - }, - else => return unexpectedErrorPosix(err), - posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor. - posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. - posix.EINVAL => unreachable, - posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. +pub const FStatError = error{ + SystemResources, + Unexpected, +}; + +pub fn fstat(fd: fd_t) FStatError!Stat { + var stat: Stat = undefined; + if (darwin.is_the_target) { + switch (darwin.getErrno(darwin.@"fstat$INODE64"(fd, &stat))) { + 0 => return stat, + EBADF => unreachable, // Always a race condition. + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } + } + + switch (errno(system.fstat(fd, &stat))) { + 0 => return stat, + EBADF => unreachable, // Always a race condition. + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), } } -pub const Thread = struct { - data: Data, +pub const KQueueError = error{ + /// The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, - pub const use_pthreads = is_posix and builtin.link_libc; + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, - /// Represents a kernel thread handle. - /// May be an integer or a pointer depending on the platform. - /// On Linux and POSIX, this is the same as Id. - pub const Handle = if (use_pthreads) - c.pthread_t - else switch (builtin.os) { - builtin.Os.linux => i32, - builtin.Os.windows => windows.HANDLE, - else => @compileError("Unsupported OS"), - }; + Unexpected, +}; - /// Represents a unique ID per thread. - /// May be an integer or pointer depending on the platform. - /// On Linux and POSIX, this is the same as Handle. - pub const Id = switch (builtin.os) { - builtin.Os.windows => windows.DWORD, - else => Handle, - }; +pub fn kqueue() KQueueError!i32 { + const rc = system.kqueue(); + switch (errno(rc)) { + 0 => return @intCast(i32, rc), + EMFILE => return error.ProcessFdQuotaExceeded, + ENFILE => return error.SystemFdQuotaExceeded, + else => |err| return unexpectedErrno(err), + } +} - pub const Data = if (use_pthreads) - struct { - handle: Thread.Handle, - mmap_addr: usize, - mmap_len: usize, - } - else switch (builtin.os) { - builtin.Os.linux => struct { - handle: Thread.Handle, - mmap_addr: usize, - mmap_len: usize, - }, - builtin.Os.windows => struct { - handle: Thread.Handle, - alloc_start: *c_void, - heap_handle: windows.HANDLE, - }, - else => @compileError("Unsupported OS"), - }; +pub const KEventError = error{ + /// The process does not have permission to register a filter. + AccessDenied, - /// Returns the ID of the calling thread. - /// Makes a syscall every time the function is called. - /// On Linux and POSIX, this Id is the same as a Handle. - pub fn getCurrentId() Id { - if (use_pthreads) { - return c.pthread_self(); - } else - return switch (builtin.os) { - builtin.Os.linux => linux.gettid(), - builtin.Os.windows => windows.GetCurrentThreadId(), - else => @compileError("Unsupported OS"), - }; - } - - /// Returns the handle of this thread. - /// On Linux and POSIX, this is the same as Id. - /// On Linux, it is possible that the thread spawned with `spawnThread` - /// finishes executing entirely before the clone syscall completes. In this - /// case, this function will return 0 rather than the no-longer-existing thread's - /// pid. - pub fn handle(self: Thread) Handle { - return self.data.handle; - } - - pub fn wait(self: *const Thread) void { - if (use_pthreads) { - const err = c.pthread_join(self.data.handle, null); - switch (err) { - 0 => {}, - posix.EINVAL => unreachable, - posix.ESRCH => unreachable, - posix.EDEADLK => unreachable, - else => unreachable, - } - assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); - } else switch (builtin.os) { - builtin.Os.linux => { - while (true) { - const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst); - if (pid_value == 0) break; - const rc = linux.futex_wait(&self.data.handle, linux.FUTEX_WAIT, pid_value, null); - switch (linux.getErrno(rc)) { - 0 => continue, - posix.EINTR => continue, - posix.EAGAIN => continue, - else => unreachable, - } - } - assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); - }, - builtin.Os.windows => { - assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); - assert(windows.CloseHandle(self.data.handle) != 0); - assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); - }, - else => @compileError("Unsupported OS"), + /// The event could not be found to be modified or deleted. + EventNotFound, + + /// No memory was available to register the event. + SystemResources, + + /// The specified process to attach to does not exist. + ProcessNotFound, + + /// changelist or eventlist had too many items on it. + /// TODO remove this possibility + Overflow, +}; + +pub fn kevent( + kq: i32, + changelist: []const Kevent, + eventlist: []Kevent, + timeout: ?*const timespec, +) KEventError!usize { + while (true) { + const rc = system.kevent( + kq, + changelist.ptr, + try math.cast(c_int, changelist.len), + eventlist.ptr, + try math.cast(c_int, eventlist.len), + timeout, + ); + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + EACCES => return error.AccessDenied, + EFAULT => unreachable, + EBADF => unreachable, // Always a race condition. + EINTR => continue, + EINVAL => unreachable, + ENOENT => return error.EventNotFound, + ENOMEM => return error.SystemResources, + ESRCH => return error.ProcessNotFound, + else => unreachable, } } -}; +} -pub const SpawnThreadError = error{ - /// A system-imposed limit on the number of threads was encountered. - /// There are a number of limits that may trigger this error: - /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), - /// which limits the number of processes and threads for a real - /// user ID, was reached; - /// * the kernel's system-wide limit on the number of processes and - /// threads, /proc/sys/kernel/threads-max, was reached (see - /// proc(5)); - /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was - /// reached (see proc(5)); or - /// * the PID limit (pids.max) imposed by the cgroup "process num‐ - /// ber" (PIDs) controller was reached. - ThreadQuotaExceeded, - - /// The kernel cannot allocate sufficient memory to allocate a task structure - /// for the child, or to copy those parts of the caller's context that need to - /// be copied. +pub const INotifyInitError = error{ + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, SystemResources, + Unexpected, +}; - /// Not enough userland memory to spawn the thread. - OutOfMemory, +/// initialize an inotify instance +pub fn inotify_init1(flags: u32) INotifyInitError!i32 { + const rc = system.inotify_init1(flags); + switch (errno(rc)) { + 0 => return @intCast(i32, rc), + EINVAL => unreachable, + EMFILE => return error.ProcessFdQuotaExceeded, + ENFILE => return error.SystemFdQuotaExceeded, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } +} - /// See https://github.com/ziglang/zig/issues/1396 +pub const INotifyAddWatchError = error{ + AccessDenied, + NameTooLong, + FileNotFound, + SystemResources, + UserResourceLimitReached, Unexpected, }; -/// caller must call wait on the returned thread -/// fn startFn(@typeOf(context)) T -/// where T is u8, noreturn, void, or !void -/// caller must call wait on the returned thread -pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { - if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode"); - // TODO compile-time call graph analysis to determine stack upper bound - // https://github.com/ziglang/zig/issues/157 - const default_stack_size = 8 * 1024 * 1024; - - const Context = @typeOf(context); - comptime assert(@ArgType(@typeOf(startFn), 0) == Context); - - if (builtin.os == builtin.Os.windows) { - const WinThread = struct { - const OuterContext = struct { - thread: Thread, - inner: Context, - }; - extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD { - const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; - switch (@typeId(@typeOf(startFn).ReturnType)) { - builtin.TypeId.Int => { - return startFn(arg); - }, - builtin.TypeId.Void => { - startFn(arg); - return 0; - }, - else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), - } - } - }; - - const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory; - const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); - const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory; - errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); - const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; - const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; - outer_context.* = WinThread.OuterContext{ - .thread = Thread{ - .data = Thread.Data{ - .heap_handle = heap_handle, - .alloc_start = bytes_ptr, - .handle = undefined, - }, - }, - .inner = context, - }; - - const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); - outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - }; - return &outer_context.thread; - } - - const MainFuncs = struct { - extern fn linuxThreadMain(ctx_addr: usize) u8 { - const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; - - switch (@typeId(@typeOf(startFn).ReturnType)) { - builtin.TypeId.Int => { - return startFn(arg); - }, - builtin.TypeId.Void => { - startFn(arg); - return 0; - }, - else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), - } - } - extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { - if (@sizeOf(Context) == 0) { - _ = startFn({}); - return null; - } else { - _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*); - return null; - } - } - }; +/// add a watch to an initialized inotify instance +pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 { + const pathname_c = try toPosixPath(pathname); + return inotify_add_watchC(inotify_fd, &pathname_c, mask); +} - const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; - - var stack_end_offset: usize = undefined; - var thread_start_offset: usize = undefined; - var context_start_offset: usize = undefined; - var tls_start_offset: usize = undefined; - const mmap_len = blk: { - // First in memory will be the stack, which grows downwards. - var l: usize = mem.alignForward(default_stack_size, os.page_size); - stack_end_offset = l; - // Above the stack, so that it can be in the same mmap call, put the Thread object. - l = mem.alignForward(l, @alignOf(Thread)); - thread_start_offset = l; - l += @sizeOf(Thread); - // Next, the Context object. - if (@sizeOf(Context) != 0) { - l = mem.alignForward(l, @alignOf(Context)); - context_start_offset = l; - l += @sizeOf(Context); - } - // Finally, the Thread Local Storage, if any. - if (!Thread.use_pthreads) { - if (linux.tls.tls_image) |tls_img| { - l = mem.alignForward(l, @alignOf(usize)); - tls_start_offset = l; - l += tls_img.alloc_size; - } - } - break :blk l; - }; - const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); - if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory; - errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0); - - const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); - thread_ptr.data.mmap_addr = mmap_addr; - thread_ptr.data.mmap_len = mmap_len; - - var arg: usize = undefined; - if (@sizeOf(Context) != 0) { - arg = mmap_addr + context_start_offset; - const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg)); - context_ptr.* = context; - } - - if (Thread.use_pthreads) { - // use pthreads - var attr: c.pthread_attr_t = undefined; - if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; - defer assert(c.pthread_attr_destroy(&attr) == 0); - - assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0); - - const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); - switch (err) { - 0 => return thread_ptr, - posix.EAGAIN => return SpawnThreadError.SystemResources, - posix.EPERM => unreachable, - posix.EINVAL => unreachable, - else => return unexpectedErrorPosix(@intCast(usize, err)), - } - } else if (builtin.os == builtin.Os.linux) { - var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | - posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | - posix.CLONE_DETACHED; - var newtls: usize = undefined; - if (linux.tls.tls_image) |tls_img| { - newtls = linux.tls.copyTLS(mmap_addr + tls_start_offset); - flags |= posix.CLONE_SETTLS; - } - const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); - const err = posix.getErrno(rc); - switch (err) { - 0 => return thread_ptr, - posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, - posix.EINVAL => unreachable, - posix.ENOMEM => return SpawnThreadError.SystemResources, - posix.ENOSPC => unreachable, - posix.EPERM => unreachable, - posix.EUSERS => unreachable, - else => return unexpectedErrorPosix(err), - } - } else { - @compileError("Unsupported OS"); +/// Same as `inotify_add_watch` except pathname is null-terminated. +pub fn inotify_add_watchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) INotifyAddWatchError!i32 { + const rc = system.inotify_add_watch(inotify_fd, pathname, mask); + switch (errno(rc)) { + 0 => return @intCast(i32, rc), + EACCES => return error.AccessDenied, + EBADF => unreachable, + EFAULT => unreachable, + EINVAL => unreachable, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOSPC => return error.UserResourceLimitReached, + else => |err| return unexpectedErrno(err), } } -pub fn posixWait(pid: i32) i32 { - var status: i32 = undefined; - while (true) { - const err = posix.getErrno(posix.waitpid(pid, &status, 0)); - switch (err) { - 0 => return status, - posix.EINTR => continue, - posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. - posix.EINVAL => unreachable, // The options argument was invalid - else => unreachable, - } +/// remove an existing watch from an inotify instance +pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void { + switch (errno(system.inotify_rm_watch(inotify_fd, wd))) { + 0 => return, + EBADF => unreachable, + EINVAL => unreachable, + else => unreachable, } } -pub fn posixFStat(fd: i32) !posix.Stat { - var stat: posix.Stat = undefined; - const err = posix.getErrno(posix.fstat(fd, &stat)); - if (err > 0) { - return switch (err) { - // We do not make this an error code because if you get EBADF it's always a bug, - // since the fd could have been reused. - posix.EBADF => unreachable, - posix.ENOMEM => error.SystemResources, - else => os.unexpectedErrorPosix(err), - }; +pub const MProtectError = error{ + AccessDenied, + OutOfMemory, + Unexpected, +}; + +/// `memory.len` must be page-aligned. +pub fn mprotect(memory: [*]align(mem.page_size) u8, protection: u32) MProtectError!void { + assert(mem.isAligned(memory.len, mem.page_size)); + switch (errno(system.mprotect(memory.ptr, memory.len, protection))) { + 0 => return, + EINVAL => unreachable, + EACCES => return error.AccessDenied, + ENOMEM => return error.OutOfMemory, + else => return unexpectedErrno(err), } +} + +pub const ForkError = error{ + SystemResources, + Unexpected, +}; - return stat; +pub fn fork() ForkError!pid_t { + const rc = system.fork(); + switch (errno(rc)) { + 0 => return @intCast(pid_t, rc), + EAGAIN => return error.SystemResources, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } } -pub const CpuCountError = error{ +pub const MMapError = error{ + /// The underlying filesystem of the specified file does not support memory mapping. + MemoryMappingNotSupported, + + /// A file descriptor refers to a non-regular file. Or a file mapping was requested, + /// but the file descriptor is not open for reading. Or `MAP_SHARED` was requested + /// and `PROT_WRITE` is set, but the file descriptor is not open in `O_RDWR` mode. + /// Or `PROT_WRITE` is set, but the file is append-only. + AccessDenied, + + /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on + /// a filesystem that was mounted no-exec. + PermissionDenied, + LockedMemoryLimitExceeded, OutOfMemory, + Unexpected, +}; + +/// Map files or devices into memory. +/// Use of a mapped region can result in these signals: +/// `length` must be page-aligned. +/// * SIGSEGV - Attempted write into a region mapped as read-only. +/// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file +pub fn mmap( + ptr: ?[*]align(mem.page_size) u8, + length: usize, + prot: u32, + flags: u32, + fd: fd_t, + offset: isize, +) MMapError![]align(mem.page_size) u8 { + assert(mem.isAligned(length, mem.page_size)); + const err = if (builtin.link_libc) blk: { + const rc = std.c.mmap(ptr, length, prot, flags, fd, offset); + if (rc != MAP_FAILED) return @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, rc))[0..length]; + break :blk @intCast(usize, system._errno().*); + } else blk: { + const rc = system.mmap(ptr, length, prot, flags, fd, offset); + const err = errno(rc); + if (err == 0) return @intToPtr([*]align(mem.page_size) u8, rc)[0..length]; + break :blk err; + }; + switch (err) { + ETXTBSY => return error.AccessDenied, + EACCES => return error.AccessDenied, + EPERM => return error.PermissionDenied, + EAGAIN => return error.LockedMemoryLimitExceeded, + EBADF => unreachable, // Always a race condition. + EOVERFLOW => unreachable, // The number of pages used for length + offset would overflow. + ENODEV => return error.MemoryMappingNotSupported, + EINVAL => unreachable, // Invalid parameters to mmap() + ENOMEM => return error.OutOfMemory, + else => return unexpectedErrno(err), + } +} + +/// Deletes the mappings for the specified address range, causing +/// further references to addresses within the range to generate invalid memory references. +/// Note that while POSIX allows unmapping a region in the middle of an existing mapping, +/// Zig's munmap function does not, for two reasons: +/// * It violates the Zig principle that resource deallocation must succeed. +/// * The Windows function, VirtualFree, has this restriction. +pub fn munmap(memory: []align(mem.page_size) u8) void { + switch (errno(system.munmap(memory.ptr, memory.len))) { + 0 => return, + EINVAL => unreachable, // Invalid parameters. + ENOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping. + else => unreachable, + } +} + +pub const AccessError = error{ PermissionDenied, + FileNotFound, + NameTooLong, + InputOutput, + SystemResources, + BadPathName, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; -pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { - switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { - var count: c_int = undefined; - var count_len: usize = @sizeOf(c_int); - const rc = posix.sysctlbyname(switch (builtin.os) { - builtin.Os.macosx => c"hw.logicalcpu", - else => c"hw.ncpu", - }, @ptrCast(*c_void, &count), &count_len, null, 0); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(usize, count), - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ENOMEM => return CpuCountError.OutOfMemory, - posix.ENOTDIR => unreachable, - posix.EISDIR => unreachable, - posix.ENOENT => unreachable, - posix.EPERM => unreachable, - else => return os.unexpectedErrorPosix(err), - } - }, - builtin.Os.linux => { - const usize_count = 16; - const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); - - var set = try allocator.alloc(usize, usize_count); - defer allocator.free(set); - - while (true) { - const rc = posix.sched_getaffinity(0, set); - const err = posix.getErrno(rc); - switch (err) { - 0 => { - if (rc < set.len * @sizeOf(usize)) { - const result = set[0 .. rc / @sizeOf(usize)]; - var sum: usize = 0; - for (result) |x| { - sum += @popCount(usize, x); - } - return sum; - } else { - set = try allocator.realloc(set, set.len * 2); - continue; - } - }, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EPERM => return CpuCountError.PermissionDenied, - posix.ESRCH => unreachable, - else => return os.unexpectedErrorPosix(err), - } - } - }, - builtin.Os.windows => { - var system_info: windows.SYSTEM_INFO = undefined; - windows.GetSystemInfo(&system_info); - return @intCast(usize, system_info.dwNumberOfProcessors); - }, - else => @compileError("unsupported OS"), +/// check user's permissions for a file +/// TODO currently this assumes `mode` is `F_OK` on Windows. +pub fn access(path: []const u8, mode: u32) AccessError!void { + if (windows.is_the_target) { + const path_w = try windows.sliceToPrefixedFileW(path); + _ = try windows.GetFileAttributesW(&path_w); + return; } + const path_c = try toPosixPath(path); + return accessC(&path_c, mode); } -pub const BsdKQueueError = error{ - /// The per-process limit on the number of open file descriptors has been reached. - ProcessFdQuotaExceeded, +/// Same as `access` except `path` is null-terminated. +pub fn accessC(path: [*]const u8, mode: u32) AccessError!void { + if (windows.is_the_target) { + const path_w = try windows.cStrToPrefixedFileW(path); + _ = try windows.GetFileAttributesW(&path_w); + return; + } + switch (errno(system.access(path, mode))) { + 0 => return, + EACCES => return error.PermissionDenied, + EROFS => return error.PermissionDenied, + ELOOP => return error.PermissionDenied, + ETXTBSY => return error.PermissionDenied, + ENOTDIR => return error.FileNotFound, + ENOENT => return error.FileNotFound, - /// The system-wide limit on the total number of open files has been reached. + ENAMETOOLONG => return error.NameTooLong, + EINVAL => unreachable, + EFAULT => unreachable, + EIO => return error.InputOutput, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } +} + +pub const PipeError = error{ SystemFdQuotaExceeded, + ProcessFdQuotaExceeded, + Unexpected, +}; - /// See https://github.com/ziglang/zig/issues/1396 +/// Creates a unidirectional data channel that can be used for interprocess communication. +pub fn pipe() PipeError![2]fd_t { + var fds: [2]fd_t = undefined; + switch (errno(system.pipe(&fds))) { + 0 => return fds, + EINVAL => unreachable, // Invalid parameters to pipe() + EFAULT => unreachable, // Invalid fds pointer + ENFILE => return error.SystemFdQuotaExceeded, + EMFILE => return error.ProcessFdQuotaExceeded, + else => |err| return unexpectedErrno(err), + } +} + +pub fn pipe2(flags: u32) PipeError![2]fd_t { + var fds: [2]fd_t = undefined; + switch (errno(system.pipe2(&fds, flags))) { + 0 => return fds, + EINVAL => unreachable, // Invalid flags + EFAULT => unreachable, // Invalid fds pointer + ENFILE => return error.SystemFdQuotaExceeded, + EMFILE => return error.ProcessFdQuotaExceeded, + else => |err| return unexpectedErrno(err), + } +} + +pub const SysCtlError = error{ + PermissionDenied, + SystemResources, + NameTooLong, Unexpected, }; -pub fn bsdKQueue() BsdKQueueError!i32 { - const rc = posix.kqueue(); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded, - posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded, - else => return unexpectedErrorPosix(err), +pub fn sysctl( + name: []const c_int, + oldp: ?*c_void, + oldlenp: ?*usize, + newp: ?*c_void, + newlen: usize, +) SysCtlError!void { + const name_len = math.cast(c_uint, name.len) catch return error.NameTooLong; + switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) { + 0 => return, + EFAULT => unreachable, + EPERM => return error.PermissionDenied, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), } } -pub const BsdKEventError = error{ - /// The process does not have permission to register a filter. - AccessDenied, +pub fn sysctlbynameC( + name: [*]const u8, + oldp: ?*c_void, + oldlenp: ?*usize, + newp: ?*c_void, + newlen: usize, +) SysCtlError!void { + switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) { + 0 => return, + EFAULT => unreachable, + EPERM => return error.PermissionDenied, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } +} - /// The event could not be found to be modified or deleted. - EventNotFound, +pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { + switch (errno(system.gettimeofday(tv, tz))) { + 0 => return, + EINVAL => unreachable, + else => unreachable, + } +} - /// No memory was available to register the event. +pub const SeekError = error{ + Unseekable, + Unexpected, +}; + +/// Repositions read/write file offset relative to the beginning. +pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { + if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) { + switch (errno(system.llseek(fd, offset, null, SEEK_SET))) { + 0 => return, + EBADF => unreachable, // always a race condition + EINVAL => return error.Unseekable, + EOVERFLOW => return error.Unseekable, + ESPIPE => return error.Unseekable, + ENXIO => return error.Unseekable, + else => |err| return unexpectedErrno(err), + } + } + if (windows.is_the_target) { + return windows.SetFilePointerEx_BEGIN(fd, offset); + } + const ipos = @bitCast(i64, offset); // the OS treats this as unsigned + switch (errno(system.lseek(fd, ipos, SEEK_SET))) { + 0 => return, + EBADF => unreachable, // always a race condition + EINVAL => return error.Unseekable, + EOVERFLOW => return error.Unseekable, + ESPIPE => return error.Unseekable, + ENXIO => return error.Unseekable, + else => |err| return unexpectedErrno(err), + } +} + +/// Repositions read/write file offset relative to the current offset. +pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { + if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) { + switch (errno(system.llseek(fd, @bitCast(u64, offset), null, SEEK_CUR))) { + 0 => return, + EBADF => unreachable, // always a race condition + EINVAL => return error.Unseekable, + EOVERFLOW => return error.Unseekable, + ESPIPE => return error.Unseekable, + ENXIO => return error.Unseekable, + else => |err| return unexpectedErrno(err), + } + } + if (windows.is_the_target) { + return windows.SetFilePointerEx_CURRENT(fd, offset); + } + switch (errno(system.lseek(fd, offset, SEEK_CUR))) { + 0 => return, + EBADF => unreachable, // always a race condition + EINVAL => return error.Unseekable, + EOVERFLOW => return error.Unseekable, + ESPIPE => return error.Unseekable, + ENXIO => return error.Unseekable, + else => |err| return unexpectedErrno(err), + } +} + +/// Repositions read/write file offset relative to the end. +pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { + if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) { + switch (errno(system.llseek(fd, @bitCast(u64, offset), null, SEEK_END))) { + EBADF => unreachable, // always a race condition + EINVAL => return error.Unseekable, + EOVERFLOW => return error.Unseekable, + ESPIPE => return error.Unseekable, + ENXIO => return error.Unseekable, + else => |err| return unexpectedErrno(err), + } + } + if (windows.is_the_target) { + return windows.SetFilePointerEx_END(fd, offset); + } + switch (errno(system.lseek(fd, offset, SEEK_END))) { + 0 => return, + EBADF => unreachable, // always a race condition + EINVAL => return error.Unseekable, + EOVERFLOW => return error.Unseekable, + ESPIPE => return error.Unseekable, + ENXIO => return error.Unseekable, + else => |err| return unexpectedErrno(err), + } +} + +/// Returns the read/write file offset relative to the beginning. +pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { + if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) { + var result: u64 = undefined; + switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) { + 0 => return result, + EBADF => unreachable, // always a race condition + EINVAL => return error.Unseekable, + EOVERFLOW => return error.Unseekable, + ESPIPE => return error.Unseekable, + ENXIO => return error.Unseekable, + else => |err| return unexpectedErrno(err), + } + } + if (windows.is_the_target) { + return windows.SetFilePointerEx_CURRENT_get(fd); + } + const rc = system.lseek(fd, 0, SEEK_CUR); + switch (errno(rc)) { + 0 => return @bitCast(u64, rc), + EBADF => unreachable, // always a race condition + EINVAL => return error.Unseekable, + EOVERFLOW => return error.Unseekable, + ESPIPE => return error.Unseekable, + ENXIO => return error.Unseekable, + else => |err| return unexpectedErrno(err), + } +} + +pub const RealPathError = error{ + FileNotFound, + AccessDenied, + NameTooLong, + NotSupported, + NotDir, + SymLinkLoop, + InputOutput, + FileTooBig, + IsDir, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NoDevice, SystemResources, + NoSpaceLeft, + FileSystem, + BadPathName, + DeviceBusy, - /// The specified process to attach to does not exist. - ProcessNotFound, + SharingViolation, + PipeBusy, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, + + PathAlreadyExists, + + Unexpected, }; -pub fn bsdKEvent( - kq: i32, - changelist: []const posix.Kevent, - eventlist: []posix.Kevent, - timeout: ?*const posix.timespec, -) BsdKEventError!usize { +/// Return the canonicalized absolute pathname. +/// Expands all symbolic links and resolves references to `.`, `..`, and +/// extra `/` characters in `pathname`. +/// The return value is a slice of `out_buffer`, but not necessarily from the beginning. +/// See also `realpathC` and `realpathW`. +pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { + if (windows.is_the_target) { + const pathname_w = try windows.sliceToPrefixedFileW(pathname); + return realpathW(&pathname_w, out_buffer); + } + const pathname_c = try toPosixPath(pathname); + return realpathC(&pathname_c, out_buffer); +} + +/// Same as `realpath` except `pathname` is null-terminated. +pub fn realpathC(pathname: [*]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { + if (windows.is_the_target) { + const pathname_w = try windows.cStrToPrefixedFileW(pathname); + return realpathW(&pathname_w, out_buffer); + } + if (linux.is_the_target and !builtin.link_libc) { + const fd = try openC(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0); + defer close(fd); + + var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; + const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable; + + return readlinkC(proc_path.ptr, out_buffer); + } + const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) { + EINVAL => unreachable, + EBADF => unreachable, + EFAULT => unreachable, + EACCES => return error.AccessDenied, + ENOENT => return error.FileNotFound, + ENOTSUP => return error.NotSupported, + ENOTDIR => return error.NotDir, + ENAMETOOLONG => return error.NameTooLong, + ELOOP => return error.SymLinkLoop, + EIO => return error.InputOutput, + else => |err| return unexpectedErrno(@intCast(usize, err)), + }; + return mem.toSlice(u8, result_path); +} + +/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded. +pub fn realpathW(pathname: [*]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { + const h_file = try windows.CreateFileW( + pathname, + windows.GENERIC_READ, + windows.FILE_SHARE_READ, + null, + windows.OPEN_EXISTING, + windows.FILE_ATTRIBUTE_NORMAL, + null, + ); + defer windows.CloseHandle(h_file); + + var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined; + const wide_len = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS); + assert(wide_len <= wide_buf.len); + const wide_slice = wide_buf[0..wide_len]; + + // Windows returns \\?\ prepended to the path. + // We strip it to make this function consistent across platforms. + const prefix = []u16{ '\\', '\\', '?', '\\' }; + const start_index = if (mem.startsWith(u16, wide_slice, prefix)) prefix.len else 0; + + // Trust that Windows gives us valid UTF-16LE. + const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice[start_index..]) catch unreachable; + return out_buffer[0..end_index]; +} + +/// Spurious wakeups are possible and no precision of timing is guaranteed. +pub fn nanosleep(seconds: u64, nanoseconds: u64) void { + var req = timespec{ + .tv_sec = math.cast(isize, seconds) catch math.maxInt(isize), + .tv_nsec = math.cast(isize, nanoseconds) catch math.maxInt(isize), + }; + var rem: timespec = undefined; while (true) { - const rc = posix.kevent(kq, changelist, eventlist, timeout); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EACCES => return BsdKEventError.AccessDenied, - posix.EFAULT => unreachable, - posix.EBADF => unreachable, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.ENOENT => return BsdKEventError.EventNotFound, - posix.ENOMEM => return BsdKEventError.SystemResources, - posix.ESRCH => return BsdKEventError.ProcessNotFound, - else => unreachable, + switch (errno(system.nanosleep(&req, &rem))) { + EFAULT => unreachable, + EINVAL => { + // Sometimes Darwin returns EINVAL for no reason. + // We treat it as a spurious wakeup. + return; + }, + EINTR => { + req = rem; + continue; + }, + // This prong handles success as well as unexpected errors. + else => return, } } } -pub fn linuxINotifyInit1(flags: u32) !i32 { - const rc = linux.inotify_init1(flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EINVAL => unreachable, - posix.EMFILE => return error.ProcessFdQuotaExceeded, - posix.ENFILE => return error.SystemFdQuotaExceeded, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), +pub const ClockGetTimeError = error{ + UnsupportedClock, + Unexpected, +}; + +pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { + switch (errno(system.clock_gettime(clk_id, tp))) { + 0 => return, + EFAULT => unreachable, + EINVAL => return error.UnsupportedClock, + else => |err| return unexpectedErrno(err), } } -pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 { - const rc = linux.inotify_add_watch(inotify_fd, pathname, mask); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EACCES => return error.AccessDenied, - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.UserResourceLimitReached, - else => return unexpectedErrorPosix(err), +pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void { + switch (errno(system.clock_getres(clk_id, res))) { + 0 => return, + EFAULT => unreachable, + EINVAL => return error.UnsupportedClock, + else => |err| return unexpectedErrno(err), } } -pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void { - const rc = linux.inotify_rm_watch(inotify_fd, wd); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EBADF => unreachable, - posix.EINVAL => unreachable, - else => unreachable, +pub const SchedGetAffinityError = error{ + PermissionDenied, + Unexpected, +}; + +pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { + var set: cpu_set_t = undefined; + switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) { + 0 => return set, + EFAULT => unreachable, + EINVAL => unreachable, + ESRCH => unreachable, + EPERM => return error.PermissionDenied, + else => |err| return unexpectedErrno(err), } } -pub const MProtectError = error{ - AccessDenied, - OutOfMemory, +/// Used to convert a slice to a null terminated slice on the stack. +/// TODO https://github.com/ziglang/zig/issues/287 +pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 { + var path_with_null: [PATH_MAX]u8 = undefined; + // >= rather than > to make room for the null byte + if (file_path.len >= PATH_MAX) return error.NameTooLong; + mem.copy(u8, &path_with_null, file_path); + path_with_null[file_path.len] = 0; + return path_with_null; +} + +/// Whether or not error.Unexpected will print its value and a stack trace. +/// if this happens the fix is to add the error code to the corresponding +/// switch expression, possibly introduce a new error in the error set, and +/// send a patch to Zig. +pub const unexpected_error_tracing = builtin.mode == .Debug; + +pub const UnexpectedError = error{ + /// 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. Unexpected, }; -/// address and length must be page-aligned -pub fn posixMProtect(address: usize, length: usize, protection: u32) MProtectError!void { - const negative_page_size = @bitCast(usize, -isize(page_size)); - const aligned_address = address & negative_page_size; - const aligned_end = (address + length + page_size - 1) & negative_page_size; - assert(address == aligned_address); - assert(length == aligned_end - aligned_address); - const rc = posix.mprotect(address, length, protection); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EINVAL => unreachable, - posix.EACCES => return error.AccessDenied, - posix.ENOMEM => return error.OutOfMemory, - else => return unexpectedErrorPosix(err), +/// Call this when you made a syscall or something that sets errno +/// and you get an unexpected error. +pub fn unexpectedErrno(err: usize) UnexpectedError { + if (unexpected_error_tracing) { + std.debug.warn("unexpected errno: {}\n", err); + std.debug.dumpCurrentStackTrace(null); } + return error.Unexpected; +} + +test "" { + _ = @import("os/darwin.zig"); + _ = @import("os/freebsd.zig"); + _ = @import("os/linux.zig"); + _ = @import("os/netbsd.zig"); + _ = @import("os/uefi.zig"); + _ = @import("os/wasi.zig"); + _ = @import("os/windows.zig"); + _ = @import("os/zen.zig"); + + _ = @import("os/test.zig"); } diff --git a/std/os/bits.zig b/std/os/bits.zig new file mode 100644 index 0000000000..7b87cc6e4d --- /dev/null +++ b/std/os/bits.zig @@ -0,0 +1,27 @@ +// Platform-dependent types and values that are used along with OS-specific APIs. +// These are imported into `std.c`, `std.os`, and `std.os.linux`. + +const builtin = @import("builtin"); + +pub use switch (builtin.os) { + .macosx, .ios, .tvos, .watchos => @import("bits/darwin.zig"), + .freebsd => @import("bits/freebsd.zig"), + .linux => @import("bits/linux.zig"), + .netbsd => @import("bits/netbsd.zig"), + .wasi => @import("bits/wasi.zig"), + .windows => @import("bits/windows.zig"), + else => struct {}, +}; + +pub const pthread_t = *@OpaqueType(); +pub const FILE = @OpaqueType(); + +pub const iovec = extern struct { + iov_base: [*]u8, + iov_len: usize, +}; + +pub const iovec_const = extern struct { + iov_base: [*]const u8, + iov_len: usize, +}; diff --git a/std/os/bits/darwin.zig b/std/os/bits/darwin.zig new file mode 100644 index 0000000000..a685735da0 --- /dev/null +++ b/std/os/bits/darwin.zig @@ -0,0 +1,1080 @@ +const std = @import("../../std.zig"); +const assert = std.debug.assert; +const maxInt = std.math.maxInt; + +pub const fd_t = c_int; +pub const pid_t = c_int; + +pub const in_port_t = u16; +pub const sa_family_t = u8; +pub const socklen_t = u32; +pub const sockaddr = extern union { + in: sockaddr_in, + in6: sockaddr_in6, +}; +pub const sockaddr_in = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + addr: u32, + zero: [8]u8, +}; +pub const sockaddr_in6 = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + flowinfo: u32, + addr: [16]u8, + scope_id: u32, +}; + +pub const timeval = extern struct { + tv_sec: c_long, + tv_usec: i32, +}; + +pub const timezone = extern struct { + tz_minuteswest: i32, + tz_dsttime: i32, +}; + +pub const mach_timebase_info_data = extern struct { + numer: u32, + denom: u32, +}; + +/// Renamed to Stat to not conflict with the stat function. +pub const Stat = extern struct { + dev: i32, + mode: u16, + nlink: u16, + ino: u64, + uid: u32, + gid: u32, + rdev: i32, + atime: usize, + atimensec: usize, + mtime: usize, + mtimensec: usize, + ctime: usize, + ctimensec: usize, + birthtime: usize, + birthtimensec: usize, + size: i64, + blocks: i64, + blksize: i32, + flags: u32, + gen: u32, + lspare: i32, + qspare: [2]i64, +}; + +pub const timespec = extern struct { + tv_sec: isize, + tv_nsec: isize, +}; + +pub const sigset_t = u32; +pub const empty_sigset = sigset_t(0); + +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name. +pub const Sigaction = extern struct { + handler: extern fn (c_int) void, + sa_mask: sigset_t, + sa_flags: c_int, +}; + +pub const dirent = extern struct { + d_ino: usize, + d_seekoff: usize, + d_reclen: u16, + d_namlen: u16, + d_type: u8, + d_name: u8, // field address is address of first byte of name +}; + +pub const pthread_attr_t = extern struct { + __sig: c_long, + __opaque: [56]u8, +}; + +/// Renamed from `kevent` to `Kevent` to avoid conflict with function name. +pub const Kevent = extern struct { + ident: usize, + filter: i16, + flags: u16, + fflags: u32, + data: isize, + udata: usize, +}; + +// sys/types.h on macos uses #pragma pack(4) so these checks are +// to make sure the struct is laid out the same. These values were +// produced from C code using the offsetof macro. +comptime { + assert(@byteOffsetOf(Kevent, "ident") == 0); + assert(@byteOffsetOf(Kevent, "filter") == 8); + assert(@byteOffsetOf(Kevent, "flags") == 10); + assert(@byteOffsetOf(Kevent, "fflags") == 12); + assert(@byteOffsetOf(Kevent, "data") == 16); + assert(@byteOffsetOf(Kevent, "udata") == 24); +} + +pub const kevent64_s = extern struct { + ident: u64, + filter: i16, + flags: u16, + fflags: u32, + data: i64, + udata: u64, + ext: [2]u64, +}; + +// sys/types.h on macos uses #pragma pack() so these checks are +// to make sure the struct is laid out the same. These values were +// produced from C code using the offsetof macro. +comptime { + assert(@byteOffsetOf(kevent64_s, "ident") == 0); + assert(@byteOffsetOf(kevent64_s, "filter") == 8); + assert(@byteOffsetOf(kevent64_s, "flags") == 10); + assert(@byteOffsetOf(kevent64_s, "fflags") == 12); + assert(@byteOffsetOf(kevent64_s, "data") == 16); + assert(@byteOffsetOf(kevent64_s, "udata") == 24); + assert(@byteOffsetOf(kevent64_s, "ext") == 32); +} + +pub const mach_port_t = c_uint; +pub const clock_serv_t = mach_port_t; +pub const clock_res_t = c_int; +pub const mach_port_name_t = natural_t; +pub const natural_t = c_uint; +pub const mach_timespec_t = extern struct { + tv_sec: c_uint, + tv_nsec: clock_res_t, +}; +pub const kern_return_t = c_int; +pub const host_t = mach_port_t; +pub const CALENDAR_CLOCK = 1; + +pub const PATH_MAX = 1024; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +/// [MC2] no permissions +pub const PROT_NONE = 0x00; + +/// [MC2] pages can be read +pub const PROT_READ = 0x01; + +/// [MC2] pages can be written +pub const PROT_WRITE = 0x02; + +/// [MC2] pages can be executed +pub const PROT_EXEC = 0x04; + +/// allocated from memory, swap space +pub const MAP_ANONYMOUS = 0x1000; + +/// map from file (default) +pub const MAP_FILE = 0x0000; + +/// interpret addr exactly +pub const MAP_FIXED = 0x0010; + +/// region may contain semaphores +pub const MAP_HASSEMAPHORE = 0x0200; + +/// changes are private +pub const MAP_PRIVATE = 0x0002; + +/// share changes +pub const MAP_SHARED = 0x0001; + +/// don't cache pages for this mapping +pub const MAP_NOCACHE = 0x0400; + +/// don't reserve needed swap area +pub const MAP_NORESERVE = 0x0040; +pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); + +/// [XSI] no hang in wait/no child to reap +pub const WNOHANG = 0x00000001; + +/// [XSI] notify on stop, untraced child +pub const WUNTRACED = 0x00000002; + +/// take signal on signal stack +pub const SA_ONSTACK = 0x0001; + +/// restart system on signal return +pub const SA_RESTART = 0x0002; + +/// reset to SIG_DFL when taking signal +pub const SA_RESETHAND = 0x0004; + +/// do not generate SIGCHLD on child stop +pub const SA_NOCLDSTOP = 0x0008; + +/// don't mask the signal we're delivering +pub const SA_NODEFER = 0x0010; + +/// don't keep zombies around +pub const SA_NOCLDWAIT = 0x0020; + +/// signal handler with SA_SIGINFO args +pub const SA_SIGINFO = 0x0040; + +/// do not bounce off kernel's sigtramp +pub const SA_USERTRAMP = 0x0100; + +/// signal handler with SA_SIGINFO args with 64bit regs information +pub const SA_64REGSET = 0x0200; + +pub const O_LARGEFILE = 0x0000; +pub const O_PATH = 0x0000; + +pub const F_OK = 0; +pub const X_OK = 1; +pub const W_OK = 2; +pub const R_OK = 4; + +/// open for reading only +pub const O_RDONLY = 0x0000; + +/// open for writing only +pub const O_WRONLY = 0x0001; + +/// open for reading and writing +pub const O_RDWR = 0x0002; + +/// do not block on open or for data to become available +pub const O_NONBLOCK = 0x0004; + +/// append on each write +pub const O_APPEND = 0x0008; + +/// create file if it does not exist +pub const O_CREAT = 0x0200; + +/// truncate size to 0 +pub const O_TRUNC = 0x0400; + +/// error if O_CREAT and the file exists +pub const O_EXCL = 0x0800; + +/// atomically obtain a shared lock +pub const O_SHLOCK = 0x0010; + +/// atomically obtain an exclusive lock +pub const O_EXLOCK = 0x0020; + +/// do not follow symlinks +pub const O_NOFOLLOW = 0x0100; + +/// allow open of symlinks +pub const O_SYMLINK = 0x200000; + +/// descriptor requested for event notifications only +pub const O_EVTONLY = 0x8000; + +/// mark as close-on-exec +pub const O_CLOEXEC = 0x1000000; + +pub const O_ACCMODE = 3; +pub const O_ALERT = 536870912; +pub const O_ASYNC = 64; +pub const O_DIRECTORY = 1048576; +pub const O_DP_GETRAWENCRYPTED = 1; +pub const O_DP_GETRAWUNENCRYPTED = 2; +pub const O_DSYNC = 4194304; +pub const O_FSYNC = O_SYNC; +pub const O_NOCTTY = 131072; +pub const O_POPUP = 2147483648; +pub const O_SYNC = 128; + +pub const SEEK_SET = 0x0; +pub const SEEK_CUR = 0x1; +pub const SEEK_END = 0x2; + +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; + +/// block specified signal set +pub const SIG_BLOCK = 1; + +/// unblock specified signal set +pub const SIG_UNBLOCK = 2; + +/// set specified signal set +pub const SIG_SETMASK = 3; + +/// hangup +pub const SIGHUP = 1; + +/// interrupt +pub const SIGINT = 2; + +/// quit +pub const SIGQUIT = 3; + +/// illegal instruction (not reset when caught) +pub const SIGILL = 4; + +/// trace trap (not reset when caught) +pub const SIGTRAP = 5; + +/// abort() +pub const SIGABRT = 6; + +/// pollable event ([XSR] generated, not supported) +pub const SIGPOLL = 7; + +/// compatibility +pub const SIGIOT = SIGABRT; + +/// EMT instruction +pub const SIGEMT = 7; + +/// floating point exception +pub const SIGFPE = 8; + +/// kill (cannot be caught or ignored) +pub const SIGKILL = 9; + +/// bus error +pub const SIGBUS = 10; + +/// segmentation violation +pub const SIGSEGV = 11; + +/// bad argument to system call +pub const SIGSYS = 12; + +/// write on a pipe with no one to read it +pub const SIGPIPE = 13; + +/// alarm clock +pub const SIGALRM = 14; + +/// software termination signal from kill +pub const SIGTERM = 15; + +/// urgent condition on IO channel +pub const SIGURG = 16; + +/// sendable stop signal not from tty +pub const SIGSTOP = 17; + +/// stop signal from tty +pub const SIGTSTP = 18; + +/// continue a stopped process +pub const SIGCONT = 19; + +/// to parent on child stop or exit +pub const SIGCHLD = 20; + +/// to readers pgrp upon background tty read +pub const SIGTTIN = 21; + +/// like TTIN for output if (tp->t_local<OSTOP) +pub const SIGTTOU = 22; + +/// input/output possible signal +pub const SIGIO = 23; + +/// exceeded CPU time limit +pub const SIGXCPU = 24; + +/// exceeded file size limit +pub const SIGXFSZ = 25; + +/// virtual time alarm +pub const SIGVTALRM = 26; + +/// profiling time alarm +pub const SIGPROF = 27; + +/// window size changes +pub const SIGWINCH = 28; + +/// information request +pub const SIGINFO = 29; + +/// user defined signal 1 +pub const SIGUSR1 = 30; + +/// user defined signal 2 +pub const SIGUSR2 = 31; + +/// no flag value +pub const KEVENT_FLAG_NONE = 0x000; + +/// immediate timeout +pub const KEVENT_FLAG_IMMEDIATE = 0x001; + +/// output events only include change +pub const KEVENT_FLAG_ERROR_EVENTS = 0x002; + +/// add event to kq (implies enable) +pub const EV_ADD = 0x0001; + +/// delete event from kq +pub const EV_DELETE = 0x0002; + +/// enable event +pub const EV_ENABLE = 0x0004; + +/// disable event (not reported) +pub const EV_DISABLE = 0x0008; + +/// only report one occurrence +pub const EV_ONESHOT = 0x0010; + +/// clear event state after reporting +pub const EV_CLEAR = 0x0020; + +/// force immediate event output +/// ... with or without EV_ERROR +/// ... use KEVENT_FLAG_ERROR_EVENTS +/// on syscalls supporting flags +pub const EV_RECEIPT = 0x0040; + +/// disable event after reporting +pub const EV_DISPATCH = 0x0080; + +/// unique kevent per udata value +pub const EV_UDATA_SPECIFIC = 0x0100; + +/// ... in combination with EV_DELETE +/// will defer delete until udata-specific +/// event enabled. EINPROGRESS will be +/// returned to indicate the deferral +pub const EV_DISPATCH2 = EV_DISPATCH | EV_UDATA_SPECIFIC; + +/// report that source has vanished +/// ... only valid with EV_DISPATCH2 +pub const EV_VANISHED = 0x0200; + +/// reserved by system +pub const EV_SYSFLAGS = 0xF000; + +/// filter-specific flag +pub const EV_FLAG0 = 0x1000; + +/// filter-specific flag +pub const EV_FLAG1 = 0x2000; + +/// EOF detected +pub const EV_EOF = 0x8000; + +/// error, data contains errno +pub const EV_ERROR = 0x4000; + +pub const EV_POLL = EV_FLAG0; +pub const EV_OOBAND = EV_FLAG1; + +pub const EVFILT_READ = -1; +pub const EVFILT_WRITE = -2; + +/// attached to aio requests +pub const EVFILT_AIO = -3; + +/// attached to vnodes +pub const EVFILT_VNODE = -4; + +/// attached to struct proc +pub const EVFILT_PROC = -5; + +/// attached to struct proc +pub const EVFILT_SIGNAL = -6; + +/// timers +pub const EVFILT_TIMER = -7; + +/// Mach portsets +pub const EVFILT_MACHPORT = -8; + +/// Filesystem events +pub const EVFILT_FS = -9; + +/// User events +pub const EVFILT_USER = -10; + +/// Virtual memory events +pub const EVFILT_VM = -12; + +/// Exception events +pub const EVFILT_EXCEPT = -15; + +pub const EVFILT_SYSCOUNT = 17; + +/// On input, NOTE_TRIGGER causes the event to be triggered for output. +pub const NOTE_TRIGGER = 0x01000000; + +/// ignore input fflags +pub const NOTE_FFNOP = 0x00000000; + +/// and fflags +pub const NOTE_FFAND = 0x40000000; + +/// or fflags +pub const NOTE_FFOR = 0x80000000; + +/// copy fflags +pub const NOTE_FFCOPY = 0xc0000000; + +/// mask for operations +pub const NOTE_FFCTRLMASK = 0xc0000000; +pub const NOTE_FFLAGSMASK = 0x00ffffff; + +/// low water mark +pub const NOTE_LOWAT = 0x00000001; + +/// OOB data +pub const NOTE_OOB = 0x00000002; + +/// vnode was removed +pub const NOTE_DELETE = 0x00000001; + +/// data contents changed +pub const NOTE_WRITE = 0x00000002; + +/// size increased +pub const NOTE_EXTEND = 0x00000004; + +/// attributes changed +pub const NOTE_ATTRIB = 0x00000008; + +/// link count changed +pub const NOTE_LINK = 0x00000010; + +/// vnode was renamed +pub const NOTE_RENAME = 0x00000020; + +/// vnode access was revoked +pub const NOTE_REVOKE = 0x00000040; + +/// No specific vnode event: to test for EVFILT_READ activation +pub const NOTE_NONE = 0x00000080; + +/// vnode was unlocked by flock(2) +pub const NOTE_FUNLOCK = 0x00000100; + +/// process exited +pub const NOTE_EXIT = 0x80000000; + +/// process forked +pub const NOTE_FORK = 0x40000000; + +/// process exec'd +pub const NOTE_EXEC = 0x20000000; + +/// shared with EVFILT_SIGNAL +pub const NOTE_SIGNAL = 0x08000000; + +/// exit status to be returned, valid for child process only +pub const NOTE_EXITSTATUS = 0x04000000; + +/// provide details on reasons for exit +pub const NOTE_EXIT_DETAIL = 0x02000000; + +/// mask for signal & exit status +pub const NOTE_PDATAMASK = 0x000fffff; +pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); + +pub const NOTE_EXIT_DETAIL_MASK = 0x00070000; +pub const NOTE_EXIT_DECRYPTFAIL = 0x00010000; +pub const NOTE_EXIT_MEMORY = 0x00020000; +pub const NOTE_EXIT_CSERROR = 0x00040000; + +/// will react on memory pressure +pub const NOTE_VM_PRESSURE = 0x80000000; + +/// will quit on memory pressure, possibly after cleaning up dirty state +pub const NOTE_VM_PRESSURE_TERMINATE = 0x40000000; + +/// will quit immediately on memory pressure +pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000; + +/// there was an error +pub const NOTE_VM_ERROR = 0x10000000; + +/// data is seconds +pub const NOTE_SECONDS = 0x00000001; + +/// data is microseconds +pub const NOTE_USECONDS = 0x00000002; + +/// data is nanoseconds +pub const NOTE_NSECONDS = 0x00000004; + +/// absolute timeout +pub const NOTE_ABSOLUTE = 0x00000008; + +/// ext[1] holds leeway for power aware timers +pub const NOTE_LEEWAY = 0x00000010; + +/// system does minimal timer coalescing +pub const NOTE_CRITICAL = 0x00000020; + +/// system does maximum timer coalescing +pub const NOTE_BACKGROUND = 0x00000040; +pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080; + +/// data is mach absolute time units +pub const NOTE_MACHTIME = 0x00000100; + +pub const AF_UNSPEC = 0; +pub const AF_LOCAL = 1; +pub const AF_UNIX = AF_LOCAL; +pub const AF_INET = 2; +pub const AF_SYS_CONTROL = 2; +pub const AF_IMPLINK = 3; +pub const AF_PUP = 4; +pub const AF_CHAOS = 5; +pub const AF_NS = 6; +pub const AF_ISO = 7; +pub const AF_OSI = AF_ISO; +pub const AF_ECMA = 8; +pub const AF_DATAKIT = 9; +pub const AF_CCITT = 10; +pub const AF_SNA = 11; +pub const AF_DECnet = 12; +pub const AF_DLI = 13; +pub const AF_LAT = 14; +pub const AF_HYLINK = 15; +pub const AF_APPLETALK = 16; +pub const AF_ROUTE = 17; +pub const AF_LINK = 18; +pub const AF_XTP = 19; +pub const AF_COIP = 20; +pub const AF_CNT = 21; +pub const AF_RTIP = 22; +pub const AF_IPX = 23; +pub const AF_SIP = 24; +pub const AF_PIP = 25; +pub const AF_ISDN = 28; +pub const AF_E164 = AF_ISDN; +pub const AF_KEY = 29; +pub const AF_INET6 = 30; +pub const AF_NATM = 31; +pub const AF_SYSTEM = 32; +pub const AF_NETBIOS = 33; +pub const AF_PPP = 34; +pub const AF_MAX = 40; + +pub const PF_UNSPEC = AF_UNSPEC; +pub const PF_LOCAL = AF_LOCAL; +pub const PF_UNIX = PF_LOCAL; +pub const PF_INET = AF_INET; +pub const PF_IMPLINK = AF_IMPLINK; +pub const PF_PUP = AF_PUP; +pub const PF_CHAOS = AF_CHAOS; +pub const PF_NS = AF_NS; +pub const PF_ISO = AF_ISO; +pub const PF_OSI = AF_ISO; +pub const PF_ECMA = AF_ECMA; +pub const PF_DATAKIT = AF_DATAKIT; +pub const PF_CCITT = AF_CCITT; +pub const PF_SNA = AF_SNA; +pub const PF_DECnet = AF_DECnet; +pub const PF_DLI = AF_DLI; +pub const PF_LAT = AF_LAT; +pub const PF_HYLINK = AF_HYLINK; +pub const PF_APPLETALK = AF_APPLETALK; +pub const PF_ROUTE = AF_ROUTE; +pub const PF_LINK = AF_LINK; +pub const PF_XTP = AF_XTP; +pub const PF_COIP = AF_COIP; +pub const PF_CNT = AF_CNT; +pub const PF_SIP = AF_SIP; +pub const PF_IPX = AF_IPX; +pub const PF_RTIP = AF_RTIP; +pub const PF_PIP = AF_PIP; +pub const PF_ISDN = AF_ISDN; +pub const PF_KEY = AF_KEY; +pub const PF_INET6 = AF_INET6; +pub const PF_NATM = AF_NATM; +pub const PF_SYSTEM = AF_SYSTEM; +pub const PF_NETBIOS = AF_NETBIOS; +pub const PF_PPP = AF_PPP; +pub const PF_MAX = AF_MAX; + +pub const SYSPROTO_EVENT = 1; +pub const SYSPROTO_CONTROL = 2; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; +pub const SOCK_MAXADDRLEN = 255; + +pub const IPPROTO_ICMP = 1; +pub const IPPROTO_ICMPV6 = 58; +pub const IPPROTO_TCP = 6; +pub const IPPROTO_UDP = 17; +pub const IPPROTO_IP = 0; +pub const IPPROTO_IPV6 = 41; + +fn wstatus(x: u32) u32 { + return x & 0o177; +} +const wstopped = 0o177; +pub fn WEXITSTATUS(x: u32) u32 { + return x >> 8; +} +pub fn WTERMSIG(x: u32) u32 { + return wstatus(x); +} +pub fn WSTOPSIG(x: u32) u32 { + return x >> 8; +} +pub fn WIFEXITED(x: u32) bool { + return wstatus(x) == 0; +} +pub fn WIFSTOPPED(x: u32) bool { + return wstatus(x) == wstopped and WSTOPSIG(x) != 0x13; +} +pub fn WIFSIGNALED(x: u32) bool { + return wstatus(x) != wstopped and wstatus(x) != 0; +} + +/// Operation not permitted +pub const EPERM = 1; + +/// No such file or directory +pub const ENOENT = 2; + +/// No such process +pub const ESRCH = 3; + +/// Interrupted system call +pub const EINTR = 4; + +/// Input/output error +pub const EIO = 5; + +/// Device not configured +pub const ENXIO = 6; + +/// Argument list too long +pub const E2BIG = 7; + +/// Exec format error +pub const ENOEXEC = 8; + +/// Bad file descriptor +pub const EBADF = 9; + +/// No child processes +pub const ECHILD = 10; + +/// Resource deadlock avoided +pub const EDEADLK = 11; + +/// Cannot allocate memory +pub const ENOMEM = 12; + +/// Permission denied +pub const EACCES = 13; + +/// Bad address +pub const EFAULT = 14; + +/// Block device required +pub const ENOTBLK = 15; + +/// Device / Resource busy +pub const EBUSY = 16; + +/// File exists +pub const EEXIST = 17; + +/// Cross-device link +pub const EXDEV = 18; + +/// Operation not supported by device +pub const ENODEV = 19; + +/// Not a directory +pub const ENOTDIR = 20; + +/// Is a directory +pub const EISDIR = 21; + +/// Invalid argument +pub const EINVAL = 22; + +/// Too many open files in system +pub const ENFILE = 23; + +/// Too many open files +pub const EMFILE = 24; + +/// Inappropriate ioctl for device +pub const ENOTTY = 25; + +/// Text file busy +pub const ETXTBSY = 26; + +/// File too large +pub const EFBIG = 27; + +/// No space left on device +pub const ENOSPC = 28; + +/// Illegal seek +pub const ESPIPE = 29; + +/// Read-only file system +pub const EROFS = 30; + +/// Too many links +pub const EMLINK = 31; +/// Broken pipe + +// math software +pub const EPIPE = 32; + +/// Numerical argument out of domain +pub const EDOM = 33; +/// Result too large + +// non-blocking and interrupt i/o +pub const ERANGE = 34; + +/// Resource temporarily unavailable +pub const EAGAIN = 35; + +/// Operation would block +pub const EWOULDBLOCK = EAGAIN; + +/// Operation now in progress +pub const EINPROGRESS = 36; +/// Operation already in progress + +// ipc/network software -- argument errors +pub const EALREADY = 37; + +/// Socket operation on non-socket +pub const ENOTSOCK = 38; + +/// Destination address required +pub const EDESTADDRREQ = 39; + +/// Message too long +pub const EMSGSIZE = 40; + +/// Protocol wrong type for socket +pub const EPROTOTYPE = 41; + +/// Protocol not available +pub const ENOPROTOOPT = 42; + +/// Protocol not supported +pub const EPROTONOSUPPORT = 43; + +/// Socket type not supported +pub const ESOCKTNOSUPPORT = 44; + +/// Operation not supported +pub const ENOTSUP = 45; + +/// Protocol family not supported +pub const EPFNOSUPPORT = 46; + +/// Address family not supported by protocol family +pub const EAFNOSUPPORT = 47; + +/// Address already in use +pub const EADDRINUSE = 48; +/// Can't assign requested address + +// ipc/network software -- operational errors +pub const EADDRNOTAVAIL = 49; + +/// Network is down +pub const ENETDOWN = 50; + +/// Network is unreachable +pub const ENETUNREACH = 51; + +/// Network dropped connection on reset +pub const ENETRESET = 52; + +/// Software caused connection abort +pub const ECONNABORTED = 53; + +/// Connection reset by peer +pub const ECONNRESET = 54; + +/// No buffer space available +pub const ENOBUFS = 55; + +/// Socket is already connected +pub const EISCONN = 56; + +/// Socket is not connected +pub const ENOTCONN = 57; + +/// Can't send after socket shutdown +pub const ESHUTDOWN = 58; + +/// Too many references: can't splice +pub const ETOOMANYREFS = 59; + +/// Operation timed out +pub const ETIMEDOUT = 60; + +/// Connection refused +pub const ECONNREFUSED = 61; + +/// Too many levels of symbolic links +pub const ELOOP = 62; + +/// File name too long +pub const ENAMETOOLONG = 63; + +/// Host is down +pub const EHOSTDOWN = 64; + +/// No route to host +pub const EHOSTUNREACH = 65; +/// Directory not empty + +// quotas & mush +pub const ENOTEMPTY = 66; + +/// Too many processes +pub const EPROCLIM = 67; + +/// Too many users +pub const EUSERS = 68; +/// Disc quota exceeded + +// Network File System +pub const EDQUOT = 69; + +/// Stale NFS file handle +pub const ESTALE = 70; + +/// Too many levels of remote in path +pub const EREMOTE = 71; + +/// RPC struct is bad +pub const EBADRPC = 72; + +/// RPC version wrong +pub const ERPCMISMATCH = 73; + +/// RPC prog. not avail +pub const EPROGUNAVAIL = 74; + +/// Program version wrong +pub const EPROGMISMATCH = 75; + +/// Bad procedure for program +pub const EPROCUNAVAIL = 76; + +/// No locks available +pub const ENOLCK = 77; + +/// Function not implemented +pub const ENOSYS = 78; + +/// Inappropriate file type or format +pub const EFTYPE = 79; + +/// Authentication error +pub const EAUTH = 80; +/// Need authenticator + +// Intelligent device errors +pub const ENEEDAUTH = 81; + +/// Device power is off +pub const EPWROFF = 82; + +/// Device error, e.g. paper out +pub const EDEVERR = 83; +/// Value too large to be stored in data type + +// Program loading errors +pub const EOVERFLOW = 84; + +/// Bad executable +pub const EBADEXEC = 85; + +/// Bad CPU type in executable +pub const EBADARCH = 86; + +/// Shared library version mismatch +pub const ESHLIBVERS = 87; + +/// Malformed Macho file +pub const EBADMACHO = 88; + +/// Operation canceled +pub const ECANCELED = 89; + +/// Identifier removed +pub const EIDRM = 90; + +/// No message of desired type +pub const ENOMSG = 91; + +/// Illegal byte sequence +pub const EILSEQ = 92; + +/// Attribute not found +pub const ENOATTR = 93; + +/// Bad message +pub const EBADMSG = 94; + +/// Reserved +pub const EMULTIHOP = 95; + +/// No message available on STREAM +pub const ENODATA = 96; + +/// Reserved +pub const ENOLINK = 97; + +/// No STREAM resources +pub const ENOSR = 98; + +/// Not a STREAM +pub const ENOSTR = 99; + +/// Protocol error +pub const EPROTO = 100; + +/// STREAM ioctl timeout +pub const ETIME = 101; + +/// No such policy registered +pub const ENOPOLICY = 103; + +/// State not recoverable +pub const ENOTRECOVERABLE = 104; + +/// Previous owner died +pub const EOWNERDEAD = 105; + +/// Interface output queue is full +pub const EQFULL = 106; + +/// Must be equal largest errno +pub const ELAST = 106; diff --git a/std/os/bits/freebsd.zig b/std/os/bits/freebsd.zig new file mode 100644 index 0000000000..1925b75f3f --- /dev/null +++ b/std/os/bits/freebsd.zig @@ -0,0 +1,837 @@ +const std = @import("../../std.zig"); +const maxInt = std.math.maxInt; + +pub const fd_t = c_int; +pub const pid_t = c_int; + +/// Renamed from `kevent` to `Kevent` to avoid conflict with function name. +pub const Kevent = extern struct { + ident: usize, + filter: i16, + flags: u16, + fflags: u32, + data: i64, + udata: usize, + // TODO ext +}; + +pub const pthread_attr_t = extern struct { + __size: [56]u8, + __align: c_long, +}; + +pub const msghdr = extern struct { + /// optional address + msg_name: ?*sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + /// optional address + msg_name: ?*const sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec_const, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + +pub const Stat = extern struct { + dev: u64, + ino: u64, + nlink: usize, + + mode: u16, + __pad0: u16, + uid: u32, + gid: u32, + __pad1: u32, + rdev: u64, + + atim: timespec, + mtim: timespec, + ctim: timespec, + birthtim: timespec, + + size: i64, + blocks: i64, + blksize: isize, + flags: u32, + gen: u64, + __spare: [10]u64, +}; + +pub const timespec = extern struct { + tv_sec: isize, + tv_nsec: isize, +}; + +pub const dirent = extern struct { + d_fileno: usize, + d_off: i64, + d_reclen: u16, + d_type: u8, + d_pad0: u8, + d_namlen: u16, + d_pad1: u16, + d_name: [256]u8, +}; + +pub const in_port_t = u16; +pub const sa_family_t = u16; + +pub const sockaddr = extern union { + in: sockaddr_in, + in6: sockaddr_in6, +}; + +pub const sockaddr_in = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + addr: [16]u8, + zero: [8]u8, +}; + +pub const sockaddr_in6 = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + flowinfo: u32, + addr: [16]u8, + scope_id: u32, +}; + +pub const CTL_KERN = 1; +pub const CTL_DEBUG = 5; + +pub const KERN_PROC = 14; // struct: process entries +pub const KERN_PROC_PATHNAME = 12; // path to executable + +pub const PATH_MAX = 1024; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +pub const PROT_NONE = 0; +pub const PROT_READ = 1; +pub const PROT_WRITE = 2; +pub const PROT_EXEC = 4; + +pub const CLOCK_REALTIME = 0; +pub const CLOCK_VIRTUAL = 1; +pub const CLOCK_PROF = 2; +pub const CLOCK_MONOTONIC = 4; +pub const CLOCK_UPTIME = 5; +pub const CLOCK_UPTIME_PRECISE = 7; +pub const CLOCK_UPTIME_FAST = 8; +pub const CLOCK_REALTIME_PRECISE = 9; +pub const CLOCK_REALTIME_FAST = 10; +pub const CLOCK_MONOTONIC_PRECISE = 11; +pub const CLOCK_MONOTONIC_FAST = 12; +pub const CLOCK_SECOND = 13; +pub const CLOCK_THREAD_CPUTIME_ID = 14; +pub const CLOCK_PROCESS_CPUTIME_ID = 15; + +pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); +pub const MAP_SHARED = 0x0001; +pub const MAP_PRIVATE = 0x0002; +pub const MAP_FIXED = 0x0010; +pub const MAP_STACK = 0x0400; +pub const MAP_NOSYNC = 0x0800; +pub const MAP_ANON = 0x1000; +pub const MAP_ANONYMOUS = MAP_ANON; +pub const MAP_FILE = 0; +pub const MAP_NORESERVE = 0; + +pub const MAP_GUARD = 0x00002000; +pub const MAP_EXCL = 0x00004000; +pub const MAP_NOCORE = 0x00020000; +pub const MAP_PREFAULT_READ = 0x00040000; +pub const MAP_32BIT = 0x00080000; + +pub const WNOHANG = 1; +pub const WUNTRACED = 2; +pub const WSTOPPED = WUNTRACED; +pub const WCONTINUED = 4; +pub const WNOWAIT = 8; +pub const WEXITED = 16; +pub const WTRAPPED = 32; + +pub const SA_ONSTACK = 0x0001; +pub const SA_RESTART = 0x0002; +pub const SA_RESETHAND = 0x0004; +pub const SA_NOCLDSTOP = 0x0008; +pub const SA_NODEFER = 0x0010; +pub const SA_NOCLDWAIT = 0x0020; +pub const SA_SIGINFO = 0x0040; + +pub const SIGHUP = 1; +pub const SIGINT = 2; +pub const SIGQUIT = 3; +pub const SIGILL = 4; +pub const SIGTRAP = 5; +pub const SIGABRT = 6; +pub const SIGIOT = SIGABRT; +pub const SIGEMT = 7; +pub const SIGFPE = 8; +pub const SIGKILL = 9; +pub const SIGBUS = 10; +pub const SIGSEGV = 11; +pub const SIGSYS = 12; +pub const SIGPIPE = 13; +pub const SIGALRM = 14; +pub const SIGTERM = 15; +pub const SIGURG = 16; +pub const SIGSTOP = 17; +pub const SIGTSTP = 18; +pub const SIGCONT = 19; +pub const SIGCHLD = 20; +pub const SIGTTIN = 21; +pub const SIGTTOU = 22; +pub const SIGIO = 23; +pub const SIGXCPU = 24; +pub const SIGXFSZ = 25; +pub const SIGVTALRM = 26; +pub const SIGPROF = 27; +pub const SIGWINCH = 28; +pub const SIGINFO = 29; +pub const SIGUSR1 = 30; +pub const SIGUSR2 = 31; +pub const SIGTHR = 32; +pub const SIGLWP = SIGTHR; +pub const SIGLIBRT = 33; + +pub const SIGRTMIN = 65; +pub const SIGRTMAX = 126; + +// access function +pub const F_OK = 0; // test for existence of file +pub const X_OK = 1; // test for execute or search permission +pub const W_OK = 2; // test for write permission +pub const R_OK = 4; // test for read permission + +pub const O_RDONLY = 0x0000; +pub const O_WRONLY = 0x0001; +pub const O_RDWR = 0x0002; +pub const O_ACCMODE = 0x0003; + +pub const O_CREAT = 0x0200; +pub const O_EXCL = 0x0800; +pub const O_NOCTTY = 0x8000; +pub const O_TRUNC = 0x0400; +pub const O_APPEND = 0x0008; +pub const O_NONBLOCK = 0x0004; +pub const O_DSYNC = 0o10000; +pub const O_SYNC = 0x0080; +pub const O_RSYNC = 0o4010000; +pub const O_DIRECTORY = 0o200000; +pub const O_NOFOLLOW = 0x0100; +pub const O_CLOEXEC = 0x00100000; + +pub const O_ASYNC = 0x0040; +pub const O_DIRECT = 0x00010000; +pub const O_LARGEFILE = 0; +pub const O_NOATIME = 0o1000000; +pub const O_PATH = 0o10000000; +pub const O_TMPFILE = 0o20200000; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_SETOWN = 8; +pub const F_GETOWN = 9; +pub const F_SETSIG = 10; +pub const F_GETSIG = 11; + +pub const F_GETLK = 5; +pub const F_SETLK = 6; +pub const F_SETLKW = 7; + +pub const F_SETOWN_EX = 15; +pub const F_GETOWN_EX = 16; + +pub const F_GETOWNER_UIDS = 17; + +pub const SEEK_SET = 0; +pub const SEEK_CUR = 1; +pub const SEEK_END = 2; + +pub const SIG_BLOCK = 1; +pub const SIG_UNBLOCK = 2; +pub const SIG_SETMASK = 3; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; + +pub const SOCK_CLOEXEC = 0x10000000; +pub const SOCK_NONBLOCK = 0x20000000; + +pub const PROTO_ip = 0o000; +pub const PROTO_icmp = 0o001; +pub const PROTO_igmp = 0o002; +pub const PROTO_ggp = 0o003; +pub const PROTO_ipencap = 0o004; +pub const PROTO_st = 0o005; +pub const PROTO_tcp = 0o006; +pub const PROTO_egp = 0o010; +pub const PROTO_pup = 0o014; +pub const PROTO_udp = 0o021; +pub const PROTO_hmp = 0o024; +pub const PROTO_xns_idp = 0o026; +pub const PROTO_rdp = 0o033; +pub const PROTO_iso_tp4 = 0o035; +pub const PROTO_xtp = 0o044; +pub const PROTO_ddp = 0o045; +pub const PROTO_idpr_cmtp = 0o046; +pub const PROTO_ipv6 = 0o051; +pub const PROTO_ipv6_route = 0o053; +pub const PROTO_ipv6_frag = 0o054; +pub const PROTO_idrp = 0o055; +pub const PROTO_rsvp = 0o056; +pub const PROTO_gre = 0o057; +pub const PROTO_esp = 0o062; +pub const PROTO_ah = 0o063; +pub const PROTO_skip = 0o071; +pub const PROTO_ipv6_icmp = 0o072; +pub const PROTO_ipv6_nonxt = 0o073; +pub const PROTO_ipv6_opts = 0o074; +pub const PROTO_rspf = 0o111; +pub const PROTO_vmtp = 0o121; +pub const PROTO_ospf = 0o131; +pub const PROTO_ipip = 0o136; +pub const PROTO_encap = 0o142; +pub const PROTO_pim = 0o147; +pub const PROTO_raw = 0o377; + +pub const PF_UNSPEC = 0; +pub const PF_LOCAL = 1; +pub const PF_UNIX = PF_LOCAL; +pub const PF_FILE = PF_LOCAL; +pub const PF_INET = 2; +pub const PF_AX25 = 3; +pub const PF_IPX = 4; +pub const PF_APPLETALK = 5; +pub const PF_NETROM = 6; +pub const PF_BRIDGE = 7; +pub const PF_ATMPVC = 8; +pub const PF_X25 = 9; +pub const PF_INET6 = 10; +pub const PF_ROSE = 11; +pub const PF_DECnet = 12; +pub const PF_NETBEUI = 13; +pub const PF_SECURITY = 14; +pub const PF_KEY = 15; +pub const PF_NETLINK = 16; +pub const PF_ROUTE = PF_NETLINK; +pub const PF_PACKET = 17; +pub const PF_ASH = 18; +pub const PF_ECONET = 19; +pub const PF_ATMSVC = 20; +pub const PF_RDS = 21; +pub const PF_SNA = 22; +pub const PF_IRDA = 23; +pub const PF_PPPOX = 24; +pub const PF_WANPIPE = 25; +pub const PF_LLC = 26; +pub const PF_IB = 27; +pub const PF_MPLS = 28; +pub const PF_CAN = 29; +pub const PF_TIPC = 30; +pub const PF_BLUETOOTH = 31; +pub const PF_IUCV = 32; +pub const PF_RXRPC = 33; +pub const PF_ISDN = 34; +pub const PF_PHONET = 35; +pub const PF_IEEE802154 = 36; +pub const PF_CAIF = 37; +pub const PF_ALG = 38; +pub const PF_NFC = 39; +pub const PF_VSOCK = 40; +pub const PF_MAX = 41; + +pub const AF_UNSPEC = PF_UNSPEC; +pub const AF_LOCAL = PF_LOCAL; +pub const AF_UNIX = AF_LOCAL; +pub const AF_FILE = AF_LOCAL; +pub const AF_INET = PF_INET; +pub const AF_AX25 = PF_AX25; +pub const AF_IPX = PF_IPX; +pub const AF_APPLETALK = PF_APPLETALK; +pub const AF_NETROM = PF_NETROM; +pub const AF_BRIDGE = PF_BRIDGE; +pub const AF_ATMPVC = PF_ATMPVC; +pub const AF_X25 = PF_X25; +pub const AF_INET6 = PF_INET6; +pub const AF_ROSE = PF_ROSE; +pub const AF_DECnet = PF_DECnet; +pub const AF_NETBEUI = PF_NETBEUI; +pub const AF_SECURITY = PF_SECURITY; +pub const AF_KEY = PF_KEY; +pub const AF_NETLINK = PF_NETLINK; +pub const AF_ROUTE = PF_ROUTE; +pub const AF_PACKET = PF_PACKET; +pub const AF_ASH = PF_ASH; +pub const AF_ECONET = PF_ECONET; +pub const AF_ATMSVC = PF_ATMSVC; +pub const AF_RDS = PF_RDS; +pub const AF_SNA = PF_SNA; +pub const AF_IRDA = PF_IRDA; +pub const AF_PPPOX = PF_PPPOX; +pub const AF_WANPIPE = PF_WANPIPE; +pub const AF_LLC = PF_LLC; +pub const AF_IB = PF_IB; +pub const AF_MPLS = PF_MPLS; +pub const AF_CAN = PF_CAN; +pub const AF_TIPC = PF_TIPC; +pub const AF_BLUETOOTH = PF_BLUETOOTH; +pub const AF_IUCV = PF_IUCV; +pub const AF_RXRPC = PF_RXRPC; +pub const AF_ISDN = PF_ISDN; +pub const AF_PHONET = PF_PHONET; +pub const AF_IEEE802154 = PF_IEEE802154; +pub const AF_CAIF = PF_CAIF; +pub const AF_ALG = PF_ALG; +pub const AF_NFC = PF_NFC; +pub const AF_VSOCK = PF_VSOCK; +pub const AF_MAX = PF_MAX; + +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; + +/// add event to kq (implies enable) +pub const EV_ADD = 0x0001; + +/// delete event from kq +pub const EV_DELETE = 0x0002; + +/// enable event +pub const EV_ENABLE = 0x0004; + +/// disable event (not reported) +pub const EV_DISABLE = 0x0008; + +/// only report one occurrence +pub const EV_ONESHOT = 0x0010; + +/// clear event state after reporting +pub const EV_CLEAR = 0x0020; + +/// force immediate event output +/// ... with or without EV_ERROR +/// ... use KEVENT_FLAG_ERROR_EVENTS +/// on syscalls supporting flags +pub const EV_RECEIPT = 0x0040; + +/// disable event after reporting +pub const EV_DISPATCH = 0x0080; + +pub const EVFILT_READ = -1; +pub const EVFILT_WRITE = -2; + +/// attached to aio requests +pub const EVFILT_AIO = -3; + +/// attached to vnodes +pub const EVFILT_VNODE = -4; + +/// attached to struct proc +pub const EVFILT_PROC = -5; + +/// attached to struct proc +pub const EVFILT_SIGNAL = -6; + +/// timers +pub const EVFILT_TIMER = -7; + +/// Process descriptors +pub const EVFILT_PROCDESC = -8; + +/// Filesystem events +pub const EVFILT_FS = -9; + +pub const EVFILT_LIO = -10; + +/// User events +pub const EVFILT_USER = -11; + +/// Sendfile events +pub const EVFILT_SENDFILE = -12; + +pub const EVFILT_EMPTY = -13; + +/// On input, NOTE_TRIGGER causes the event to be triggered for output. +pub const NOTE_TRIGGER = 0x01000000; + +/// ignore input fflags +pub const NOTE_FFNOP = 0x00000000; + +/// and fflags +pub const NOTE_FFAND = 0x40000000; + +/// or fflags +pub const NOTE_FFOR = 0x80000000; + +/// copy fflags +pub const NOTE_FFCOPY = 0xc0000000; + +/// mask for operations +pub const NOTE_FFCTRLMASK = 0xc0000000; +pub const NOTE_FFLAGSMASK = 0x00ffffff; + +/// low water mark +pub const NOTE_LOWAT = 0x00000001; + +/// behave like poll() +pub const NOTE_FILE_POLL = 0x00000002; + +/// vnode was removed +pub const NOTE_DELETE = 0x00000001; + +/// data contents changed +pub const NOTE_WRITE = 0x00000002; + +/// size increased +pub const NOTE_EXTEND = 0x00000004; + +/// attributes changed +pub const NOTE_ATTRIB = 0x00000008; + +/// link count changed +pub const NOTE_LINK = 0x00000010; + +/// vnode was renamed +pub const NOTE_RENAME = 0x00000020; + +/// vnode access was revoked +pub const NOTE_REVOKE = 0x00000040; + +/// vnode was opened +pub const NOTE_OPEN = 0x00000080; + +/// file closed, fd did not allow write +pub const NOTE_CLOSE = 0x00000100; + +/// file closed, fd did allow write +pub const NOTE_CLOSE_WRITE = 0x00000200; + +/// file was read +pub const NOTE_READ = 0x00000400; + +/// process exited +pub const NOTE_EXIT = 0x80000000; + +/// process forked +pub const NOTE_FORK = 0x40000000; + +/// process exec'd +pub const NOTE_EXEC = 0x20000000; + +/// mask for signal & exit status +pub const NOTE_PDATAMASK = 0x000fffff; +pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); + +/// data is seconds +pub const NOTE_SECONDS = 0x00000001; + +/// data is milliseconds +pub const NOTE_MSECONDS = 0x00000002; + +/// data is microseconds +pub const NOTE_USECONDS = 0x00000004; + +/// data is nanoseconds +pub const NOTE_NSECONDS = 0x00000008; + +/// timeout is absolute +pub const NOTE_ABSTIME = 0x00000010; + +pub const TCGETS = 0x5401; +pub const TCSETS = 0x5402; +pub const TCSETSW = 0x5403; +pub const TCSETSF = 0x5404; +pub const TCGETA = 0x5405; +pub const TCSETA = 0x5406; +pub const TCSETAW = 0x5407; +pub const TCSETAF = 0x5408; +pub const TCSBRK = 0x5409; +pub const TCXONC = 0x540A; +pub const TCFLSH = 0x540B; +pub const TIOCEXCL = 0x540C; +pub const TIOCNXCL = 0x540D; +pub const TIOCSCTTY = 0x540E; +pub const TIOCGPGRP = 0x540F; +pub const TIOCSPGRP = 0x5410; +pub const TIOCOUTQ = 0x5411; +pub const TIOCSTI = 0x5412; +pub const TIOCGWINSZ = 0x5413; +pub const TIOCSWINSZ = 0x5414; +pub const TIOCMGET = 0x5415; +pub const TIOCMBIS = 0x5416; +pub const TIOCMBIC = 0x5417; +pub const TIOCMSET = 0x5418; +pub const TIOCGSOFTCAR = 0x5419; +pub const TIOCSSOFTCAR = 0x541A; +pub const FIONREAD = 0x541B; +pub const TIOCINQ = FIONREAD; +pub const TIOCLINUX = 0x541C; +pub const TIOCCONS = 0x541D; +pub const TIOCGSERIAL = 0x541E; +pub const TIOCSSERIAL = 0x541F; +pub const TIOCPKT = 0x5420; +pub const FIONBIO = 0x5421; +pub const TIOCNOTTY = 0x5422; +pub const TIOCSETD = 0x5423; +pub const TIOCGETD = 0x5424; +pub const TCSBRKP = 0x5425; +pub const TIOCSBRK = 0x5427; +pub const TIOCCBRK = 0x5428; +pub const TIOCGSID = 0x5429; +pub const TIOCGRS485 = 0x542E; +pub const TIOCSRS485 = 0x542F; +pub const TIOCGPTN = 0x80045430; +pub const TIOCSPTLCK = 0x40045431; +pub const TIOCGDEV = 0x80045432; +pub const TCGETX = 0x5432; +pub const TCSETX = 0x5433; +pub const TCSETXF = 0x5434; +pub const TCSETXW = 0x5435; +pub const TIOCSIG = 0x40045436; +pub const TIOCVHANGUP = 0x5437; +pub const TIOCGPKT = 0x80045438; +pub const TIOCGPTLCK = 0x80045439; +pub const TIOCGEXCL = 0x80045440; + +pub fn WEXITSTATUS(s: u32) u32 { + return (s & 0xff00) >> 8; +} +pub fn WTERMSIG(s: u32) u32 { + return s & 0x7f; +} +pub fn WSTOPSIG(s: u32) u32 { + return WEXITSTATUS(s); +} +pub fn WIFEXITED(s: u32) bool { + return WTERMSIG(s) == 0; +} +pub fn WIFSTOPPED(s: u32) bool { + return @intCast(u16, (((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00; +} +pub fn WIFSIGNALED(s: u32) bool { + return (s & 0xffff) -% 1 < 0xff; +} + +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, +}; + +const NSIG = 32; + +pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); +pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); + +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = extern struct { + /// signal handler + __sigaction_u: extern union { + __sa_handler: extern fn (i32) void, + __sa_sigaction: extern fn (i32, *__siginfo, usize) void, + }, + + /// see signal options + sa_flags: u32, + + /// signal mask to apply + sa_mask: sigset_t, +}; + +pub const _SIG_WORDS = 4; +pub const _SIG_MAXSIG = 128; + +pub inline fn _SIG_IDX(sig: usize) usize { + return sig - 1; +} +pub inline fn _SIG_WORD(sig: usize) usize { + return_SIG_IDX(sig) >> 5; +} +pub inline fn _SIG_BIT(sig: usize) usize { + return 1 << (_SIG_IDX(sig) & 31); +} +pub inline fn _SIG_VALID(sig: usize) usize { + return sig <= _SIG_MAXSIG and sig > 0; +} + +pub const sigset_t = extern struct { + __bits: [_SIG_WORDS]u32, +}; + +pub const EPERM = 1; // Operation not permitted +pub const ENOENT = 2; // No such file or directory +pub const ESRCH = 3; // No such process +pub const EINTR = 4; // Interrupted system call +pub const EIO = 5; // Input/output error +pub const ENXIO = 6; // Device not configured +pub const E2BIG = 7; // Argument list too long +pub const ENOEXEC = 8; // Exec format error +pub const EBADF = 9; // Bad file descriptor +pub const ECHILD = 10; // No child processes +pub const EDEADLK = 11; // Resource deadlock avoided +// 11 was EAGAIN +pub const ENOMEM = 12; // Cannot allocate memory +pub const EACCES = 13; // Permission denied +pub const EFAULT = 14; // Bad address +pub const ENOTBLK = 15; // Block device required +pub const EBUSY = 16; // Device busy +pub const EEXIST = 17; // File exists +pub const EXDEV = 18; // Cross-device link +pub const ENODEV = 19; // Operation not supported by device +pub const ENOTDIR = 20; // Not a directory +pub const EISDIR = 21; // Is a directory +pub const EINVAL = 22; // Invalid argument +pub const ENFILE = 23; // Too many open files in system +pub const EMFILE = 24; // Too many open files +pub const ENOTTY = 25; // Inappropriate ioctl for device +pub const ETXTBSY = 26; // Text file busy +pub const EFBIG = 27; // File too large +pub const ENOSPC = 28; // No space left on device +pub const ESPIPE = 29; // Illegal seek +pub const EROFS = 30; // Read-only filesystem +pub const EMLINK = 31; // Too many links +pub const EPIPE = 32; // Broken pipe + +// math software +pub const EDOM = 33; // Numerical argument out of domain +pub const ERANGE = 34; // Result too large + +// non-blocking and interrupt i/o +pub const EAGAIN = 35; // Resource temporarily unavailable +pub const EWOULDBLOCK = EAGAIN; // Operation would block +pub const EINPROGRESS = 36; // Operation now in progress +pub const EALREADY = 37; // Operation already in progress + +// ipc/network software -- argument errors +pub const ENOTSOCK = 38; // Socket operation on non-socket +pub const EDESTADDRREQ = 39; // Destination address required +pub const EMSGSIZE = 40; // Message too long +pub const EPROTOTYPE = 41; // Protocol wrong type for socket +pub const ENOPROTOOPT = 42; // Protocol not available +pub const EPROTONOSUPPORT = 43; // Protocol not supported +pub const ESOCKTNOSUPPORT = 44; // Socket type not supported +pub const EOPNOTSUPP = 45; // Operation not supported +pub const ENOTSUP = EOPNOTSUPP; // Operation not supported +pub const EPFNOSUPPORT = 46; // Protocol family not supported +pub const EAFNOSUPPORT = 47; // Address family not supported by protocol family +pub const EADDRINUSE = 48; // Address already in use +pub const EADDRNOTAVAIL = 49; // Can't assign requested address + +// ipc/network software -- operational errors +pub const ENETDOWN = 50; // Network is down +pub const ENETUNREACH = 51; // Network is unreachable +pub const ENETRESET = 52; // Network dropped connection on reset +pub const ECONNABORTED = 53; // Software caused connection abort +pub const ECONNRESET = 54; // Connection reset by peer +pub const ENOBUFS = 55; // No buffer space available +pub const EISCONN = 56; // Socket is already connected +pub const ENOTCONN = 57; // Socket is not connected +pub const ESHUTDOWN = 58; // Can't send after socket shutdown +pub const ETOOMANYREFS = 59; // Too many references: can't splice +pub const ETIMEDOUT = 60; // Operation timed out +pub const ECONNREFUSED = 61; // Connection refused + +pub const ELOOP = 62; // Too many levels of symbolic links +pub const ENAMETOOLONG = 63; // File name too long + +// should be rearranged +pub const EHOSTDOWN = 64; // Host is down +pub const EHOSTUNREACH = 65; // No route to host +pub const ENOTEMPTY = 66; // Directory not empty + +// quotas & mush +pub const EPROCLIM = 67; // Too many processes +pub const EUSERS = 68; // Too many users +pub const EDQUOT = 69; // Disc quota exceeded + +// Network File System +pub const ESTALE = 70; // Stale NFS file handle +pub const EREMOTE = 71; // Too many levels of remote in path +pub const EBADRPC = 72; // RPC struct is bad +pub const ERPCMISMATCH = 73; // RPC version wrong +pub const EPROGUNAVAIL = 74; // RPC prog. not avail +pub const EPROGMISMATCH = 75; // Program version wrong +pub const EPROCUNAVAIL = 76; // Bad procedure for program + +pub const ENOLCK = 77; // No locks available +pub const ENOSYS = 78; // Function not implemented + +pub const EFTYPE = 79; // Inappropriate file type or format +pub const EAUTH = 80; // Authentication error +pub const ENEEDAUTH = 81; // Need authenticator +pub const EIDRM = 82; // Identifier removed +pub const ENOMSG = 83; // No message of desired type +pub const EOVERFLOW = 84; // Value too large to be stored in data type +pub const ECANCELED = 85; // Operation canceled +pub const EILSEQ = 86; // Illegal byte sequence +pub const ENOATTR = 87; // Attribute not found + +pub const EDOOFUS = 88; // Programming error + +pub const EBADMSG = 89; // Bad message +pub const EMULTIHOP = 90; // Multihop attempted +pub const ENOLINK = 91; // Link has been severed +pub const EPROTO = 92; // Protocol error + +pub const ENOTCAPABLE = 93; // Capabilities insufficient +pub const ECAPMODE = 94; // Not permitted in capability mode +pub const ENOTRECOVERABLE = 95; // State not recoverable +pub const EOWNERDEAD = 96; // Previous owner died + +pub const ELAST = 96; // Must be equal largest errno diff --git a/std/os/bits/linux.zig b/std/os/bits/linux.zig new file mode 100644 index 0000000000..6532e72362 --- /dev/null +++ b/std/os/bits/linux.zig @@ -0,0 +1,931 @@ +const builtin = @import("builtin"); +const std = @import("../../std.zig"); +const maxInt = std.math.maxInt; +use @import("../bits.zig"); + +pub use @import("linux/errno.zig"); +pub use switch (builtin.arch) { + .x86_64 => @import("linux/x86_64.zig"), + .aarch64 => @import("linux/arm64.zig"), + else => struct {}, +}; + +pub const pid_t = i32; +pub const fd_t = i32; + +pub const PATH_MAX = 4096; +pub const IOV_MAX = 1024; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +pub const FUTEX_WAIT = 0; +pub const FUTEX_WAKE = 1; +pub const FUTEX_FD = 2; +pub const FUTEX_REQUEUE = 3; +pub const FUTEX_CMP_REQUEUE = 4; +pub const FUTEX_WAKE_OP = 5; +pub const FUTEX_LOCK_PI = 6; +pub const FUTEX_UNLOCK_PI = 7; +pub const FUTEX_TRYLOCK_PI = 8; +pub const FUTEX_WAIT_BITSET = 9; + +pub const FUTEX_PRIVATE_FLAG = 128; + +pub const FUTEX_CLOCK_REALTIME = 256; + +pub const PROT_NONE = 0; +pub const PROT_READ = 1; +pub const PROT_WRITE = 2; +pub const PROT_EXEC = 4; +pub const PROT_GROWSDOWN = 0x01000000; +pub const PROT_GROWSUP = 0x02000000; + +pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); +pub const MAP_SHARED = 0x01; +pub const MAP_PRIVATE = 0x02; +pub const MAP_TYPE = 0x0f; +pub const MAP_FIXED = 0x10; +pub const MAP_ANONYMOUS = 0x20; +pub const MAP_NORESERVE = 0x4000; +pub const MAP_GROWSDOWN = 0x0100; +pub const MAP_DENYWRITE = 0x0800; +pub const MAP_EXECUTABLE = 0x1000; +pub const MAP_LOCKED = 0x2000; +pub const MAP_POPULATE = 0x8000; +pub const MAP_NONBLOCK = 0x10000; +pub const MAP_STACK = 0x20000; +pub const MAP_HUGETLB = 0x40000; +pub const MAP_FILE = 0; + +pub const F_OK = 0; +pub const X_OK = 1; +pub const W_OK = 2; +pub const R_OK = 4; + +pub const WNOHANG = 1; +pub const WUNTRACED = 2; +pub const WSTOPPED = 2; +pub const WEXITED = 4; +pub const WCONTINUED = 8; +pub const WNOWAIT = 0x1000000; + +pub const SA_NOCLDSTOP = 1; +pub const SA_NOCLDWAIT = 2; +pub const SA_SIGINFO = 4; +pub const SA_ONSTACK = 0x08000000; +pub const SA_RESTART = 0x10000000; +pub const SA_NODEFER = 0x40000000; +pub const SA_RESETHAND = 0x80000000; +pub const SA_RESTORER = 0x04000000; + +pub const SIGHUP = 1; +pub const SIGINT = 2; +pub const SIGQUIT = 3; +pub const SIGILL = 4; +pub const SIGTRAP = 5; +pub const SIGABRT = 6; +pub const SIGIOT = SIGABRT; +pub const SIGBUS = 7; +pub const SIGFPE = 8; +pub const SIGKILL = 9; +pub const SIGUSR1 = 10; +pub const SIGSEGV = 11; +pub const SIGUSR2 = 12; +pub const SIGPIPE = 13; +pub const SIGALRM = 14; +pub const SIGTERM = 15; +pub const SIGSTKFLT = 16; +pub const SIGCHLD = 17; +pub const SIGCONT = 18; +pub const SIGSTOP = 19; +pub const SIGTSTP = 20; +pub const SIGTTIN = 21; +pub const SIGTTOU = 22; +pub const SIGURG = 23; +pub const SIGXCPU = 24; +pub const SIGXFSZ = 25; +pub const SIGVTALRM = 26; +pub const SIGPROF = 27; +pub const SIGWINCH = 28; +pub const SIGIO = 29; +pub const SIGPOLL = 29; +pub const SIGPWR = 30; +pub const SIGSYS = 31; +pub const SIGUNUSED = SIGSYS; + +pub const O_RDONLY = 0o0; +pub const O_WRONLY = 0o1; +pub const O_RDWR = 0o2; + +pub const SEEK_SET = 0; +pub const SEEK_CUR = 1; +pub const SEEK_END = 2; + +pub const SIG_BLOCK = 0; +pub const SIG_UNBLOCK = 1; +pub const SIG_SETMASK = 2; + +pub const PROTO_ip = 0o000; +pub const PROTO_icmp = 0o001; +pub const PROTO_igmp = 0o002; +pub const PROTO_ggp = 0o003; +pub const PROTO_ipencap = 0o004; +pub const PROTO_st = 0o005; +pub const PROTO_tcp = 0o006; +pub const PROTO_egp = 0o010; +pub const PROTO_pup = 0o014; +pub const PROTO_udp = 0o021; +pub const PROTO_hmp = 0o024; +pub const PROTO_xns_idp = 0o026; +pub const PROTO_rdp = 0o033; +pub const PROTO_iso_tp4 = 0o035; +pub const PROTO_xtp = 0o044; +pub const PROTO_ddp = 0o045; +pub const PROTO_idpr_cmtp = 0o046; +pub const PROTO_ipv6 = 0o051; +pub const PROTO_ipv6_route = 0o053; +pub const PROTO_ipv6_frag = 0o054; +pub const PROTO_idrp = 0o055; +pub const PROTO_rsvp = 0o056; +pub const PROTO_gre = 0o057; +pub const PROTO_esp = 0o062; +pub const PROTO_ah = 0o063; +pub const PROTO_skip = 0o071; +pub const PROTO_ipv6_icmp = 0o072; +pub const PROTO_ipv6_nonxt = 0o073; +pub const PROTO_ipv6_opts = 0o074; +pub const PROTO_rspf = 0o111; +pub const PROTO_vmtp = 0o121; +pub const PROTO_ospf = 0o131; +pub const PROTO_ipip = 0o136; +pub const PROTO_encap = 0o142; +pub const PROTO_pim = 0o147; +pub const PROTO_raw = 0o377; + +pub const SHUT_RD = 0; +pub const SHUT_WR = 1; +pub const SHUT_RDWR = 2; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; +pub const SOCK_DCCP = 6; +pub const SOCK_PACKET = 10; +pub const SOCK_CLOEXEC = 0o2000000; +pub const SOCK_NONBLOCK = 0o4000; + +pub const PF_UNSPEC = 0; +pub const PF_LOCAL = 1; +pub const PF_UNIX = PF_LOCAL; +pub const PF_FILE = PF_LOCAL; +pub const PF_INET = 2; +pub const PF_AX25 = 3; +pub const PF_IPX = 4; +pub const PF_APPLETALK = 5; +pub const PF_NETROM = 6; +pub const PF_BRIDGE = 7; +pub const PF_ATMPVC = 8; +pub const PF_X25 = 9; +pub const PF_INET6 = 10; +pub const PF_ROSE = 11; +pub const PF_DECnet = 12; +pub const PF_NETBEUI = 13; +pub const PF_SECURITY = 14; +pub const PF_KEY = 15; +pub const PF_NETLINK = 16; +pub const PF_ROUTE = PF_NETLINK; +pub const PF_PACKET = 17; +pub const PF_ASH = 18; +pub const PF_ECONET = 19; +pub const PF_ATMSVC = 20; +pub const PF_RDS = 21; +pub const PF_SNA = 22; +pub const PF_IRDA = 23; +pub const PF_PPPOX = 24; +pub const PF_WANPIPE = 25; +pub const PF_LLC = 26; +pub const PF_IB = 27; +pub const PF_MPLS = 28; +pub const PF_CAN = 29; +pub const PF_TIPC = 30; +pub const PF_BLUETOOTH = 31; +pub const PF_IUCV = 32; +pub const PF_RXRPC = 33; +pub const PF_ISDN = 34; +pub const PF_PHONET = 35; +pub const PF_IEEE802154 = 36; +pub const PF_CAIF = 37; +pub const PF_ALG = 38; +pub const PF_NFC = 39; +pub const PF_VSOCK = 40; +pub const PF_KCM = 41; +pub const PF_QIPCRTR = 42; +pub const PF_SMC = 43; +pub const PF_MAX = 44; + +pub const AF_UNSPEC = PF_UNSPEC; +pub const AF_LOCAL = PF_LOCAL; +pub const AF_UNIX = AF_LOCAL; +pub const AF_FILE = AF_LOCAL; +pub const AF_INET = PF_INET; +pub const AF_AX25 = PF_AX25; +pub const AF_IPX = PF_IPX; +pub const AF_APPLETALK = PF_APPLETALK; +pub const AF_NETROM = PF_NETROM; +pub const AF_BRIDGE = PF_BRIDGE; +pub const AF_ATMPVC = PF_ATMPVC; +pub const AF_X25 = PF_X25; +pub const AF_INET6 = PF_INET6; +pub const AF_ROSE = PF_ROSE; +pub const AF_DECnet = PF_DECnet; +pub const AF_NETBEUI = PF_NETBEUI; +pub const AF_SECURITY = PF_SECURITY; +pub const AF_KEY = PF_KEY; +pub const AF_NETLINK = PF_NETLINK; +pub const AF_ROUTE = PF_ROUTE; +pub const AF_PACKET = PF_PACKET; +pub const AF_ASH = PF_ASH; +pub const AF_ECONET = PF_ECONET; +pub const AF_ATMSVC = PF_ATMSVC; +pub const AF_RDS = PF_RDS; +pub const AF_SNA = PF_SNA; +pub const AF_IRDA = PF_IRDA; +pub const AF_PPPOX = PF_PPPOX; +pub const AF_WANPIPE = PF_WANPIPE; +pub const AF_LLC = PF_LLC; +pub const AF_IB = PF_IB; +pub const AF_MPLS = PF_MPLS; +pub const AF_CAN = PF_CAN; +pub const AF_TIPC = PF_TIPC; +pub const AF_BLUETOOTH = PF_BLUETOOTH; +pub const AF_IUCV = PF_IUCV; +pub const AF_RXRPC = PF_RXRPC; +pub const AF_ISDN = PF_ISDN; +pub const AF_PHONET = PF_PHONET; +pub const AF_IEEE802154 = PF_IEEE802154; +pub const AF_CAIF = PF_CAIF; +pub const AF_ALG = PF_ALG; +pub const AF_NFC = PF_NFC; +pub const AF_VSOCK = PF_VSOCK; +pub const AF_KCM = PF_KCM; +pub const AF_QIPCRTR = PF_QIPCRTR; +pub const AF_SMC = PF_SMC; +pub const AF_MAX = PF_MAX; + +pub const SO_DEBUG = 1; +pub const SO_REUSEADDR = 2; +pub const SO_TYPE = 3; +pub const SO_ERROR = 4; +pub const SO_DONTROUTE = 5; +pub const SO_BROADCAST = 6; +pub const SO_SNDBUF = 7; +pub const SO_RCVBUF = 8; +pub const SO_KEEPALIVE = 9; +pub const SO_OOBINLINE = 10; +pub const SO_NO_CHECK = 11; +pub const SO_PRIORITY = 12; +pub const SO_LINGER = 13; +pub const SO_BSDCOMPAT = 14; +pub const SO_REUSEPORT = 15; +pub const SO_PASSCRED = 16; +pub const SO_PEERCRED = 17; +pub const SO_RCVLOWAT = 18; +pub const SO_SNDLOWAT = 19; +pub const SO_RCVTIMEO = 20; +pub const SO_SNDTIMEO = 21; +pub const SO_ACCEPTCONN = 30; +pub const SO_SNDBUFFORCE = 32; +pub const SO_RCVBUFFORCE = 33; +pub const SO_PROTOCOL = 38; +pub const SO_DOMAIN = 39; + +pub const SO_SECURITY_AUTHENTICATION = 22; +pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; +pub const SO_SECURITY_ENCRYPTION_NETWORK = 24; + +pub const SO_BINDTODEVICE = 25; + +pub const SO_ATTACH_FILTER = 26; +pub const SO_DETACH_FILTER = 27; +pub const SO_GET_FILTER = SO_ATTACH_FILTER; + +pub const SO_PEERNAME = 28; +pub const SO_TIMESTAMP = 29; +pub const SCM_TIMESTAMP = SO_TIMESTAMP; + +pub const SO_PEERSEC = 31; +pub const SO_PASSSEC = 34; +pub const SO_TIMESTAMPNS = 35; +pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS; +pub const SO_MARK = 36; +pub const SO_TIMESTAMPING = 37; +pub const SCM_TIMESTAMPING = SO_TIMESTAMPING; +pub const SO_RXQ_OVFL = 40; +pub const SO_WIFI_STATUS = 41; +pub const SCM_WIFI_STATUS = SO_WIFI_STATUS; +pub const SO_PEEK_OFF = 42; +pub const SO_NOFCS = 43; +pub const SO_LOCK_FILTER = 44; +pub const SO_SELECT_ERR_QUEUE = 45; +pub const SO_BUSY_POLL = 46; +pub const SO_MAX_PACING_RATE = 47; +pub const SO_BPF_EXTENSIONS = 48; +pub const SO_INCOMING_CPU = 49; +pub const SO_ATTACH_BPF = 50; +pub const SO_DETACH_BPF = SO_DETACH_FILTER; +pub const SO_ATTACH_REUSEPORT_CBPF = 51; +pub const SO_ATTACH_REUSEPORT_EBPF = 52; +pub const SO_CNX_ADVICE = 53; +pub const SCM_TIMESTAMPING_OPT_STATS = 54; +pub const SO_MEMINFO = 55; +pub const SO_INCOMING_NAPI_ID = 56; +pub const SO_COOKIE = 57; +pub const SCM_TIMESTAMPING_PKTINFO = 58; +pub const SO_PEERGROUPS = 59; +pub const SO_ZEROCOPY = 60; + +pub const SOL_SOCKET = 1; + +pub const SOL_IP = 0; +pub const SOL_IPV6 = 41; +pub const SOL_ICMPV6 = 58; + +pub const SOL_RAW = 255; +pub const SOL_DECNET = 261; +pub const SOL_X25 = 262; +pub const SOL_PACKET = 263; +pub const SOL_ATM = 264; +pub const SOL_AAL = 265; +pub const SOL_IRDA = 266; +pub const SOL_NETBEUI = 267; +pub const SOL_LLC = 268; +pub const SOL_DCCP = 269; +pub const SOL_NETLINK = 270; +pub const SOL_TIPC = 271; +pub const SOL_RXRPC = 272; +pub const SOL_PPPOL2TP = 273; +pub const SOL_BLUETOOTH = 274; +pub const SOL_PNPIPE = 275; +pub const SOL_RDS = 276; +pub const SOL_IUCV = 277; +pub const SOL_CAIF = 278; +pub const SOL_ALG = 279; +pub const SOL_NFC = 280; +pub const SOL_KCM = 281; +pub const SOL_TLS = 282; + +pub const SOMAXCONN = 128; + +pub const MSG_OOB = 0x0001; +pub const MSG_PEEK = 0x0002; +pub const MSG_DONTROUTE = 0x0004; +pub const MSG_CTRUNC = 0x0008; +pub const MSG_PROXY = 0x0010; +pub const MSG_TRUNC = 0x0020; +pub const MSG_DONTWAIT = 0x0040; +pub const MSG_EOR = 0x0080; +pub const MSG_WAITALL = 0x0100; +pub const MSG_FIN = 0x0200; +pub const MSG_SYN = 0x0400; +pub const MSG_CONFIRM = 0x0800; +pub const MSG_RST = 0x1000; +pub const MSG_ERRQUEUE = 0x2000; +pub const MSG_NOSIGNAL = 0x4000; +pub const MSG_MORE = 0x8000; +pub const MSG_WAITFORONE = 0x10000; +pub const MSG_BATCH = 0x40000; +pub const MSG_ZEROCOPY = 0x4000000; +pub const MSG_FASTOPEN = 0x20000000; +pub const MSG_CMSG_CLOEXEC = 0x40000000; + +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; + +pub const TCGETS = 0x5401; +pub const TCSETS = 0x5402; +pub const TCSETSW = 0x5403; +pub const TCSETSF = 0x5404; +pub const TCGETA = 0x5405; +pub const TCSETA = 0x5406; +pub const TCSETAW = 0x5407; +pub const TCSETAF = 0x5408; +pub const TCSBRK = 0x5409; +pub const TCXONC = 0x540A; +pub const TCFLSH = 0x540B; +pub const TIOCEXCL = 0x540C; +pub const TIOCNXCL = 0x540D; +pub const TIOCSCTTY = 0x540E; +pub const TIOCGPGRP = 0x540F; +pub const TIOCSPGRP = 0x5410; +pub const TIOCOUTQ = 0x5411; +pub const TIOCSTI = 0x5412; +pub const TIOCGWINSZ = 0x5413; +pub const TIOCSWINSZ = 0x5414; +pub const TIOCMGET = 0x5415; +pub const TIOCMBIS = 0x5416; +pub const TIOCMBIC = 0x5417; +pub const TIOCMSET = 0x5418; +pub const TIOCGSOFTCAR = 0x5419; +pub const TIOCSSOFTCAR = 0x541A; +pub const FIONREAD = 0x541B; +pub const TIOCINQ = FIONREAD; +pub const TIOCLINUX = 0x541C; +pub const TIOCCONS = 0x541D; +pub const TIOCGSERIAL = 0x541E; +pub const TIOCSSERIAL = 0x541F; +pub const TIOCPKT = 0x5420; +pub const FIONBIO = 0x5421; +pub const TIOCNOTTY = 0x5422; +pub const TIOCSETD = 0x5423; +pub const TIOCGETD = 0x5424; +pub const TCSBRKP = 0x5425; +pub const TIOCSBRK = 0x5427; +pub const TIOCCBRK = 0x5428; +pub const TIOCGSID = 0x5429; +pub const TIOCGRS485 = 0x542E; +pub const TIOCSRS485 = 0x542F; +pub const TIOCGPTN = 0x80045430; +pub const TIOCSPTLCK = 0x40045431; +pub const TIOCGDEV = 0x80045432; +pub const TCGETX = 0x5432; +pub const TCSETX = 0x5433; +pub const TCSETXF = 0x5434; +pub const TCSETXW = 0x5435; +pub const TIOCSIG = 0x40045436; +pub const TIOCVHANGUP = 0x5437; +pub const TIOCGPKT = 0x80045438; +pub const TIOCGPTLCK = 0x80045439; +pub const TIOCGEXCL = 0x80045440; + +pub const EPOLL_CLOEXEC = O_CLOEXEC; + +pub const EPOLL_CTL_ADD = 1; +pub const EPOLL_CTL_DEL = 2; +pub const EPOLL_CTL_MOD = 3; + +pub const EPOLLIN = 0x001; +pub const EPOLLPRI = 0x002; +pub const EPOLLOUT = 0x004; +pub const EPOLLRDNORM = 0x040; +pub const EPOLLRDBAND = 0x080; +pub const EPOLLWRNORM = 0x100; +pub const EPOLLWRBAND = 0x200; +pub const EPOLLMSG = 0x400; +pub const EPOLLERR = 0x008; +pub const EPOLLHUP = 0x010; +pub const EPOLLRDHUP = 0x2000; +pub const EPOLLEXCLUSIVE = (u32(1) << 28); +pub const EPOLLWAKEUP = (u32(1) << 29); +pub const EPOLLONESHOT = (u32(1) << 30); +pub const EPOLLET = (u32(1) << 31); + +pub const CLOCK_REALTIME = 0; +pub const CLOCK_MONOTONIC = 1; +pub const CLOCK_PROCESS_CPUTIME_ID = 2; +pub const CLOCK_THREAD_CPUTIME_ID = 3; +pub const CLOCK_MONOTONIC_RAW = 4; +pub const CLOCK_REALTIME_COARSE = 5; +pub const CLOCK_MONOTONIC_COARSE = 6; +pub const CLOCK_BOOTTIME = 7; +pub const CLOCK_REALTIME_ALARM = 8; +pub const CLOCK_BOOTTIME_ALARM = 9; +pub const CLOCK_SGI_CYCLE = 10; +pub const CLOCK_TAI = 11; + +pub const CSIGNAL = 0x000000ff; +pub const CLONE_VM = 0x00000100; +pub const CLONE_FS = 0x00000200; +pub const CLONE_FILES = 0x00000400; +pub const CLONE_SIGHAND = 0x00000800; +pub const CLONE_PTRACE = 0x00002000; +pub const CLONE_VFORK = 0x00004000; +pub const CLONE_PARENT = 0x00008000; +pub const CLONE_THREAD = 0x00010000; +pub const CLONE_NEWNS = 0x00020000; +pub const CLONE_SYSVSEM = 0x00040000; +pub const CLONE_SETTLS = 0x00080000; +pub const CLONE_PARENT_SETTID = 0x00100000; +pub const CLONE_CHILD_CLEARTID = 0x00200000; +pub const CLONE_DETACHED = 0x00400000; +pub const CLONE_UNTRACED = 0x00800000; +pub const CLONE_CHILD_SETTID = 0x01000000; +pub const CLONE_NEWCGROUP = 0x02000000; +pub const CLONE_NEWUTS = 0x04000000; +pub const CLONE_NEWIPC = 0x08000000; +pub const CLONE_NEWUSER = 0x10000000; +pub const CLONE_NEWPID = 0x20000000; +pub const CLONE_NEWNET = 0x40000000; +pub const CLONE_IO = 0x80000000; + +pub const EFD_SEMAPHORE = 1; +pub const EFD_CLOEXEC = O_CLOEXEC; +pub const EFD_NONBLOCK = O_NONBLOCK; + +pub const MS_RDONLY = 1; +pub const MS_NOSUID = 2; +pub const MS_NODEV = 4; +pub const MS_NOEXEC = 8; +pub const MS_SYNCHRONOUS = 16; +pub const MS_REMOUNT = 32; +pub const MS_MANDLOCK = 64; +pub const MS_DIRSYNC = 128; +pub const MS_NOATIME = 1024; +pub const MS_NODIRATIME = 2048; +pub const MS_BIND = 4096; +pub const MS_MOVE = 8192; +pub const MS_REC = 16384; +pub const MS_SILENT = 32768; +pub const MS_POSIXACL = (1 << 16); +pub const MS_UNBINDABLE = (1 << 17); +pub const MS_PRIVATE = (1 << 18); +pub const MS_SLAVE = (1 << 19); +pub const MS_SHARED = (1 << 20); +pub const MS_RELATIME = (1 << 21); +pub const MS_KERNMOUNT = (1 << 22); +pub const MS_I_VERSION = (1 << 23); +pub const MS_STRICTATIME = (1 << 24); +pub const MS_LAZYTIME = (1 << 25); +pub const MS_NOREMOTELOCK = (1 << 27); +pub const MS_NOSEC = (1 << 28); +pub const MS_BORN = (1 << 29); +pub const MS_ACTIVE = (1 << 30); +pub const MS_NOUSER = (1 << 31); + +pub const MS_RMT_MASK = (MS_RDONLY | MS_SYNCHRONOUS | MS_MANDLOCK | MS_I_VERSION | MS_LAZYTIME); + +pub const MS_MGC_VAL = 0xc0ed0000; +pub const MS_MGC_MSK = 0xffff0000; + +pub const MNT_FORCE = 1; +pub const MNT_DETACH = 2; +pub const MNT_EXPIRE = 4; +pub const UMOUNT_NOFOLLOW = 8; + +pub const IN_CLOEXEC = O_CLOEXEC; +pub const IN_NONBLOCK = O_NONBLOCK; + +pub const IN_ACCESS = 0x00000001; +pub const IN_MODIFY = 0x00000002; +pub const IN_ATTRIB = 0x00000004; +pub const IN_CLOSE_WRITE = 0x00000008; +pub const IN_CLOSE_NOWRITE = 0x00000010; +pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE; +pub const IN_OPEN = 0x00000020; +pub const IN_MOVED_FROM = 0x00000040; +pub const IN_MOVED_TO = 0x00000080; +pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO; +pub const IN_CREATE = 0x00000100; +pub const IN_DELETE = 0x00000200; +pub const IN_DELETE_SELF = 0x00000400; +pub const IN_MOVE_SELF = 0x00000800; +pub const IN_ALL_EVENTS = 0x00000fff; + +pub const IN_UNMOUNT = 0x00002000; +pub const IN_Q_OVERFLOW = 0x00004000; +pub const IN_IGNORED = 0x00008000; + +pub const IN_ONLYDIR = 0x01000000; +pub const IN_DONT_FOLLOW = 0x02000000; +pub const IN_EXCL_UNLINK = 0x04000000; +pub const IN_MASK_ADD = 0x20000000; + +pub const IN_ISDIR = 0x40000000; +pub const IN_ONESHOT = 0x80000000; + +pub const S_IFMT = 0o170000; + +pub const S_IFDIR = 0o040000; +pub const S_IFCHR = 0o020000; +pub const S_IFBLK = 0o060000; +pub const S_IFREG = 0o100000; +pub const S_IFIFO = 0o010000; +pub const S_IFLNK = 0o120000; +pub const S_IFSOCK = 0o140000; + +pub const S_ISUID = 0o4000; +pub const S_ISGID = 0o2000; +pub const S_ISVTX = 0o1000; +pub const S_IRUSR = 0o400; +pub const S_IWUSR = 0o200; +pub const S_IXUSR = 0o100; +pub const S_IRWXU = 0o700; +pub const S_IRGRP = 0o040; +pub const S_IWGRP = 0o020; +pub const S_IXGRP = 0o010; +pub const S_IRWXG = 0o070; +pub const S_IROTH = 0o004; +pub const S_IWOTH = 0o002; +pub const S_IXOTH = 0o001; +pub const S_IRWXO = 0o007; + +pub fn S_ISREG(m: u32) bool { + return m & S_IFMT == S_IFREG; +} + +pub fn S_ISDIR(m: u32) bool { + return m & S_IFMT == S_IFDIR; +} + +pub fn S_ISCHR(m: u32) bool { + return m & S_IFMT == S_IFCHR; +} + +pub fn S_ISBLK(m: u32) bool { + return m & S_IFMT == S_IFBLK; +} + +pub fn S_ISFIFO(m: u32) bool { + return m & S_IFMT == S_IFIFO; +} + +pub fn S_ISLNK(m: u32) bool { + return m & S_IFMT == S_IFLNK; +} + +pub fn S_ISSOCK(m: u32) bool { + return m & S_IFMT == S_IFSOCK; +} + +pub const TFD_NONBLOCK = O_NONBLOCK; +pub const TFD_CLOEXEC = O_CLOEXEC; + +pub const TFD_TIMER_ABSTIME = 1; +pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1); + +pub fn WEXITSTATUS(s: u32) u32 { + return (s & 0xff00) >> 8; +} +pub fn WTERMSIG(s: u32) u32 { + return s & 0x7f; +} +pub fn WSTOPSIG(s: u32) u32 { + return WEXITSTATUS(s); +} +pub fn WIFEXITED(s: u32) bool { + return WTERMSIG(s) == 0; +} +pub fn WIFSTOPPED(s: u32) bool { + return @intCast(u16, ((s & 0xffff) *% 0x10001) >> 8) > 0x7f00; +} +pub fn WIFSIGNALED(s: u32) bool { + return (s & 0xffff) -% 1 < 0xff; +} + +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, +}; + +pub const NSIG = 65; +pub const sigset_t = [128 / @sizeOf(usize)]usize; +pub const all_mask = []u32{ 0xffffffff, 0xffffffff }; +pub const app_mask = []u32{ 0xfffffffc, 0x7fffffff }; + +pub const k_sigaction = extern struct { + handler: extern fn (i32) void, + flags: usize, + restorer: extern fn () void, + mask: [2]u32, +}; + +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = struct { + handler: extern fn (i32) void, + mask: sigset_t, + flags: u32, +}; + +pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); +pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); +pub const empty_sigset = []usize{0} ** sigset_t.len; + +pub const in_port_t = u16; +pub const sa_family_t = u16; +pub const socklen_t = u32; + +/// This intentionally only has ip4 and ip6 +pub const sockaddr = extern union { + in: sockaddr_in, + in6: sockaddr_in6, +}; + +pub const sockaddr_in = extern struct { + family: sa_family_t, + port: in_port_t, + addr: u32, + zero: [8]u8, +}; + +pub const sockaddr_in6 = extern struct { + family: sa_family_t, + port: in_port_t, + flowinfo: u32, + addr: [16]u8, + scope_id: u32, +}; + +pub const sockaddr_un = extern struct { + family: sa_family_t, + path: [108]u8, +}; + +pub const mmsghdr = extern struct { + msg_hdr: msghdr, + msg_len: u32, +}; + +pub const mmsghdr_const = extern struct { + msg_hdr: msghdr_const, + msg_len: u32, +}; + +pub const epoll_data = extern union { + ptr: usize, + fd: i32, + @"u32": u32, + @"u64": u64, +}; + +// On x86_64 the structure is packed so that it matches the definition of its +// 32bit counterpart +pub const epoll_event = if (builtin.arch != .x86_64) + extern struct { + events: u32, + data: epoll_data, + } +else + packed struct { + events: u32, + data: epoll_data, + }; + +pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; +pub const _LINUX_CAPABILITY_U32S_1 = 1; + +pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026; +pub const _LINUX_CAPABILITY_U32S_2 = 2; + +pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522; +pub const _LINUX_CAPABILITY_U32S_3 = 2; + +pub const VFS_CAP_REVISION_MASK = 0xFF000000; +pub const VFS_CAP_REVISION_SHIFT = 24; +pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; +pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; + +pub const VFS_CAP_REVISION_1 = 0x01000000; +pub const VFS_CAP_U32_1 = 1; +pub const XATTR_CAPS_SZ_1 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_1); + +pub const VFS_CAP_REVISION_2 = 0x02000000; +pub const VFS_CAP_U32_2 = 2; +pub const XATTR_CAPS_SZ_2 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_2); + +pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; +pub const VFS_CAP_U32 = VFS_CAP_U32_2; +pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; + +pub const vfs_cap_data = extern struct { + //all of these are mandated as little endian + //when on disk. + const Data = struct { + permitted: u32, + inheritable: u32, + }; + + magic_etc: u32, + data: [VFS_CAP_U32]Data, +}; + +pub const CAP_CHOWN = 0; +pub const CAP_DAC_OVERRIDE = 1; +pub const CAP_DAC_READ_SEARCH = 2; +pub const CAP_FOWNER = 3; +pub const CAP_FSETID = 4; +pub const CAP_KILL = 5; +pub const CAP_SETGID = 6; +pub const CAP_SETUID = 7; +pub const CAP_SETPCAP = 8; +pub const CAP_LINUX_IMMUTABLE = 9; +pub const CAP_NET_BIND_SERVICE = 10; +pub const CAP_NET_BROADCAST = 11; +pub const CAP_NET_ADMIN = 12; +pub const CAP_NET_RAW = 13; +pub const CAP_IPC_LOCK = 14; +pub const CAP_IPC_OWNER = 15; +pub const CAP_SYS_MODULE = 16; +pub const CAP_SYS_RAWIO = 17; +pub const CAP_SYS_CHROOT = 18; +pub const CAP_SYS_PTRACE = 19; +pub const CAP_SYS_PACCT = 20; +pub const CAP_SYS_ADMIN = 21; +pub const CAP_SYS_BOOT = 22; +pub const CAP_SYS_NICE = 23; +pub const CAP_SYS_RESOURCE = 24; +pub const CAP_SYS_TIME = 25; +pub const CAP_SYS_TTY_CONFIG = 26; +pub const CAP_MKNOD = 27; +pub const CAP_LEASE = 28; +pub const CAP_AUDIT_WRITE = 29; +pub const CAP_AUDIT_CONTROL = 30; +pub const CAP_SETFCAP = 31; +pub const CAP_MAC_OVERRIDE = 32; +pub const CAP_MAC_ADMIN = 33; +pub const CAP_SYSLOG = 34; +pub const CAP_WAKE_ALARM = 35; +pub const CAP_BLOCK_SUSPEND = 36; +pub const CAP_AUDIT_READ = 37; +pub const CAP_LAST_CAP = CAP_AUDIT_READ; + +pub fn cap_valid(u8: x) bool { + return x >= 0 and x <= CAP_LAST_CAP; +} + +pub fn CAP_TO_MASK(cap: u8) u32 { + return u32(1) << u5(cap & 31); +} + +pub fn CAP_TO_INDEX(cap: u8) u8 { + return cap >> 5; +} + +pub const cap_t = extern struct { + hdrp: *cap_user_header_t, + datap: *cap_user_data_t, +}; + +pub const cap_user_header_t = extern struct { + version: u32, + pid: usize, +}; + +pub const cap_user_data_t = extern struct { + effective: u32, + permitted: u32, + inheritable: u32, +}; + +pub const inotify_event = extern struct { + wd: i32, + mask: u32, + cookie: u32, + len: u32, + //name: [?]u8, +}; + +pub const dirent64 = extern struct { + d_ino: u64, + d_off: u64, + d_reclen: u16, + d_type: u8, + d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 +}; + +pub const dl_phdr_info = extern struct { + dlpi_addr: usize, + dlpi_name: ?[*]const u8, + dlpi_phdr: [*]std.elf.Phdr, + dlpi_phnum: u16, +}; + +pub const pthread_attr_t = extern struct { + __size: [56]u8, + __align: c_long, +}; + +pub const CPU_SETSIZE = 128; +pub const cpu_set_t = [CPU_SETSIZE / @sizeOf(usize)]usize; +pub const cpu_count_t = @IntType(false, std.math.log2(CPU_SETSIZE * 8)); + +pub fn CPU_COUNT(set: cpu_set_t) cpu_count_t { + var sum: cpu_count_t = 0; + for (set) |x| { + sum += @popCount(usize, x); + } + return sum; +} + +// TODO port these over +//#define CPU_SET(i, set) CPU_SET_S(i,sizeof(cpu_set_t),set) +//#define CPU_CLR(i, set) CPU_CLR_S(i,sizeof(cpu_set_t),set) +//#define CPU_ISSET(i, set) CPU_ISSET_S(i,sizeof(cpu_set_t),set) +//#define CPU_AND(d,s1,s2) CPU_AND_S(sizeof(cpu_set_t),d,s1,s2) +//#define CPU_OR(d,s1,s2) CPU_OR_S(sizeof(cpu_set_t),d,s1,s2) +//#define CPU_XOR(d,s1,s2) CPU_XOR_S(sizeof(cpu_set_t),d,s1,s2) +//#define CPU_COUNT(set) CPU_COUNT_S(sizeof(cpu_set_t),set) +//#define CPU_ZERO(set) CPU_ZERO_S(sizeof(cpu_set_t),set) +//#define CPU_EQUAL(s1,s2) CPU_EQUAL_S(sizeof(cpu_set_t),s1,s2) diff --git a/std/os/bits/linux/arm64.zig b/std/os/bits/linux/arm64.zig new file mode 100644 index 0000000000..8966df30d2 --- /dev/null +++ b/std/os/bits/linux/arm64.zig @@ -0,0 +1,405 @@ +// arm64-specific declarations that are intended to be imported into the POSIX namespace. +// This does include Linux-only APIs. + +const std = @import("../../std.zig"); +const linux = std.os.linux; +const socklen_t = linux.socklen_t; +const iovec = linux.iovec; +const iovec_const = linux.iovec_const; + +pub const SYS_io_setup = 0; +pub const SYS_io_destroy = 1; +pub const SYS_io_submit = 2; +pub const SYS_io_cancel = 3; +pub const SYS_io_getevents = 4; +pub const SYS_setxattr = 5; +pub const SYS_lsetxattr = 6; +pub const SYS_fsetxattr = 7; +pub const SYS_getxattr = 8; +pub const SYS_lgetxattr = 9; +pub const SYS_fgetxattr = 10; +pub const SYS_listxattr = 11; +pub const SYS_llistxattr = 12; +pub const SYS_flistxattr = 13; +pub const SYS_removexattr = 14; +pub const SYS_lremovexattr = 15; +pub const SYS_fremovexattr = 16; +pub const SYS_getcwd = 17; +pub const SYS_lookup_dcookie = 18; +pub const SYS_eventfd2 = 19; +pub const SYS_epoll_create1 = 20; +pub const SYS_epoll_ctl = 21; +pub const SYS_epoll_pwait = 22; +pub const SYS_dup = 23; +pub const SYS_dup3 = 24; +pub const SYS_fcntl = 25; +pub const SYS_inotify_init1 = 26; +pub const SYS_inotify_add_watch = 27; +pub const SYS_inotify_rm_watch = 28; +pub const SYS_ioctl = 29; +pub const SYS_ioprio_set = 30; +pub const SYS_ioprio_get = 31; +pub const SYS_flock = 32; +pub const SYS_mknodat = 33; +pub const SYS_mkdirat = 34; +pub const SYS_unlinkat = 35; +pub const SYS_symlinkat = 36; +pub const SYS_linkat = 37; +pub const SYS_renameat = 38; +pub const SYS_umount2 = 39; +pub const SYS_mount = 40; +pub const SYS_pivot_root = 41; +pub const SYS_nfsservctl = 42; +pub const SYS_statfs = 43; +pub const SYS_fstatfs = 44; +pub const SYS_truncate = 45; +pub const SYS_ftruncate = 46; +pub const SYS_fallocate = 47; +pub const SYS_faccessat = 48; +pub const SYS_chdir = 49; +pub const SYS_fchdir = 50; +pub const SYS_chroot = 51; +pub const SYS_fchmod = 52; +pub const SYS_fchmodat = 53; +pub const SYS_fchownat = 54; +pub const SYS_fchown = 55; +pub const SYS_openat = 56; +pub const SYS_close = 57; +pub const SYS_vhangup = 58; +pub const SYS_pipe2 = 59; +pub const SYS_quotactl = 60; +pub const SYS_getdents64 = 61; +pub const SYS_lseek = 62; +pub const SYS_read = 63; +pub const SYS_write = 64; +pub const SYS_readv = 65; +pub const SYS_writev = 66; +pub const SYS_pread64 = 67; +pub const SYS_pwrite64 = 68; +pub const SYS_preadv = 69; +pub const SYS_pwritev = 70; +pub const SYS_pselect6 = 72; +pub const SYS_ppoll = 73; +pub const SYS_signalfd4 = 74; +pub const SYS_vmsplice = 75; +pub const SYS_splice = 76; +pub const SYS_tee = 77; +pub const SYS_readlinkat = 78; +pub const SYS_fstatat = 79; +pub const SYS_fstat = 80; +pub const SYS_sync = 81; +pub const SYS_fsync = 82; +pub const SYS_fdatasync = 83; +pub const SYS_sync_file_range2 = 84; +pub const SYS_sync_file_range = 84; +pub const SYS_timerfd_create = 85; +pub const SYS_timerfd_settime = 86; +pub const SYS_timerfd_gettime = 87; +pub const SYS_utimensat = 88; +pub const SYS_acct = 89; +pub const SYS_capget = 90; +pub const SYS_capset = 91; +pub const SYS_personality = 92; +pub const SYS_exit = 93; +pub const SYS_exit_group = 94; +pub const SYS_waitid = 95; +pub const SYS_set_tid_address = 96; +pub const SYS_unshare = 97; +pub const SYS_futex = 98; +pub const SYS_set_robust_list = 99; +pub const SYS_get_robust_list = 100; +pub const SYS_nanosleep = 101; +pub const SYS_getitimer = 102; +pub const SYS_setitimer = 103; +pub const SYS_kexec_load = 104; +pub const SYS_init_module = 105; +pub const SYS_delete_module = 106; +pub const SYS_timer_create = 107; +pub const SYS_timer_gettime = 108; +pub const SYS_timer_getoverrun = 109; +pub const SYS_timer_settime = 110; +pub const SYS_timer_delete = 111; +pub const SYS_clock_settime = 112; +pub const SYS_clock_gettime = 113; +pub const SYS_clock_getres = 114; +pub const SYS_clock_nanosleep = 115; +pub const SYS_syslog = 116; +pub const SYS_ptrace = 117; +pub const SYS_sched_setparam = 118; +pub const SYS_sched_setscheduler = 119; +pub const SYS_sched_getscheduler = 120; +pub const SYS_sched_getparam = 121; +pub const SYS_sched_setaffinity = 122; +pub const SYS_sched_getaffinity = 123; +pub const SYS_sched_yield = 124; +pub const SYS_sched_get_priority_max = 125; +pub const SYS_sched_get_priority_min = 126; +pub const SYS_sched_rr_get_interval = 127; +pub const SYS_restart_syscall = 128; +pub const SYS_kill = 129; +pub const SYS_tkill = 130; +pub const SYS_tgkill = 131; +pub const SYS_sigaltstack = 132; +pub const SYS_rt_sigsuspend = 133; +pub const SYS_rt_sigaction = 134; +pub const SYS_rt_sigprocmask = 135; +pub const SYS_rt_sigpending = 136; +pub const SYS_rt_sigtimedwait = 137; +pub const SYS_rt_sigqueueinfo = 138; +pub const SYS_rt_sigreturn = 139; +pub const SYS_setpriority = 140; +pub const SYS_getpriority = 141; +pub const SYS_reboot = 142; +pub const SYS_setregid = 143; +pub const SYS_setgid = 144; +pub const SYS_setreuid = 145; +pub const SYS_setuid = 146; +pub const SYS_setresuid = 147; +pub const SYS_getresuid = 148; +pub const SYS_setresgid = 149; +pub const SYS_getresgid = 150; +pub const SYS_setfsuid = 151; +pub const SYS_setfsgid = 152; +pub const SYS_times = 153; +pub const SYS_setpgid = 154; +pub const SYS_getpgid = 155; +pub const SYS_getsid = 156; +pub const SYS_setsid = 157; +pub const SYS_getgroups = 158; +pub const SYS_setgroups = 159; +pub const SYS_uname = 160; +pub const SYS_sethostname = 161; +pub const SYS_setdomainname = 162; +pub const SYS_getrlimit = 163; +pub const SYS_setrlimit = 164; +pub const SYS_getrusage = 165; +pub const SYS_umask = 166; +pub const SYS_prctl = 167; +pub const SYS_getcpu = 168; +pub const SYS_gettimeofday = 169; +pub const SYS_settimeofday = 170; +pub const SYS_adjtimex = 171; +pub const SYS_getpid = 172; +pub const SYS_getppid = 173; +pub const SYS_getuid = 174; +pub const SYS_geteuid = 175; +pub const SYS_getgid = 176; +pub const SYS_getegid = 177; +pub const SYS_gettid = 178; +pub const SYS_sysinfo = 179; +pub const SYS_mq_open = 180; +pub const SYS_mq_unlink = 181; +pub const SYS_mq_timedsend = 182; +pub const SYS_mq_timedreceive = 183; +pub const SYS_mq_notify = 184; +pub const SYS_mq_getsetattr = 185; +pub const SYS_msgget = 186; +pub const SYS_msgctl = 187; +pub const SYS_msgrcv = 188; +pub const SYS_msgsnd = 189; +pub const SYS_semget = 190; +pub const SYS_semctl = 191; +pub const SYS_semtimedop = 192; +pub const SYS_semop = 193; +pub const SYS_shmget = 194; +pub const SYS_shmctl = 195; +pub const SYS_shmat = 196; +pub const SYS_shmdt = 197; +pub const SYS_socket = 198; +pub const SYS_socketpair = 199; +pub const SYS_bind = 200; +pub const SYS_listen = 201; +pub const SYS_accept = 202; +pub const SYS_connect = 203; +pub const SYS_getsockname = 204; +pub const SYS_getpeername = 205; +pub const SYS_sendto = 206; +pub const SYS_recvfrom = 207; +pub const SYS_setsockopt = 208; +pub const SYS_getsockopt = 209; +pub const SYS_shutdown = 210; +pub const SYS_sendmsg = 211; +pub const SYS_recvmsg = 212; +pub const SYS_readahead = 213; +pub const SYS_brk = 214; +pub const SYS_munmap = 215; +pub const SYS_mremap = 216; +pub const SYS_add_key = 217; +pub const SYS_request_key = 218; +pub const SYS_keyctl = 219; +pub const SYS_clone = 220; +pub const SYS_execve = 221; +pub const SYS_mmap = 222; +pub const SYS_fadvise64 = 223; +pub const SYS_swapon = 224; +pub const SYS_swapoff = 225; +pub const SYS_mprotect = 226; +pub const SYS_msync = 227; +pub const SYS_mlock = 228; +pub const SYS_munlock = 229; +pub const SYS_mlockall = 230; +pub const SYS_munlockall = 231; +pub const SYS_mincore = 232; +pub const SYS_madvise = 233; +pub const SYS_remap_file_pages = 234; +pub const SYS_mbind = 235; +pub const SYS_get_mempolicy = 236; +pub const SYS_set_mempolicy = 237; +pub const SYS_migrate_pages = 238; +pub const SYS_move_pages = 239; +pub const SYS_rt_tgsigqueueinfo = 240; +pub const SYS_perf_event_open = 241; +pub const SYS_accept4 = 242; +pub const SYS_recvmmsg = 243; +pub const SYS_arch_specific_syscall = 244; +pub const SYS_wait4 = 260; +pub const SYS_prlimit64 = 261; +pub const SYS_fanotify_init = 262; +pub const SYS_fanotify_mark = 263; +pub const SYS_clock_adjtime = 266; +pub const SYS_syncfs = 267; +pub const SYS_setns = 268; +pub const SYS_sendmmsg = 269; +pub const SYS_process_vm_readv = 270; +pub const SYS_process_vm_writev = 271; +pub const SYS_kcmp = 272; +pub const SYS_finit_module = 273; +pub const SYS_sched_setattr = 274; +pub const SYS_sched_getattr = 275; +pub const SYS_renameat2 = 276; +pub const SYS_seccomp = 277; +pub const SYS_getrandom = 278; +pub const SYS_memfd_create = 279; +pub const SYS_bpf = 280; +pub const SYS_execveat = 281; +pub const SYS_userfaultfd = 282; +pub const SYS_membarrier = 283; +pub const SYS_mlock2 = 284; +pub const SYS_copy_file_range = 285; +pub const SYS_preadv2 = 286; +pub const SYS_pwritev2 = 287; +pub const SYS_pkey_mprotect = 288; +pub const SYS_pkey_alloc = 289; +pub const SYS_pkey_free = 290; +pub const SYS_statx = 291; +pub const SYS_io_pgetevents = 292; +pub const SYS_rseq = 293; +pub const SYS_kexec_file_load = 294; +pub const SYS_pidfd_send_signal = 424; +pub const SYS_io_uring_setup = 425; +pub const SYS_io_uring_enter = 426; +pub const SYS_io_uring_register = 427; + +pub const O_CREAT = 0o100; +pub const O_EXCL = 0o200; +pub const O_NOCTTY = 0o400; +pub const O_TRUNC = 0o1000; +pub const O_APPEND = 0o2000; +pub const O_NONBLOCK = 0o4000; +pub const O_DSYNC = 0o10000; +pub const O_SYNC = 0o4010000; +pub const O_RSYNC = 0o4010000; +pub const O_DIRECTORY = 0o200000; +pub const O_NOFOLLOW = 0o400000; +pub const O_CLOEXEC = 0o2000000; + +pub const O_ASYNC = 0o20000; +pub const O_DIRECT = 0o40000; +pub const O_LARGEFILE = 0; +pub const O_NOATIME = 0o1000000; +pub const O_PATH = 0o10000000; +pub const O_TMPFILE = 0o20200000; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_SETOWN = 8; +pub const F_GETOWN = 9; +pub const F_SETSIG = 10; +pub const F_GETSIG = 11; + +pub const F_GETLK = 5; +pub const F_SETLK = 6; +pub const F_SETLKW = 7; + +pub const F_SETOWN_EX = 15; +pub const F_GETOWN_EX = 16; + +pub const F_GETOWNER_UIDS = 17; + +pub const AT_FDCWD = -100; +pub const AT_SYMLINK_NOFOLLOW = 0x100; +pub const AT_REMOVEDIR = 0x200; +pub const AT_SYMLINK_FOLLOW = 0x400; +pub const AT_NO_AUTOMOUNT = 0x800; +pub const AT_EMPTY_PATH = 0x1000; + +pub const VDSO_USEFUL = true; +pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; +pub const VDSO_CGT_VER = "LINUX_2.6.39"; + +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: i32, + __pad1: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + __pad1: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t, + msg_flags: i32, +}; + +/// Renamed to Stat to not conflict with the stat function. +pub const Stat = extern struct { + dev: u64, + ino: u64, + nlink: usize, + + mode: u32, + uid: u32, + gid: u32, + __pad0: u32, + rdev: u64, + size: i64, + blksize: isize, + blocks: i64, + + atim: timespec, + mtim: timespec, + ctim: timespec, + __unused: [3]isize, +}; + +pub const timespec = extern struct { + tv_sec: isize, + tv_nsec: isize, +}; + +pub const timeval = extern struct { + tv_sec: isize, + tv_usec: isize, +}; + +pub const timezone = extern struct { + tz_minuteswest: i32, + tz_dsttime: i32, +}; + +pub const Elf_Symndx = u32; diff --git a/std/os/linux/errno.zig b/std/os/bits/linux/errno.zig index 5ad8777f92..741f76fdee 100644 --- a/std/os/linux/errno.zig +++ b/std/os/bits/linux/errno.zig @@ -279,6 +279,7 @@ pub const ESOCKTNOSUPPORT = 94; /// Operation not supported on transport endpoint pub const EOPNOTSUPP = 95; +pub const ENOTSUP = EOPNOTSUPP; /// Protocol family not supported pub const EPFNOSUPPORT = 96; diff --git a/std/os/bits/linux/x86_64.zig b/std/os/bits/linux/x86_64.zig new file mode 100644 index 0000000000..12ac6b8d7a --- /dev/null +++ b/std/os/bits/linux/x86_64.zig @@ -0,0 +1,470 @@ +// x86-64-specific declarations that are intended to be imported into the POSIX namespace. +const std = @import("../../../std.zig"); + +const linux = std.os.linux; +const sockaddr = linux.sockaddr; +const socklen_t = linux.socklen_t; +const iovec = linux.iovec; +const iovec_const = linux.iovec_const; + +pub const SYS_read = 0; +pub const SYS_write = 1; +pub const SYS_open = 2; +pub const SYS_close = 3; +pub const SYS_stat = 4; +pub const SYS_fstat = 5; +pub const SYS_lstat = 6; +pub const SYS_poll = 7; +pub const SYS_lseek = 8; +pub const SYS_mmap = 9; +pub const SYS_mprotect = 10; +pub const SYS_munmap = 11; +pub const SYS_brk = 12; +pub const SYS_rt_sigaction = 13; +pub const SYS_rt_sigprocmask = 14; +pub const SYS_rt_sigreturn = 15; +pub const SYS_ioctl = 16; +pub const SYS_pread = 17; +pub const SYS_pwrite = 18; +pub const SYS_readv = 19; +pub const SYS_writev = 20; +pub const SYS_access = 21; +pub const SYS_pipe = 22; +pub const SYS_select = 23; +pub const SYS_sched_yield = 24; +pub const SYS_mremap = 25; +pub const SYS_msync = 26; +pub const SYS_mincore = 27; +pub const SYS_madvise = 28; +pub const SYS_shmget = 29; +pub const SYS_shmat = 30; +pub const SYS_shmctl = 31; +pub const SYS_dup = 32; +pub const SYS_dup2 = 33; +pub const SYS_pause = 34; +pub const SYS_nanosleep = 35; +pub const SYS_getitimer = 36; +pub const SYS_alarm = 37; +pub const SYS_setitimer = 38; +pub const SYS_getpid = 39; +pub const SYS_sendfile = 40; +pub const SYS_socket = 41; +pub const SYS_connect = 42; +pub const SYS_accept = 43; +pub const SYS_sendto = 44; +pub const SYS_recvfrom = 45; +pub const SYS_sendmsg = 46; +pub const SYS_recvmsg = 47; +pub const SYS_shutdown = 48; +pub const SYS_bind = 49; +pub const SYS_listen = 50; +pub const SYS_getsockname = 51; +pub const SYS_getpeername = 52; +pub const SYS_socketpair = 53; +pub const SYS_setsockopt = 54; +pub const SYS_getsockopt = 55; +pub const SYS_clone = 56; +pub const SYS_fork = 57; +pub const SYS_vfork = 58; +pub const SYS_execve = 59; +pub const SYS_exit = 60; +pub const SYS_wait4 = 61; +pub const SYS_kill = 62; +pub const SYS_uname = 63; +pub const SYS_semget = 64; +pub const SYS_semop = 65; +pub const SYS_semctl = 66; +pub const SYS_shmdt = 67; +pub const SYS_msgget = 68; +pub const SYS_msgsnd = 69; +pub const SYS_msgrcv = 70; +pub const SYS_msgctl = 71; +pub const SYS_fcntl = 72; +pub const SYS_flock = 73; +pub const SYS_fsync = 74; +pub const SYS_fdatasync = 75; +pub const SYS_truncate = 76; +pub const SYS_ftruncate = 77; +pub const SYS_getdents = 78; +pub const SYS_getcwd = 79; +pub const SYS_chdir = 80; +pub const SYS_fchdir = 81; +pub const SYS_rename = 82; +pub const SYS_mkdir = 83; +pub const SYS_rmdir = 84; +pub const SYS_creat = 85; +pub const SYS_link = 86; +pub const SYS_unlink = 87; +pub const SYS_symlink = 88; +pub const SYS_readlink = 89; +pub const SYS_chmod = 90; +pub const SYS_fchmod = 91; +pub const SYS_chown = 92; +pub const SYS_fchown = 93; +pub const SYS_lchown = 94; +pub const SYS_umask = 95; +pub const SYS_gettimeofday = 96; +pub const SYS_getrlimit = 97; +pub const SYS_getrusage = 98; +pub const SYS_sysinfo = 99; +pub const SYS_times = 100; +pub const SYS_ptrace = 101; +pub const SYS_getuid = 102; +pub const SYS_syslog = 103; +pub const SYS_getgid = 104; +pub const SYS_setuid = 105; +pub const SYS_setgid = 106; +pub const SYS_geteuid = 107; +pub const SYS_getegid = 108; +pub const SYS_setpgid = 109; +pub const SYS_getppid = 110; +pub const SYS_getpgrp = 111; +pub const SYS_setsid = 112; +pub const SYS_setreuid = 113; +pub const SYS_setregid = 114; +pub const SYS_getgroups = 115; +pub const SYS_setgroups = 116; +pub const SYS_setresuid = 117; +pub const SYS_getresuid = 118; +pub const SYS_setresgid = 119; +pub const SYS_getresgid = 120; +pub const SYS_getpgid = 121; +pub const SYS_setfsuid = 122; +pub const SYS_setfsgid = 123; +pub const SYS_getsid = 124; +pub const SYS_capget = 125; +pub const SYS_capset = 126; +pub const SYS_rt_sigpending = 127; +pub const SYS_rt_sigtimedwait = 128; +pub const SYS_rt_sigqueueinfo = 129; +pub const SYS_rt_sigsuspend = 130; +pub const SYS_sigaltstack = 131; +pub const SYS_utime = 132; +pub const SYS_mknod = 133; +pub const SYS_uselib = 134; +pub const SYS_personality = 135; +pub const SYS_ustat = 136; +pub const SYS_statfs = 137; +pub const SYS_fstatfs = 138; +pub const SYS_sysfs = 139; +pub const SYS_getpriority = 140; +pub const SYS_setpriority = 141; +pub const SYS_sched_setparam = 142; +pub const SYS_sched_getparam = 143; +pub const SYS_sched_setscheduler = 144; +pub const SYS_sched_getscheduler = 145; +pub const SYS_sched_get_priority_max = 146; +pub const SYS_sched_get_priority_min = 147; +pub const SYS_sched_rr_get_interval = 148; +pub const SYS_mlock = 149; +pub const SYS_munlock = 150; +pub const SYS_mlockall = 151; +pub const SYS_munlockall = 152; +pub const SYS_vhangup = 153; +pub const SYS_modify_ldt = 154; +pub const SYS_pivot_root = 155; +pub const SYS__sysctl = 156; +pub const SYS_prctl = 157; +pub const SYS_arch_prctl = 158; +pub const SYS_adjtimex = 159; +pub const SYS_setrlimit = 160; +pub const SYS_chroot = 161; +pub const SYS_sync = 162; +pub const SYS_acct = 163; +pub const SYS_settimeofday = 164; +pub const SYS_mount = 165; +pub const SYS_umount2 = 166; +pub const SYS_swapon = 167; +pub const SYS_swapoff = 168; +pub const SYS_reboot = 169; +pub const SYS_sethostname = 170; +pub const SYS_setdomainname = 171; +pub const SYS_iopl = 172; +pub const SYS_ioperm = 173; +pub const SYS_create_module = 174; +pub const SYS_init_module = 175; +pub const SYS_delete_module = 176; +pub const SYS_get_kernel_syms = 177; +pub const SYS_query_module = 178; +pub const SYS_quotactl = 179; +pub const SYS_nfsservctl = 180; +pub const SYS_getpmsg = 181; +pub const SYS_putpmsg = 182; +pub const SYS_afs_syscall = 183; +pub const SYS_tuxcall = 184; +pub const SYS_security = 185; +pub const SYS_gettid = 186; +pub const SYS_readahead = 187; +pub const SYS_setxattr = 188; +pub const SYS_lsetxattr = 189; +pub const SYS_fsetxattr = 190; +pub const SYS_getxattr = 191; +pub const SYS_lgetxattr = 192; +pub const SYS_fgetxattr = 193; +pub const SYS_listxattr = 194; +pub const SYS_llistxattr = 195; +pub const SYS_flistxattr = 196; +pub const SYS_removexattr = 197; +pub const SYS_lremovexattr = 198; +pub const SYS_fremovexattr = 199; +pub const SYS_tkill = 200; +pub const SYS_time = 201; +pub const SYS_futex = 202; +pub const SYS_sched_setaffinity = 203; +pub const SYS_sched_getaffinity = 204; +pub const SYS_set_thread_area = 205; +pub const SYS_io_setup = 206; +pub const SYS_io_destroy = 207; +pub const SYS_io_getevents = 208; +pub const SYS_io_submit = 209; +pub const SYS_io_cancel = 210; +pub const SYS_get_thread_area = 211; +pub const SYS_lookup_dcookie = 212; +pub const SYS_epoll_create = 213; +pub const SYS_epoll_ctl_old = 214; +pub const SYS_epoll_wait_old = 215; +pub const SYS_remap_file_pages = 216; +pub const SYS_getdents64 = 217; +pub const SYS_set_tid_address = 218; +pub const SYS_restart_syscall = 219; +pub const SYS_semtimedop = 220; +pub const SYS_fadvise64 = 221; +pub const SYS_timer_create = 222; +pub const SYS_timer_settime = 223; +pub const SYS_timer_gettime = 224; +pub const SYS_timer_getoverrun = 225; +pub const SYS_timer_delete = 226; +pub const SYS_clock_settime = 227; +pub const SYS_clock_gettime = 228; +pub const SYS_clock_getres = 229; +pub const SYS_clock_nanosleep = 230; +pub const SYS_exit_group = 231; +pub const SYS_epoll_wait = 232; +pub const SYS_epoll_ctl = 233; +pub const SYS_tgkill = 234; +pub const SYS_utimes = 235; +pub const SYS_vserver = 236; +pub const SYS_mbind = 237; +pub const SYS_set_mempolicy = 238; +pub const SYS_get_mempolicy = 239; +pub const SYS_mq_open = 240; +pub const SYS_mq_unlink = 241; +pub const SYS_mq_timedsend = 242; +pub const SYS_mq_timedreceive = 243; +pub const SYS_mq_notify = 244; +pub const SYS_mq_getsetattr = 245; +pub const SYS_kexec_load = 246; +pub const SYS_waitid = 247; +pub const SYS_add_key = 248; +pub const SYS_request_key = 249; +pub const SYS_keyctl = 250; +pub const SYS_ioprio_set = 251; +pub const SYS_ioprio_get = 252; +pub const SYS_inotify_init = 253; +pub const SYS_inotify_add_watch = 254; +pub const SYS_inotify_rm_watch = 255; +pub const SYS_migrate_pages = 256; +pub const SYS_openat = 257; +pub const SYS_mkdirat = 258; +pub const SYS_mknodat = 259; +pub const SYS_fchownat = 260; +pub const SYS_futimesat = 261; +pub const SYS_newfstatat = 262; +pub const SYS_fstatat = 262; +pub const SYS_unlinkat = 263; +pub const SYS_renameat = 264; +pub const SYS_linkat = 265; +pub const SYS_symlinkat = 266; +pub const SYS_readlinkat = 267; +pub const SYS_fchmodat = 268; +pub const SYS_faccessat = 269; +pub const SYS_pselect6 = 270; +pub const SYS_ppoll = 271; +pub const SYS_unshare = 272; +pub const SYS_set_robust_list = 273; +pub const SYS_get_robust_list = 274; +pub const SYS_splice = 275; +pub const SYS_tee = 276; +pub const SYS_sync_file_range = 277; +pub const SYS_vmsplice = 278; +pub const SYS_move_pages = 279; +pub const SYS_utimensat = 280; +pub const SYS_epoll_pwait = 281; +pub const SYS_signalfd = 282; +pub const SYS_timerfd_create = 283; +pub const SYS_eventfd = 284; +pub const SYS_fallocate = 285; +pub const SYS_timerfd_settime = 286; +pub const SYS_timerfd_gettime = 287; +pub const SYS_accept4 = 288; +pub const SYS_signalfd4 = 289; +pub const SYS_eventfd2 = 290; +pub const SYS_epoll_create1 = 291; +pub const SYS_dup3 = 292; +pub const SYS_pipe2 = 293; +pub const SYS_inotify_init1 = 294; +pub const SYS_preadv = 295; +pub const SYS_pwritev = 296; +pub const SYS_rt_tgsigqueueinfo = 297; +pub const SYS_perf_event_open = 298; +pub const SYS_recvmmsg = 299; +pub const SYS_fanotify_init = 300; +pub const SYS_fanotify_mark = 301; +pub const SYS_prlimit64 = 302; +pub const SYS_name_to_handle_at = 303; +pub const SYS_open_by_handle_at = 304; +pub const SYS_clock_adjtime = 305; +pub const SYS_syncfs = 306; +pub const SYS_sendmmsg = 307; +pub const SYS_setns = 308; +pub const SYS_getcpu = 309; +pub const SYS_process_vm_readv = 310; +pub const SYS_process_vm_writev = 311; +pub const SYS_kcmp = 312; +pub const SYS_finit_module = 313; +pub const SYS_sched_setattr = 314; +pub const SYS_sched_getattr = 315; +pub const SYS_renameat2 = 316; +pub const SYS_seccomp = 317; +pub const SYS_getrandom = 318; +pub const SYS_memfd_create = 319; +pub const SYS_kexec_file_load = 320; +pub const SYS_bpf = 321; +pub const SYS_execveat = 322; +pub const SYS_userfaultfd = 323; +pub const SYS_membarrier = 324; +pub const SYS_mlock2 = 325; +pub const SYS_copy_file_range = 326; +pub const SYS_preadv2 = 327; +pub const SYS_pwritev2 = 328; +pub const SYS_pkey_mprotect = 329; +pub const SYS_pkey_alloc = 330; +pub const SYS_pkey_free = 331; +pub const SYS_statx = 332; +pub const SYS_io_pgetevents = 333; +pub const SYS_rseq = 334; +pub const SYS_pidfd_send_signal = 424; +pub const SYS_io_uring_setup = 425; +pub const SYS_io_uring_enter = 426; +pub const SYS_io_uring_register = 427; + +pub const O_CREAT = 0o100; +pub const O_EXCL = 0o200; +pub const O_NOCTTY = 0o400; +pub const O_TRUNC = 0o1000; +pub const O_APPEND = 0o2000; +pub const O_NONBLOCK = 0o4000; +pub const O_DSYNC = 0o10000; +pub const O_SYNC = 0o4010000; +pub const O_RSYNC = 0o4010000; +pub const O_DIRECTORY = 0o200000; +pub const O_NOFOLLOW = 0o400000; +pub const O_CLOEXEC = 0o2000000; + +pub const O_ASYNC = 0o20000; +pub const O_DIRECT = 0o40000; +pub const O_LARGEFILE = 0; +pub const O_NOATIME = 0o1000000; +pub const O_PATH = 0o10000000; +pub const O_TMPFILE = 0o20200000; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_SETOWN = 8; +pub const F_GETOWN = 9; +pub const F_SETSIG = 10; +pub const F_GETSIG = 11; + +pub const F_GETLK = 5; +pub const F_SETLK = 6; +pub const F_SETLKW = 7; + +pub const F_SETOWN_EX = 15; +pub const F_GETOWN_EX = 16; + +pub const F_GETOWNER_UIDS = 17; + +pub const AT_FDCWD = -100; +pub const AT_SYMLINK_NOFOLLOW = 0x100; +pub const AT_REMOVEDIR = 0x200; +pub const AT_SYMLINK_FOLLOW = 0x400; +pub const AT_NO_AUTOMOUNT = 0x800; +pub const AT_EMPTY_PATH = 0x1000; + +pub const VDSO_USEFUL = true; +pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; +pub const VDSO_CGT_VER = "LINUX_2.6"; +pub const VDSO_GETCPU_SYM = "__vdso_getcpu"; +pub const VDSO_GETCPU_VER = "LINUX_2.6"; + +pub const ARCH_SET_GS = 0x1001; +pub const ARCH_SET_FS = 0x1002; +pub const ARCH_GET_FS = 0x1003; +pub const ARCH_GET_GS = 0x1004; + +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: i32, + __pad1: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + __pad1: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t, + msg_flags: i32, +}; + +/// Renamed to Stat to not conflict with the stat function. +pub const Stat = extern struct { + dev: u64, + ino: u64, + nlink: usize, + + mode: u32, + uid: u32, + gid: u32, + __pad0: u32, + rdev: u64, + size: i64, + blksize: isize, + blocks: i64, + + atim: timespec, + mtim: timespec, + ctim: timespec, + __unused: [3]isize, +}; + +pub const timespec = extern struct { + tv_sec: isize, + tv_nsec: isize, +}; + +pub const timeval = extern struct { + tv_sec: isize, + tv_usec: isize, +}; + +pub const timezone = extern struct { + tz_minuteswest: i32, + tz_dsttime: i32, +}; + +pub const Elf_Symndx = u32; diff --git a/std/os/bits/netbsd.zig b/std/os/bits/netbsd.zig new file mode 100644 index 0000000000..fc4c2904e0 --- /dev/null +++ b/std/os/bits/netbsd.zig @@ -0,0 +1,725 @@ +const std = @import("../../std.zig"); +const maxInt = std.math.maxInt; + +pub const fd_t = c_int; +pub const pid_t = c_int; + +/// Renamed from `kevent` to `Kevent` to avoid conflict with function name. +pub const Kevent = extern struct { + ident: usize, + filter: i32, + flags: u32, + fflags: u32, + data: i64, + udata: usize, +}; + +pub const pthread_attr_t = extern struct { + pta_magic: u32, + pta_flags: c_int, + pta_private: *c_void, +}; + +pub const msghdr = extern struct { + /// optional address + msg_name: ?*sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + /// optional address + msg_name: ?*const sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec_const, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + +pub const Stat = extern struct { + dev: u64, + mode: u32, + ino: u64, + nlink: usize, + + uid: u32, + gid: u32, + rdev: u64, + + atim: timespec, + mtim: timespec, + ctim: timespec, + birthtim: timespec, + + size: i64, + blocks: i64, + blksize: isize, + flags: u32, + gen: u32, + __spare: [2]u32, +}; + +pub const timespec = extern struct { + tv_sec: i64, + tv_nsec: isize, +}; + +pub const dirent = extern struct { + d_fileno: u64, + d_reclen: u16, + d_namlen: u16, + d_type: u8, + d_off: i64, + d_name: [512]u8, +}; + +pub const in_port_t = u16; +pub const sa_family_t = u8; + +pub const sockaddr = extern union { + in: sockaddr_in, + in6: sockaddr_in6, +}; + +pub const sockaddr_in = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + addr: u32, + zero: [8]u8, +}; + +pub const sockaddr_in6 = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + flowinfo: u32, + addr: [16]u8, + scope_id: u32, +}; + +pub const CTL_KERN = 1; +pub const CTL_DEBUG = 5; + +pub const KERN_PROC_ARGS = 48; // struct: process argv/env +pub const KERN_PROC_PATHNAME = 5; // path to executable + +pub const PATH_MAX = 1024; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +pub const PROT_NONE = 0; +pub const PROT_READ = 1; +pub const PROT_WRITE = 2; +pub const PROT_EXEC = 4; + +pub const CLOCK_REALTIME = 0; +pub const CLOCK_VIRTUAL = 1; +pub const CLOCK_PROF = 2; +pub const CLOCK_MONOTONIC = 3; +pub const CLOCK_THREAD_CPUTIME_ID = 0x20000000; +pub const CLOCK_PROCESS_CPUTIME_ID = 0x40000000; + +pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); +pub const MAP_SHARED = 0x0001; +pub const MAP_PRIVATE = 0x0002; +pub const MAP_REMAPDUP = 0x0004; +pub const MAP_FIXED = 0x0010; +pub const MAP_RENAME = 0x0020; +pub const MAP_NORESERVE = 0x0040; +pub const MAP_INHERIT = 0x0080; +pub const MAP_HASSEMAPHORE = 0x0200; +pub const MAP_TRYFIXED = 0x0400; +pub const MAP_WIRED = 0x0800; + +pub const MAP_FILE = 0x0000; +pub const MAP_NOSYNC = 0x0800; +pub const MAP_ANON = 0x1000; +pub const MAP_ANONYMOUS = MAP_ANON; +pub const MAP_STACK = 0x2000; + +pub const WNOHANG = 0x00000001; +pub const WUNTRACED = 0x00000002; +pub const WSTOPPED = WUNTRACED; +pub const WCONTINUED = 0x00000010; +pub const WNOWAIT = 0x00010000; +pub const WEXITED = 0x00000020; +pub const WTRAPPED = 0x00000040; + +pub const SA_ONSTACK = 0x0001; +pub const SA_RESTART = 0x0002; +pub const SA_RESETHAND = 0x0004; +pub const SA_NOCLDSTOP = 0x0008; +pub const SA_NODEFER = 0x0010; +pub const SA_NOCLDWAIT = 0x0020; +pub const SA_SIGINFO = 0x0040; + +pub const SIGHUP = 1; +pub const SIGINT = 2; +pub const SIGQUIT = 3; +pub const SIGILL = 4; +pub const SIGTRAP = 5; +pub const SIGABRT = 6; +pub const SIGIOT = SIGABRT; +pub const SIGEMT = 7; +pub const SIGFPE = 8; +pub const SIGKILL = 9; +pub const SIGBUS = 10; +pub const SIGSEGV = 11; +pub const SIGSYS = 12; +pub const SIGPIPE = 13; +pub const SIGALRM = 14; +pub const SIGTERM = 15; +pub const SIGURG = 16; +pub const SIGSTOP = 17; +pub const SIGTSTP = 18; +pub const SIGCONT = 19; +pub const SIGCHLD = 20; +pub const SIGTTIN = 21; +pub const SIGTTOU = 22; +pub const SIGIO = 23; +pub const SIGXCPU = 24; +pub const SIGXFSZ = 25; +pub const SIGVTALRM = 26; +pub const SIGPROF = 27; +pub const SIGWINCH = 28; +pub const SIGINFO = 29; +pub const SIGUSR1 = 30; +pub const SIGUSR2 = 31; +pub const SIGPWR = 32; + +pub const SIGRTMIN = 33; +pub const SIGRTMAX = 63; + +// access function +pub const F_OK = 0; // test for existence of file +pub const X_OK = 1; // test for execute or search permission +pub const W_OK = 2; // test for write permission +pub const R_OK = 4; // test for read permission + +pub const O_RDONLY = 0x0000; +pub const O_WRONLY = 0x0001; +pub const O_RDWR = 0x0002; +pub const O_ACCMODE = 0x0003; + +pub const O_CREAT = 0x0200; +pub const O_EXCL = 0x0800; +pub const O_NOCTTY = 0x8000; +pub const O_TRUNC = 0x0400; +pub const O_APPEND = 0x0008; +pub const O_NONBLOCK = 0x0004; +pub const O_DSYNC = 0x00010000; +pub const O_SYNC = 0x0080; +pub const O_RSYNC = 0x00020000; +pub const O_DIRECTORY = 0x00080000; +pub const O_NOFOLLOW = 0x00000100; +pub const O_CLOEXEC = 0x00400000; + +pub const O_ASYNC = 0x0040; +pub const O_DIRECT = 0x00080000; +pub const O_LARGEFILE = 0; +pub const O_NOATIME = 0; +pub const O_PATH = 0; +pub const O_TMPFILE = 0; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_GETOWN = 5; +pub const F_SETOWN = 6; + +pub const F_GETLK = 7; +pub const F_SETLK = 8; +pub const F_SETLKW = 9; + +pub const SEEK_SET = 0; +pub const SEEK_CUR = 1; +pub const SEEK_END = 2; + +pub const SIG_BLOCK = 1; +pub const SIG_UNBLOCK = 2; +pub const SIG_SETMASK = 3; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; + +pub const SOCK_CLOEXEC = 0x10000000; +pub const SOCK_NONBLOCK = 0x20000000; + +pub const PROTO_ip = 0; +pub const PROTO_icmp = 1; +pub const PROTO_igmp = 2; +pub const PROTO_ggp = 3; +pub const PROTO_ipencap = 4; +pub const PROTO_tcp = 6; +pub const PROTO_egp = 8; +pub const PROTO_pup = 12; +pub const PROTO_udp = 17; +pub const PROTO_xns_idp = 22; +pub const PROTO_iso_tp4 = 29; +pub const PROTO_ipv6 = 41; +pub const PROTO_ipv6_route = 43; +pub const PROTO_ipv6_frag = 44; +pub const PROTO_rsvp = 46; +pub const PROTO_gre = 47; +pub const PROTO_esp = 50; +pub const PROTO_ah = 51; +pub const PROTO_ipv6_icmp = 58; +pub const PROTO_ipv6_nonxt = 59; +pub const PROTO_ipv6_opts = 60; +pub const PROTO_encap = 98; +pub const PROTO_pim = 103; +pub const PROTO_raw = 255; + +pub const PF_UNSPEC = 0; +pub const PF_LOCAL = 1; +pub const PF_UNIX = PF_LOCAL; +pub const PF_FILE = PF_LOCAL; +pub const PF_INET = 2; +pub const PF_APPLETALK = 16; +pub const PF_INET6 = 24; +pub const PF_DECnet = 12; +pub const PF_KEY = 29; +pub const PF_ROUTE = 34; +pub const PF_SNA = 11; +pub const PF_MPLS = 33; +pub const PF_CAN = 35; +pub const PF_BLUETOOTH = 31; +pub const PF_ISDN = 26; +pub const PF_MAX = 37; + +pub const AF_UNSPEC = PF_UNSPEC; +pub const AF_LOCAL = PF_LOCAL; +pub const AF_UNIX = AF_LOCAL; +pub const AF_FILE = AF_LOCAL; +pub const AF_INET = PF_INET; +pub const AF_APPLETALK = PF_APPLETALK; +pub const AF_INET6 = PF_INET6; +pub const AF_KEY = PF_KEY; +pub const AF_ROUTE = PF_ROUTE; +pub const AF_SNA = PF_SNA; +pub const AF_MPLS = PF_MPLS; +pub const AF_CAN = PF_CAN; +pub const AF_BLUETOOTH = PF_BLUETOOTH; +pub const AF_ISDN = PF_ISDN; +pub const AF_MAX = PF_MAX; + +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; + +/// add event to kq (implies enable) +pub const EV_ADD = 0x0001; + +/// delete event from kq +pub const EV_DELETE = 0x0002; + +/// enable event +pub const EV_ENABLE = 0x0004; + +/// disable event (not reported) +pub const EV_DISABLE = 0x0008; + +/// only report one occurrence +pub const EV_ONESHOT = 0x0010; + +/// clear event state after reporting +pub const EV_CLEAR = 0x0020; + +/// force immediate event output +/// ... with or without EV_ERROR +/// ... use KEVENT_FLAG_ERROR_EVENTS +/// on syscalls supporting flags +pub const EV_RECEIPT = 0x0040; + +/// disable event after reporting +pub const EV_DISPATCH = 0x0080; + +pub const EVFILT_READ = 0; +pub const EVFILT_WRITE = 1; + +/// attached to aio requests +pub const EVFILT_AIO = 2; + +/// attached to vnodes +pub const EVFILT_VNODE = 3; + +/// attached to struct proc +pub const EVFILT_PROC = 4; + +/// attached to struct proc +pub const EVFILT_SIGNAL = 5; + +/// timers +pub const EVFILT_TIMER = 6; + +/// Filesystem events +pub const EVFILT_FS = 7; + +/// On input, NOTE_TRIGGER causes the event to be triggered for output. +pub const NOTE_TRIGGER = 0x08000000; + +/// low water mark +pub const NOTE_LOWAT = 0x00000001; + +/// vnode was removed +pub const NOTE_DELETE = 0x00000001; + +/// data contents changed +pub const NOTE_WRITE = 0x00000002; + +/// size increased +pub const NOTE_EXTEND = 0x00000004; + +/// attributes changed +pub const NOTE_ATTRIB = 0x00000008; + +/// link count changed +pub const NOTE_LINK = 0x00000010; + +/// vnode was renamed +pub const NOTE_RENAME = 0x00000020; + +/// vnode access was revoked +pub const NOTE_REVOKE = 0x00000040; + +/// process exited +pub const NOTE_EXIT = 0x80000000; + +/// process forked +pub const NOTE_FORK = 0x40000000; + +/// process exec'd +pub const NOTE_EXEC = 0x20000000; + +/// mask for signal & exit status +pub const NOTE_PDATAMASK = 0x000fffff; +pub const NOTE_PCTRLMASK = 0xf0000000; + +pub const TIOCCBRK = 0x2000747a; +pub const TIOCCDTR = 0x20007478; +pub const TIOCCONS = 0x80047462; +pub const TIOCDCDTIMESTAMP = 0x40107458; +pub const TIOCDRAIN = 0x2000745e; +pub const TIOCEXCL = 0x2000740d; +pub const TIOCEXT = 0x80047460; +pub const TIOCFLAG_CDTRCTS = 0x10; +pub const TIOCFLAG_CLOCAL = 0x2; +pub const TIOCFLAG_CRTSCTS = 0x4; +pub const TIOCFLAG_MDMBUF = 0x8; +pub const TIOCFLAG_SOFTCAR = 0x1; +pub const TIOCFLUSH = 0x80047410; +pub const TIOCGETA = 0x402c7413; +pub const TIOCGETD = 0x4004741a; +pub const TIOCGFLAGS = 0x4004745d; +pub const TIOCGLINED = 0x40207442; +pub const TIOCGPGRP = 0x40047477; +pub const TIOCGQSIZE = 0x40047481; +pub const TIOCGRANTPT = 0x20007447; +pub const TIOCGSID = 0x40047463; +pub const TIOCGSIZE = 0x40087468; +pub const TIOCGWINSZ = 0x40087468; +pub const TIOCMBIC = 0x8004746b; +pub const TIOCMBIS = 0x8004746c; +pub const TIOCMGET = 0x4004746a; +pub const TIOCMSET = 0x8004746d; +pub const TIOCM_CAR = 0x40; +pub const TIOCM_CD = 0x40; +pub const TIOCM_CTS = 0x20; +pub const TIOCM_DSR = 0x100; +pub const TIOCM_DTR = 0x2; +pub const TIOCM_LE = 0x1; +pub const TIOCM_RI = 0x80; +pub const TIOCM_RNG = 0x80; +pub const TIOCM_RTS = 0x4; +pub const TIOCM_SR = 0x10; +pub const TIOCM_ST = 0x8; +pub const TIOCNOTTY = 0x20007471; +pub const TIOCNXCL = 0x2000740e; +pub const TIOCOUTQ = 0x40047473; +pub const TIOCPKT = 0x80047470; +pub const TIOCPKT_DATA = 0x0; +pub const TIOCPKT_DOSTOP = 0x20; +pub const TIOCPKT_FLUSHREAD = 0x1; +pub const TIOCPKT_FLUSHWRITE = 0x2; +pub const TIOCPKT_IOCTL = 0x40; +pub const TIOCPKT_NOSTOP = 0x10; +pub const TIOCPKT_START = 0x8; +pub const TIOCPKT_STOP = 0x4; +pub const TIOCPTMGET = 0x40287446; +pub const TIOCPTSNAME = 0x40287448; +pub const TIOCRCVFRAME = 0x80087445; +pub const TIOCREMOTE = 0x80047469; +pub const TIOCSBRK = 0x2000747b; +pub const TIOCSCTTY = 0x20007461; +pub const TIOCSDTR = 0x20007479; +pub const TIOCSETA = 0x802c7414; +pub const TIOCSETAF = 0x802c7416; +pub const TIOCSETAW = 0x802c7415; +pub const TIOCSETD = 0x8004741b; +pub const TIOCSFLAGS = 0x8004745c; +pub const TIOCSIG = 0x2000745f; +pub const TIOCSLINED = 0x80207443; +pub const TIOCSPGRP = 0x80047476; +pub const TIOCSQSIZE = 0x80047480; +pub const TIOCSSIZE = 0x80087467; +pub const TIOCSTART = 0x2000746e; +pub const TIOCSTAT = 0x80047465; +pub const TIOCSTI = 0x80017472; +pub const TIOCSTOP = 0x2000746f; +pub const TIOCSWINSZ = 0x80087467; +pub const TIOCUCNTL = 0x80047466; +pub const TIOCXMTFRAME = 0x80087444; + +pub fn WEXITSTATUS(s: u32) u32 { + return (s >> 8) & 0xff; +} +pub fn WTERMSIG(s: u32) u32 { + return s & 0x7f; +} +pub fn WSTOPSIG(s: u32) u32 { + return WEXITSTATUS(s); +} +pub fn WIFEXITED(s: u32) bool { + return WTERMSIG(s) == 0; +} + +pub fn WIFCONTINUED(s: u32) bool { + return ((s & 0x7f) == 0xffff); +} + +pub fn WIFSTOPPED(s: u32) bool { + return ((s & 0x7f != 0x7f) and !WIFCONTINUED(s)); +} + +pub fn WIFSIGNALED(s: u32) bool { + return !WIFSTOPPED(s) and !WIFCONTINUED(s) and !WIFEXITED(s); +} + +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, +}; + +const NSIG = 32; + +pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); +pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); + +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = extern struct { + /// signal handler + __sigaction_u: extern union { + __sa_handler: extern fn (i32) void, + __sa_sigaction: extern fn (i32, *__siginfo, usize) void, + }, + + /// see signal options + sa_flags: u32, + + /// signal mask to apply + sa_mask: sigset_t, +}; + +pub const _SIG_WORDS = 4; +pub const _SIG_MAXSIG = 128; + +pub inline fn _SIG_IDX(sig: usize) usize { + return sig - 1; +} +pub inline fn _SIG_WORD(sig: usize) usize { + return_SIG_IDX(sig) >> 5; +} +pub inline fn _SIG_BIT(sig: usize) usize { + return 1 << (_SIG_IDX(sig) & 31); +} +pub inline fn _SIG_VALID(sig: usize) usize { + return sig <= _SIG_MAXSIG and sig > 0; +} + +pub const sigset_t = extern struct { + __bits: [_SIG_WORDS]u32, +}; + +pub const EPERM = 1; // Operation not permitted +pub const ENOENT = 2; // No such file or directory +pub const ESRCH = 3; // No such process +pub const EINTR = 4; // Interrupted system call +pub const EIO = 5; // Input/output error +pub const ENXIO = 6; // Device not configured +pub const E2BIG = 7; // Argument list too long +pub const ENOEXEC = 8; // Exec format error +pub const EBADF = 9; // Bad file descriptor +pub const ECHILD = 10; // No child processes +pub const EDEADLK = 11; // Resource deadlock avoided +// 11 was EAGAIN +pub const ENOMEM = 12; // Cannot allocate memory +pub const EACCES = 13; // Permission denied +pub const EFAULT = 14; // Bad address +pub const ENOTBLK = 15; // Block device required +pub const EBUSY = 16; // Device busy +pub const EEXIST = 17; // File exists +pub const EXDEV = 18; // Cross-device link +pub const ENODEV = 19; // Operation not supported by device +pub const ENOTDIR = 20; // Not a directory +pub const EISDIR = 21; // Is a directory +pub const EINVAL = 22; // Invalid argument +pub const ENFILE = 23; // Too many open files in system +pub const EMFILE = 24; // Too many open files +pub const ENOTTY = 25; // Inappropriate ioctl for device +pub const ETXTBSY = 26; // Text file busy +pub const EFBIG = 27; // File too large +pub const ENOSPC = 28; // No space left on device +pub const ESPIPE = 29; // Illegal seek +pub const EROFS = 30; // Read-only file system +pub const EMLINK = 31; // Too many links +pub const EPIPE = 32; // Broken pipe + +// math software +pub const EDOM = 33; // Numerical argument out of domain +pub const ERANGE = 34; // Result too large or too small + +// non-blocking and interrupt i/o +pub const EAGAIN = 35; // Resource temporarily unavailable +pub const EWOULDBLOCK = EAGAIN; // Operation would block +pub const EINPROGRESS = 36; // Operation now in progress +pub const EALREADY = 37; // Operation already in progress + +// ipc/network software -- argument errors +pub const ENOTSOCK = 38; // Socket operation on non-socket +pub const EDESTADDRREQ = 39; // Destination address required +pub const EMSGSIZE = 40; // Message too long +pub const EPROTOTYPE = 41; // Protocol wrong type for socket +pub const ENOPROTOOPT = 42; // Protocol option not available +pub const EPROTONOSUPPORT = 43; // Protocol not supported +pub const ESOCKTNOSUPPORT = 44; // Socket type not supported +pub const EOPNOTSUPP = 45; // Operation not supported +pub const EPFNOSUPPORT = 46; // Protocol family not supported +pub const EAFNOSUPPORT = 47; // Address family not supported by protocol family +pub const EADDRINUSE = 48; // Address already in use +pub const EADDRNOTAVAIL = 49; // Can't assign requested address + +// ipc/network software -- operational errors +pub const ENETDOWN = 50; // Network is down +pub const ENETUNREACH = 51; // Network is unreachable +pub const ENETRESET = 52; // Network dropped connection on reset +pub const ECONNABORTED = 53; // Software caused connection abort +pub const ECONNRESET = 54; // Connection reset by peer +pub const ENOBUFS = 55; // No buffer space available +pub const EISCONN = 56; // Socket is already connected +pub const ENOTCONN = 57; // Socket is not connected +pub const ESHUTDOWN = 58; // Can't send after socket shutdown +pub const ETOOMANYREFS = 59; // Too many references: can't splice +pub const ETIMEDOUT = 60; // Operation timed out +pub const ECONNREFUSED = 61; // Connection refused + +pub const ELOOP = 62; // Too many levels of symbolic links +pub const ENAMETOOLONG = 63; // File name too long + +// should be rearranged +pub const EHOSTDOWN = 64; // Host is down +pub const EHOSTUNREACH = 65; // No route to host +pub const ENOTEMPTY = 66; // Directory not empty + +// quotas & mush +pub const EPROCLIM = 67; // Too many processes +pub const EUSERS = 68; // Too many users +pub const EDQUOT = 69; // Disc quota exceeded + +// Network File System +pub const ESTALE = 70; // Stale NFS file handle +pub const EREMOTE = 71; // Too many levels of remote in path +pub const EBADRPC = 72; // RPC struct is bad +pub const ERPCMISMATCH = 73; // RPC version wrong +pub const EPROGUNAVAIL = 74; // RPC prog. not avail +pub const EPROGMISMATCH = 75; // Program version wrong +pub const EPROCUNAVAIL = 76; // Bad procedure for program + +pub const ENOLCK = 77; // No locks available +pub const ENOSYS = 78; // Function not implemented + +pub const EFTYPE = 79; // Inappropriate file type or format +pub const EAUTH = 80; // Authentication error +pub const ENEEDAUTH = 81; // Need authenticator + +// SystemV IPC +pub const EIDRM = 82; // Identifier removed +pub const ENOMSG = 83; // No message of desired type +pub const EOVERFLOW = 84; // Value too large to be stored in data type + +// Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 +pub const EILSEQ = 85; // Illegal byte sequence + +// From IEEE Std 1003.1-2001 +// Base, Realtime, Threads or Thread Priority Scheduling option errors +pub const ENOTSUP = 86; // Not supported + +// Realtime option errors +pub const ECANCELED = 87; // Operation canceled + +// Realtime, XSI STREAMS option errors +pub const EBADMSG = 88; // Bad or Corrupt message + +// XSI STREAMS option errors +pub const ENODATA = 89; // No message available +pub const ENOSR = 90; // No STREAM resources +pub const ENOSTR = 91; // Not a STREAM +pub const ETIME = 92; // STREAM ioctl timeout + +// File system extended attribute errors +pub const ENOATTR = 93; // Attribute not found + +// Realtime, XSI STREAMS option errors +pub const EMULTIHOP = 94; // Multihop attempted +pub const ENOLINK = 95; // Link has been severed +pub const EPROTO = 96; // Protocol error + +pub const ELAST = 96; // Must equal largest errno diff --git a/std/os/wasi/core.zig b/std/os/bits/wasi.zig index f2bef73be9..93d2a82fde 100644 --- a/std/os/wasi/core.zig +++ b/std/os/bits/wasi.zig @@ -1,5 +1,6 @@ -// Based on https://github.com/CraneStation/wasi-sysroot/blob/wasi/libc-bottom-half/headers/public/wasi/core.h -// and https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; pub const advice_t = u8; pub const ADVICE_NORMAL: advice_t = 0; @@ -9,11 +10,6 @@ pub const ADVICE_WILLNEED: advice_t = 3; pub const ADVICE_DONTNEED: advice_t = 4; pub const ADVICE_NOREUSE: advice_t = 5; -pub const ciovec_t = extern struct { - buf: [*]const u8, - buf_len: usize, -}; - pub const clockid_t = u32; pub const CLOCK_REALTIME: clockid_t = 0; pub const CLOCK_MONOTONIC: clockid_t = 1; @@ -182,11 +178,6 @@ pub const FILESTAT_SET_MTIM_NOW: fstflags_t = 0x0008; pub const inode_t = u64; -pub const iovec_t = extern struct { - buf: [*]u8, - buf_len: usize, -}; - pub const linkcount_t = u32; pub const lookupflags_t = u32; @@ -314,61 +305,3 @@ pub const whence_t = u8; pub const WHENCE_CUR: whence_t = 0; pub const WHENCE_END: whence_t = 1; pub const WHENCE_SET: whence_t = 2; - -pub extern "wasi_unstable" fn args_get(argv: [*][*]u8, argv_buf: [*]u8) errno_t; -pub extern "wasi_unstable" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t; - -pub extern "wasi_unstable" fn clock_res_get(clock_id: clockid_t, resolution: *timestamp_t) errno_t; -pub extern "wasi_unstable" fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t; - -pub extern "wasi_unstable" fn environ_get(environ: [*]?[*]u8, environ_buf: [*]u8) errno_t; -pub extern "wasi_unstable" fn environ_sizes_get(environ_count: *usize, environ_buf_size: *usize) errno_t; - -pub extern "wasi_unstable" fn fd_advise(fd: fd_t, offset: filesize_t, len: filesize_t, advice: advice_t) errno_t; -pub extern "wasi_unstable" fn fd_allocate(fd: fd_t, offset: filesize_t, len: filesize_t) errno_t; -pub extern "wasi_unstable" fn fd_close(fd: fd_t) errno_t; -pub extern "wasi_unstable" fn fd_datasync(fd: fd_t) errno_t; -pub extern "wasi_unstable" fn fd_pread(fd: fd_t, iovs: *const iovec_t, iovs_len: usize, offset: filesize_t, nread: *usize) errno_t; -pub extern "wasi_unstable" fn fd_pwrite(fd: fd_t, iovs: *const ciovec_t, iovs_len: usize, offset: filesize_t, nwritten: *usize) errno_t; -pub extern "wasi_unstable" fn fd_read(fd: fd_t, iovs: *const iovec_t, iovs_len: usize, nread: *usize) errno_t; -pub extern "wasi_unstable" fn fd_readdir(fd: fd_t, buf: [*]u8, buf_len: usize, cookie: dircookie_t, bufused: *usize) errno_t; -pub extern "wasi_unstable" fn fd_renumber(from: fd_t, to: fd_t) errno_t; -pub extern "wasi_unstable" fn fd_seek(fd: fd_t, offset: filedelta_t, whence: whence_t, newoffset: *filesize_t) errno_t; -pub extern "wasi_unstable" fn fd_sync(fd: fd_t) errno_t; -pub extern "wasi_unstable" fn fd_tell(fd: fd_t, newoffset: *filesize_t) errno_t; -pub extern "wasi_unstable" fn fd_write(fd: fd_t, iovs: *const ciovec_t, iovs_len: usize, nwritten: *usize) errno_t; - -pub extern "wasi_unstable" fn fd_fdstat_get(fd: fd_t, buf: *fdstat_t) errno_t; -pub extern "wasi_unstable" fn fd_fdstat_set_flags(fd: fd_t, flags: fdflags_t) errno_t; -pub extern "wasi_unstable" fn fd_fdstat_set_rights(fd: fd_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t) errno_t; - -pub extern "wasi_unstable" fn fd_filestat_get(fd: fd_t, buf: *filestat_t) errno_t; -pub extern "wasi_unstable" fn fd_filestat_set_size(fd: fd_t, st_size: filesize_t) errno_t; -pub extern "wasi_unstable" fn fd_filestat_set_times(fd: fd_t, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; - -pub extern "wasi_unstable" fn fd_prestat_get(fd: fd_t, buf: *prestat_t) errno_t; -pub extern "wasi_unstable" fn fd_prestat_dir_name(fd: fd_t, path: [*]u8, path_len: usize) errno_t; - -pub extern "wasi_unstable" fn path_create_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_filestat_get(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, buf: *filestat_t) errno_t; -pub extern "wasi_unstable" fn path_filestat_set_times(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; -pub extern "wasi_unstable" fn path_link(old_fd: fd_t, old_flags: lookupflags_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_open(dirfd: fd_t, dirflags: lookupflags_t, path: [*]const u8, path_len: usize, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, fs_flags: fdflags_t, fd: *fd_t) errno_t; -pub extern "wasi_unstable" fn path_readlink(fd: fd_t, path: [*]const u8, path_len: usize, buf: [*]u8, buf_len: usize, bufused: *usize) errno_t; -pub extern "wasi_unstable" fn path_remove_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_rename(old_fd: fd_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_symlink(old_path: [*]const u8, old_path_len: usize, fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_unlink_file(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; - -pub extern "wasi_unstable" fn poll_oneoff(in: *const subscription_t, out: *event_t, nsubscriptions: usize, nevents: *usize) errno_t; - -pub extern "wasi_unstable" fn proc_exit(rval: exitcode_t) noreturn; -pub extern "wasi_unstable" fn proc_raise(sig: signal_t) errno_t; - -pub extern "wasi_unstable" fn random_get(buf: [*]u8, buf_len: usize) errno_t; - -pub extern "wasi_unstable" fn sched_yield() errno_t; - -pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t; -pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; -pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; diff --git a/std/os/bits/windows.zig b/std/os/bits/windows.zig new file mode 100644 index 0000000000..a242113ba0 --- /dev/null +++ b/std/os/bits/windows.zig @@ -0,0 +1,167 @@ +// The reference for these types and values is Microsoft Windows's ucrt (Universal C RunTime). + +use @import("../windows/bits.zig"); + +pub const fd_t = HANDLE; +pub const pid_t = HANDLE; + +pub const PATH_MAX = 260; + +pub const time_t = c_longlong; + +pub const timespec = extern struct { + tv_sec: time_t, + tv_nsec: c_long, +}; + +pub const sig_atomic_t = c_int; + +/// maximum signal number + 1 +pub const NSIG = 23; + +// Signal types + +/// interrupt +pub const SIGINT = 2; + +/// illegal instruction - invalid function image +pub const SIGILL = 4; + +/// floating point exception +pub const SIGFPE = 8; + +/// segment violation +pub const SIGSEGV = 11; + +/// Software termination signal from kill +pub const SIGTERM = 15; + +/// Ctrl-Break sequence +pub const SIGBREAK = 21; + +/// abnormal termination triggered by abort call +pub const SIGABRT = 22; + +/// SIGABRT compatible with other platforms, same as SIGABRT +pub const SIGABRT_COMPAT = 6; + +// Signal action codes + +/// default signal action +pub const SIG_DFL = 0; + +/// ignore signal +pub const SIG_IGN = 1; + +/// return current value +pub const SIG_GET = 2; + +/// signal gets error +pub const SIG_SGE = 3; + +/// acknowledge +pub const SIG_ACK = 4; + +/// Signal error value (returned by signal call on error) +pub const SIG_ERR = -1; + +pub const SEEK_SET = 0; +pub const SEEK_CUR = 1; +pub const SEEK_END = 2; + +pub const EPERM = 1; +pub const ENOENT = 2; +pub const ESRCH = 3; +pub const EINTR = 4; +pub const EIO = 5; +pub const ENXIO = 6; +pub const E2BIG = 7; +pub const ENOEXEC = 8; +pub const EBADF = 9; +pub const ECHILD = 10; +pub const EAGAIN = 11; +pub const ENOMEM = 12; +pub const EACCES = 13; +pub const EFAULT = 14; +pub const EBUSY = 16; +pub const EEXIST = 17; +pub const EXDEV = 18; +pub const ENODEV = 19; +pub const ENOTDIR = 20; +pub const EISDIR = 21; +pub const ENFILE = 23; +pub const EMFILE = 24; +pub const ENOTTY = 25; +pub const EFBIG = 27; +pub const ENOSPC = 28; +pub const ESPIPE = 29; +pub const EROFS = 30; +pub const EMLINK = 31; +pub const EPIPE = 32; +pub const EDOM = 33; +pub const EDEADLK = 36; +pub const ENAMETOOLONG = 38; +pub const ENOLCK = 39; +pub const ENOSYS = 40; +pub const ENOTEMPTY = 41; + +pub const EINVAL = 22; +pub const ERANGE = 34; +pub const EILSEQ = 42; +pub const STRUNCATE = 80; + +// Support EDEADLOCK for compatibility with older Microsoft C versions +pub const EDEADLOCK = EDEADLK; + +// POSIX Supplement +pub const EADDRINUSE = 100; +pub const EADDRNOTAVAIL = 101; +pub const EAFNOSUPPORT = 102; +pub const EALREADY = 103; +pub const EBADMSG = 104; +pub const ECANCELED = 105; +pub const ECONNABORTED = 106; +pub const ECONNREFUSED = 107; +pub const ECONNRESET = 108; +pub const EDESTADDRREQ = 109; +pub const EHOSTUNREACH = 110; +pub const EIDRM = 111; +pub const EINPROGRESS = 112; +pub const EISCONN = 113; +pub const ELOOP = 114; +pub const EMSGSIZE = 115; +pub const ENETDOWN = 116; +pub const ENETRESET = 117; +pub const ENETUNREACH = 118; +pub const ENOBUFS = 119; +pub const ENODATA = 120; +pub const ENOLINK = 121; +pub const ENOMSG = 122; +pub const ENOPROTOOPT = 123; +pub const ENOSR = 124; +pub const ENOSTR = 125; +pub const ENOTCONN = 126; +pub const ENOTRECOVERABLE = 127; +pub const ENOTSOCK = 128; +pub const ENOTSUP = 129; +pub const EOPNOTSUPP = 130; +pub const EOTHER = 131; +pub const EOVERFLOW = 132; +pub const EOWNERDEAD = 133; +pub const EPROTO = 134; +pub const EPROTONOSUPPORT = 135; +pub const EPROTOTYPE = 136; +pub const ETIME = 137; +pub const ETIMEDOUT = 138; +pub const ETXTBSY = 139; +pub const EWOULDBLOCK = 140; +pub const EDQUOT = 10069; + +pub const F_OK = 0; + +// These are workarounds for "use of undeclared identifier" compile errors +// TODO make the compiler even more lazy. don't emit "use of undeclared identifier" errors +// for if branches that aren't taken. +pub const SIGKILL = @compileError("Windows libc does not have this"); + + diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 04122100f4..d6e0a1b77c 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -1,885 +1,7 @@ +const builtin = @import("builtin"); const std = @import("../std.zig"); -const c = std.c; -const assert = std.debug.assert; -const maxInt = std.math.maxInt; - -pub use @import("darwin/errno.zig"); - -pub const PATH_MAX = 1024; - -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; - -/// [MC2] no permissions -pub const PROT_NONE = 0x00; - -/// [MC2] pages can be read -pub const PROT_READ = 0x01; - -/// [MC2] pages can be written -pub const PROT_WRITE = 0x02; - -/// [MC2] pages can be executed -pub const PROT_EXEC = 0x04; - -/// allocated from memory, swap space -pub const MAP_ANONYMOUS = 0x1000; - -/// map from file (default) -pub const MAP_FILE = 0x0000; - -/// interpret addr exactly -pub const MAP_FIXED = 0x0010; - -/// region may contain semaphores -pub const MAP_HASSEMAPHORE = 0x0200; - -/// changes are private -pub const MAP_PRIVATE = 0x0002; - -/// share changes -pub const MAP_SHARED = 0x0001; - -/// don't cache pages for this mapping -pub const MAP_NOCACHE = 0x0400; - -/// don't reserve needed swap area -pub const MAP_NORESERVE = 0x0040; -pub const MAP_FAILED = maxInt(usize); - -/// [XSI] no hang in wait/no child to reap -pub const WNOHANG = 0x00000001; - -/// [XSI] notify on stop, untraced child -pub const WUNTRACED = 0x00000002; - -/// take signal on signal stack -pub const SA_ONSTACK = 0x0001; - -/// restart system on signal return -pub const SA_RESTART = 0x0002; - -/// reset to SIG_DFL when taking signal -pub const SA_RESETHAND = 0x0004; - -/// do not generate SIGCHLD on child stop -pub const SA_NOCLDSTOP = 0x0008; - -/// don't mask the signal we're delivering -pub const SA_NODEFER = 0x0010; - -/// don't keep zombies around -pub const SA_NOCLDWAIT = 0x0020; - -/// signal handler with SA_SIGINFO args -pub const SA_SIGINFO = 0x0040; - -/// do not bounce off kernel's sigtramp -pub const SA_USERTRAMP = 0x0100; - -/// signal handler with SA_SIGINFO args with 64bit regs information -pub const SA_64REGSET = 0x0200; - -pub const O_LARGEFILE = 0x0000; -pub const O_PATH = 0x0000; - -pub const F_OK = 0; -pub const X_OK = 1; -pub const W_OK = 2; -pub const R_OK = 4; - -/// open for reading only -pub const O_RDONLY = 0x0000; - -/// open for writing only -pub const O_WRONLY = 0x0001; - -/// open for reading and writing -pub const O_RDWR = 0x0002; - -/// do not block on open or for data to become available -pub const O_NONBLOCK = 0x0004; - -/// append on each write -pub const O_APPEND = 0x0008; - -/// create file if it does not exist -pub const O_CREAT = 0x0200; - -/// truncate size to 0 -pub const O_TRUNC = 0x0400; - -/// error if O_CREAT and the file exists -pub const O_EXCL = 0x0800; - -/// atomically obtain a shared lock -pub const O_SHLOCK = 0x0010; - -/// atomically obtain an exclusive lock -pub const O_EXLOCK = 0x0020; - -/// do not follow symlinks -pub const O_NOFOLLOW = 0x0100; - -/// allow open of symlinks -pub const O_SYMLINK = 0x200000; - -/// descriptor requested for event notifications only -pub const O_EVTONLY = 0x8000; - -/// mark as close-on-exec -pub const O_CLOEXEC = 0x1000000; - -pub const O_ACCMODE = 3; -pub const O_ALERT = 536870912; -pub const O_ASYNC = 64; -pub const O_DIRECTORY = 1048576; -pub const O_DP_GETRAWENCRYPTED = 1; -pub const O_DP_GETRAWUNENCRYPTED = 2; -pub const O_DSYNC = 4194304; -pub const O_FSYNC = O_SYNC; -pub const O_NOCTTY = 131072; -pub const O_POPUP = 2147483648; -pub const O_SYNC = 128; - -pub const SEEK_SET = 0x0; -pub const SEEK_CUR = 0x1; -pub const SEEK_END = 0x2; - -pub const DT_UNKNOWN = 0; -pub const DT_FIFO = 1; -pub const DT_CHR = 2; -pub const DT_DIR = 4; -pub const DT_BLK = 6; -pub const DT_REG = 8; -pub const DT_LNK = 10; -pub const DT_SOCK = 12; -pub const DT_WHT = 14; - -/// block specified signal set -pub const SIG_BLOCK = 1; - -/// unblock specified signal set -pub const SIG_UNBLOCK = 2; - -/// set specified signal set -pub const SIG_SETMASK = 3; - -/// hangup -pub const SIGHUP = 1; - -/// interrupt -pub const SIGINT = 2; - -/// quit -pub const SIGQUIT = 3; - -/// illegal instruction (not reset when caught) -pub const SIGILL = 4; - -/// trace trap (not reset when caught) -pub const SIGTRAP = 5; - -/// abort() -pub const SIGABRT = 6; - -/// pollable event ([XSR] generated, not supported) -pub const SIGPOLL = 7; - -/// compatibility -pub const SIGIOT = SIGABRT; - -/// EMT instruction -pub const SIGEMT = 7; - -/// floating point exception -pub const SIGFPE = 8; - -/// kill (cannot be caught or ignored) -pub const SIGKILL = 9; - -/// bus error -pub const SIGBUS = 10; - -/// segmentation violation -pub const SIGSEGV = 11; - -/// bad argument to system call -pub const SIGSYS = 12; - -/// write on a pipe with no one to read it -pub const SIGPIPE = 13; - -/// alarm clock -pub const SIGALRM = 14; - -/// software termination signal from kill -pub const SIGTERM = 15; - -/// urgent condition on IO channel -pub const SIGURG = 16; - -/// sendable stop signal not from tty -pub const SIGSTOP = 17; - -/// stop signal from tty -pub const SIGTSTP = 18; - -/// continue a stopped process -pub const SIGCONT = 19; - -/// to parent on child stop or exit -pub const SIGCHLD = 20; - -/// to readers pgrp upon background tty read -pub const SIGTTIN = 21; - -/// like TTIN for output if (tp->t_local<OSTOP) -pub const SIGTTOU = 22; - -/// input/output possible signal -pub const SIGIO = 23; - -/// exceeded CPU time limit -pub const SIGXCPU = 24; - -/// exceeded file size limit -pub const SIGXFSZ = 25; - -/// virtual time alarm -pub const SIGVTALRM = 26; - -/// profiling time alarm -pub const SIGPROF = 27; - -/// window size changes -pub const SIGWINCH = 28; - -/// information request -pub const SIGINFO = 29; - -/// user defined signal 1 -pub const SIGUSR1 = 30; - -/// user defined signal 2 -pub const SIGUSR2 = 31; - -/// no flag value -pub const KEVENT_FLAG_NONE = 0x000; - -/// immediate timeout -pub const KEVENT_FLAG_IMMEDIATE = 0x001; - -/// output events only include change -pub const KEVENT_FLAG_ERROR_EVENTS = 0x002; - -/// add event to kq (implies enable) -pub const EV_ADD = 0x0001; - -/// delete event from kq -pub const EV_DELETE = 0x0002; - -/// enable event -pub const EV_ENABLE = 0x0004; - -/// disable event (not reported) -pub const EV_DISABLE = 0x0008; - -/// only report one occurrence -pub const EV_ONESHOT = 0x0010; - -/// clear event state after reporting -pub const EV_CLEAR = 0x0020; - -/// force immediate event output -/// ... with or without EV_ERROR -/// ... use KEVENT_FLAG_ERROR_EVENTS -/// on syscalls supporting flags -pub const EV_RECEIPT = 0x0040; - -/// disable event after reporting -pub const EV_DISPATCH = 0x0080; - -/// unique kevent per udata value -pub const EV_UDATA_SPECIFIC = 0x0100; - -/// ... in combination with EV_DELETE -/// will defer delete until udata-specific -/// event enabled. EINPROGRESS will be -/// returned to indicate the deferral -pub const EV_DISPATCH2 = EV_DISPATCH | EV_UDATA_SPECIFIC; - -/// report that source has vanished -/// ... only valid with EV_DISPATCH2 -pub const EV_VANISHED = 0x0200; - -/// reserved by system -pub const EV_SYSFLAGS = 0xF000; - -/// filter-specific flag -pub const EV_FLAG0 = 0x1000; - -/// filter-specific flag -pub const EV_FLAG1 = 0x2000; - -/// EOF detected -pub const EV_EOF = 0x8000; - -/// error, data contains errno -pub const EV_ERROR = 0x4000; - -pub const EV_POLL = EV_FLAG0; -pub const EV_OOBAND = EV_FLAG1; - -pub const EVFILT_READ = -1; -pub const EVFILT_WRITE = -2; - -/// attached to aio requests -pub const EVFILT_AIO = -3; - -/// attached to vnodes -pub const EVFILT_VNODE = -4; - -/// attached to struct proc -pub const EVFILT_PROC = -5; - -/// attached to struct proc -pub const EVFILT_SIGNAL = -6; - -/// timers -pub const EVFILT_TIMER = -7; - -/// Mach portsets -pub const EVFILT_MACHPORT = -8; - -/// Filesystem events -pub const EVFILT_FS = -9; - -/// User events -pub const EVFILT_USER = -10; - -/// Virtual memory events -pub const EVFILT_VM = -12; - -/// Exception events -pub const EVFILT_EXCEPT = -15; - -pub const EVFILT_SYSCOUNT = 17; - -/// On input, NOTE_TRIGGER causes the event to be triggered for output. -pub const NOTE_TRIGGER = 0x01000000; - -/// ignore input fflags -pub const NOTE_FFNOP = 0x00000000; - -/// and fflags -pub const NOTE_FFAND = 0x40000000; - -/// or fflags -pub const NOTE_FFOR = 0x80000000; - -/// copy fflags -pub const NOTE_FFCOPY = 0xc0000000; - -/// mask for operations -pub const NOTE_FFCTRLMASK = 0xc0000000; -pub const NOTE_FFLAGSMASK = 0x00ffffff; - -/// low water mark -pub const NOTE_LOWAT = 0x00000001; - -/// OOB data -pub const NOTE_OOB = 0x00000002; - -/// vnode was removed -pub const NOTE_DELETE = 0x00000001; - -/// data contents changed -pub const NOTE_WRITE = 0x00000002; - -/// size increased -pub const NOTE_EXTEND = 0x00000004; - -/// attributes changed -pub const NOTE_ATTRIB = 0x00000008; - -/// link count changed -pub const NOTE_LINK = 0x00000010; - -/// vnode was renamed -pub const NOTE_RENAME = 0x00000020; - -/// vnode access was revoked -pub const NOTE_REVOKE = 0x00000040; - -/// No specific vnode event: to test for EVFILT_READ activation -pub const NOTE_NONE = 0x00000080; - -/// vnode was unlocked by flock(2) -pub const NOTE_FUNLOCK = 0x00000100; - -/// process exited -pub const NOTE_EXIT = 0x80000000; - -/// process forked -pub const NOTE_FORK = 0x40000000; - -/// process exec'd -pub const NOTE_EXEC = 0x20000000; - -/// shared with EVFILT_SIGNAL -pub const NOTE_SIGNAL = 0x08000000; - -/// exit status to be returned, valid for child process only -pub const NOTE_EXITSTATUS = 0x04000000; - -/// provide details on reasons for exit -pub const NOTE_EXIT_DETAIL = 0x02000000; - -/// mask for signal & exit status -pub const NOTE_PDATAMASK = 0x000fffff; -pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); - -pub const NOTE_EXIT_DETAIL_MASK = 0x00070000; -pub const NOTE_EXIT_DECRYPTFAIL = 0x00010000; -pub const NOTE_EXIT_MEMORY = 0x00020000; -pub const NOTE_EXIT_CSERROR = 0x00040000; - -/// will react on memory pressure -pub const NOTE_VM_PRESSURE = 0x80000000; - -/// will quit on memory pressure, possibly after cleaning up dirty state -pub const NOTE_VM_PRESSURE_TERMINATE = 0x40000000; - -/// will quit immediately on memory pressure -pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000; - -/// there was an error -pub const NOTE_VM_ERROR = 0x10000000; - -/// data is seconds -pub const NOTE_SECONDS = 0x00000001; - -/// data is microseconds -pub const NOTE_USECONDS = 0x00000002; - -/// data is nanoseconds -pub const NOTE_NSECONDS = 0x00000004; - -/// absolute timeout -pub const NOTE_ABSOLUTE = 0x00000008; - -/// ext[1] holds leeway for power aware timers -pub const NOTE_LEEWAY = 0x00000010; - -/// system does minimal timer coalescing -pub const NOTE_CRITICAL = 0x00000020; - -/// system does maximum timer coalescing -pub const NOTE_BACKGROUND = 0x00000040; -pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080; - -/// data is mach absolute time units -pub const NOTE_MACHTIME = 0x00000100; - -pub const AF_UNSPEC = 0; -pub const AF_LOCAL = 1; -pub const AF_UNIX = AF_LOCAL; -pub const AF_INET = 2; -pub const AF_SYS_CONTROL = 2; -pub const AF_IMPLINK = 3; -pub const AF_PUP = 4; -pub const AF_CHAOS = 5; -pub const AF_NS = 6; -pub const AF_ISO = 7; -pub const AF_OSI = AF_ISO; -pub const AF_ECMA = 8; -pub const AF_DATAKIT = 9; -pub const AF_CCITT = 10; -pub const AF_SNA = 11; -pub const AF_DECnet = 12; -pub const AF_DLI = 13; -pub const AF_LAT = 14; -pub const AF_HYLINK = 15; -pub const AF_APPLETALK = 16; -pub const AF_ROUTE = 17; -pub const AF_LINK = 18; -pub const AF_XTP = 19; -pub const AF_COIP = 20; -pub const AF_CNT = 21; -pub const AF_RTIP = 22; -pub const AF_IPX = 23; -pub const AF_SIP = 24; -pub const AF_PIP = 25; -pub const AF_ISDN = 28; -pub const AF_E164 = AF_ISDN; -pub const AF_KEY = 29; -pub const AF_INET6 = 30; -pub const AF_NATM = 31; -pub const AF_SYSTEM = 32; -pub const AF_NETBIOS = 33; -pub const AF_PPP = 34; -pub const AF_MAX = 40; - -pub const PF_UNSPEC = AF_UNSPEC; -pub const PF_LOCAL = AF_LOCAL; -pub const PF_UNIX = PF_LOCAL; -pub const PF_INET = AF_INET; -pub const PF_IMPLINK = AF_IMPLINK; -pub const PF_PUP = AF_PUP; -pub const PF_CHAOS = AF_CHAOS; -pub const PF_NS = AF_NS; -pub const PF_ISO = AF_ISO; -pub const PF_OSI = AF_ISO; -pub const PF_ECMA = AF_ECMA; -pub const PF_DATAKIT = AF_DATAKIT; -pub const PF_CCITT = AF_CCITT; -pub const PF_SNA = AF_SNA; -pub const PF_DECnet = AF_DECnet; -pub const PF_DLI = AF_DLI; -pub const PF_LAT = AF_LAT; -pub const PF_HYLINK = AF_HYLINK; -pub const PF_APPLETALK = AF_APPLETALK; -pub const PF_ROUTE = AF_ROUTE; -pub const PF_LINK = AF_LINK; -pub const PF_XTP = AF_XTP; -pub const PF_COIP = AF_COIP; -pub const PF_CNT = AF_CNT; -pub const PF_SIP = AF_SIP; -pub const PF_IPX = AF_IPX; -pub const PF_RTIP = AF_RTIP; -pub const PF_PIP = AF_PIP; -pub const PF_ISDN = AF_ISDN; -pub const PF_KEY = AF_KEY; -pub const PF_INET6 = AF_INET6; -pub const PF_NATM = AF_NATM; -pub const PF_SYSTEM = AF_SYSTEM; -pub const PF_NETBIOS = AF_NETBIOS; -pub const PF_PPP = AF_PPP; -pub const PF_MAX = AF_MAX; - -pub const SYSPROTO_EVENT = 1; -pub const SYSPROTO_CONTROL = 2; - -pub const SOCK_STREAM = 1; -pub const SOCK_DGRAM = 2; -pub const SOCK_RAW = 3; -pub const SOCK_RDM = 4; -pub const SOCK_SEQPACKET = 5; -pub const SOCK_MAXADDRLEN = 255; - -pub const IPPROTO_ICMP = 1; -pub const IPPROTO_ICMPV6 = 58; -pub const IPPROTO_TCP = 6; -pub const IPPROTO_UDP = 17; -pub const IPPROTO_IP = 0; -pub const IPPROTO_IPV6 = 41; - -fn wstatus(x: i32) i32 { - return x & 0o177; -} -const wstopped = 0o177; -pub fn WEXITSTATUS(x: i32) i32 { - return x >> 8; -} -pub fn WTERMSIG(x: i32) i32 { - return wstatus(x); -} -pub fn WSTOPSIG(x: i32) i32 { - return x >> 8; -} -pub fn WIFEXITED(x: i32) bool { - return wstatus(x) == 0; -} -pub fn WIFSTOPPED(x: i32) bool { - return wstatus(x) == wstopped and WSTOPSIG(x) != 0x13; -} -pub fn WIFSIGNALED(x: i32) bool { - return wstatus(x) != wstopped and wstatus(x) != 0; -} - -/// Get the errno from a syscall return value, or 0 for no error. -pub fn getErrno(r: usize) usize { - const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; -} - -pub fn close(fd: i32) usize { - return errnoWrap(c.close(fd)); -} - -pub fn abort() noreturn { - c.abort(); -} - -// bind(int socket, const struct sockaddr *address, socklen_t address_len) -pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - return errnoWrap(c.bind(@bitCast(c_int, fd), addr, len)); -} - -pub fn exit(code: i32) noreturn { - c.exit(code); -} - -pub fn isatty(fd: i32) bool { - return c.isatty(fd) != 0; -} - -pub fn fstat(fd: i32, buf: *c.Stat) usize { - return errnoWrap(c.@"fstat$INODE64"(fd, buf)); -} - -pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { - return errnoWrap(c.lseek(fd, offset, whence)); -} - -// TODO https://github.com/ziglang/zig/issues/265 on the whole file -pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { - return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); -} - -pub fn raise(sig: i32) usize { - return errnoWrap(c.raise(sig)); -} - -pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); -} - -pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); -} - -pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { - return errnoWrap(c.stat(path, buf)); -} - -pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); -} - -pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); -} - -pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap( - @ptrCast(?*c_void, address), - length, - @bitCast(c_int, @intCast(c_uint, prot)), - @bitCast(c_int, c_uint(flags)), - fd, - offset, - ); - const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); - return errnoWrap(isize_result); -} - -pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr(?*c_void, address), length)); -} - -pub fn unlink(path: [*]const u8) usize { - return errnoWrap(c.unlink(path)); -} - -pub fn getcwd(buf: [*]u8, size: usize) usize { - return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn waitpid(pid: i32, status: *i32, options: u32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); -} - -pub fn fork() usize { - return errnoWrap(c.fork()); -} - -pub fn access(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.access(path, mode)); -} - -pub fn pipe(fds: *[2]i32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.pipe(@ptrCast(*[2]c_int, fds))); -} - -pub fn getdirentries64(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { - return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep))); -} - -pub fn kqueue() usize { - return errnoWrap(c.kqueue()); -} - -pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { - return errnoWrap(c.kevent( - kq, - changelist.ptr, - @intCast(c_int, changelist.len), - eventlist.ptr, - @intCast(c_int, eventlist.len), - timeout, - )); -} - -pub fn kevent64( - kq: i32, - changelist: []const kevent64_s, - eventlist: []kevent64_s, - flags: u32, - timeout: ?*const timespec, -) usize { - return errnoWrap(c.kevent64(kq, changelist.ptr, changelist.len, eventlist.ptr, eventlist.len, flags, timeout)); -} - -pub fn mkdir(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.mkdir(path, mode)); -} - -pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.symlink(existing, new)); -} - -pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); -} - -pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); -} - -pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { - return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); -} - -pub fn rename(old: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.rename(old, new)); -} - -pub fn rmdir(path: [*]const u8) usize { - return errnoWrap(c.rmdir(path)); -} - -pub fn chdir(path: [*]const u8) usize { - return errnoWrap(c.chdir(path)); -} - -pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { - return errnoWrap(c.execve(path, argv, envp)); -} - -pub fn dup2(old: i32, new: i32) usize { - return errnoWrap(c.dup2(old, new)); -} - -pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return errnoWrap(c.readlink(path, buf_ptr, buf_len)); -} - -pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) usize { - return errnoWrap(c.gettimeofday(tv, tz)); -} - -pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { - return errnoWrap(c.nanosleep(req, rem)); -} - -pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { - return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn setreuid(ruid: u32, euid: u32) usize { - return errnoWrap(c.setreuid(ruid, euid)); -} - -pub fn setregid(rgid: u32, egid: u32) usize { - return errnoWrap(c.setregid(rgid, egid)); -} - -pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { - return errnoWrap(c.sigprocmask(@bitCast(c_int, flags), set, oldset)); -} - -pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { - assert(sig != SIGKILL); - assert(sig != SIGSTOP); - var cact = c.Sigaction{ - .handler = @ptrCast(extern fn (c_int) void, act.handler), - .sa_flags = @bitCast(c_int, act.flags), - .sa_mask = act.mask, - }; - var coact: c.Sigaction = undefined; - const result = errnoWrap(c.sigaction(sig, &cact, &coact)); - if (result != 0) { - return result; - } - if (oact) |old| { - old.* = Sigaction{ - .handler = @ptrCast(extern fn (i32) void, coact.handler), - .flags = @bitCast(u32, coact.sa_flags), - .mask = coact.sa_mask, - }; - } - return result; -} - -pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { - return errnoWrap(c.socket(@bitCast(c_int, domain), @bitCast(c_int, socket_type), @bitCast(c_int, protocol))); -} - -pub const iovec = extern struct { - iov_base: [*]u8, - iov_len: usize, +pub const is_the_target = switch (builtin.os) { + .macosx, .tvos, .watchos, .ios => true, + else => false, }; - -pub const iovec_const = extern struct { - iov_base: [*]const u8, - iov_len: usize, -}; - -pub const sigset_t = c.sigset_t; -pub const empty_sigset = sigset_t(0); - -pub const timespec = c.timespec; -pub const Stat = c.Stat; -pub const dirent = c.dirent; - -pub const in_port_t = c.in_port_t; -pub const sa_family_t = c.sa_family_t; -pub const socklen_t = c.socklen_t; - -pub const sockaddr = c.sockaddr; -pub const sockaddr_in = c.sockaddr_in; -pub const sockaddr_in6 = c.sockaddr_in6; - -/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall. -pub const Kevent = c.Kevent; -pub const kevent64_s = c.kevent64_s; - -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = struct { - handler: extern fn (i32) void, - mask: sigset_t, - flags: u32, -}; - -pub fn sigaddset(set: *sigset_t, signo: u5) void { - set.* |= u32(1) << (signo - 1); -} - -/// Takes the return value from a syscall and formats it back in the way -/// that the kernel represents it to libc. Errno was a mistake, let's make -/// it go away forever. -fn errnoWrap(value: isize) usize { - return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); -} - -pub const timezone = c.timezone; -pub const timeval = c.timeval; -pub const mach_timebase_info_data = c.mach_timebase_info_data; - -pub const mach_absolute_time = c.mach_absolute_time; -pub const mach_timebase_info = c.mach_timebase_info; +pub use std.c; diff --git a/std/os/darwin/errno.zig b/std/os/darwin/errno.zig deleted file mode 100644 index 438f3382ad..0000000000 --- a/std/os/darwin/errno.zig +++ /dev/null @@ -1,328 +0,0 @@ -/// Operation not permitted -pub const EPERM = 1; - -/// No such file or directory -pub const ENOENT = 2; - -/// No such process -pub const ESRCH = 3; - -/// Interrupted system call -pub const EINTR = 4; - -/// Input/output error -pub const EIO = 5; - -/// Device not configured -pub const ENXIO = 6; - -/// Argument list too long -pub const E2BIG = 7; - -/// Exec format error -pub const ENOEXEC = 8; - -/// Bad file descriptor -pub const EBADF = 9; - -/// No child processes -pub const ECHILD = 10; - -/// Resource deadlock avoided -pub const EDEADLK = 11; - -/// Cannot allocate memory -pub const ENOMEM = 12; - -/// Permission denied -pub const EACCES = 13; - -/// Bad address -pub const EFAULT = 14; - -/// Block device required -pub const ENOTBLK = 15; - -/// Device / Resource busy -pub const EBUSY = 16; - -/// File exists -pub const EEXIST = 17; - -/// Cross-device link -pub const EXDEV = 18; - -/// Operation not supported by device -pub const ENODEV = 19; - -/// Not a directory -pub const ENOTDIR = 20; - -/// Is a directory -pub const EISDIR = 21; - -/// Invalid argument -pub const EINVAL = 22; - -/// Too many open files in system -pub const ENFILE = 23; - -/// Too many open files -pub const EMFILE = 24; - -/// Inappropriate ioctl for device -pub const ENOTTY = 25; - -/// Text file busy -pub const ETXTBSY = 26; - -/// File too large -pub const EFBIG = 27; - -/// No space left on device -pub const ENOSPC = 28; - -/// Illegal seek -pub const ESPIPE = 29; - -/// Read-only file system -pub const EROFS = 30; - -/// Too many links -pub const EMLINK = 31; -/// Broken pipe - -// math software -pub const EPIPE = 32; - -/// Numerical argument out of domain -pub const EDOM = 33; -/// Result too large - -// non-blocking and interrupt i/o -pub const ERANGE = 34; - -/// Resource temporarily unavailable -pub const EAGAIN = 35; - -/// Operation would block -pub const EWOULDBLOCK = EAGAIN; - -/// Operation now in progress -pub const EINPROGRESS = 36; -/// Operation already in progress - -// ipc/network software -- argument errors -pub const EALREADY = 37; - -/// Socket operation on non-socket -pub const ENOTSOCK = 38; - -/// Destination address required -pub const EDESTADDRREQ = 39; - -/// Message too long -pub const EMSGSIZE = 40; - -/// Protocol wrong type for socket -pub const EPROTOTYPE = 41; - -/// Protocol not available -pub const ENOPROTOOPT = 42; - -/// Protocol not supported -pub const EPROTONOSUPPORT = 43; - -/// Socket type not supported -pub const ESOCKTNOSUPPORT = 44; - -/// Operation not supported -pub const ENOTSUP = 45; - -/// Protocol family not supported -pub const EPFNOSUPPORT = 46; - -/// Address family not supported by protocol family -pub const EAFNOSUPPORT = 47; - -/// Address already in use -pub const EADDRINUSE = 48; -/// Can't assign requested address - -// ipc/network software -- operational errors -pub const EADDRNOTAVAIL = 49; - -/// Network is down -pub const ENETDOWN = 50; - -/// Network is unreachable -pub const ENETUNREACH = 51; - -/// Network dropped connection on reset -pub const ENETRESET = 52; - -/// Software caused connection abort -pub const ECONNABORTED = 53; - -/// Connection reset by peer -pub const ECONNRESET = 54; - -/// No buffer space available -pub const ENOBUFS = 55; - -/// Socket is already connected -pub const EISCONN = 56; - -/// Socket is not connected -pub const ENOTCONN = 57; - -/// Can't send after socket shutdown -pub const ESHUTDOWN = 58; - -/// Too many references: can't splice -pub const ETOOMANYREFS = 59; - -/// Operation timed out -pub const ETIMEDOUT = 60; - -/// Connection refused -pub const ECONNREFUSED = 61; - -/// Too many levels of symbolic links -pub const ELOOP = 62; - -/// File name too long -pub const ENAMETOOLONG = 63; - -/// Host is down -pub const EHOSTDOWN = 64; - -/// No route to host -pub const EHOSTUNREACH = 65; -/// Directory not empty - -// quotas & mush -pub const ENOTEMPTY = 66; - -/// Too many processes -pub const EPROCLIM = 67; - -/// Too many users -pub const EUSERS = 68; -/// Disc quota exceeded - -// Network File System -pub const EDQUOT = 69; - -/// Stale NFS file handle -pub const ESTALE = 70; - -/// Too many levels of remote in path -pub const EREMOTE = 71; - -/// RPC struct is bad -pub const EBADRPC = 72; - -/// RPC version wrong -pub const ERPCMISMATCH = 73; - -/// RPC prog. not avail -pub const EPROGUNAVAIL = 74; - -/// Program version wrong -pub const EPROGMISMATCH = 75; - -/// Bad procedure for program -pub const EPROCUNAVAIL = 76; - -/// No locks available -pub const ENOLCK = 77; - -/// Function not implemented -pub const ENOSYS = 78; - -/// Inappropriate file type or format -pub const EFTYPE = 79; - -/// Authentication error -pub const EAUTH = 80; -/// Need authenticator - -// Intelligent device errors -pub const ENEEDAUTH = 81; - -/// Device power is off -pub const EPWROFF = 82; - -/// Device error, e.g. paper out -pub const EDEVERR = 83; -/// Value too large to be stored in data type - -// Program loading errors -pub const EOVERFLOW = 84; - -/// Bad executable -pub const EBADEXEC = 85; - -/// Bad CPU type in executable -pub const EBADARCH = 86; - -/// Shared library version mismatch -pub const ESHLIBVERS = 87; - -/// Malformed Macho file -pub const EBADMACHO = 88; - -/// Operation canceled -pub const ECANCELED = 89; - -/// Identifier removed -pub const EIDRM = 90; - -/// No message of desired type -pub const ENOMSG = 91; - -/// Illegal byte sequence -pub const EILSEQ = 92; - -/// Attribute not found -pub const ENOATTR = 93; - -/// Bad message -pub const EBADMSG = 94; - -/// Reserved -pub const EMULTIHOP = 95; - -/// No message available on STREAM -pub const ENODATA = 96; - -/// Reserved -pub const ENOLINK = 97; - -/// No STREAM resources -pub const ENOSR = 98; - -/// Not a STREAM -pub const ENOSTR = 99; - -/// Protocol error -pub const EPROTO = 100; - -/// STREAM ioctl timeout -pub const ETIME = 101; - -/// No such policy registered -pub const ENOPOLICY = 103; - -/// State not recoverable -pub const ENOTRECOVERABLE = 104; - -/// Previous owner died -pub const EOWNERDEAD = 105; - -/// Interface output queue is full -pub const EQFULL = 106; - -/// Must be equal largest errno -pub const ELAST = 106; diff --git a/std/os/file.zig b/std/os/file.zig deleted file mode 100644 index d223d55a46..0000000000 --- a/std/os/file.zig +++ /dev/null @@ -1,512 +0,0 @@ -const std = @import("../std.zig"); -const builtin = @import("builtin"); -const os = std.os; -const io = std.io; -const mem = std.mem; -const math = std.math; -const assert = std.debug.assert; -const posix = os.posix; -const windows = os.windows; -const Os = builtin.Os; -const windows_util = @import("windows/util.zig"); -const maxInt = std.math.maxInt; - -const is_posix = builtin.os != builtin.Os.windows; -const is_windows = builtin.os == builtin.Os.windows; - -pub const File = struct { - /// The OS-specific file descriptor or file handle. - handle: os.FileHandle, - - pub const Mode = switch (builtin.os) { - Os.windows => void, - else => u32, - }; - - pub const default_mode = switch (builtin.os) { - Os.windows => {}, - else => 0o666, - }; - - pub const OpenError = os.WindowsOpenError || os.PosixOpenError; - - /// `openRead` except with a null terminated path - pub fn openReadC(path: [*]const u8) OpenError!File { - if (is_posix) { - const flags = posix.O_LARGEFILE | posix.O_RDONLY; - const fd = try os.posixOpenC(path, flags, 0); - return openHandle(fd); - } - if (is_windows) { - return openRead(mem.toSliceConst(u8, path)); - } - @compileError("Unsupported OS"); - } - - /// Call close to clean up. - pub fn openRead(path: []const u8) OpenError!File { - if (is_posix) { - const path_c = try os.toPosixPath(path); - return openReadC(&path_c); - } - if (is_windows) { - const path_w = try windows_util.sliceToPrefixedFileW(path); - return openReadW(&path_w); - } - @compileError("Unsupported OS"); - } - - pub fn openReadW(path_w: [*]const u16) OpenError!File { - const handle = try os.windowsOpenW( - path_w, - windows.GENERIC_READ, - windows.FILE_SHARE_READ, - windows.OPEN_EXISTING, - windows.FILE_ATTRIBUTE_NORMAL, - ); - return openHandle(handle); - } - - /// Calls `openWriteMode` with os.File.default_mode for the 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. - /// Call close to clean up. - 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); - return openHandle(fd); - } else if (is_windows) { - const path_w = try windows_util.sliceToPrefixedFileW(path); - return openWriteModeW(&path_w, file_mode); - } else { - @compileError("TODO implement openWriteMode for this OS"); - } - } - - pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File { - const handle = try os.windowsOpenW( - path_w, - windows.GENERIC_WRITE, - windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, - windows.CREATE_ALWAYS, - windows.FILE_ATTRIBUTE_NORMAL, - ); - return openHandle(handle); - } - - /// If the path does not exist it will be created. - /// If a file already exists in the destination this returns OpenError.PathAlreadyExists - /// Call close to clean up. - pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File { - if (is_posix) { - const path_c = try os.toPosixPath(path); - return openWriteNoClobberC(&path_c, file_mode); - } else if (is_windows) { - const path_w = try windows_util.sliceToPrefixedFileW(path); - return openWriteNoClobberW(&path_w, file_mode); - } else { - @compileError("TODO implement openWriteMode for this OS"); - } - } - - pub fn openWriteNoClobberC(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.posixOpenC(path, flags, file_mode); - return openHandle(fd); - } else if (is_windows) { - const path_w = try windows_util.cStrToPrefixedFileW(path); - return openWriteNoClobberW(&path_w, file_mode); - } else { - @compileError("TODO implement openWriteMode for this OS"); - } - } - - pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File { - const handle = try os.windowsOpenW( - path_w, - windows.GENERIC_WRITE, - windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, - windows.CREATE_NEW, - windows.FILE_ATTRIBUTE_NORMAL, - ); - return openHandle(handle); - } - - pub fn openHandle(handle: os.FileHandle) File { - return File{ .handle = handle }; - } - - pub const AccessError = error{ - PermissionDenied, - FileNotFound, - NameTooLong, - InputOutput, - SystemResources, - BadPathName, - - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, - - Unexpected, - }; - - /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. - /// Otherwise use `access` or `accessC`. - pub fn accessW(path: [*]const u16) AccessError!void { - if (os.windows.GetFileAttributesW(path) != os.windows.INVALID_FILE_ATTRIBUTES) { - return; - } - - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, - else => return os.unexpectedErrorWindows(err), - } - } - - /// Call if you have a UTF-8 encoded, null-terminated string. - /// Otherwise use `access` or `accessW`. - pub fn accessC(path: [*]const u8) AccessError!void { - if (is_windows) { - const path_w = try windows_util.cStrToPrefixedFileW(path); - return accessW(&path_w); - } - if (is_posix) { - const result = posix.access(path, posix.F_OK); - const err = posix.getErrno(result); - switch (err) { - 0 => return, - posix.EACCES => return error.PermissionDenied, - posix.EROFS => return error.PermissionDenied, - posix.ELOOP => return error.PermissionDenied, - posix.ETXTBSY => return error.PermissionDenied, - posix.ENOTDIR => return error.FileNotFound, - posix.ENOENT => return error.FileNotFound, - - posix.ENAMETOOLONG => return error.NameTooLong, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EIO => return error.InputOutput, - posix.ENOMEM => return error.SystemResources, - else => return os.unexpectedErrorPosix(err), - } - } - @compileError("Unsupported OS"); - } - - pub fn access(path: []const u8) AccessError!void { - if (is_windows) { - const path_w = try windows_util.sliceToPrefixedFileW(path); - return accessW(&path_w); - } - 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("Unsupported 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 { - os.close(self.handle); - } - - /// Calls `os.isTty` on `self.handle`. - pub fn isTty(self: File) bool { - return os.isTty(self.handle); - } - - pub const SeekError = error{ - /// TODO make this error impossible to get - Overflow, - Unseekable, - Unexpected, - }; - - pub fn seekForward(self: File, amount: i64) SeekError!void { - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const iamount = try math.cast(isize, amount); - const result = posix.lseek(self.handle, iamount, posix.SEEK_CUR); - const err = posix.getErrno(result); - if (err > 0) { - return switch (err) { - // We do not make this an error code because if you get EBADF it's always a bug, - // since the fd could have been reused. - posix.EBADF => unreachable, - posix.EINVAL => error.Unseekable, - posix.EOVERFLOW => error.Unseekable, - posix.ESPIPE => error.Unseekable, - posix.ENXIO => error.Unseekable, - else => os.unexpectedErrorPosix(err), - }; - } - }, - Os.windows => { - if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.INVALID_PARAMETER => unreachable, - else => os.unexpectedErrorWindows(err), - }; - } - }, - else => @compileError("unsupported OS"), - } - } - - pub fn seekTo(self: File, pos: u64) SeekError!void { - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const ipos = try math.cast(isize, pos); - const result = posix.lseek(self.handle, ipos, posix.SEEK_SET); - const err = posix.getErrno(result); - if (err > 0) { - return switch (err) { - // We do not make this an error code because if you get EBADF it's always a bug, - // since the fd could have been reused. - posix.EBADF => unreachable, - posix.EINVAL => error.Unseekable, - posix.EOVERFLOW => error.Unseekable, - posix.ESPIPE => error.Unseekable, - posix.ENXIO => error.Unseekable, - else => os.unexpectedErrorPosix(err), - }; - } - }, - Os.windows => { - const ipos = try math.cast(isize, pos); - if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.INVALID_PARAMETER => unreachable, - windows.ERROR.INVALID_HANDLE => unreachable, - else => os.unexpectedErrorWindows(err), - }; - } - }, - else => @compileError("unsupported OS: " ++ @tagName(builtin.os)), - } - } - - pub const GetSeekPosError = error{ - SystemResources, - Unseekable, - Unexpected, - }; - - pub fn getPos(self: File) GetSeekPosError!u64 { - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); - const err = posix.getErrno(result); - if (err > 0) { - return switch (err) { - // We do not make this an error code because if you get EBADF it's always a bug, - // since the fd could have been reused. - posix.EBADF => unreachable, - posix.EINVAL => error.Unseekable, - posix.EOVERFLOW => error.Unseekable, - posix.ESPIPE => error.Unseekable, - posix.ENXIO => error.Unseekable, - else => os.unexpectedErrorPosix(err), - }; - } - return u64(result); - }, - Os.windows => { - var pos: windows.LARGE_INTEGER = undefined; - if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.INVALID_PARAMETER => unreachable, - else => os.unexpectedErrorWindows(err), - }; - } - - return @intCast(u64, pos); - }, - else => @compileError("unsupported OS"), - } - } - - pub fn getEndPos(self: File) GetSeekPosError!u64 { - if (is_posix) { - const stat = try os.posixFStat(self.handle); - return @intCast(u64, stat.size); - } else if (is_windows) { - var file_size: windows.LARGE_INTEGER = undefined; - if (windows.GetFileSizeEx(self.handle, &file_size) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - } - return @intCast(u64, file_size); - } else { - @compileError("TODO support getEndPos on this OS"); - } - } - - pub const ModeError = error{ - SystemResources, - Unexpected, - }; - - pub fn mode(self: File) ModeError!Mode { - if (is_posix) { - var stat: posix.Stat = undefined; - const err = posix.getErrno(posix.fstat(self.handle, &stat)); - if (err > 0) { - return switch (err) { - // We do not make this an error code because if you get EBADF it's always a bug, - // since the fd could have been reused. - posix.EBADF => unreachable, - posix.ENOMEM => error.SystemResources, - else => os.unexpectedErrorPosix(err), - }; - } - - // TODO: we should be able to cast u16 to ModeError!u32, making this - // explicit cast not necessary - return Mode(stat.mode); - } else if (is_windows) { - return {}; - } else { - @compileError("TODO support file mode on this OS"); - } - } - - pub const ReadError = os.WindowsReadError || os.PosixReadError; - - pub fn read(self: File, buffer: []u8) ReadError!usize { - if (is_posix) { - return os.posixRead(self.handle, buffer); - } else if (is_windows) { - var index: usize = 0; - while (index < buffer.len) { - const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(maxInt(windows.DWORD)), buffer.len - index)); - var amt_read: windows.DWORD = undefined; - if (windows.ReadFile(self.handle, buffer.ptr + index, want_read_count, &amt_read, null) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.OPERATION_ABORTED => continue, - windows.ERROR.BROKEN_PIPE => return index, - else => os.unexpectedErrorWindows(err), - }; - } - if (amt_read == 0) return index; - index += amt_read; - } - return index; - } else { - @compileError("Unsupported OS"); - } - } - - pub const WriteError = os.WindowsWriteError || os.PosixWriteError; - - pub fn write(self: File, bytes: []const u8) WriteError!void { - if (is_posix) { - try os.posixWrite(self.handle, bytes); - } else if (is_windows) { - try os.windowsWrite(self.handle, bytes); - } else { - @compileError("Unsupported OS"); - } - } - - pub fn inStream(file: File) InStream { - return InStream{ - .file = file, - .stream = InStream.Stream{ .readFn = InStream.readFn }, - }; - } - - pub fn outStream(file: File) OutStream { - return OutStream{ - .file = file, - .stream = OutStream.Stream{ .writeFn = OutStream.writeFn }, - }; - } - - pub fn seekableStream(file: File) SeekableStream { - return SeekableStream{ - .file = file, - .stream = SeekableStream.Stream{ - .seekToFn = SeekableStream.seekToFn, - .seekForwardFn = SeekableStream.seekForwardFn, - .getPosFn = SeekableStream.getPosFn, - .getEndPosFn = SeekableStream.getEndPosFn, - }, - }; - } - - /// Implementation of io.InStream trait for File - pub const InStream = struct { - file: File, - stream: Stream, - - pub const Error = ReadError; - pub const Stream = io.InStream(Error); - - fn readFn(in_stream: *Stream, buffer: []u8) Error!usize { - const self = @fieldParentPtr(InStream, "stream", in_stream); - return self.file.read(buffer); - } - }; - - /// Implementation of io.OutStream trait for File - pub const OutStream = struct { - file: File, - stream: Stream, - - pub const Error = WriteError; - pub const Stream = io.OutStream(Error); - - fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void { - const self = @fieldParentPtr(OutStream, "stream", out_stream); - return self.file.write(bytes); - } - }; - - /// Implementation of io.SeekableStream trait for File - pub const SeekableStream = struct { - file: File, - stream: Stream, - - pub const Stream = io.SeekableStream(SeekError, GetSeekPosError); - - pub fn seekToFn(seekable_stream: *Stream, pos: u64) SeekError!void { - const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); - return self.file.seekTo(pos); - } - - pub fn seekForwardFn(seekable_stream: *Stream, amt: i64) SeekError!void { - const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); - return self.file.seekForward(amt); - } - - pub fn getEndPosFn(seekable_stream: *Stream) GetSeekPosError!u64 { - const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); - return self.file.getEndPos(); - } - - pub fn getPosFn(seekable_stream: *Stream) GetSeekPosError!u64 { - const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); - return self.file.getPos(); - } - }; -}; diff --git a/std/os/freebsd.zig b/std/os/freebsd.zig index c806067706..c0f3382bd0 100644 --- a/std/os/freebsd.zig +++ b/std/os/freebsd.zig @@ -1,845 +1,4 @@ -const builtin = @import("builtin"); - -pub use @import("freebsd/errno.zig"); - const std = @import("../std.zig"); -const c = std.c; - -const assert = std.debug.assert; -const maxInt = std.math.maxInt; -pub const Kevent = c.Kevent; - -pub const CTL_KERN = 1; -pub const CTL_DEBUG = 5; - -pub const KERN_PROC = 14; // struct: process entries -pub const KERN_PROC_PATHNAME = 12; // path to executable - -pub const PATH_MAX = 1024; - -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; - -pub const PROT_NONE = 0; -pub const PROT_READ = 1; -pub const PROT_WRITE = 2; -pub const PROT_EXEC = 4; - -pub const CLOCK_REALTIME = 0; -pub const CLOCK_VIRTUAL = 1; -pub const CLOCK_PROF = 2; -pub const CLOCK_MONOTONIC = 4; -pub const CLOCK_UPTIME = 5; -pub const CLOCK_UPTIME_PRECISE = 7; -pub const CLOCK_UPTIME_FAST = 8; -pub const CLOCK_REALTIME_PRECISE = 9; -pub const CLOCK_REALTIME_FAST = 10; -pub const CLOCK_MONOTONIC_PRECISE = 11; -pub const CLOCK_MONOTONIC_FAST = 12; -pub const CLOCK_SECOND = 13; -pub const CLOCK_THREAD_CPUTIME_ID = 14; -pub const CLOCK_PROCESS_CPUTIME_ID = 15; - -pub const MAP_FAILED = maxInt(usize); -pub const MAP_SHARED = 0x0001; -pub const MAP_PRIVATE = 0x0002; -pub const MAP_FIXED = 0x0010; -pub const MAP_STACK = 0x0400; -pub const MAP_NOSYNC = 0x0800; -pub const MAP_ANON = 0x1000; -pub const MAP_ANONYMOUS = MAP_ANON; -pub const MAP_FILE = 0; -pub const MAP_NORESERVE = 0; - -pub const MAP_GUARD = 0x00002000; -pub const MAP_EXCL = 0x00004000; -pub const MAP_NOCORE = 0x00020000; -pub const MAP_PREFAULT_READ = 0x00040000; -pub const MAP_32BIT = 0x00080000; - -pub const WNOHANG = 1; -pub const WUNTRACED = 2; -pub const WSTOPPED = WUNTRACED; -pub const WCONTINUED = 4; -pub const WNOWAIT = 8; -pub const WEXITED = 16; -pub const WTRAPPED = 32; - -pub const SA_ONSTACK = 0x0001; -pub const SA_RESTART = 0x0002; -pub const SA_RESETHAND = 0x0004; -pub const SA_NOCLDSTOP = 0x0008; -pub const SA_NODEFER = 0x0010; -pub const SA_NOCLDWAIT = 0x0020; -pub const SA_SIGINFO = 0x0040; - -pub const SIGHUP = 1; -pub const SIGINT = 2; -pub const SIGQUIT = 3; -pub const SIGILL = 4; -pub const SIGTRAP = 5; -pub const SIGABRT = 6; -pub const SIGIOT = SIGABRT; -pub const SIGEMT = 7; -pub const SIGFPE = 8; -pub const SIGKILL = 9; -pub const SIGBUS = 10; -pub const SIGSEGV = 11; -pub const SIGSYS = 12; -pub const SIGPIPE = 13; -pub const SIGALRM = 14; -pub const SIGTERM = 15; -pub const SIGURG = 16; -pub const SIGSTOP = 17; -pub const SIGTSTP = 18; -pub const SIGCONT = 19; -pub const SIGCHLD = 20; -pub const SIGTTIN = 21; -pub const SIGTTOU = 22; -pub const SIGIO = 23; -pub const SIGXCPU = 24; -pub const SIGXFSZ = 25; -pub const SIGVTALRM = 26; -pub const SIGPROF = 27; -pub const SIGWINCH = 28; -pub const SIGINFO = 29; -pub const SIGUSR1 = 30; -pub const SIGUSR2 = 31; -pub const SIGTHR = 32; -pub const SIGLWP = SIGTHR; -pub const SIGLIBRT = 33; - -pub const SIGRTMIN = 65; -pub const SIGRTMAX = 126; - -// access function -pub const F_OK = 0; // test for existence of file -pub const X_OK = 1; // test for execute or search permission -pub const W_OK = 2; // test for write permission -pub const R_OK = 4; // test for read permission - -pub const O_RDONLY = 0x0000; -pub const O_WRONLY = 0x0001; -pub const O_RDWR = 0x0002; -pub const O_ACCMODE = 0x0003; - -pub const O_CREAT = 0x0200; -pub const O_EXCL = 0x0800; -pub const O_NOCTTY = 0x8000; -pub const O_TRUNC = 0x0400; -pub const O_APPEND = 0x0008; -pub const O_NONBLOCK = 0x0004; -pub const O_DSYNC = 0o10000; -pub const O_SYNC = 0x0080; -pub const O_RSYNC = 0o4010000; -pub const O_DIRECTORY = 0o200000; -pub const O_NOFOLLOW = 0x0100; -pub const O_CLOEXEC = 0x00100000; - -pub const O_ASYNC = 0x0040; -pub const O_DIRECT = 0x00010000; -pub const O_LARGEFILE = 0; -pub const O_NOATIME = 0o1000000; -pub const O_PATH = 0o10000000; -pub const O_TMPFILE = 0o20200000; -pub const O_NDELAY = O_NONBLOCK; - -pub const F_DUPFD = 0; -pub const F_GETFD = 1; -pub const F_SETFD = 2; -pub const F_GETFL = 3; -pub const F_SETFL = 4; - -pub const F_SETOWN = 8; -pub const F_GETOWN = 9; -pub const F_SETSIG = 10; -pub const F_GETSIG = 11; - -pub const F_GETLK = 5; -pub const F_SETLK = 6; -pub const F_SETLKW = 7; - -pub const F_SETOWN_EX = 15; -pub const F_GETOWN_EX = 16; - -pub const F_GETOWNER_UIDS = 17; - -pub const SEEK_SET = 0; -pub const SEEK_CUR = 1; -pub const SEEK_END = 2; - -pub const SIG_BLOCK = 1; -pub const SIG_UNBLOCK = 2; -pub const SIG_SETMASK = 3; - -pub const SOCK_STREAM = 1; -pub const SOCK_DGRAM = 2; -pub const SOCK_RAW = 3; -pub const SOCK_RDM = 4; -pub const SOCK_SEQPACKET = 5; - -pub const SOCK_CLOEXEC = 0x10000000; -pub const SOCK_NONBLOCK = 0x20000000; - -pub const PROTO_ip = 0o000; -pub const PROTO_icmp = 0o001; -pub const PROTO_igmp = 0o002; -pub const PROTO_ggp = 0o003; -pub const PROTO_ipencap = 0o004; -pub const PROTO_st = 0o005; -pub const PROTO_tcp = 0o006; -pub const PROTO_egp = 0o010; -pub const PROTO_pup = 0o014; -pub const PROTO_udp = 0o021; -pub const PROTO_hmp = 0o024; -pub const PROTO_xns_idp = 0o026; -pub const PROTO_rdp = 0o033; -pub const PROTO_iso_tp4 = 0o035; -pub const PROTO_xtp = 0o044; -pub const PROTO_ddp = 0o045; -pub const PROTO_idpr_cmtp = 0o046; -pub const PROTO_ipv6 = 0o051; -pub const PROTO_ipv6_route = 0o053; -pub const PROTO_ipv6_frag = 0o054; -pub const PROTO_idrp = 0o055; -pub const PROTO_rsvp = 0o056; -pub const PROTO_gre = 0o057; -pub const PROTO_esp = 0o062; -pub const PROTO_ah = 0o063; -pub const PROTO_skip = 0o071; -pub const PROTO_ipv6_icmp = 0o072; -pub const PROTO_ipv6_nonxt = 0o073; -pub const PROTO_ipv6_opts = 0o074; -pub const PROTO_rspf = 0o111; -pub const PROTO_vmtp = 0o121; -pub const PROTO_ospf = 0o131; -pub const PROTO_ipip = 0o136; -pub const PROTO_encap = 0o142; -pub const PROTO_pim = 0o147; -pub const PROTO_raw = 0o377; - -pub const PF_UNSPEC = 0; -pub const PF_LOCAL = 1; -pub const PF_UNIX = PF_LOCAL; -pub const PF_FILE = PF_LOCAL; -pub const PF_INET = 2; -pub const PF_AX25 = 3; -pub const PF_IPX = 4; -pub const PF_APPLETALK = 5; -pub const PF_NETROM = 6; -pub const PF_BRIDGE = 7; -pub const PF_ATMPVC = 8; -pub const PF_X25 = 9; -pub const PF_INET6 = 10; -pub const PF_ROSE = 11; -pub const PF_DECnet = 12; -pub const PF_NETBEUI = 13; -pub const PF_SECURITY = 14; -pub const PF_KEY = 15; -pub const PF_NETLINK = 16; -pub const PF_ROUTE = PF_NETLINK; -pub const PF_PACKET = 17; -pub const PF_ASH = 18; -pub const PF_ECONET = 19; -pub const PF_ATMSVC = 20; -pub const PF_RDS = 21; -pub const PF_SNA = 22; -pub const PF_IRDA = 23; -pub const PF_PPPOX = 24; -pub const PF_WANPIPE = 25; -pub const PF_LLC = 26; -pub const PF_IB = 27; -pub const PF_MPLS = 28; -pub const PF_CAN = 29; -pub const PF_TIPC = 30; -pub const PF_BLUETOOTH = 31; -pub const PF_IUCV = 32; -pub const PF_RXRPC = 33; -pub const PF_ISDN = 34; -pub const PF_PHONET = 35; -pub const PF_IEEE802154 = 36; -pub const PF_CAIF = 37; -pub const PF_ALG = 38; -pub const PF_NFC = 39; -pub const PF_VSOCK = 40; -pub const PF_MAX = 41; - -pub const AF_UNSPEC = PF_UNSPEC; -pub const AF_LOCAL = PF_LOCAL; -pub const AF_UNIX = AF_LOCAL; -pub const AF_FILE = AF_LOCAL; -pub const AF_INET = PF_INET; -pub const AF_AX25 = PF_AX25; -pub const AF_IPX = PF_IPX; -pub const AF_APPLETALK = PF_APPLETALK; -pub const AF_NETROM = PF_NETROM; -pub const AF_BRIDGE = PF_BRIDGE; -pub const AF_ATMPVC = PF_ATMPVC; -pub const AF_X25 = PF_X25; -pub const AF_INET6 = PF_INET6; -pub const AF_ROSE = PF_ROSE; -pub const AF_DECnet = PF_DECnet; -pub const AF_NETBEUI = PF_NETBEUI; -pub const AF_SECURITY = PF_SECURITY; -pub const AF_KEY = PF_KEY; -pub const AF_NETLINK = PF_NETLINK; -pub const AF_ROUTE = PF_ROUTE; -pub const AF_PACKET = PF_PACKET; -pub const AF_ASH = PF_ASH; -pub const AF_ECONET = PF_ECONET; -pub const AF_ATMSVC = PF_ATMSVC; -pub const AF_RDS = PF_RDS; -pub const AF_SNA = PF_SNA; -pub const AF_IRDA = PF_IRDA; -pub const AF_PPPOX = PF_PPPOX; -pub const AF_WANPIPE = PF_WANPIPE; -pub const AF_LLC = PF_LLC; -pub const AF_IB = PF_IB; -pub const AF_MPLS = PF_MPLS; -pub const AF_CAN = PF_CAN; -pub const AF_TIPC = PF_TIPC; -pub const AF_BLUETOOTH = PF_BLUETOOTH; -pub const AF_IUCV = PF_IUCV; -pub const AF_RXRPC = PF_RXRPC; -pub const AF_ISDN = PF_ISDN; -pub const AF_PHONET = PF_PHONET; -pub const AF_IEEE802154 = PF_IEEE802154; -pub const AF_CAIF = PF_CAIF; -pub const AF_ALG = PF_ALG; -pub const AF_NFC = PF_NFC; -pub const AF_VSOCK = PF_VSOCK; -pub const AF_MAX = PF_MAX; - -pub const DT_UNKNOWN = 0; -pub const DT_FIFO = 1; -pub const DT_CHR = 2; -pub const DT_DIR = 4; -pub const DT_BLK = 6; -pub const DT_REG = 8; -pub const DT_LNK = 10; -pub const DT_SOCK = 12; -pub const DT_WHT = 14; - -/// add event to kq (implies enable) -pub const EV_ADD = 0x0001; - -/// delete event from kq -pub const EV_DELETE = 0x0002; - -/// enable event -pub const EV_ENABLE = 0x0004; - -/// disable event (not reported) -pub const EV_DISABLE = 0x0008; - -/// only report one occurrence -pub const EV_ONESHOT = 0x0010; - -/// clear event state after reporting -pub const EV_CLEAR = 0x0020; - -/// force immediate event output -/// ... with or without EV_ERROR -/// ... use KEVENT_FLAG_ERROR_EVENTS -/// on syscalls supporting flags -pub const EV_RECEIPT = 0x0040; - -/// disable event after reporting -pub const EV_DISPATCH = 0x0080; - -pub const EVFILT_READ = -1; -pub const EVFILT_WRITE = -2; - -/// attached to aio requests -pub const EVFILT_AIO = -3; - -/// attached to vnodes -pub const EVFILT_VNODE = -4; - -/// attached to struct proc -pub const EVFILT_PROC = -5; - -/// attached to struct proc -pub const EVFILT_SIGNAL = -6; - -/// timers -pub const EVFILT_TIMER = -7; - -/// Process descriptors -pub const EVFILT_PROCDESC = -8; - -/// Filesystem events -pub const EVFILT_FS = -9; - -pub const EVFILT_LIO = -10; - -/// User events -pub const EVFILT_USER = -11; - -/// Sendfile events -pub const EVFILT_SENDFILE = -12; - -pub const EVFILT_EMPTY = -13; - -/// On input, NOTE_TRIGGER causes the event to be triggered for output. -pub const NOTE_TRIGGER = 0x01000000; - -/// ignore input fflags -pub const NOTE_FFNOP = 0x00000000; - -/// and fflags -pub const NOTE_FFAND = 0x40000000; - -/// or fflags -pub const NOTE_FFOR = 0x80000000; - -/// copy fflags -pub const NOTE_FFCOPY = 0xc0000000; - -/// mask for operations -pub const NOTE_FFCTRLMASK = 0xc0000000; -pub const NOTE_FFLAGSMASK = 0x00ffffff; - -/// low water mark -pub const NOTE_LOWAT = 0x00000001; - -/// behave like poll() -pub const NOTE_FILE_POLL = 0x00000002; - -/// vnode was removed -pub const NOTE_DELETE = 0x00000001; - -/// data contents changed -pub const NOTE_WRITE = 0x00000002; - -/// size increased -pub const NOTE_EXTEND = 0x00000004; - -/// attributes changed -pub const NOTE_ATTRIB = 0x00000008; - -/// link count changed -pub const NOTE_LINK = 0x00000010; - -/// vnode was renamed -pub const NOTE_RENAME = 0x00000020; - -/// vnode access was revoked -pub const NOTE_REVOKE = 0x00000040; - -/// vnode was opened -pub const NOTE_OPEN = 0x00000080; - -/// file closed, fd did not allow write -pub const NOTE_CLOSE = 0x00000100; - -/// file closed, fd did allow write -pub const NOTE_CLOSE_WRITE = 0x00000200; - -/// file was read -pub const NOTE_READ = 0x00000400; - -/// process exited -pub const NOTE_EXIT = 0x80000000; - -/// process forked -pub const NOTE_FORK = 0x40000000; - -/// process exec'd -pub const NOTE_EXEC = 0x20000000; - -/// mask for signal & exit status -pub const NOTE_PDATAMASK = 0x000fffff; -pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); - -/// data is seconds -pub const NOTE_SECONDS = 0x00000001; - -/// data is milliseconds -pub const NOTE_MSECONDS = 0x00000002; - -/// data is microseconds -pub const NOTE_USECONDS = 0x00000004; - -/// data is nanoseconds -pub const NOTE_NSECONDS = 0x00000008; - -/// timeout is absolute -pub const NOTE_ABSTIME = 0x00000010; - -pub const TCGETS = 0x5401; -pub const TCSETS = 0x5402; -pub const TCSETSW = 0x5403; -pub const TCSETSF = 0x5404; -pub const TCGETA = 0x5405; -pub const TCSETA = 0x5406; -pub const TCSETAW = 0x5407; -pub const TCSETAF = 0x5408; -pub const TCSBRK = 0x5409; -pub const TCXONC = 0x540A; -pub const TCFLSH = 0x540B; -pub const TIOCEXCL = 0x540C; -pub const TIOCNXCL = 0x540D; -pub const TIOCSCTTY = 0x540E; -pub const TIOCGPGRP = 0x540F; -pub const TIOCSPGRP = 0x5410; -pub const TIOCOUTQ = 0x5411; -pub const TIOCSTI = 0x5412; -pub const TIOCGWINSZ = 0x5413; -pub const TIOCSWINSZ = 0x5414; -pub const TIOCMGET = 0x5415; -pub const TIOCMBIS = 0x5416; -pub const TIOCMBIC = 0x5417; -pub const TIOCMSET = 0x5418; -pub const TIOCGSOFTCAR = 0x5419; -pub const TIOCSSOFTCAR = 0x541A; -pub const FIONREAD = 0x541B; -pub const TIOCINQ = FIONREAD; -pub const TIOCLINUX = 0x541C; -pub const TIOCCONS = 0x541D; -pub const TIOCGSERIAL = 0x541E; -pub const TIOCSSERIAL = 0x541F; -pub const TIOCPKT = 0x5420; -pub const FIONBIO = 0x5421; -pub const TIOCNOTTY = 0x5422; -pub const TIOCSETD = 0x5423; -pub const TIOCGETD = 0x5424; -pub const TCSBRKP = 0x5425; -pub const TIOCSBRK = 0x5427; -pub const TIOCCBRK = 0x5428; -pub const TIOCGSID = 0x5429; -pub const TIOCGRS485 = 0x542E; -pub const TIOCSRS485 = 0x542F; -pub const TIOCGPTN = 0x80045430; -pub const TIOCSPTLCK = 0x40045431; -pub const TIOCGDEV = 0x80045432; -pub const TCGETX = 0x5432; -pub const TCSETX = 0x5433; -pub const TCSETXF = 0x5434; -pub const TCSETXW = 0x5435; -pub const TIOCSIG = 0x40045436; -pub const TIOCVHANGUP = 0x5437; -pub const TIOCGPKT = 0x80045438; -pub const TIOCGPTLCK = 0x80045439; -pub const TIOCGEXCL = 0x80045440; - -pub const sockaddr = c.sockaddr; -pub const sockaddr_in = c.sockaddr_in; -pub const sockaddr_in6 = c.sockaddr_in6; - -fn unsigned(s: i32) u32 { - return @bitCast(u32, s); -} -fn signed(s: u32) i32 { - return @bitCast(i32, s); -} -pub fn WEXITSTATUS(s: i32) i32 { - return signed((unsigned(s) & 0xff00) >> 8); -} -pub fn WTERMSIG(s: i32) i32 { - return signed(unsigned(s) & 0x7f); -} -pub fn WSTOPSIG(s: i32) i32 { - return WEXITSTATUS(s); -} -pub fn WIFEXITED(s: i32) bool { - return WTERMSIG(s) == 0; -} -pub fn WIFSTOPPED(s: i32) bool { - return @intCast(u16, (((unsigned(s) & 0xffff) *% 0x10001) >> 8)) > 0x7f00; -} -pub fn WIFSIGNALED(s: i32) bool { - return (unsigned(s) & 0xffff) -% 1 < 0xff; -} - -pub const winsize = extern struct { - ws_row: u16, - ws_col: u16, - ws_xpixel: u16, - ws_ypixel: u16, -}; - -/// Get the errno from a syscall return value, or 0 for no error. -pub fn getErrno(r: usize) usize { - const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; -} - -pub fn dup2(old: i32, new: i32) usize { - return errnoWrap(c.dup2(old, new)); -} - -pub fn chdir(path: [*]const u8) usize { - return errnoWrap(c.chdir(path)); -} - -pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { - return errnoWrap(c.execve(path, argv, envp)); -} - -pub fn fork() usize { - return errnoWrap(c.fork()); -} - -pub fn access(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.access(path, mode)); -} - -pub fn getcwd(buf: [*]u8, size: usize) usize { - return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { - return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); -} - -pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { - return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); -} - -pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { - return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn isatty(fd: i32) bool { - return c.isatty(fd) != 0; -} - -pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return errnoWrap(c.readlink(path, buf_ptr, buf_len)); -} - -pub fn mkdir(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.mkdir(path, mode)); -} - -pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap( - @ptrCast(?*c_void, address), - length, - @bitCast(c_int, @intCast(c_uint, prot)), - @bitCast(c_int, c_uint(flags)), - fd, - offset, - ); - const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); - return errnoWrap(isize_result); -} - -pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr(?*c_void, address), length)); -} - -pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); -} - -pub fn rmdir(path: [*]const u8) usize { - return errnoWrap(c.rmdir(path)); -} - -pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.symlink(existing, new)); -} - -pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); -} - -pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { - return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); -} - -pub fn pipe(fd: *[2]i32) usize { - return pipe2(fd, 0); -} - -pub fn pipe2(fd: *[2]i32, flags: u32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); -} - -pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); -} - -pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); -} - -pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { - return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); -} - -pub fn rename(old: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.rename(old, new)); -} - -pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { - return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); -} - -pub fn create(path: [*]const u8, perm: usize) usize { - return arch.syscall2(SYS_creat, @ptrToInt(path), perm); -} - -pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { - return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); -} - -pub fn close(fd: i32) usize { - return errnoWrap(c.close(fd)); -} - -pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { - return errnoWrap(c.lseek(fd, offset, whence)); -} - -pub fn exit(code: i32) noreturn { - c.exit(code); -} - -pub fn kill(pid: i32, sig: i32) usize { - return errnoWrap(c.kill(pid, sig)); -} - -pub fn unlink(path: [*]const u8) usize { - return errnoWrap(c.unlink(path)); -} - -pub fn waitpid(pid: i32, status: *i32, options: u32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); -} - -pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { - return errnoWrap(c.nanosleep(req, rem)); -} - -pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { - return errnoWrap(c.clock_gettime(clk_id, tp)); -} - -pub fn clock_getres(clk_id: i32, tp: *timespec) usize { - return errnoWrap(c.clock_getres(clk_id, tp)); -} - -pub fn setuid(uid: u32) usize { - return errnoWrap(c.setuid(uid)); -} - -pub fn setgid(gid: u32) usize { - return errnoWrap(c.setgid(gid)); -} - -pub fn setreuid(ruid: u32, euid: u32) usize { - return errnoWrap(c.setreuid(ruid, euid)); -} - -pub fn setregid(rgid: u32, egid: u32) usize { - return errnoWrap(c.setregid(rgid, egid)); -} - -const NSIG = 32; - -pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); -pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); -pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); - -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = extern struct { - /// signal handler - __sigaction_u: extern union { - __sa_handler: extern fn (i32) void, - __sa_sigaction: extern fn (i32, *__siginfo, usize) void, - }, - - /// see signal options - sa_flags: u32, - - /// signal mask to apply - sa_mask: sigset_t, -}; - -pub const _SIG_WORDS = 4; -pub const _SIG_MAXSIG = 128; - -pub inline fn _SIG_IDX(sig: usize) usize { - return sig - 1; -} -pub inline fn _SIG_WORD(sig: usize) usize { - return_SIG_IDX(sig) >> 5; -} -pub inline fn _SIG_BIT(sig: usize) usize { - return 1 << (_SIG_IDX(sig) & 31); -} -pub inline fn _SIG_VALID(sig: usize) usize { - return sig <= _SIG_MAXSIG and sig > 0; -} - -pub const sigset_t = extern struct { - __bits: [_SIG_WORDS]u32, -}; - -pub fn raise(sig: i32) usize { - return errnoWrap(c.raise(sig)); -} - -pub const Stat = c.Stat; -pub const dirent = c.dirent; -pub const timespec = c.timespec; - -pub fn fstat(fd: i32, buf: *c.Stat) usize { - return errnoWrap(c.fstat(fd, buf)); -} -pub const iovec = extern struct { - iov_base: [*]u8, - iov_len: usize, -}; - -pub const iovec_const = extern struct { - iov_base: [*]const u8, - iov_len: usize, -}; - -// TODO avoid libc dependency -pub fn kqueue() usize { - return errnoWrap(c.kqueue()); -} - -// TODO avoid libc dependency -pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { - return errnoWrap(c.kevent( - kq, - changelist.ptr, - @intCast(c_int, changelist.len), - eventlist.ptr, - @intCast(c_int, eventlist.len), - timeout, - )); -} - -// TODO avoid libc dependency -pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); -} - -// TODO avoid libc dependency -pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); -} - -// TODO avoid libc dependency -pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { - return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); -} - -// TODO avoid libc dependency - -/// Takes the return value from a syscall and formats it back in the way -/// that the kernel represents it to libc. Errno was a mistake, let's make -/// it go away forever. -fn errnoWrap(value: isize) usize { - return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); -} +const builtin = @import("builtin"); +pub const is_the_target = builtin.os == .freebsd; +pub use std.c; diff --git a/std/os/freebsd/errno.zig b/std/os/freebsd/errno.zig deleted file mode 100644 index 0a0d825f5a..0000000000 --- a/std/os/freebsd/errno.zig +++ /dev/null @@ -1,121 +0,0 @@ -pub const EPERM = 1; // Operation not permitted -pub const ENOENT = 2; // No such file or directory -pub const ESRCH = 3; // No such process -pub const EINTR = 4; // Interrupted system call -pub const EIO = 5; // Input/output error -pub const ENXIO = 6; // Device not configured -pub const E2BIG = 7; // Argument list too long -pub const ENOEXEC = 8; // Exec format error -pub const EBADF = 9; // Bad file descriptor -pub const ECHILD = 10; // No child processes -pub const EDEADLK = 11; // Resource deadlock avoided -// 11 was EAGAIN -pub const ENOMEM = 12; // Cannot allocate memory -pub const EACCES = 13; // Permission denied -pub const EFAULT = 14; // Bad address -pub const ENOTBLK = 15; // Block device required -pub const EBUSY = 16; // Device busy -pub const EEXIST = 17; // File exists -pub const EXDEV = 18; // Cross-device link -pub const ENODEV = 19; // Operation not supported by device -pub const ENOTDIR = 20; // Not a directory -pub const EISDIR = 21; // Is a directory -pub const EINVAL = 22; // Invalid argument -pub const ENFILE = 23; // Too many open files in system -pub const EMFILE = 24; // Too many open files -pub const ENOTTY = 25; // Inappropriate ioctl for device -pub const ETXTBSY = 26; // Text file busy -pub const EFBIG = 27; // File too large -pub const ENOSPC = 28; // No space left on device -pub const ESPIPE = 29; // Illegal seek -pub const EROFS = 30; // Read-only filesystem -pub const EMLINK = 31; // Too many links -pub const EPIPE = 32; // Broken pipe - -// math software -pub const EDOM = 33; // Numerical argument out of domain -pub const ERANGE = 34; // Result too large - -// non-blocking and interrupt i/o -pub const EAGAIN = 35; // Resource temporarily unavailable -pub const EWOULDBLOCK = EAGAIN; // Operation would block -pub const EINPROGRESS = 36; // Operation now in progress -pub const EALREADY = 37; // Operation already in progress - -// ipc/network software -- argument errors -pub const ENOTSOCK = 38; // Socket operation on non-socket -pub const EDESTADDRREQ = 39; // Destination address required -pub const EMSGSIZE = 40; // Message too long -pub const EPROTOTYPE = 41; // Protocol wrong type for socket -pub const ENOPROTOOPT = 42; // Protocol not available -pub const EPROTONOSUPPORT = 43; // Protocol not supported -pub const ESOCKTNOSUPPORT = 44; // Socket type not supported -pub const EOPNOTSUPP = 45; // Operation not supported -pub const ENOTSUP = EOPNOTSUPP; // Operation not supported -pub const EPFNOSUPPORT = 46; // Protocol family not supported -pub const EAFNOSUPPORT = 47; // Address family not supported by protocol family -pub const EADDRINUSE = 48; // Address already in use -pub const EADDRNOTAVAIL = 49; // Can't assign requested address - -// ipc/network software -- operational errors -pub const ENETDOWN = 50; // Network is down -pub const ENETUNREACH = 51; // Network is unreachable -pub const ENETRESET = 52; // Network dropped connection on reset -pub const ECONNABORTED = 53; // Software caused connection abort -pub const ECONNRESET = 54; // Connection reset by peer -pub const ENOBUFS = 55; // No buffer space available -pub const EISCONN = 56; // Socket is already connected -pub const ENOTCONN = 57; // Socket is not connected -pub const ESHUTDOWN = 58; // Can't send after socket shutdown -pub const ETOOMANYREFS = 59; // Too many references: can't splice -pub const ETIMEDOUT = 60; // Operation timed out -pub const ECONNREFUSED = 61; // Connection refused - -pub const ELOOP = 62; // Too many levels of symbolic links -pub const ENAMETOOLONG = 63; // File name too long - -// should be rearranged -pub const EHOSTDOWN = 64; // Host is down -pub const EHOSTUNREACH = 65; // No route to host -pub const ENOTEMPTY = 66; // Directory not empty - -// quotas & mush -pub const EPROCLIM = 67; // Too many processes -pub const EUSERS = 68; // Too many users -pub const EDQUOT = 69; // Disc quota exceeded - -// Network File System -pub const ESTALE = 70; // Stale NFS file handle -pub const EREMOTE = 71; // Too many levels of remote in path -pub const EBADRPC = 72; // RPC struct is bad -pub const ERPCMISMATCH = 73; // RPC version wrong -pub const EPROGUNAVAIL = 74; // RPC prog. not avail -pub const EPROGMISMATCH = 75; // Program version wrong -pub const EPROCUNAVAIL = 76; // Bad procedure for program - -pub const ENOLCK = 77; // No locks available -pub const ENOSYS = 78; // Function not implemented - -pub const EFTYPE = 79; // Inappropriate file type or format -pub const EAUTH = 80; // Authentication error -pub const ENEEDAUTH = 81; // Need authenticator -pub const EIDRM = 82; // Identifier removed -pub const ENOMSG = 83; // No message of desired type -pub const EOVERFLOW = 84; // Value too large to be stored in data type -pub const ECANCELED = 85; // Operation canceled -pub const EILSEQ = 86; // Illegal byte sequence -pub const ENOATTR = 87; // Attribute not found - -pub const EDOOFUS = 88; // Programming error - -pub const EBADMSG = 89; // Bad message -pub const EMULTIHOP = 90; // Multihop attempted -pub const ENOLINK = 91; // Link has been severed -pub const EPROTO = 92; // Protocol error - -pub const ENOTCAPABLE = 93; // Capabilities insufficient -pub const ECAPMODE = 94; // Not permitted in capability mode -pub const ENOTRECOVERABLE = 95; // State not recoverable -pub const EOWNERDEAD = 96; // Previous owner died - -pub const ELAST = 96; // Must be equal largest errno diff --git a/std/os/get_user_id.zig b/std/os/get_user_id.zig deleted file mode 100644 index db44c6838f..0000000000 --- a/std/os/get_user_id.zig +++ /dev/null @@ -1,104 +0,0 @@ -const builtin = @import("builtin"); -const Os = builtin.Os; -const os = @import("../os.zig"); -const io = @import("../io.zig"); - -pub const UserInfo = struct { - uid: u32, - gid: u32, -}; - -/// POSIX function which gets a uid from username. -pub fn getUserInfo(name: []const u8) !UserInfo { - return switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => posixGetUserInfo(name), - else => @compileError("Unsupported OS"), - }; -} - -const State = enum { - Start, - WaitForNextLine, - SkipPassword, - ReadUserId, - ReadGroupId, -}; - -// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else -// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`. - -pub fn posixGetUserInfo(name: []const u8) !UserInfo { - var in_stream = try io.InStream.open("/etc/passwd", null); - defer in_stream.close(); - - var buf: [os.page_size]u8 = undefined; - var name_index: usize = 0; - var state = State.Start; - var uid: u32 = 0; - var gid: u32 = 0; - - while (true) { - const amt_read = try in_stream.read(buf[0..]); - for (buf[0..amt_read]) |byte| { - switch (state) { - State.Start => switch (byte) { - ':' => { - state = if (name_index == name.len) State.SkipPassword else State.WaitForNextLine; - }, - '\n' => return error.CorruptPasswordFile, - else => { - if (name_index == name.len or name[name_index] != byte) { - state = State.WaitForNextLine; - } - name_index += 1; - }, - }, - State.WaitForNextLine => switch (byte) { - '\n' => { - name_index = 0; - state = State.Start; - }, - else => continue, - }, - State.SkipPassword => switch (byte) { - '\n' => return error.CorruptPasswordFile, - ':' => { - state = State.ReadUserId; - }, - else => continue, - }, - State.ReadUserId => switch (byte) { - ':' => { - state = State.ReadGroupId; - }, - '\n' => return error.CorruptPasswordFile, - else => { - const digit = switch (byte) { - '0'...'9' => byte - '0', - else => return error.CorruptPasswordFile, - }; - if (@mulWithOverflow(u32, uid, 10, *uid)) return error.CorruptPasswordFile; - if (@addWithOverflow(u32, uid, digit, *uid)) return error.CorruptPasswordFile; - }, - }, - State.ReadGroupId => switch (byte) { - '\n', ':' => { - return UserInfo{ - .uid = uid, - .gid = gid, - }; - }, - else => { - const digit = switch (byte) { - '0'...'9' => byte - '0', - else => return error.CorruptPasswordFile, - }; - if (@mulWithOverflow(u32, gid, 10, *gid)) return error.CorruptPasswordFile; - if (@addWithOverflow(u32, gid, digit, *gid)) return error.CorruptPasswordFile; - }, - }, - } - } - if (amt_read < buf.len) return error.UserNotFound; - } -} diff --git a/std/os/linux.zig b/std/os/linux.zig index ecbbf72d42..282aa19bf1 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -1,709 +1,61 @@ +// This file provides the system interface functions for Linux matching those +// that are provided by libc, whether or not libc is linked. The following +// abstractions are made: +// * Work around kernel bugs and limitations. For example, see sendmmsg. +// * Implement all the syscalls in the same way that libc functions will +// provide `rename` when only the `renameat` syscall exists. +// * Does not support POSIX thread cancellation. const std = @import("../std.zig"); -const assert = std.debug.assert; const builtin = @import("builtin"); +const assert = std.debug.assert; const maxInt = std.math.maxInt; const elf = std.elf; -pub const tls = @import("linux/tls.zig"); const vdso = @import("linux/vdso.zig"); const dl = @import("../dynamic_library.zig"); + +pub const is_the_target = builtin.os == .linux; pub use switch (builtin.arch) { - builtin.Arch.x86_64 => @import("linux/x86_64.zig"), - builtin.Arch.i386 => @import("linux/i386.zig"), - builtin.Arch.aarch64 => @import("linux/arm64.zig"), - else => @compileError("unsupported arch"), + .x86_64 => @import("linux/x86_64.zig"), + .aarch64 => @import("linux/arm64.zig"), + else => struct {}, }; -pub use @import("linux/errno.zig"); - -pub const PATH_MAX = 4096; -pub const IOV_MAX = 1024; - -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; - -pub const FUTEX_WAIT = 0; -pub const FUTEX_WAKE = 1; -pub const FUTEX_FD = 2; -pub const FUTEX_REQUEUE = 3; -pub const FUTEX_CMP_REQUEUE = 4; -pub const FUTEX_WAKE_OP = 5; -pub const FUTEX_LOCK_PI = 6; -pub const FUTEX_UNLOCK_PI = 7; -pub const FUTEX_TRYLOCK_PI = 8; -pub const FUTEX_WAIT_BITSET = 9; - -pub const FUTEX_PRIVATE_FLAG = 128; - -pub const FUTEX_CLOCK_REALTIME = 256; - -pub const PROT_NONE = 0; -pub const PROT_READ = 1; -pub const PROT_WRITE = 2; -pub const PROT_EXEC = 4; -pub const PROT_GROWSDOWN = 0x01000000; -pub const PROT_GROWSUP = 0x02000000; - -pub const MAP_FAILED = maxInt(usize); -pub const MAP_SHARED = 0x01; -pub const MAP_PRIVATE = 0x02; -pub const MAP_TYPE = 0x0f; -pub const MAP_FIXED = 0x10; -pub const MAP_ANONYMOUS = 0x20; -pub const MAP_NORESERVE = 0x4000; -pub const MAP_GROWSDOWN = 0x0100; -pub const MAP_DENYWRITE = 0x0800; -pub const MAP_EXECUTABLE = 0x1000; -pub const MAP_LOCKED = 0x2000; -pub const MAP_POPULATE = 0x8000; -pub const MAP_NONBLOCK = 0x10000; -pub const MAP_STACK = 0x20000; -pub const MAP_HUGETLB = 0x40000; -pub const MAP_FILE = 0; - -pub const F_OK = 0; -pub const X_OK = 1; -pub const W_OK = 2; -pub const R_OK = 4; - -pub const WNOHANG = 1; -pub const WUNTRACED = 2; -pub const WSTOPPED = 2; -pub const WEXITED = 4; -pub const WCONTINUED = 8; -pub const WNOWAIT = 0x1000000; - -pub const SA_NOCLDSTOP = 1; -pub const SA_NOCLDWAIT = 2; -pub const SA_SIGINFO = 4; -pub const SA_ONSTACK = 0x08000000; -pub const SA_RESTART = 0x10000000; -pub const SA_NODEFER = 0x40000000; -pub const SA_RESETHAND = 0x80000000; -pub const SA_RESTORER = 0x04000000; - -pub const SIGHUP = 1; -pub const SIGINT = 2; -pub const SIGQUIT = 3; -pub const SIGILL = 4; -pub const SIGTRAP = 5; -pub const SIGABRT = 6; -pub const SIGIOT = SIGABRT; -pub const SIGBUS = 7; -pub const SIGFPE = 8; -pub const SIGKILL = 9; -pub const SIGUSR1 = 10; -pub const SIGSEGV = 11; -pub const SIGUSR2 = 12; -pub const SIGPIPE = 13; -pub const SIGALRM = 14; -pub const SIGTERM = 15; -pub const SIGSTKFLT = 16; -pub const SIGCHLD = 17; -pub const SIGCONT = 18; -pub const SIGSTOP = 19; -pub const SIGTSTP = 20; -pub const SIGTTIN = 21; -pub const SIGTTOU = 22; -pub const SIGURG = 23; -pub const SIGXCPU = 24; -pub const SIGXFSZ = 25; -pub const SIGVTALRM = 26; -pub const SIGPROF = 27; -pub const SIGWINCH = 28; -pub const SIGIO = 29; -pub const SIGPOLL = 29; -pub const SIGPWR = 30; -pub const SIGSYS = 31; -pub const SIGUNUSED = SIGSYS; - -pub const O_RDONLY = 0o0; -pub const O_WRONLY = 0o1; -pub const O_RDWR = 0o2; - -pub const SEEK_SET = 0; -pub const SEEK_CUR = 1; -pub const SEEK_END = 2; - -pub const SIG_BLOCK = 0; -pub const SIG_UNBLOCK = 1; -pub const SIG_SETMASK = 2; - -pub const PROTO_ip = 0o000; -pub const PROTO_icmp = 0o001; -pub const PROTO_igmp = 0o002; -pub const PROTO_ggp = 0o003; -pub const PROTO_ipencap = 0o004; -pub const PROTO_st = 0o005; -pub const PROTO_tcp = 0o006; -pub const PROTO_egp = 0o010; -pub const PROTO_pup = 0o014; -pub const PROTO_udp = 0o021; -pub const PROTO_hmp = 0o024; -pub const PROTO_xns_idp = 0o026; -pub const PROTO_rdp = 0o033; -pub const PROTO_iso_tp4 = 0o035; -pub const PROTO_xtp = 0o044; -pub const PROTO_ddp = 0o045; -pub const PROTO_idpr_cmtp = 0o046; -pub const PROTO_ipv6 = 0o051; -pub const PROTO_ipv6_route = 0o053; -pub const PROTO_ipv6_frag = 0o054; -pub const PROTO_idrp = 0o055; -pub const PROTO_rsvp = 0o056; -pub const PROTO_gre = 0o057; -pub const PROTO_esp = 0o062; -pub const PROTO_ah = 0o063; -pub const PROTO_skip = 0o071; -pub const PROTO_ipv6_icmp = 0o072; -pub const PROTO_ipv6_nonxt = 0o073; -pub const PROTO_ipv6_opts = 0o074; -pub const PROTO_rspf = 0o111; -pub const PROTO_vmtp = 0o121; -pub const PROTO_ospf = 0o131; -pub const PROTO_ipip = 0o136; -pub const PROTO_encap = 0o142; -pub const PROTO_pim = 0o147; -pub const PROTO_raw = 0o377; - -pub const SHUT_RD = 0; -pub const SHUT_WR = 1; -pub const SHUT_RDWR = 2; - -pub const SOCK_STREAM = 1; -pub const SOCK_DGRAM = 2; -pub const SOCK_RAW = 3; -pub const SOCK_RDM = 4; -pub const SOCK_SEQPACKET = 5; -pub const SOCK_DCCP = 6; -pub const SOCK_PACKET = 10; -pub const SOCK_CLOEXEC = 0o2000000; -pub const SOCK_NONBLOCK = 0o4000; - -pub const PF_UNSPEC = 0; -pub const PF_LOCAL = 1; -pub const PF_UNIX = PF_LOCAL; -pub const PF_FILE = PF_LOCAL; -pub const PF_INET = 2; -pub const PF_AX25 = 3; -pub const PF_IPX = 4; -pub const PF_APPLETALK = 5; -pub const PF_NETROM = 6; -pub const PF_BRIDGE = 7; -pub const PF_ATMPVC = 8; -pub const PF_X25 = 9; -pub const PF_INET6 = 10; -pub const PF_ROSE = 11; -pub const PF_DECnet = 12; -pub const PF_NETBEUI = 13; -pub const PF_SECURITY = 14; -pub const PF_KEY = 15; -pub const PF_NETLINK = 16; -pub const PF_ROUTE = PF_NETLINK; -pub const PF_PACKET = 17; -pub const PF_ASH = 18; -pub const PF_ECONET = 19; -pub const PF_ATMSVC = 20; -pub const PF_RDS = 21; -pub const PF_SNA = 22; -pub const PF_IRDA = 23; -pub const PF_PPPOX = 24; -pub const PF_WANPIPE = 25; -pub const PF_LLC = 26; -pub const PF_IB = 27; -pub const PF_MPLS = 28; -pub const PF_CAN = 29; -pub const PF_TIPC = 30; -pub const PF_BLUETOOTH = 31; -pub const PF_IUCV = 32; -pub const PF_RXRPC = 33; -pub const PF_ISDN = 34; -pub const PF_PHONET = 35; -pub const PF_IEEE802154 = 36; -pub const PF_CAIF = 37; -pub const PF_ALG = 38; -pub const PF_NFC = 39; -pub const PF_VSOCK = 40; -pub const PF_KCM = 41; -pub const PF_QIPCRTR = 42; -pub const PF_SMC = 43; -pub const PF_MAX = 44; - -pub const AF_UNSPEC = PF_UNSPEC; -pub const AF_LOCAL = PF_LOCAL; -pub const AF_UNIX = AF_LOCAL; -pub const AF_FILE = AF_LOCAL; -pub const AF_INET = PF_INET; -pub const AF_AX25 = PF_AX25; -pub const AF_IPX = PF_IPX; -pub const AF_APPLETALK = PF_APPLETALK; -pub const AF_NETROM = PF_NETROM; -pub const AF_BRIDGE = PF_BRIDGE; -pub const AF_ATMPVC = PF_ATMPVC; -pub const AF_X25 = PF_X25; -pub const AF_INET6 = PF_INET6; -pub const AF_ROSE = PF_ROSE; -pub const AF_DECnet = PF_DECnet; -pub const AF_NETBEUI = PF_NETBEUI; -pub const AF_SECURITY = PF_SECURITY; -pub const AF_KEY = PF_KEY; -pub const AF_NETLINK = PF_NETLINK; -pub const AF_ROUTE = PF_ROUTE; -pub const AF_PACKET = PF_PACKET; -pub const AF_ASH = PF_ASH; -pub const AF_ECONET = PF_ECONET; -pub const AF_ATMSVC = PF_ATMSVC; -pub const AF_RDS = PF_RDS; -pub const AF_SNA = PF_SNA; -pub const AF_IRDA = PF_IRDA; -pub const AF_PPPOX = PF_PPPOX; -pub const AF_WANPIPE = PF_WANPIPE; -pub const AF_LLC = PF_LLC; -pub const AF_IB = PF_IB; -pub const AF_MPLS = PF_MPLS; -pub const AF_CAN = PF_CAN; -pub const AF_TIPC = PF_TIPC; -pub const AF_BLUETOOTH = PF_BLUETOOTH; -pub const AF_IUCV = PF_IUCV; -pub const AF_RXRPC = PF_RXRPC; -pub const AF_ISDN = PF_ISDN; -pub const AF_PHONET = PF_PHONET; -pub const AF_IEEE802154 = PF_IEEE802154; -pub const AF_CAIF = PF_CAIF; -pub const AF_ALG = PF_ALG; -pub const AF_NFC = PF_NFC; -pub const AF_VSOCK = PF_VSOCK; -pub const AF_KCM = PF_KCM; -pub const AF_QIPCRTR = PF_QIPCRTR; -pub const AF_SMC = PF_SMC; -pub const AF_MAX = PF_MAX; - -pub const SO_DEBUG = 1; -pub const SO_REUSEADDR = 2; -pub const SO_TYPE = 3; -pub const SO_ERROR = 4; -pub const SO_DONTROUTE = 5; -pub const SO_BROADCAST = 6; -pub const SO_SNDBUF = 7; -pub const SO_RCVBUF = 8; -pub const SO_KEEPALIVE = 9; -pub const SO_OOBINLINE = 10; -pub const SO_NO_CHECK = 11; -pub const SO_PRIORITY = 12; -pub const SO_LINGER = 13; -pub const SO_BSDCOMPAT = 14; -pub const SO_REUSEPORT = 15; -pub const SO_PASSCRED = 16; -pub const SO_PEERCRED = 17; -pub const SO_RCVLOWAT = 18; -pub const SO_SNDLOWAT = 19; -pub const SO_RCVTIMEO = 20; -pub const SO_SNDTIMEO = 21; -pub const SO_ACCEPTCONN = 30; -pub const SO_SNDBUFFORCE = 32; -pub const SO_RCVBUFFORCE = 33; -pub const SO_PROTOCOL = 38; -pub const SO_DOMAIN = 39; - -pub const SO_SECURITY_AUTHENTICATION = 22; -pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; -pub const SO_SECURITY_ENCRYPTION_NETWORK = 24; - -pub const SO_BINDTODEVICE = 25; - -pub const SO_ATTACH_FILTER = 26; -pub const SO_DETACH_FILTER = 27; -pub const SO_GET_FILTER = SO_ATTACH_FILTER; - -pub const SO_PEERNAME = 28; -pub const SO_TIMESTAMP = 29; -pub const SCM_TIMESTAMP = SO_TIMESTAMP; - -pub const SO_PEERSEC = 31; -pub const SO_PASSSEC = 34; -pub const SO_TIMESTAMPNS = 35; -pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS; -pub const SO_MARK = 36; -pub const SO_TIMESTAMPING = 37; -pub const SCM_TIMESTAMPING = SO_TIMESTAMPING; -pub const SO_RXQ_OVFL = 40; -pub const SO_WIFI_STATUS = 41; -pub const SCM_WIFI_STATUS = SO_WIFI_STATUS; -pub const SO_PEEK_OFF = 42; -pub const SO_NOFCS = 43; -pub const SO_LOCK_FILTER = 44; -pub const SO_SELECT_ERR_QUEUE = 45; -pub const SO_BUSY_POLL = 46; -pub const SO_MAX_PACING_RATE = 47; -pub const SO_BPF_EXTENSIONS = 48; -pub const SO_INCOMING_CPU = 49; -pub const SO_ATTACH_BPF = 50; -pub const SO_DETACH_BPF = SO_DETACH_FILTER; -pub const SO_ATTACH_REUSEPORT_CBPF = 51; -pub const SO_ATTACH_REUSEPORT_EBPF = 52; -pub const SO_CNX_ADVICE = 53; -pub const SCM_TIMESTAMPING_OPT_STATS = 54; -pub const SO_MEMINFO = 55; -pub const SO_INCOMING_NAPI_ID = 56; -pub const SO_COOKIE = 57; -pub const SCM_TIMESTAMPING_PKTINFO = 58; -pub const SO_PEERGROUPS = 59; -pub const SO_ZEROCOPY = 60; - -pub const SOL_SOCKET = 1; - -pub const SOL_IP = 0; -pub const SOL_IPV6 = 41; -pub const SOL_ICMPV6 = 58; - -pub const SOL_RAW = 255; -pub const SOL_DECNET = 261; -pub const SOL_X25 = 262; -pub const SOL_PACKET = 263; -pub const SOL_ATM = 264; -pub const SOL_AAL = 265; -pub const SOL_IRDA = 266; -pub const SOL_NETBEUI = 267; -pub const SOL_LLC = 268; -pub const SOL_DCCP = 269; -pub const SOL_NETLINK = 270; -pub const SOL_TIPC = 271; -pub const SOL_RXRPC = 272; -pub const SOL_PPPOL2TP = 273; -pub const SOL_BLUETOOTH = 274; -pub const SOL_PNPIPE = 275; -pub const SOL_RDS = 276; -pub const SOL_IUCV = 277; -pub const SOL_CAIF = 278; -pub const SOL_ALG = 279; -pub const SOL_NFC = 280; -pub const SOL_KCM = 281; -pub const SOL_TLS = 282; - -pub const SOMAXCONN = 128; - -pub const MSG_OOB = 0x0001; -pub const MSG_PEEK = 0x0002; -pub const MSG_DONTROUTE = 0x0004; -pub const MSG_CTRUNC = 0x0008; -pub const MSG_PROXY = 0x0010; -pub const MSG_TRUNC = 0x0020; -pub const MSG_DONTWAIT = 0x0040; -pub const MSG_EOR = 0x0080; -pub const MSG_WAITALL = 0x0100; -pub const MSG_FIN = 0x0200; -pub const MSG_SYN = 0x0400; -pub const MSG_CONFIRM = 0x0800; -pub const MSG_RST = 0x1000; -pub const MSG_ERRQUEUE = 0x2000; -pub const MSG_NOSIGNAL = 0x4000; -pub const MSG_MORE = 0x8000; -pub const MSG_WAITFORONE = 0x10000; -pub const MSG_BATCH = 0x40000; -pub const MSG_ZEROCOPY = 0x4000000; -pub const MSG_FASTOPEN = 0x20000000; -pub const MSG_CMSG_CLOEXEC = 0x40000000; - -pub const DT_UNKNOWN = 0; -pub const DT_FIFO = 1; -pub const DT_CHR = 2; -pub const DT_DIR = 4; -pub const DT_BLK = 6; -pub const DT_REG = 8; -pub const DT_LNK = 10; -pub const DT_SOCK = 12; -pub const DT_WHT = 14; - -pub const TCGETS = 0x5401; -pub const TCSETS = 0x5402; -pub const TCSETSW = 0x5403; -pub const TCSETSF = 0x5404; -pub const TCGETA = 0x5405; -pub const TCSETA = 0x5406; -pub const TCSETAW = 0x5407; -pub const TCSETAF = 0x5408; -pub const TCSBRK = 0x5409; -pub const TCXONC = 0x540A; -pub const TCFLSH = 0x540B; -pub const TIOCEXCL = 0x540C; -pub const TIOCNXCL = 0x540D; -pub const TIOCSCTTY = 0x540E; -pub const TIOCGPGRP = 0x540F; -pub const TIOCSPGRP = 0x5410; -pub const TIOCOUTQ = 0x5411; -pub const TIOCSTI = 0x5412; -pub const TIOCGWINSZ = 0x5413; -pub const TIOCSWINSZ = 0x5414; -pub const TIOCMGET = 0x5415; -pub const TIOCMBIS = 0x5416; -pub const TIOCMBIC = 0x5417; -pub const TIOCMSET = 0x5418; -pub const TIOCGSOFTCAR = 0x5419; -pub const TIOCSSOFTCAR = 0x541A; -pub const FIONREAD = 0x541B; -pub const TIOCINQ = FIONREAD; -pub const TIOCLINUX = 0x541C; -pub const TIOCCONS = 0x541D; -pub const TIOCGSERIAL = 0x541E; -pub const TIOCSSERIAL = 0x541F; -pub const TIOCPKT = 0x5420; -pub const FIONBIO = 0x5421; -pub const TIOCNOTTY = 0x5422; -pub const TIOCSETD = 0x5423; -pub const TIOCGETD = 0x5424; -pub const TCSBRKP = 0x5425; -pub const TIOCSBRK = 0x5427; -pub const TIOCCBRK = 0x5428; -pub const TIOCGSID = 0x5429; -pub const TIOCGRS485 = 0x542E; -pub const TIOCSRS485 = 0x542F; -pub const TIOCGPTN = 0x80045430; -pub const TIOCSPTLCK = 0x40045431; -pub const TIOCGDEV = 0x80045432; -pub const TCGETX = 0x5432; -pub const TCSETX = 0x5433; -pub const TCSETXF = 0x5434; -pub const TCSETXW = 0x5435; -pub const TIOCSIG = 0x40045436; -pub const TIOCVHANGUP = 0x5437; -pub const TIOCGPKT = 0x80045438; -pub const TIOCGPTLCK = 0x80045439; -pub const TIOCGEXCL = 0x80045440; - -pub const EPOLL_CLOEXEC = O_CLOEXEC; - -pub const EPOLL_CTL_ADD = 1; -pub const EPOLL_CTL_DEL = 2; -pub const EPOLL_CTL_MOD = 3; - -pub const EPOLLIN = 0x001; -pub const EPOLLPRI = 0x002; -pub const EPOLLOUT = 0x004; -pub const EPOLLRDNORM = 0x040; -pub const EPOLLRDBAND = 0x080; -pub const EPOLLWRNORM = 0x100; -pub const EPOLLWRBAND = 0x200; -pub const EPOLLMSG = 0x400; -pub const EPOLLERR = 0x008; -pub const EPOLLHUP = 0x010; -pub const EPOLLRDHUP = 0x2000; -pub const EPOLLEXCLUSIVE = (u32(1) << 28); -pub const EPOLLWAKEUP = (u32(1) << 29); -pub const EPOLLONESHOT = (u32(1) << 30); -pub const EPOLLET = (u32(1) << 31); - -pub const CLOCK_REALTIME = 0; -pub const CLOCK_MONOTONIC = 1; -pub const CLOCK_PROCESS_CPUTIME_ID = 2; -pub const CLOCK_THREAD_CPUTIME_ID = 3; -pub const CLOCK_MONOTONIC_RAW = 4; -pub const CLOCK_REALTIME_COARSE = 5; -pub const CLOCK_MONOTONIC_COARSE = 6; -pub const CLOCK_BOOTTIME = 7; -pub const CLOCK_REALTIME_ALARM = 8; -pub const CLOCK_BOOTTIME_ALARM = 9; -pub const CLOCK_SGI_CYCLE = 10; -pub const CLOCK_TAI = 11; - -pub const CSIGNAL = 0x000000ff; -pub const CLONE_VM = 0x00000100; -pub const CLONE_FS = 0x00000200; -pub const CLONE_FILES = 0x00000400; -pub const CLONE_SIGHAND = 0x00000800; -pub const CLONE_PTRACE = 0x00002000; -pub const CLONE_VFORK = 0x00004000; -pub const CLONE_PARENT = 0x00008000; -pub const CLONE_THREAD = 0x00010000; -pub const CLONE_NEWNS = 0x00020000; -pub const CLONE_SYSVSEM = 0x00040000; -pub const CLONE_SETTLS = 0x00080000; -pub const CLONE_PARENT_SETTID = 0x00100000; -pub const CLONE_CHILD_CLEARTID = 0x00200000; -pub const CLONE_DETACHED = 0x00400000; -pub const CLONE_UNTRACED = 0x00800000; -pub const CLONE_CHILD_SETTID = 0x01000000; -pub const CLONE_NEWCGROUP = 0x02000000; -pub const CLONE_NEWUTS = 0x04000000; -pub const CLONE_NEWIPC = 0x08000000; -pub const CLONE_NEWUSER = 0x10000000; -pub const CLONE_NEWPID = 0x20000000; -pub const CLONE_NEWNET = 0x40000000; -pub const CLONE_IO = 0x80000000; - -pub const EFD_SEMAPHORE = 1; -pub const EFD_CLOEXEC = O_CLOEXEC; -pub const EFD_NONBLOCK = O_NONBLOCK; - -pub const MS_RDONLY = 1; -pub const MS_NOSUID = 2; -pub const MS_NODEV = 4; -pub const MS_NOEXEC = 8; -pub const MS_SYNCHRONOUS = 16; -pub const MS_REMOUNT = 32; -pub const MS_MANDLOCK = 64; -pub const MS_DIRSYNC = 128; -pub const MS_NOATIME = 1024; -pub const MS_NODIRATIME = 2048; -pub const MS_BIND = 4096; -pub const MS_MOVE = 8192; -pub const MS_REC = 16384; -pub const MS_SILENT = 32768; -pub const MS_POSIXACL = (1 << 16); -pub const MS_UNBINDABLE = (1 << 17); -pub const MS_PRIVATE = (1 << 18); -pub const MS_SLAVE = (1 << 19); -pub const MS_SHARED = (1 << 20); -pub const MS_RELATIME = (1 << 21); -pub const MS_KERNMOUNT = (1 << 22); -pub const MS_I_VERSION = (1 << 23); -pub const MS_STRICTATIME = (1 << 24); -pub const MS_LAZYTIME = (1 << 25); -pub const MS_NOREMOTELOCK = (1 << 27); -pub const MS_NOSEC = (1 << 28); -pub const MS_BORN = (1 << 29); -pub const MS_ACTIVE = (1 << 30); -pub const MS_NOUSER = (1 << 31); - -pub const MS_RMT_MASK = (MS_RDONLY | MS_SYNCHRONOUS | MS_MANDLOCK | MS_I_VERSION | MS_LAZYTIME); - -pub const MS_MGC_VAL = 0xc0ed0000; -pub const MS_MGC_MSK = 0xffff0000; - -pub const MNT_FORCE = 1; -pub const MNT_DETACH = 2; -pub const MNT_EXPIRE = 4; -pub const UMOUNT_NOFOLLOW = 8; - -pub const IN_CLOEXEC = O_CLOEXEC; -pub const IN_NONBLOCK = O_NONBLOCK; - -pub const IN_ACCESS = 0x00000001; -pub const IN_MODIFY = 0x00000002; -pub const IN_ATTRIB = 0x00000004; -pub const IN_CLOSE_WRITE = 0x00000008; -pub const IN_CLOSE_NOWRITE = 0x00000010; -pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE; -pub const IN_OPEN = 0x00000020; -pub const IN_MOVED_FROM = 0x00000040; -pub const IN_MOVED_TO = 0x00000080; -pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO; -pub const IN_CREATE = 0x00000100; -pub const IN_DELETE = 0x00000200; -pub const IN_DELETE_SELF = 0x00000400; -pub const IN_MOVE_SELF = 0x00000800; -pub const IN_ALL_EVENTS = 0x00000fff; - -pub const IN_UNMOUNT = 0x00002000; -pub const IN_Q_OVERFLOW = 0x00004000; -pub const IN_IGNORED = 0x00008000; - -pub const IN_ONLYDIR = 0x01000000; -pub const IN_DONT_FOLLOW = 0x02000000; -pub const IN_EXCL_UNLINK = 0x04000000; -pub const IN_MASK_ADD = 0x20000000; - -pub const IN_ISDIR = 0x40000000; -pub const IN_ONESHOT = 0x80000000; - -pub const S_IFMT = 0o170000; - -pub const S_IFDIR = 0o040000; -pub const S_IFCHR = 0o020000; -pub const S_IFBLK = 0o060000; -pub const S_IFREG = 0o100000; -pub const S_IFIFO = 0o010000; -pub const S_IFLNK = 0o120000; -pub const S_IFSOCK = 0o140000; - -pub const S_ISUID = 0o4000; -pub const S_ISGID = 0o2000; -pub const S_ISVTX = 0o1000; -pub const S_IRUSR = 0o400; -pub const S_IWUSR = 0o200; -pub const S_IXUSR = 0o100; -pub const S_IRWXU = 0o700; -pub const S_IRGRP = 0o040; -pub const S_IWGRP = 0o020; -pub const S_IXGRP = 0o010; -pub const S_IRWXG = 0o070; -pub const S_IROTH = 0o004; -pub const S_IWOTH = 0o002; -pub const S_IXOTH = 0o001; -pub const S_IRWXO = 0o007; - -pub fn S_ISREG(m: u32) bool { - return m & S_IFMT == S_IFREG; -} - -pub fn S_ISDIR(m: u32) bool { - return m & S_IFMT == S_IFDIR; -} - -pub fn S_ISCHR(m: u32) bool { - return m & S_IFMT == S_IFCHR; -} - -pub fn S_ISBLK(m: u32) bool { - return m & S_IFMT == S_IFBLK; -} - -pub fn S_ISFIFO(m: u32) bool { - return m & S_IFMT == S_IFIFO; -} - -pub fn S_ISLNK(m: u32) bool { - return m & S_IFMT == S_IFLNK; -} - -pub fn S_ISSOCK(m: u32) bool { - return m & S_IFMT == S_IFSOCK; -} - -pub const TFD_NONBLOCK = O_NONBLOCK; -pub const TFD_CLOEXEC = O_CLOEXEC; +pub use @import("bits.zig"); +pub const tls = @import("linux/tls.zig"); -pub const TFD_TIMER_ABSTIME = 1; -pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1); +/// Set by startup code, used by `getauxval`. +pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; -fn unsigned(s: i32) u32 { - return @bitCast(u32, s); -} -fn signed(s: u32) i32 { - return @bitCast(i32, s); -} -pub fn WEXITSTATUS(s: i32) i32 { - return signed((unsigned(s) & 0xff00) >> 8); -} -pub fn WTERMSIG(s: i32) i32 { - return signed(unsigned(s) & 0x7f); -} -pub fn WSTOPSIG(s: i32) i32 { - return WEXITSTATUS(s); -} -pub fn WIFEXITED(s: i32) bool { - return WTERMSIG(s) == 0; -} -pub fn WIFSTOPPED(s: i32) bool { - return @intCast(u16, ((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; -} -pub fn WIFSIGNALED(s: i32) bool { - return (unsigned(s) & 0xffff) -% 1 < 0xff; +/// See `std.elf` for the constants. +pub fn getauxval(index: usize) usize { + const auxv = elf_aux_maybe orelse return 0; + var i: usize = 0; + while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { + if (auxv[i].a_type == index) + return auxv[i].a_un.a_val; + } + return 0; } -pub const winsize = extern struct { - ws_row: u16, - ws_col: u16, - ws_xpixel: u16, - ws_ypixel: u16, -}; - /// Get the errno from a syscall return value, or 0 for no error. -pub fn getErrno(r: usize) usize { +pub fn getErrno(r: usize) u12 { const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; + return if (signed_r > -4096 and signed_r < 0) @intCast(u12, -signed_r) else 0; } pub fn dup2(old: i32, new: i32) usize { - return dup3(old, new, 0); + if (@hasDecl(@This(), "SYS_dup2")) { + return syscall2(SYS_dup2, @bitCast(usize, isize(old)), @bitCast(usize, isize(new))); + } else { + if (old == new) { + if (std.debug.runtime_safety) { + const rc = syscall2(SYS_fcntl, @bitCast(usize, isize(old)), F_GETFD); + if (@bitCast(isize, rc) < 0) return rc; + } + return @intCast(usize, old); + } else { + return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), 0); + } + } } pub fn dup3(old: i32, new: i32, flags: u32) usize { @@ -726,7 +78,11 @@ pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[* } pub fn fork() usize { - return clone2(SIGCHLD, 0); + if (@hasDecl(@This(), "SYS_fork")) { + return syscall0(SYS_fork); + } else { + return syscall2(SYS_clone, SIGCHLD, 0); + } } /// This must be inline, and inline call the syscall function, because if the @@ -750,6 +106,10 @@ pub fn getcwd(buf: [*]u8, size: usize) usize { return syscall2(SYS_getcwd, @ptrToInt(buf), size); } +pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { + return syscall3(SYS_getdents, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); +} + pub fn getdents64(fd: i32, dirp: [*]u8, count: usize) usize { return syscall3(SYS_getdents64, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); } @@ -766,14 +126,13 @@ pub fn inotify_rm_watch(fd: i32, wd: i32) usize { return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); } -pub fn isatty(fd: i32) bool { - var wsz: winsize = undefined; - return syscall3(SYS_ioctl, @bitCast(usize, isize(fd)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; -} - // TODO https://github.com/ziglang/zig/issues/265 pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return readlinkat(AT_FDCWD, path, buf_ptr, buf_len); + if (@hasDecl(@This(), "SYS_readlink")) { + return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); + } else { + return syscall4(SYS_readlinkat, AT_FDCWD, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); + } } // TODO https://github.com/ziglang/zig/issues/265 @@ -783,7 +142,11 @@ pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, // TODO https://github.com/ziglang/zig/issues/265 pub fn mkdir(path: [*]const u8, mode: u32) usize { - return mkdirat(AT_FDCWD, path, mode); + if (@hasDecl(@This(), "SYS_mkdir")) { + return syscall2(SYS_mkdir, @ptrToInt(path), mode); + } else { + return syscall3(SYS_mkdirat, AT_FDCWD, @ptrToInt(path), mode); + } } // TODO https://github.com/ziglang/zig/issues/265 @@ -810,12 +173,12 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, of return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @bitCast(usize, isize(fd)), @bitCast(usize, offset)); } -pub fn mprotect(address: usize, length: usize, protection: usize) usize { - return syscall3(SYS_mprotect, address, length, protection); +pub fn mprotect(address: [*]const u8, length: usize, protection: usize) usize { + return syscall3(SYS_mprotect, @ptrToInt(address), length, protection); } -pub fn munmap(address: usize, length: usize) usize { - return syscall2(SYS_munmap, address, length); +pub fn munmap(address: [*]const u8, length: usize) usize { + return syscall2(SYS_munmap, @ptrToInt(address), length); } pub fn read(fd: i32, buf: [*]u8, count: usize) usize { @@ -840,12 +203,20 @@ pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) us // TODO https://github.com/ziglang/zig/issues/265 pub fn rmdir(path: [*]const u8) usize { - return unlinkat(AT_FDCWD, path, AT_REMOVEDIR); + if (@hasDecl(@This(), "SYS_rmdir")) { + return syscall1(SYS_rmdir, @ptrToInt(path)); + } else { + return syscall3(SYS_unlinkat, AT_FDCWD, @ptrToInt(path), AT_REMOVEDIR); + } } // TODO https://github.com/ziglang/zig/issues/265 pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { - return symlinkat(existing, AT_FDCWD, new); + if (@hasDecl(@This(), "SYS_symlink")) { + return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new)); + } else { + return syscall3(SYS_symlinkat, @ptrToInt(existing), AT_FDCWD, @ptrToInt(new)); + } } // TODO https://github.com/ziglang/zig/issues/265 @@ -860,15 +231,20 @@ pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { // TODO https://github.com/ziglang/zig/issues/265 pub fn access(path: [*]const u8, mode: u32) usize { - return faccessat(AT_FDCWD, path, mode); + return syscall2(SYS_access, @ptrToInt(path), mode); } +// TODO https://github.com/ziglang/zig/issues/265 pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize { return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); } pub fn pipe(fd: *[2]i32) usize { - return pipe2(fd, 0); + if (@hasDecl(@This(), "SYS_pipe")) { + return syscall1(SYS_pipe, @ptrToInt(fd)); + } else { + return syscall2(SYS_pipe2, @ptrToInt(fd), 0); + } } pub fn pipe2(fd: *[2]i32, flags: u32) usize { @@ -885,17 +261,51 @@ pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { // TODO https://github.com/ziglang/zig/issues/265 pub fn rename(old: [*]const u8, new: [*]const u8) usize { - return renameat2(AT_FDCWD, old, AT_FDCWD, new, 0); + if (@hasDecl(@This(), "SYS_rename")) { + return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new)); + } else if (@hasDecl(@This(), "SYS_renameat")) { + return syscall4(SYS_renameat, AT_FDCWD, @ptrToInt(old), AT_FDCWD, @ptrToInt(new)); + } else { + return syscall5(SYS_renameat2, AT_FDCWD, @ptrToInt(old), AT_FDCWD, @ptrToInt(new), 0); + } +} + +pub fn renameat(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8) usize { + if (@hasDecl(@This(), "SYS_renameat")) { + return syscall4( + SYS_renameat, + @bitCast(usize, isize(oldfd)), + @ptrToInt(old), + @bitCast(usize, isize(newfd)), + @ptrToInt(new), + ); + } else { + return syscall5( + SYS_renameat2, + @bitCast(usize, isize(oldfd)), + @ptrToInt(old), + @bitCast(usize, isize(newfd)), + @ptrToInt(new), + 0, + ); + } } // TODO https://github.com/ziglang/zig/issues/265 pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize { - return syscall5(SYS_renameat2, @bitCast(usize, isize(oldfd)), @ptrToInt(oldpath), @bitCast(usize, isize(newfd)), @ptrToInt(newpath), flags); + return syscall5( + SYS_renameat2, + @bitCast(usize, isize(oldfd)), + @ptrToInt(oldpath), + @bitCast(usize, isize(newfd)), + @ptrToInt(newpath), + flags, + ); } // TODO https://github.com/ziglang/zig/issues/265 pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { - return openat(AT_FDCWD, path, flags, perm); + return syscall3(SYS_open, @ptrToInt(path), flags, perm); } // TODO https://github.com/ziglang/zig/issues/265 @@ -923,8 +333,21 @@ pub fn close(fd: i32) usize { return syscall1(SYS_close, @bitCast(usize, isize(fd))); } -pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { - return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos); +/// Can only be called on 32 bit systems. For 64 bit see `lseek`. +pub fn llseek(fd: i32, offset: u64, result: ?*u64, whence: usize) usize { + return syscall5( + SYS__llseek, + @bitCast(usize, isize(fd)), + @truncate(usize, offset >> 32), + @truncate(usize, offset), + @ptrToInt(result), + whence, + ); +} + +/// Can only be called on 64 bit systems. For 32 bit see `llseek`. +pub fn lseek(fd: i32, offset: i64, whence: usize) usize { + return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), whence); } pub fn exit(status: i32) noreturn { @@ -947,7 +370,11 @@ pub fn kill(pid: i32, sig: i32) usize { // TODO https://github.com/ziglang/zig/issues/265 pub fn unlink(path: [*]const u8) usize { - return unlinkat(AT_FDCWD, path, 0); + if (@hasDecl(@This(), "SYS_unlink")) { + return syscall1(SYS_unlink, @ptrToInt(path)); + } else { + return syscall3(SYS_unlinkat, AT_FDCWD, @ptrToInt(path), 0); + } } // TODO https://github.com/ziglang/zig/issues/265 @@ -955,8 +382,8 @@ pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize { return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags); } -pub fn waitpid(pid: i32, status: *i32, options: i32) usize { - return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); +pub fn waitpid(pid: i32, status: *u32, flags: u32) usize { + return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), flags, 0); } var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); @@ -1113,48 +540,15 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti return 0; } -const NSIG = 65; -const sigset_t = [128 / @sizeOf(usize)]usize; -const all_mask = []u32{ 0xffffffff, 0xffffffff }; -const app_mask = []u32{ 0xfffffffc, 0x7fffffff }; - -const k_sigaction = extern struct { - handler: extern fn (i32) void, - flags: usize, - restorer: extern fn () void, - mask: [2]u32, -}; - -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = struct { - handler: extern fn (i32) void, - mask: sigset_t, - flags: u32, -}; - -pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); -pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); -pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); -pub const empty_sigset = []usize{0} ** sigset_t.len; - -pub fn raise(sig: i32) usize { - var set: sigset_t = undefined; - blockAppSignals(&set); - const tid = syscall0(SYS_gettid); - const ret = syscall2(SYS_tkill, tid, @bitCast(usize, isize(sig))); - restoreSignals(&set); - return ret; -} - -fn blockAllSignals(set: *sigset_t) void { +pub fn blockAllSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); } -fn blockAppSignals(set: *sigset_t) void { +pub fn blockAppSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); } -fn restoreSignals(set: *sigset_t) void { +pub fn restoreSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); } @@ -1168,56 +562,6 @@ pub fn sigismember(set: *const sigset_t, sig: u6) bool { return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; } -pub const in_port_t = u16; -pub const sa_family_t = u16; -pub const socklen_t = u32; - -/// This intentionally only has ip4 and ip6 -pub const sockaddr = extern union { - in: sockaddr_in, - in6: sockaddr_in6, -}; - -pub const sockaddr_in = extern struct { - family: sa_family_t, - port: in_port_t, - addr: u32, - zero: [8]u8, -}; - -pub const sockaddr_in6 = extern struct { - family: sa_family_t, - port: in_port_t, - flowinfo: u32, - addr: [16]u8, - scope_id: u32, -}; - -pub const sockaddr_un = extern struct { - family: sa_family_t, - path: [108]u8, -}; - -pub const iovec = extern struct { - iov_base: [*]u8, - iov_len: usize, -}; - -pub const iovec_const = extern struct { - iov_base: [*]const u8, - iov_len: usize, -}; - -pub const mmsghdr = extern struct { - msg_hdr: msghdr, - msg_len: u32, -}; - -pub const mmsghdr_const = extern struct { - msg_hdr: msghdr_const, - msg_len: u32, -}; - pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); } @@ -1328,12 +672,12 @@ pub fn fstat(fd: i32, stat_buf: *Stat) usize { // TODO https://github.com/ziglang/zig/issues/265 pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { - return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT); + return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf)); } // TODO https://github.com/ziglang/zig/issues/265 pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { - return fstatat(AF_FDCWD, pathname, statbuf, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT); + return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf)); } // TODO https://github.com/ziglang/zig/issues/265 @@ -1400,30 +744,13 @@ pub fn fremovexattr(fd: usize, name: [*]const u8) usize { return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); } -pub fn sched_getaffinity(pid: i32, set: []usize) usize { - return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr)); +pub fn sched_getaffinity(pid: i32, size: usize, set: *cpu_set_t) usize { + const rc = syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), size, @ptrToInt(set)); + if (@bitCast(isize, rc) < 0) return rc; + if (rc < size) @memset(@ptrCast([*]u8, set) + rc, 0, size - rc); + return 0; } -pub const epoll_data = extern union { - ptr: usize, - fd: i32, - @"u32": u32, - @"u64": u64, -}; - -// On x86_64 the structure is packed so that it matches the definition of its -// 32bit counterpart -pub const epoll_event = if (builtin.arch != .x86_64) - extern struct { - events: u32, - data: epoll_data, - } -else - packed struct { - events: u32, - data: epoll_data, - }; - pub fn epoll_create() usize { return epoll_create1(0); } @@ -1437,7 +764,13 @@ pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { } pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { - return epoll_pwait(epoll_fd, events, maxevents, timeout, null); + return syscall4( + SYS_epoll_wait, + @bitCast(usize, isize(epoll_fd)), + @ptrToInt(events), + maxevents, + @bitCast(usize, isize(timeout)), + ); } pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize { @@ -1473,112 +806,6 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_va return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); } -pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; -pub const _LINUX_CAPABILITY_U32S_1 = 1; - -pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026; -pub const _LINUX_CAPABILITY_U32S_2 = 2; - -pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522; -pub const _LINUX_CAPABILITY_U32S_3 = 2; - -pub const VFS_CAP_REVISION_MASK = 0xFF000000; -pub const VFS_CAP_REVISION_SHIFT = 24; -pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; -pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; - -pub const VFS_CAP_REVISION_1 = 0x01000000; -pub const VFS_CAP_U32_1 = 1; -pub const XATTR_CAPS_SZ_1 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_1); - -pub const VFS_CAP_REVISION_2 = 0x02000000; -pub const VFS_CAP_U32_2 = 2; -pub const XATTR_CAPS_SZ_2 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_2); - -pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; -pub const VFS_CAP_U32 = VFS_CAP_U32_2; -pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; - -pub const vfs_cap_data = extern struct { - //all of these are mandated as little endian - //when on disk. - const Data = struct { - permitted: u32, - inheritable: u32, - }; - - magic_etc: u32, - data: [VFS_CAP_U32]Data, -}; - -pub const CAP_CHOWN = 0; -pub const CAP_DAC_OVERRIDE = 1; -pub const CAP_DAC_READ_SEARCH = 2; -pub const CAP_FOWNER = 3; -pub const CAP_FSETID = 4; -pub const CAP_KILL = 5; -pub const CAP_SETGID = 6; -pub const CAP_SETUID = 7; -pub const CAP_SETPCAP = 8; -pub const CAP_LINUX_IMMUTABLE = 9; -pub const CAP_NET_BIND_SERVICE = 10; -pub const CAP_NET_BROADCAST = 11; -pub const CAP_NET_ADMIN = 12; -pub const CAP_NET_RAW = 13; -pub const CAP_IPC_LOCK = 14; -pub const CAP_IPC_OWNER = 15; -pub const CAP_SYS_MODULE = 16; -pub const CAP_SYS_RAWIO = 17; -pub const CAP_SYS_CHROOT = 18; -pub const CAP_SYS_PTRACE = 19; -pub const CAP_SYS_PACCT = 20; -pub const CAP_SYS_ADMIN = 21; -pub const CAP_SYS_BOOT = 22; -pub const CAP_SYS_NICE = 23; -pub const CAP_SYS_RESOURCE = 24; -pub const CAP_SYS_TIME = 25; -pub const CAP_SYS_TTY_CONFIG = 26; -pub const CAP_MKNOD = 27; -pub const CAP_LEASE = 28; -pub const CAP_AUDIT_WRITE = 29; -pub const CAP_AUDIT_CONTROL = 30; -pub const CAP_SETFCAP = 31; -pub const CAP_MAC_OVERRIDE = 32; -pub const CAP_MAC_ADMIN = 33; -pub const CAP_SYSLOG = 34; -pub const CAP_WAKE_ALARM = 35; -pub const CAP_BLOCK_SUSPEND = 36; -pub const CAP_AUDIT_READ = 37; -pub const CAP_LAST_CAP = CAP_AUDIT_READ; - -pub fn cap_valid(u8: x) bool { - return x >= 0 and x <= CAP_LAST_CAP; -} - -pub fn CAP_TO_MASK(cap: u8) u32 { - return u32(1) << u5(cap & 31); -} - -pub fn CAP_TO_INDEX(cap: u8) u8 { - return cap >> 5; -} - -pub const cap_t = extern struct { - hdrp: *cap_user_header_t, - datap: *cap_user_data_t, -}; - -pub const cap_user_header_t = extern struct { - version: u32, - pid: usize, -}; - -pub const cap_user_data_t = extern struct { - effective: u32, - permitted: u32, - inheritable: u32, -}; - pub fn unshare(flags: usize) usize { return syscall1(SYS_unshare, flags); } @@ -1591,29 +818,6 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); } -pub const inotify_event = extern struct { - wd: i32, - mask: u32, - cookie: u32, - len: u32, - //name: [?]u8, -}; - -pub const dirent64 = extern struct { - d_ino: u64, - d_off: u64, - d_reclen: u16, - d_type: u8, - d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 -}; - -pub const dl_phdr_info = extern struct { - dlpi_addr: usize, - dlpi_name: ?[*]const u8, - dlpi_phdr: [*]elf.Phdr, - dlpi_phnum: u16, -}; - // XXX: This should be weak extern const __ehdr_start: elf.Ehdr = undefined; @@ -1671,8 +875,8 @@ pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_inf return last_r; } -test "import" { - if (builtin.os == builtin.Os.linux) { +test "" { + if (is_the_target) { _ = @import("linux/test.zig"); } } diff --git a/std/os/linux/arm64.zig b/std/os/linux/arm64.zig index d752166280..94cf8818de 100644 --- a/std/os/linux/arm64.zig +++ b/std/os/linux/arm64.zig @@ -1,344 +1,3 @@ -const std = @import("../../std.zig"); -const linux = std.os.linux; -const socklen_t = linux.socklen_t; -const iovec = linux.iovec; -const iovec_const = linux.iovec_const; - -pub const SYS_io_setup = 0; -pub const SYS_io_destroy = 1; -pub const SYS_io_submit = 2; -pub const SYS_io_cancel = 3; -pub const SYS_io_getevents = 4; -pub const SYS_setxattr = 5; -pub const SYS_lsetxattr = 6; -pub const SYS_fsetxattr = 7; -pub const SYS_getxattr = 8; -pub const SYS_lgetxattr = 9; -pub const SYS_fgetxattr = 10; -pub const SYS_listxattr = 11; -pub const SYS_llistxattr = 12; -pub const SYS_flistxattr = 13; -pub const SYS_removexattr = 14; -pub const SYS_lremovexattr = 15; -pub const SYS_fremovexattr = 16; -pub const SYS_getcwd = 17; -pub const SYS_lookup_dcookie = 18; -pub const SYS_eventfd2 = 19; -pub const SYS_epoll_create1 = 20; -pub const SYS_epoll_ctl = 21; -pub const SYS_epoll_pwait = 22; -pub const SYS_dup = 23; -pub const SYS_dup3 = 24; -pub const SYS_fcntl = 25; -pub const SYS_inotify_init1 = 26; -pub const SYS_inotify_add_watch = 27; -pub const SYS_inotify_rm_watch = 28; -pub const SYS_ioctl = 29; -pub const SYS_ioprio_set = 30; -pub const SYS_ioprio_get = 31; -pub const SYS_flock = 32; -pub const SYS_mknodat = 33; -pub const SYS_mkdirat = 34; -pub const SYS_unlinkat = 35; -pub const SYS_symlinkat = 36; -pub const SYS_linkat = 37; -pub const SYS_renameat = 38; -pub const SYS_umount2 = 39; -pub const SYS_mount = 40; -pub const SYS_pivot_root = 41; -pub const SYS_nfsservctl = 42; -pub const SYS_statfs = 43; -pub const SYS_fstatfs = 44; -pub const SYS_truncate = 45; -pub const SYS_ftruncate = 46; -pub const SYS_fallocate = 47; -pub const SYS_faccessat = 48; -pub const SYS_chdir = 49; -pub const SYS_fchdir = 50; -pub const SYS_chroot = 51; -pub const SYS_fchmod = 52; -pub const SYS_fchmodat = 53; -pub const SYS_fchownat = 54; -pub const SYS_fchown = 55; -pub const SYS_openat = 56; -pub const SYS_close = 57; -pub const SYS_vhangup = 58; -pub const SYS_pipe2 = 59; -pub const SYS_quotactl = 60; -pub const SYS_getdents64 = 61; -pub const SYS_lseek = 62; -pub const SYS_read = 63; -pub const SYS_write = 64; -pub const SYS_readv = 65; -pub const SYS_writev = 66; -pub const SYS_pread64 = 67; -pub const SYS_pwrite64 = 68; -pub const SYS_preadv = 69; -pub const SYS_pwritev = 70; -pub const SYS_pselect6 = 72; -pub const SYS_ppoll = 73; -pub const SYS_signalfd4 = 74; -pub const SYS_vmsplice = 75; -pub const SYS_splice = 76; -pub const SYS_tee = 77; -pub const SYS_readlinkat = 78; -pub const SYS_fstatat = 79; -pub const SYS_fstat = 80; -pub const SYS_sync = 81; -pub const SYS_fsync = 82; -pub const SYS_fdatasync = 83; -pub const SYS_sync_file_range2 = 84; -pub const SYS_sync_file_range = 84; -pub const SYS_timerfd_create = 85; -pub const SYS_timerfd_settime = 86; -pub const SYS_timerfd_gettime = 87; -pub const SYS_utimensat = 88; -pub const SYS_acct = 89; -pub const SYS_capget = 90; -pub const SYS_capset = 91; -pub const SYS_personality = 92; -pub const SYS_exit = 93; -pub const SYS_exit_group = 94; -pub const SYS_waitid = 95; -pub const SYS_set_tid_address = 96; -pub const SYS_unshare = 97; -pub const SYS_futex = 98; -pub const SYS_set_robust_list = 99; -pub const SYS_get_robust_list = 100; -pub const SYS_nanosleep = 101; -pub const SYS_getitimer = 102; -pub const SYS_setitimer = 103; -pub const SYS_kexec_load = 104; -pub const SYS_init_module = 105; -pub const SYS_delete_module = 106; -pub const SYS_timer_create = 107; -pub const SYS_timer_gettime = 108; -pub const SYS_timer_getoverrun = 109; -pub const SYS_timer_settime = 110; -pub const SYS_timer_delete = 111; -pub const SYS_clock_settime = 112; -pub const SYS_clock_gettime = 113; -pub const SYS_clock_getres = 114; -pub const SYS_clock_nanosleep = 115; -pub const SYS_syslog = 116; -pub const SYS_ptrace = 117; -pub const SYS_sched_setparam = 118; -pub const SYS_sched_setscheduler = 119; -pub const SYS_sched_getscheduler = 120; -pub const SYS_sched_getparam = 121; -pub const SYS_sched_setaffinity = 122; -pub const SYS_sched_getaffinity = 123; -pub const SYS_sched_yield = 124; -pub const SYS_sched_get_priority_max = 125; -pub const SYS_sched_get_priority_min = 126; -pub const SYS_sched_rr_get_interval = 127; -pub const SYS_restart_syscall = 128; -pub const SYS_kill = 129; -pub const SYS_tkill = 130; -pub const SYS_tgkill = 131; -pub const SYS_sigaltstack = 132; -pub const SYS_rt_sigsuspend = 133; -pub const SYS_rt_sigaction = 134; -pub const SYS_rt_sigprocmask = 135; -pub const SYS_rt_sigpending = 136; -pub const SYS_rt_sigtimedwait = 137; -pub const SYS_rt_sigqueueinfo = 138; -pub const SYS_rt_sigreturn = 139; -pub const SYS_setpriority = 140; -pub const SYS_getpriority = 141; -pub const SYS_reboot = 142; -pub const SYS_setregid = 143; -pub const SYS_setgid = 144; -pub const SYS_setreuid = 145; -pub const SYS_setuid = 146; -pub const SYS_setresuid = 147; -pub const SYS_getresuid = 148; -pub const SYS_setresgid = 149; -pub const SYS_getresgid = 150; -pub const SYS_setfsuid = 151; -pub const SYS_setfsgid = 152; -pub const SYS_times = 153; -pub const SYS_setpgid = 154; -pub const SYS_getpgid = 155; -pub const SYS_getsid = 156; -pub const SYS_setsid = 157; -pub const SYS_getgroups = 158; -pub const SYS_setgroups = 159; -pub const SYS_uname = 160; -pub const SYS_sethostname = 161; -pub const SYS_setdomainname = 162; -pub const SYS_getrlimit = 163; -pub const SYS_setrlimit = 164; -pub const SYS_getrusage = 165; -pub const SYS_umask = 166; -pub const SYS_prctl = 167; -pub const SYS_getcpu = 168; -pub const SYS_gettimeofday = 169; -pub const SYS_settimeofday = 170; -pub const SYS_adjtimex = 171; -pub const SYS_getpid = 172; -pub const SYS_getppid = 173; -pub const SYS_getuid = 174; -pub const SYS_geteuid = 175; -pub const SYS_getgid = 176; -pub const SYS_getegid = 177; -pub const SYS_gettid = 178; -pub const SYS_sysinfo = 179; -pub const SYS_mq_open = 180; -pub const SYS_mq_unlink = 181; -pub const SYS_mq_timedsend = 182; -pub const SYS_mq_timedreceive = 183; -pub const SYS_mq_notify = 184; -pub const SYS_mq_getsetattr = 185; -pub const SYS_msgget = 186; -pub const SYS_msgctl = 187; -pub const SYS_msgrcv = 188; -pub const SYS_msgsnd = 189; -pub const SYS_semget = 190; -pub const SYS_semctl = 191; -pub const SYS_semtimedop = 192; -pub const SYS_semop = 193; -pub const SYS_shmget = 194; -pub const SYS_shmctl = 195; -pub const SYS_shmat = 196; -pub const SYS_shmdt = 197; -pub const SYS_socket = 198; -pub const SYS_socketpair = 199; -pub const SYS_bind = 200; -pub const SYS_listen = 201; -pub const SYS_accept = 202; -pub const SYS_connect = 203; -pub const SYS_getsockname = 204; -pub const SYS_getpeername = 205; -pub const SYS_sendto = 206; -pub const SYS_recvfrom = 207; -pub const SYS_setsockopt = 208; -pub const SYS_getsockopt = 209; -pub const SYS_shutdown = 210; -pub const SYS_sendmsg = 211; -pub const SYS_recvmsg = 212; -pub const SYS_readahead = 213; -pub const SYS_brk = 214; -pub const SYS_munmap = 215; -pub const SYS_mremap = 216; -pub const SYS_add_key = 217; -pub const SYS_request_key = 218; -pub const SYS_keyctl = 219; -pub const SYS_clone = 220; -pub const SYS_execve = 221; -pub const SYS_mmap = 222; -pub const SYS_fadvise64 = 223; -pub const SYS_swapon = 224; -pub const SYS_swapoff = 225; -pub const SYS_mprotect = 226; -pub const SYS_msync = 227; -pub const SYS_mlock = 228; -pub const SYS_munlock = 229; -pub const SYS_mlockall = 230; -pub const SYS_munlockall = 231; -pub const SYS_mincore = 232; -pub const SYS_madvise = 233; -pub const SYS_remap_file_pages = 234; -pub const SYS_mbind = 235; -pub const SYS_get_mempolicy = 236; -pub const SYS_set_mempolicy = 237; -pub const SYS_migrate_pages = 238; -pub const SYS_move_pages = 239; -pub const SYS_rt_tgsigqueueinfo = 240; -pub const SYS_perf_event_open = 241; -pub const SYS_accept4 = 242; -pub const SYS_recvmmsg = 243; -pub const SYS_arch_specific_syscall = 244; -pub const SYS_wait4 = 260; -pub const SYS_prlimit64 = 261; -pub const SYS_fanotify_init = 262; -pub const SYS_fanotify_mark = 263; -pub const SYS_clock_adjtime = 266; -pub const SYS_syncfs = 267; -pub const SYS_setns = 268; -pub const SYS_sendmmsg = 269; -pub const SYS_process_vm_readv = 270; -pub const SYS_process_vm_writev = 271; -pub const SYS_kcmp = 272; -pub const SYS_finit_module = 273; -pub const SYS_sched_setattr = 274; -pub const SYS_sched_getattr = 275; -pub const SYS_renameat2 = 276; -pub const SYS_seccomp = 277; -pub const SYS_getrandom = 278; -pub const SYS_memfd_create = 279; -pub const SYS_bpf = 280; -pub const SYS_execveat = 281; -pub const SYS_userfaultfd = 282; -pub const SYS_membarrier = 283; -pub const SYS_mlock2 = 284; -pub const SYS_copy_file_range = 285; -pub const SYS_preadv2 = 286; -pub const SYS_pwritev2 = 287; -pub const SYS_pkey_mprotect = 288; -pub const SYS_pkey_alloc = 289; -pub const SYS_pkey_free = 290; -pub const SYS_statx = 291; -pub const SYS_io_pgetevents = 292; -pub const SYS_rseq = 293; -pub const SYS_kexec_file_load = 294; -pub const SYS_pidfd_send_signal = 424; -pub const SYS_io_uring_setup = 425; -pub const SYS_io_uring_enter = 426; -pub const SYS_io_uring_register = 427; - -pub const O_CREAT = 0o100; -pub const O_EXCL = 0o200; -pub const O_NOCTTY = 0o400; -pub const O_TRUNC = 0o1000; -pub const O_APPEND = 0o2000; -pub const O_NONBLOCK = 0o4000; -pub const O_DSYNC = 0o10000; -pub const O_SYNC = 0o4010000; -pub const O_RSYNC = 0o4010000; -pub const O_DIRECTORY = 0o200000; -pub const O_NOFOLLOW = 0o400000; -pub const O_CLOEXEC = 0o2000000; - -pub const O_ASYNC = 0o20000; -pub const O_DIRECT = 0o40000; -pub const O_LARGEFILE = 0; -pub const O_NOATIME = 0o1000000; -pub const O_PATH = 0o10000000; -pub const O_TMPFILE = 0o20200000; -pub const O_NDELAY = O_NONBLOCK; - -pub const F_DUPFD = 0; -pub const F_GETFD = 1; -pub const F_SETFD = 2; -pub const F_GETFL = 3; -pub const F_SETFL = 4; - -pub const F_SETOWN = 8; -pub const F_GETOWN = 9; -pub const F_SETSIG = 10; -pub const F_GETSIG = 11; - -pub const F_GETLK = 5; -pub const F_SETLK = 6; -pub const F_SETLKW = 7; - -pub const F_SETOWN_EX = 15; -pub const F_GETOWN_EX = 16; - -pub const F_GETOWNER_UIDS = 17; - -pub const AT_FDCWD = -100; -pub const AT_SYMLINK_NOFOLLOW = 0x100; -pub const AT_REMOVEDIR = 0x200; -pub const AT_SYMLINK_FOLLOW = 0x400; -pub const AT_NO_AUTOMOUNT = 0x800; -pub const AT_EMPTY_PATH = 0x1000; - -pub const VDSO_USEFUL = true; -pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; -pub const VDSO_CGT_VER = "LINUX_2.6.39"; - pub fn syscall0(number: usize) usize { return asm volatile ("svc #0" : [ret] "={x0}" (-> usize) @@ -419,65 +78,3 @@ pub fn syscall6( /// This matches the libc clone function. pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; - -pub const msghdr = extern struct { - msg_name: ?*sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec, - msg_iovlen: i32, - __pad1: i32, - msg_control: ?*c_void, - msg_controllen: socklen_t, - __pad2: socklen_t, - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - msg_name: ?*const sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec_const, - msg_iovlen: i32, - __pad1: i32, - msg_control: ?*c_void, - msg_controllen: socklen_t, - __pad2: socklen_t, - msg_flags: i32, -}; - -/// Renamed to Stat to not conflict with the stat function. -pub const Stat = extern struct { - dev: u64, - ino: u64, - nlink: usize, - - mode: u32, - uid: u32, - gid: u32, - __pad0: u32, - rdev: u64, - size: i64, - blksize: isize, - blocks: i64, - - atim: timespec, - mtim: timespec, - ctim: timespec, - __unused: [3]isize, -}; - -pub const timespec = extern struct { - tv_sec: isize, - tv_nsec: isize, -}; - -pub const timeval = extern struct { - tv_sec: isize, - tv_usec: isize, -}; - -pub const timezone = extern struct { - tz_minuteswest: i32, - tz_dsttime: i32, -}; - -pub const Elf_Symndx = u32; diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index 1949d00298..c78b071c74 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -11,7 +11,7 @@ test "getpid" { test "timer" { const epoll_fd = linux.epoll_create(); - var err = linux.getErrno(epoll_fd); + var err: usize = linux.getErrno(epoll_fd); expect(err == 0); const timer_fd = linux.timerfd_create(linux.CLOCK_MONOTONIC, 0); diff --git a/std/os/linux/tls.zig b/std/os/linux/tls.zig index 8640e71e62..a10d68663b 100644 --- a/std/os/linux/tls.zig +++ b/std/os/linux/tls.zig @@ -1,6 +1,6 @@ const std = @import("std"); +const os = std.os; const mem = std.mem; -const posix = std.os.posix; const elf = std.elf; const builtin = @import("builtin"); const assert = std.debug.assert; @@ -126,7 +126,7 @@ pub fn initTLS() void { var tls_phdr: ?*elf.Phdr = null; var img_base: usize = 0; - const auxv = std.os.linux_elf_aux_maybe.?; + const auxv = std.os.linux.elf_aux_maybe.?; var at_phent: usize = undefined; var at_phnum: usize = undefined; var at_phdr: usize = undefined; @@ -237,9 +237,14 @@ pub fn allocateTLS(size: usize) usize { return @ptrToInt(&main_thread_tls_buffer); } - const addr = posix.mmap(null, size, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS, -1, 0); + const slice = os.mmap( + null, + size, + os.PROT_READ | os.PROT_WRITE, + os.MAP_PRIVATE | os.MAP_ANONYMOUS, + -1, + 0, + ) catch @panic("out of memory"); - if (posix.getErrno(addr) != 0) @panic("out of memory"); - - return addr; + return @ptrToInt(slice.ptr); } diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index efd69d8542..86d54bfbf8 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -1,12 +1,11 @@ const std = @import("../../std.zig"); const elf = std.elf; const linux = std.os.linux; -const cstr = std.cstr; const mem = std.mem; const maxInt = std.math.maxInt; pub fn lookup(vername: []const u8, name: []const u8) usize { - const vdso_addr = std.os.linuxGetAuxVal(std.elf.AT_SYSINFO_EHDR); + const vdso_addr = std.os.system.getauxval(std.elf.AT_SYSINFO_EHDR); if (vdso_addr == 0) return 0; const eh = @intToPtr(*elf.Ehdr, vdso_addr); @@ -66,7 +65,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { if (0 == (u32(1) << @intCast(u5, syms[i].st_info & 0xf) & OK_TYPES)) continue; if (0 == (u32(1) << @intCast(u5, syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == syms[i].st_shndx) continue; - if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue; + if (!mem.eql(u8, name, mem.toSliceConst(u8, strings + syms[i].st_name))) continue; if (maybe_versym) |versym| { if (!checkver(maybe_verdef.?, versym[i], vername, strings)) continue; @@ -88,5 +87,5 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [ def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); } const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); - return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name)); + return mem.eql(u8, vername, mem.toSliceConst(u8, strings + aux.vda_name)); } diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index f00a4680a4..fa866cff4c 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -1,410 +1,4 @@ -const std = @import("../../std.zig"); -const linux = std.os.linux; -const sockaddr = linux.sockaddr; -const socklen_t = linux.socklen_t; -const iovec = linux.iovec; -const iovec_const = linux.iovec_const; - -pub const SYS_read = 0; -pub const SYS_write = 1; -pub const SYS_open = 2; -pub const SYS_close = 3; -pub const SYS_stat = 4; -pub const SYS_fstat = 5; -pub const SYS_lstat = 6; -pub const SYS_poll = 7; -pub const SYS_lseek = 8; -pub const SYS_mmap = 9; -pub const SYS_mprotect = 10; -pub const SYS_munmap = 11; -pub const SYS_brk = 12; -pub const SYS_rt_sigaction = 13; -pub const SYS_rt_sigprocmask = 14; -pub const SYS_rt_sigreturn = 15; -pub const SYS_ioctl = 16; -pub const SYS_pread = 17; -pub const SYS_pwrite = 18; -pub const SYS_readv = 19; -pub const SYS_writev = 20; -pub const SYS_access = 21; -pub const SYS_pipe = 22; -pub const SYS_select = 23; -pub const SYS_sched_yield = 24; -pub const SYS_mremap = 25; -pub const SYS_msync = 26; -pub const SYS_mincore = 27; -pub const SYS_madvise = 28; -pub const SYS_shmget = 29; -pub const SYS_shmat = 30; -pub const SYS_shmctl = 31; -pub const SYS_dup = 32; -pub const SYS_dup2 = 33; -pub const SYS_pause = 34; -pub const SYS_nanosleep = 35; -pub const SYS_getitimer = 36; -pub const SYS_alarm = 37; -pub const SYS_setitimer = 38; -pub const SYS_getpid = 39; -pub const SYS_sendfile = 40; -pub const SYS_socket = 41; -pub const SYS_connect = 42; -pub const SYS_accept = 43; -pub const SYS_sendto = 44; -pub const SYS_recvfrom = 45; -pub const SYS_sendmsg = 46; -pub const SYS_recvmsg = 47; -pub const SYS_shutdown = 48; -pub const SYS_bind = 49; -pub const SYS_listen = 50; -pub const SYS_getsockname = 51; -pub const SYS_getpeername = 52; -pub const SYS_socketpair = 53; -pub const SYS_setsockopt = 54; -pub const SYS_getsockopt = 55; -pub const SYS_clone = 56; -pub const SYS_fork = 57; -pub const SYS_vfork = 58; -pub const SYS_execve = 59; -pub const SYS_exit = 60; -pub const SYS_wait4 = 61; -pub const SYS_kill = 62; -pub const SYS_uname = 63; -pub const SYS_semget = 64; -pub const SYS_semop = 65; -pub const SYS_semctl = 66; -pub const SYS_shmdt = 67; -pub const SYS_msgget = 68; -pub const SYS_msgsnd = 69; -pub const SYS_msgrcv = 70; -pub const SYS_msgctl = 71; -pub const SYS_fcntl = 72; -pub const SYS_flock = 73; -pub const SYS_fsync = 74; -pub const SYS_fdatasync = 75; -pub const SYS_truncate = 76; -pub const SYS_ftruncate = 77; -pub const SYS_getdents = 78; -pub const SYS_getcwd = 79; -pub const SYS_chdir = 80; -pub const SYS_fchdir = 81; -pub const SYS_rename = 82; -pub const SYS_mkdir = 83; -pub const SYS_rmdir = 84; -pub const SYS_creat = 85; -pub const SYS_link = 86; -pub const SYS_unlink = 87; -pub const SYS_symlink = 88; -pub const SYS_readlink = 89; -pub const SYS_chmod = 90; -pub const SYS_fchmod = 91; -pub const SYS_chown = 92; -pub const SYS_fchown = 93; -pub const SYS_lchown = 94; -pub const SYS_umask = 95; -pub const SYS_gettimeofday = 96; -pub const SYS_getrlimit = 97; -pub const SYS_getrusage = 98; -pub const SYS_sysinfo = 99; -pub const SYS_times = 100; -pub const SYS_ptrace = 101; -pub const SYS_getuid = 102; -pub const SYS_syslog = 103; -pub const SYS_getgid = 104; -pub const SYS_setuid = 105; -pub const SYS_setgid = 106; -pub const SYS_geteuid = 107; -pub const SYS_getegid = 108; -pub const SYS_setpgid = 109; -pub const SYS_getppid = 110; -pub const SYS_getpgrp = 111; -pub const SYS_setsid = 112; -pub const SYS_setreuid = 113; -pub const SYS_setregid = 114; -pub const SYS_getgroups = 115; -pub const SYS_setgroups = 116; -pub const SYS_setresuid = 117; -pub const SYS_getresuid = 118; -pub const SYS_setresgid = 119; -pub const SYS_getresgid = 120; -pub const SYS_getpgid = 121; -pub const SYS_setfsuid = 122; -pub const SYS_setfsgid = 123; -pub const SYS_getsid = 124; -pub const SYS_capget = 125; -pub const SYS_capset = 126; -pub const SYS_rt_sigpending = 127; -pub const SYS_rt_sigtimedwait = 128; -pub const SYS_rt_sigqueueinfo = 129; -pub const SYS_rt_sigsuspend = 130; -pub const SYS_sigaltstack = 131; -pub const SYS_utime = 132; -pub const SYS_mknod = 133; -pub const SYS_uselib = 134; -pub const SYS_personality = 135; -pub const SYS_ustat = 136; -pub const SYS_statfs = 137; -pub const SYS_fstatfs = 138; -pub const SYS_sysfs = 139; -pub const SYS_getpriority = 140; -pub const SYS_setpriority = 141; -pub const SYS_sched_setparam = 142; -pub const SYS_sched_getparam = 143; -pub const SYS_sched_setscheduler = 144; -pub const SYS_sched_getscheduler = 145; -pub const SYS_sched_get_priority_max = 146; -pub const SYS_sched_get_priority_min = 147; -pub const SYS_sched_rr_get_interval = 148; -pub const SYS_mlock = 149; -pub const SYS_munlock = 150; -pub const SYS_mlockall = 151; -pub const SYS_munlockall = 152; -pub const SYS_vhangup = 153; -pub const SYS_modify_ldt = 154; -pub const SYS_pivot_root = 155; -pub const SYS__sysctl = 156; -pub const SYS_prctl = 157; -pub const SYS_arch_prctl = 158; -pub const SYS_adjtimex = 159; -pub const SYS_setrlimit = 160; -pub const SYS_chroot = 161; -pub const SYS_sync = 162; -pub const SYS_acct = 163; -pub const SYS_settimeofday = 164; -pub const SYS_mount = 165; -pub const SYS_umount2 = 166; -pub const SYS_swapon = 167; -pub const SYS_swapoff = 168; -pub const SYS_reboot = 169; -pub const SYS_sethostname = 170; -pub const SYS_setdomainname = 171; -pub const SYS_iopl = 172; -pub const SYS_ioperm = 173; -pub const SYS_create_module = 174; -pub const SYS_init_module = 175; -pub const SYS_delete_module = 176; -pub const SYS_get_kernel_syms = 177; -pub const SYS_query_module = 178; -pub const SYS_quotactl = 179; -pub const SYS_nfsservctl = 180; -pub const SYS_getpmsg = 181; -pub const SYS_putpmsg = 182; -pub const SYS_afs_syscall = 183; -pub const SYS_tuxcall = 184; -pub const SYS_security = 185; -pub const SYS_gettid = 186; -pub const SYS_readahead = 187; -pub const SYS_setxattr = 188; -pub const SYS_lsetxattr = 189; -pub const SYS_fsetxattr = 190; -pub const SYS_getxattr = 191; -pub const SYS_lgetxattr = 192; -pub const SYS_fgetxattr = 193; -pub const SYS_listxattr = 194; -pub const SYS_llistxattr = 195; -pub const SYS_flistxattr = 196; -pub const SYS_removexattr = 197; -pub const SYS_lremovexattr = 198; -pub const SYS_fremovexattr = 199; -pub const SYS_tkill = 200; -pub const SYS_time = 201; -pub const SYS_futex = 202; -pub const SYS_sched_setaffinity = 203; -pub const SYS_sched_getaffinity = 204; -pub const SYS_set_thread_area = 205; -pub const SYS_io_setup = 206; -pub const SYS_io_destroy = 207; -pub const SYS_io_getevents = 208; -pub const SYS_io_submit = 209; -pub const SYS_io_cancel = 210; -pub const SYS_get_thread_area = 211; -pub const SYS_lookup_dcookie = 212; -pub const SYS_epoll_create = 213; -pub const SYS_epoll_ctl_old = 214; -pub const SYS_epoll_wait_old = 215; -pub const SYS_remap_file_pages = 216; -pub const SYS_getdents64 = 217; -pub const SYS_set_tid_address = 218; -pub const SYS_restart_syscall = 219; -pub const SYS_semtimedop = 220; -pub const SYS_fadvise64 = 221; -pub const SYS_timer_create = 222; -pub const SYS_timer_settime = 223; -pub const SYS_timer_gettime = 224; -pub const SYS_timer_getoverrun = 225; -pub const SYS_timer_delete = 226; -pub const SYS_clock_settime = 227; -pub const SYS_clock_gettime = 228; -pub const SYS_clock_getres = 229; -pub const SYS_clock_nanosleep = 230; -pub const SYS_exit_group = 231; -pub const SYS_epoll_wait = 232; -pub const SYS_epoll_ctl = 233; -pub const SYS_tgkill = 234; -pub const SYS_utimes = 235; -pub const SYS_vserver = 236; -pub const SYS_mbind = 237; -pub const SYS_set_mempolicy = 238; -pub const SYS_get_mempolicy = 239; -pub const SYS_mq_open = 240; -pub const SYS_mq_unlink = 241; -pub const SYS_mq_timedsend = 242; -pub const SYS_mq_timedreceive = 243; -pub const SYS_mq_notify = 244; -pub const SYS_mq_getsetattr = 245; -pub const SYS_kexec_load = 246; -pub const SYS_waitid = 247; -pub const SYS_add_key = 248; -pub const SYS_request_key = 249; -pub const SYS_keyctl = 250; -pub const SYS_ioprio_set = 251; -pub const SYS_ioprio_get = 252; -pub const SYS_inotify_init = 253; -pub const SYS_inotify_add_watch = 254; -pub const SYS_inotify_rm_watch = 255; -pub const SYS_migrate_pages = 256; -pub const SYS_openat = 257; -pub const SYS_mkdirat = 258; -pub const SYS_mknodat = 259; -pub const SYS_fchownat = 260; -pub const SYS_futimesat = 261; -pub const SYS_newfstatat = 262; -// https://github.com/ziglang/zig/issues/1439 -pub const SYS_fstatat = 262; -pub const SYS_unlinkat = 263; -pub const SYS_renameat = 264; -pub const SYS_linkat = 265; -pub const SYS_symlinkat = 266; -pub const SYS_readlinkat = 267; -pub const SYS_fchmodat = 268; -pub const SYS_faccessat = 269; -pub const SYS_pselect6 = 270; -pub const SYS_ppoll = 271; -pub const SYS_unshare = 272; -pub const SYS_set_robust_list = 273; -pub const SYS_get_robust_list = 274; -pub const SYS_splice = 275; -pub const SYS_tee = 276; -pub const SYS_sync_file_range = 277; -pub const SYS_vmsplice = 278; -pub const SYS_move_pages = 279; -pub const SYS_utimensat = 280; -pub const SYS_epoll_pwait = 281; -pub const SYS_signalfd = 282; -pub const SYS_timerfd_create = 283; -pub const SYS_eventfd = 284; -pub const SYS_fallocate = 285; -pub const SYS_timerfd_settime = 286; -pub const SYS_timerfd_gettime = 287; -pub const SYS_accept4 = 288; -pub const SYS_signalfd4 = 289; -pub const SYS_eventfd2 = 290; -pub const SYS_epoll_create1 = 291; -pub const SYS_dup3 = 292; -pub const SYS_pipe2 = 293; -pub const SYS_inotify_init1 = 294; -pub const SYS_preadv = 295; -pub const SYS_pwritev = 296; -pub const SYS_rt_tgsigqueueinfo = 297; -pub const SYS_perf_event_open = 298; -pub const SYS_recvmmsg = 299; -pub const SYS_fanotify_init = 300; -pub const SYS_fanotify_mark = 301; -pub const SYS_prlimit64 = 302; -pub const SYS_name_to_handle_at = 303; -pub const SYS_open_by_handle_at = 304; -pub const SYS_clock_adjtime = 305; -pub const SYS_syncfs = 306; -pub const SYS_sendmmsg = 307; -pub const SYS_setns = 308; -pub const SYS_getcpu = 309; -pub const SYS_process_vm_readv = 310; -pub const SYS_process_vm_writev = 311; -pub const SYS_kcmp = 312; -pub const SYS_finit_module = 313; -pub const SYS_sched_setattr = 314; -pub const SYS_sched_getattr = 315; -pub const SYS_renameat2 = 316; -pub const SYS_seccomp = 317; -pub const SYS_getrandom = 318; -pub const SYS_memfd_create = 319; -pub const SYS_kexec_file_load = 320; -pub const SYS_bpf = 321; -pub const SYS_execveat = 322; -pub const SYS_userfaultfd = 323; -pub const SYS_membarrier = 324; -pub const SYS_mlock2 = 325; -pub const SYS_copy_file_range = 326; -pub const SYS_preadv2 = 327; -pub const SYS_pwritev2 = 328; -pub const SYS_pkey_mprotect = 329; -pub const SYS_pkey_alloc = 330; -pub const SYS_pkey_free = 331; -pub const SYS_statx = 332; -pub const SYS_io_pgetevents = 333; -pub const SYS_rseq = 334; -pub const SYS_pidfd_send_signal = 424; -pub const SYS_io_uring_setup = 425; -pub const SYS_io_uring_enter = 426; -pub const SYS_io_uring_register = 427; - -pub const O_CREAT = 0o100; -pub const O_EXCL = 0o200; -pub const O_NOCTTY = 0o400; -pub const O_TRUNC = 0o1000; -pub const O_APPEND = 0o2000; -pub const O_NONBLOCK = 0o4000; -pub const O_DSYNC = 0o10000; -pub const O_SYNC = 0o4010000; -pub const O_RSYNC = 0o4010000; -pub const O_DIRECTORY = 0o200000; -pub const O_NOFOLLOW = 0o400000; -pub const O_CLOEXEC = 0o2000000; - -pub const O_ASYNC = 0o20000; -pub const O_DIRECT = 0o40000; -pub const O_LARGEFILE = 0; -pub const O_NOATIME = 0o1000000; -pub const O_PATH = 0o10000000; -pub const O_TMPFILE = 0o20200000; -pub const O_NDELAY = O_NONBLOCK; - -pub const F_DUPFD = 0; -pub const F_GETFD = 1; -pub const F_SETFD = 2; -pub const F_GETFL = 3; -pub const F_SETFL = 4; - -pub const F_SETOWN = 8; -pub const F_GETOWN = 9; -pub const F_SETSIG = 10; -pub const F_GETSIG = 11; - -pub const F_GETLK = 5; -pub const F_SETLK = 6; -pub const F_SETLKW = 7; - -pub const F_SETOWN_EX = 15; -pub const F_GETOWN_EX = 16; - -pub const F_GETOWNER_UIDS = 17; - -pub const AT_FDCWD = -100; -pub const AT_SYMLINK_NOFOLLOW = 0x100; -pub const AT_REMOVEDIR = 0x200; -pub const AT_SYMLINK_FOLLOW = 0x400; -pub const AT_NO_AUTOMOUNT = 0x800; -pub const AT_EMPTY_PATH = 0x1000; - -pub const VDSO_USEFUL = true; -pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; -pub const VDSO_CGT_VER = "LINUX_2.6"; -pub const VDSO_GETCPU_SYM = "__vdso_getcpu"; -pub const VDSO_GETCPU_VER = "LINUX_2.6"; - -pub const ARCH_SET_GS = 0x1001; -pub const ARCH_SET_FS = 0x1002; -pub const ARCH_GET_FS = 0x1003; -pub const ARCH_GET_GS = 0x1004; +use @import("../bits.zig"); pub fn syscall0(number: usize) usize { return asm volatile ("syscall" @@ -501,65 +95,3 @@ pub nakedcc fn restore_rt() void { : "rcx", "r11" ); } - -pub const msghdr = extern struct { - msg_name: ?*sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec, - msg_iovlen: i32, - __pad1: i32, - msg_control: ?*c_void, - msg_controllen: socklen_t, - __pad2: socklen_t, - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - msg_name: ?*const sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec_const, - msg_iovlen: i32, - __pad1: i32, - msg_control: ?*c_void, - msg_controllen: socklen_t, - __pad2: socklen_t, - msg_flags: i32, -}; - -/// Renamed to Stat to not conflict with the stat function. -pub const Stat = extern struct { - dev: u64, - ino: u64, - nlink: usize, - - mode: u32, - uid: u32, - gid: u32, - __pad0: u32, - rdev: u64, - size: i64, - blksize: isize, - blocks: i64, - - atim: timespec, - mtim: timespec, - ctim: timespec, - __unused: [3]isize, -}; - -pub const timespec = extern struct { - tv_sec: isize, - tv_nsec: isize, -}; - -pub const timeval = extern struct { - tv_sec: isize, - tv_usec: isize, -}; - -pub const timezone = extern struct { - tz_minuteswest: i32, - tz_dsttime: i32, -}; - -pub const Elf_Symndx = u32; diff --git a/std/os/netbsd.zig b/std/os/netbsd.zig index 2d4b6ff124..25e72c10a8 100644 --- a/std/os/netbsd.zig +++ b/std/os/netbsd.zig @@ -1,724 +1,4 @@ const builtin = @import("builtin"); - -pub use @import("netbsd/errno.zig"); - const std = @import("../std.zig"); -const c = std.c; - -const assert = std.debug.assert; -const maxInt = std.math.maxInt; -pub const Kevent = c.Kevent; - -pub const CTL_KERN = 1; -pub const CTL_DEBUG = 5; - -pub const KERN_PROC_ARGS = 48; // struct: process argv/env -pub const KERN_PROC_PATHNAME = 5; // path to executable - -pub const PATH_MAX = 1024; - -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; - -pub const PROT_NONE = 0; -pub const PROT_READ = 1; -pub const PROT_WRITE = 2; -pub const PROT_EXEC = 4; - -pub const CLOCK_REALTIME = 0; -pub const CLOCK_VIRTUAL = 1; -pub const CLOCK_PROF = 2; -pub const CLOCK_MONOTONIC = 3; -pub const CLOCK_THREAD_CPUTIME_ID = 0x20000000; -pub const CLOCK_PROCESS_CPUTIME_ID = 0x40000000; - -pub const MAP_FAILED = maxInt(usize); -pub const MAP_SHARED = 0x0001; -pub const MAP_PRIVATE = 0x0002; -pub const MAP_REMAPDUP = 0x0004; -pub const MAP_FIXED = 0x0010; -pub const MAP_RENAME = 0x0020; -pub const MAP_NORESERVE = 0x0040; -pub const MAP_INHERIT = 0x0080; -pub const MAP_HASSEMAPHORE = 0x0200; -pub const MAP_TRYFIXED = 0x0400; -pub const MAP_WIRED = 0x0800; - -pub const MAP_FILE = 0x0000; -pub const MAP_NOSYNC = 0x0800; -pub const MAP_ANON = 0x1000; -pub const MAP_ANONYMOUS = MAP_ANON; -pub const MAP_STACK = 0x2000; - -pub const WNOHANG = 0x00000001; -pub const WUNTRACED = 0x00000002; -pub const WSTOPPED = WUNTRACED; -pub const WCONTINUED = 0x00000010; -pub const WNOWAIT = 0x00010000; -pub const WEXITED = 0x00000020; -pub const WTRAPPED = 0x00000040; - -pub const SA_ONSTACK = 0x0001; -pub const SA_RESTART = 0x0002; -pub const SA_RESETHAND = 0x0004; -pub const SA_NOCLDSTOP = 0x0008; -pub const SA_NODEFER = 0x0010; -pub const SA_NOCLDWAIT = 0x0020; -pub const SA_SIGINFO = 0x0040; - -pub const SIGHUP = 1; -pub const SIGINT = 2; -pub const SIGQUIT = 3; -pub const SIGILL = 4; -pub const SIGTRAP = 5; -pub const SIGABRT = 6; -pub const SIGIOT = SIGABRT; -pub const SIGEMT = 7; -pub const SIGFPE = 8; -pub const SIGKILL = 9; -pub const SIGBUS = 10; -pub const SIGSEGV = 11; -pub const SIGSYS = 12; -pub const SIGPIPE = 13; -pub const SIGALRM = 14; -pub const SIGTERM = 15; -pub const SIGURG = 16; -pub const SIGSTOP = 17; -pub const SIGTSTP = 18; -pub const SIGCONT = 19; -pub const SIGCHLD = 20; -pub const SIGTTIN = 21; -pub const SIGTTOU = 22; -pub const SIGIO = 23; -pub const SIGXCPU = 24; -pub const SIGXFSZ = 25; -pub const SIGVTALRM = 26; -pub const SIGPROF = 27; -pub const SIGWINCH = 28; -pub const SIGINFO = 29; -pub const SIGUSR1 = 30; -pub const SIGUSR2 = 31; -pub const SIGPWR = 32; - -pub const SIGRTMIN = 33; -pub const SIGRTMAX = 63; - -// access function -pub const F_OK = 0; // test for existence of file -pub const X_OK = 1; // test for execute or search permission -pub const W_OK = 2; // test for write permission -pub const R_OK = 4; // test for read permission - -pub const O_RDONLY = 0x0000; -pub const O_WRONLY = 0x0001; -pub const O_RDWR = 0x0002; -pub const O_ACCMODE = 0x0003; - -pub const O_CREAT = 0x0200; -pub const O_EXCL = 0x0800; -pub const O_NOCTTY = 0x8000; -pub const O_TRUNC = 0x0400; -pub const O_APPEND = 0x0008; -pub const O_NONBLOCK = 0x0004; -pub const O_DSYNC = 0x00010000; -pub const O_SYNC = 0x0080; -pub const O_RSYNC = 0x00020000; -pub const O_DIRECTORY = 0x00080000; -pub const O_NOFOLLOW = 0x00000100; -pub const O_CLOEXEC = 0x00400000; - -pub const O_ASYNC = 0x0040; -pub const O_DIRECT = 0x00080000; -pub const O_LARGEFILE = 0; -pub const O_NOATIME = 0; -pub const O_PATH = 0; -pub const O_TMPFILE = 0; -pub const O_NDELAY = O_NONBLOCK; - -pub const F_DUPFD = 0; -pub const F_GETFD = 1; -pub const F_SETFD = 2; -pub const F_GETFL = 3; -pub const F_SETFL = 4; - -pub const F_GETOWN = 5; -pub const F_SETOWN = 6; - -pub const F_GETLK = 7; -pub const F_SETLK = 8; -pub const F_SETLKW = 9; - -pub const SEEK_SET = 0; -pub const SEEK_CUR = 1; -pub const SEEK_END = 2; - -pub const SIG_BLOCK = 1; -pub const SIG_UNBLOCK = 2; -pub const SIG_SETMASK = 3; - -pub const SOCK_STREAM = 1; -pub const SOCK_DGRAM = 2; -pub const SOCK_RAW = 3; -pub const SOCK_RDM = 4; -pub const SOCK_SEQPACKET = 5; - -pub const SOCK_CLOEXEC = 0x10000000; -pub const SOCK_NONBLOCK = 0x20000000; - -pub const PROTO_ip = 0; -pub const PROTO_icmp = 1; -pub const PROTO_igmp = 2; -pub const PROTO_ggp = 3; -pub const PROTO_ipencap = 4; -pub const PROTO_tcp = 6; -pub const PROTO_egp = 8; -pub const PROTO_pup = 12; -pub const PROTO_udp = 17; -pub const PROTO_xns_idp = 22; -pub const PROTO_iso_tp4 = 29; -pub const PROTO_ipv6 = 41; -pub const PROTO_ipv6_route = 43; -pub const PROTO_ipv6_frag = 44; -pub const PROTO_rsvp = 46; -pub const PROTO_gre = 47; -pub const PROTO_esp = 50; -pub const PROTO_ah = 51; -pub const PROTO_ipv6_icmp = 58; -pub const PROTO_ipv6_nonxt = 59; -pub const PROTO_ipv6_opts = 60; -pub const PROTO_encap = 98; -pub const PROTO_pim = 103; -pub const PROTO_raw = 255; - -pub const PF_UNSPEC = 0; -pub const PF_LOCAL = 1; -pub const PF_UNIX = PF_LOCAL; -pub const PF_FILE = PF_LOCAL; -pub const PF_INET = 2; -pub const PF_APPLETALK = 16; -pub const PF_INET6 = 24; -pub const PF_DECnet = 12; -pub const PF_KEY = 29; -pub const PF_ROUTE = 34; -pub const PF_SNA = 11; -pub const PF_MPLS = 33; -pub const PF_CAN = 35; -pub const PF_BLUETOOTH = 31; -pub const PF_ISDN = 26; -pub const PF_MAX = 37; - -pub const AF_UNSPEC = PF_UNSPEC; -pub const AF_LOCAL = PF_LOCAL; -pub const AF_UNIX = AF_LOCAL; -pub const AF_FILE = AF_LOCAL; -pub const AF_INET = PF_INET; -pub const AF_APPLETALK = PF_APPLETALK; -pub const AF_INET6 = PF_INET6; -pub const AF_KEY = PF_KEY; -pub const AF_ROUTE = PF_ROUTE; -pub const AF_SNA = PF_SNA; -pub const AF_MPLS = PF_MPLS; -pub const AF_CAN = PF_CAN; -pub const AF_BLUETOOTH = PF_BLUETOOTH; -pub const AF_ISDN = PF_ISDN; -pub const AF_MAX = PF_MAX; - -pub const DT_UNKNOWN = 0; -pub const DT_FIFO = 1; -pub const DT_CHR = 2; -pub const DT_DIR = 4; -pub const DT_BLK = 6; -pub const DT_REG = 8; -pub const DT_LNK = 10; -pub const DT_SOCK = 12; -pub const DT_WHT = 14; - -/// add event to kq (implies enable) -pub const EV_ADD = 0x0001; - -/// delete event from kq -pub const EV_DELETE = 0x0002; - -/// enable event -pub const EV_ENABLE = 0x0004; - -/// disable event (not reported) -pub const EV_DISABLE = 0x0008; - -/// only report one occurrence -pub const EV_ONESHOT = 0x0010; - -/// clear event state after reporting -pub const EV_CLEAR = 0x0020; - -/// force immediate event output -/// ... with or without EV_ERROR -/// ... use KEVENT_FLAG_ERROR_EVENTS -/// on syscalls supporting flags -pub const EV_RECEIPT = 0x0040; - -/// disable event after reporting -pub const EV_DISPATCH = 0x0080; - -pub const EVFILT_READ = 0; -pub const EVFILT_WRITE = 1; - -/// attached to aio requests -pub const EVFILT_AIO = 2; - -/// attached to vnodes -pub const EVFILT_VNODE = 3; - -/// attached to struct proc -pub const EVFILT_PROC = 4; - -/// attached to struct proc -pub const EVFILT_SIGNAL = 5; - -/// timers -pub const EVFILT_TIMER = 6; - -/// Filesystem events -pub const EVFILT_FS = 7; - -/// On input, NOTE_TRIGGER causes the event to be triggered for output. -pub const NOTE_TRIGGER = 0x08000000; - -/// low water mark -pub const NOTE_LOWAT = 0x00000001; - -/// vnode was removed -pub const NOTE_DELETE = 0x00000001; - -/// data contents changed -pub const NOTE_WRITE = 0x00000002; - -/// size increased -pub const NOTE_EXTEND = 0x00000004; - -/// attributes changed -pub const NOTE_ATTRIB = 0x00000008; - -/// link count changed -pub const NOTE_LINK = 0x00000010; - -/// vnode was renamed -pub const NOTE_RENAME = 0x00000020; - -/// vnode access was revoked -pub const NOTE_REVOKE = 0x00000040; - -/// process exited -pub const NOTE_EXIT = 0x80000000; - -/// process forked -pub const NOTE_FORK = 0x40000000; - -/// process exec'd -pub const NOTE_EXEC = 0x20000000; - -/// mask for signal & exit status -pub const NOTE_PDATAMASK = 0x000fffff; -pub const NOTE_PCTRLMASK = 0xf0000000; - -pub const TIOCCBRK = 0x2000747a; -pub const TIOCCDTR = 0x20007478; -pub const TIOCCONS = 0x80047462; -pub const TIOCDCDTIMESTAMP = 0x40107458; -pub const TIOCDRAIN = 0x2000745e; -pub const TIOCEXCL = 0x2000740d; -pub const TIOCEXT = 0x80047460; -pub const TIOCFLAG_CDTRCTS = 0x10; -pub const TIOCFLAG_CLOCAL = 0x2; -pub const TIOCFLAG_CRTSCTS = 0x4; -pub const TIOCFLAG_MDMBUF = 0x8; -pub const TIOCFLAG_SOFTCAR = 0x1; -pub const TIOCFLUSH = 0x80047410; -pub const TIOCGETA = 0x402c7413; -pub const TIOCGETD = 0x4004741a; -pub const TIOCGFLAGS = 0x4004745d; -pub const TIOCGLINED = 0x40207442; -pub const TIOCGPGRP = 0x40047477; -pub const TIOCGQSIZE = 0x40047481; -pub const TIOCGRANTPT = 0x20007447; -pub const TIOCGSID = 0x40047463; -pub const TIOCGSIZE = 0x40087468; -pub const TIOCGWINSZ = 0x40087468; -pub const TIOCMBIC = 0x8004746b; -pub const TIOCMBIS = 0x8004746c; -pub const TIOCMGET = 0x4004746a; -pub const TIOCMSET = 0x8004746d; -pub const TIOCM_CAR = 0x40; -pub const TIOCM_CD = 0x40; -pub const TIOCM_CTS = 0x20; -pub const TIOCM_DSR = 0x100; -pub const TIOCM_DTR = 0x2; -pub const TIOCM_LE = 0x1; -pub const TIOCM_RI = 0x80; -pub const TIOCM_RNG = 0x80; -pub const TIOCM_RTS = 0x4; -pub const TIOCM_SR = 0x10; -pub const TIOCM_ST = 0x8; -pub const TIOCNOTTY = 0x20007471; -pub const TIOCNXCL = 0x2000740e; -pub const TIOCOUTQ = 0x40047473; -pub const TIOCPKT = 0x80047470; -pub const TIOCPKT_DATA = 0x0; -pub const TIOCPKT_DOSTOP = 0x20; -pub const TIOCPKT_FLUSHREAD = 0x1; -pub const TIOCPKT_FLUSHWRITE = 0x2; -pub const TIOCPKT_IOCTL = 0x40; -pub const TIOCPKT_NOSTOP = 0x10; -pub const TIOCPKT_START = 0x8; -pub const TIOCPKT_STOP = 0x4; -pub const TIOCPTMGET = 0x40287446; -pub const TIOCPTSNAME = 0x40287448; -pub const TIOCRCVFRAME = 0x80087445; -pub const TIOCREMOTE = 0x80047469; -pub const TIOCSBRK = 0x2000747b; -pub const TIOCSCTTY = 0x20007461; -pub const TIOCSDTR = 0x20007479; -pub const TIOCSETA = 0x802c7414; -pub const TIOCSETAF = 0x802c7416; -pub const TIOCSETAW = 0x802c7415; -pub const TIOCSETD = 0x8004741b; -pub const TIOCSFLAGS = 0x8004745c; -pub const TIOCSIG = 0x2000745f; -pub const TIOCSLINED = 0x80207443; -pub const TIOCSPGRP = 0x80047476; -pub const TIOCSQSIZE = 0x80047480; -pub const TIOCSSIZE = 0x80087467; -pub const TIOCSTART = 0x2000746e; -pub const TIOCSTAT = 0x80047465; -pub const TIOCSTI = 0x80017472; -pub const TIOCSTOP = 0x2000746f; -pub const TIOCSWINSZ = 0x80087467; -pub const TIOCUCNTL = 0x80047466; -pub const TIOCXMTFRAME = 0x80087444; - -pub const sockaddr = c.sockaddr; -pub const sockaddr_in = c.sockaddr_in; -pub const sockaddr_in6 = c.sockaddr_in6; - -fn unsigned(s: i32) u32 { - return @bitCast(u32, s); -} -fn signed(s: u32) i32 { - return @bitCast(i32, s); -} -pub fn WEXITSTATUS(s: i32) i32 { - return signed((unsigned(s) >> 8) & 0xff); -} -pub fn WTERMSIG(s: i32) i32 { - return signed(unsigned(s) & 0x7f); -} -pub fn WSTOPSIG(s: i32) i32 { - return WEXITSTATUS(s); -} -pub fn WIFEXITED(s: i32) bool { - return WTERMSIG(s) == 0; -} - -pub fn WIFCONTINUED(s: i32) bool { - return ((s & 0x7f) == 0xffff); -} - -pub fn WIFSTOPPED(s: i32) bool { - return ((s & 0x7f != 0x7f) and !WIFCONTINUED(s)); -} - -pub fn WIFSIGNALED(s: i32) bool { - return !WIFSTOPPED(s) and !WIFCONTINUED(s) and !WIFEXITED(s); -} - -pub const winsize = extern struct { - ws_row: u16, - ws_col: u16, - ws_xpixel: u16, - ws_ypixel: u16, -}; - -/// Get the errno from a syscall return value, or 0 for no error. -pub fn getErrno(r: usize) usize { - const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; -} - -pub fn dup2(old: i32, new: i32) usize { - return errnoWrap(c.dup2(old, new)); -} - -pub fn chdir(path: [*]const u8) usize { - return errnoWrap(c.chdir(path)); -} - -pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { - return errnoWrap(c.execve(path, argv, envp)); -} - -pub fn fork() usize { - return errnoWrap(c.fork()); -} - -pub fn access(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.access(path, mode)); -} - -pub fn getcwd(buf: [*]u8, size: usize) usize { - return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { - return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); -} - -pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { - return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); -} - -pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { - return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn isatty(fd: i32) bool { - return c.isatty(fd) != 0; -} - -pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return errnoWrap(c.readlink(path, buf_ptr, buf_len)); -} - -pub fn mkdir(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.mkdir(path, mode)); -} - -pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap( - @ptrCast(?*c_void, address), - length, - @bitCast(c_int, @intCast(c_uint, prot)), - @bitCast(c_int, c_uint(flags)), - fd, - offset, - ); - const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); - return errnoWrap(isize_result); -} - -pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); -} - -pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); -} - -pub fn rmdir(path: [*]const u8) usize { - return errnoWrap(c.rmdir(path)); -} - -pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.symlink(existing, new)); -} - -pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); -} - -pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { - return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); -} - -pub fn pipe(fd: *[2]i32) usize { - return pipe2(fd, 0); -} - -pub fn pipe2(fd: *[2]i32, flags: u32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); -} - -pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); -} - -pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); -} - -pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { - return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); -} - -pub fn rename(old: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.rename(old, new)); -} - -pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { - return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); -} - -pub fn create(path: [*]const u8, perm: usize) usize { - return arch.syscall2(SYS_creat, @ptrToInt(path), perm); -} - -pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { - return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); -} - -pub fn close(fd: i32) usize { - return errnoWrap(c.close(fd)); -} - -pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { - return errnoWrap(c.lseek(fd, offset, whence)); -} - -pub fn exit(code: i32) noreturn { - c.exit(code); -} - -pub fn kill(pid: i32, sig: i32) usize { - return errnoWrap(c.kill(pid, sig)); -} - -pub fn unlink(path: [*]const u8) usize { - return errnoWrap(c.unlink(path)); -} - -pub fn waitpid(pid: i32, status: *i32, options: u32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); -} - -pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { - return errnoWrap(c.nanosleep(req, rem)); -} - -pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { - return errnoWrap(c.clock_gettime(clk_id, tp)); -} - -pub fn clock_getres(clk_id: i32, tp: *timespec) usize { - return errnoWrap(c.clock_getres(clk_id, tp)); -} - -pub fn setuid(uid: u32) usize { - return errnoWrap(c.setuid(uid)); -} - -pub fn setgid(gid: u32) usize { - return errnoWrap(c.setgid(gid)); -} - -pub fn setreuid(ruid: u32, euid: u32) usize { - return errnoWrap(c.setreuid(ruid, euid)); -} - -pub fn setregid(rgid: u32, egid: u32) usize { - return errnoWrap(c.setregid(rgid, egid)); -} - -const NSIG = 32; - -pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); -pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); -pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); - -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = extern struct { - /// signal handler - __sigaction_u: extern union { - __sa_handler: extern fn (i32) void, - __sa_sigaction: extern fn (i32, *__siginfo, usize) void, - }, - - /// see signal options - sa_flags: u32, - - /// signal mask to apply - sa_mask: sigset_t, -}; - -pub const _SIG_WORDS = 4; -pub const _SIG_MAXSIG = 128; - -pub inline fn _SIG_IDX(sig: usize) usize { - return sig - 1; -} -pub inline fn _SIG_WORD(sig: usize) usize { - return_SIG_IDX(sig) >> 5; -} -pub inline fn _SIG_BIT(sig: usize) usize { - return 1 << (_SIG_IDX(sig) & 31); -} -pub inline fn _SIG_VALID(sig: usize) usize { - return sig <= _SIG_MAXSIG and sig > 0; -} - -pub const sigset_t = extern struct { - __bits: [_SIG_WORDS]u32, -}; - -pub fn raise(sig: i32) usize { - return errnoWrap(c.raise(sig)); -} - -pub const Stat = c.Stat; -pub const dirent = c.dirent; -pub const timespec = c.timespec; - -pub fn fstat(fd: i32, buf: *c.Stat) usize { - return errnoWrap(c.fstat(fd, buf)); -} -pub const iovec = extern struct { - iov_base: [*]u8, - iov_len: usize, -}; - -pub const iovec_const = extern struct { - iov_base: [*]const u8, - iov_len: usize, -}; - -// TODO avoid libc dependency -pub fn kqueue() usize { - return errnoWrap(c.kqueue()); -} - -// TODO avoid libc dependency -pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { - return errnoWrap(c.kevent( - kq, - changelist.ptr, - @intCast(c_int, changelist.len), - eventlist.ptr, - @intCast(c_int, eventlist.len), - timeout, - )); -} - -// TODO avoid libc dependency -pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); -} - -// TODO avoid libc dependency -pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); -} - -// TODO avoid libc dependency -pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { - return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); -} - -// TODO avoid libc dependency - -/// Takes the return value from a syscall and formats it back in the way -/// that the kernel represents it to libc. Errno was a mistake, let's make -/// it go away forever. -fn errnoWrap(value: isize) usize { - return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); -} +pub const is_the_target = builtin.os == .netbsd; +pub use std.c; diff --git a/std/os/netbsd/errno.zig b/std/os/netbsd/errno.zig deleted file mode 100644 index 1772c2ab07..0000000000 --- a/std/os/netbsd/errno.zig +++ /dev/null @@ -1,134 +0,0 @@ -pub const EPERM = 1; // Operation not permitted -pub const ENOENT = 2; // No such file or directory -pub const ESRCH = 3; // No such process -pub const EINTR = 4; // Interrupted system call -pub const EIO = 5; // Input/output error -pub const ENXIO = 6; // Device not configured -pub const E2BIG = 7; // Argument list too long -pub const ENOEXEC = 8; // Exec format error -pub const EBADF = 9; // Bad file descriptor -pub const ECHILD = 10; // No child processes -pub const EDEADLK = 11; // Resource deadlock avoided -// 11 was EAGAIN -pub const ENOMEM = 12; // Cannot allocate memory -pub const EACCES = 13; // Permission denied -pub const EFAULT = 14; // Bad address -pub const ENOTBLK = 15; // Block device required -pub const EBUSY = 16; // Device busy -pub const EEXIST = 17; // File exists -pub const EXDEV = 18; // Cross-device link -pub const ENODEV = 19; // Operation not supported by device -pub const ENOTDIR = 20; // Not a directory -pub const EISDIR = 21; // Is a directory -pub const EINVAL = 22; // Invalid argument -pub const ENFILE = 23; // Too many open files in system -pub const EMFILE = 24; // Too many open files -pub const ENOTTY = 25; // Inappropriate ioctl for device -pub const ETXTBSY = 26; // Text file busy -pub const EFBIG = 27; // File too large -pub const ENOSPC = 28; // No space left on device -pub const ESPIPE = 29; // Illegal seek -pub const EROFS = 30; // Read-only file system -pub const EMLINK = 31; // Too many links -pub const EPIPE = 32; // Broken pipe - -// math software -pub const EDOM = 33; // Numerical argument out of domain -pub const ERANGE = 34; // Result too large or too small - -// non-blocking and interrupt i/o -pub const EAGAIN = 35; // Resource temporarily unavailable -pub const EWOULDBLOCK = EAGAIN; // Operation would block -pub const EINPROGRESS = 36; // Operation now in progress -pub const EALREADY = 37; // Operation already in progress - -// ipc/network software -- argument errors -pub const ENOTSOCK = 38; // Socket operation on non-socket -pub const EDESTADDRREQ = 39; // Destination address required -pub const EMSGSIZE = 40; // Message too long -pub const EPROTOTYPE = 41; // Protocol wrong type for socket -pub const ENOPROTOOPT = 42; // Protocol option not available -pub const EPROTONOSUPPORT = 43; // Protocol not supported -pub const ESOCKTNOSUPPORT = 44; // Socket type not supported -pub const EOPNOTSUPP = 45; // Operation not supported -pub const EPFNOSUPPORT = 46; // Protocol family not supported -pub const EAFNOSUPPORT = 47; // Address family not supported by protocol family -pub const EADDRINUSE = 48; // Address already in use -pub const EADDRNOTAVAIL = 49; // Can't assign requested address - -// ipc/network software -- operational errors -pub const ENETDOWN = 50; // Network is down -pub const ENETUNREACH = 51; // Network is unreachable -pub const ENETRESET = 52; // Network dropped connection on reset -pub const ECONNABORTED = 53; // Software caused connection abort -pub const ECONNRESET = 54; // Connection reset by peer -pub const ENOBUFS = 55; // No buffer space available -pub const EISCONN = 56; // Socket is already connected -pub const ENOTCONN = 57; // Socket is not connected -pub const ESHUTDOWN = 58; // Can't send after socket shutdown -pub const ETOOMANYREFS = 59; // Too many references: can't splice -pub const ETIMEDOUT = 60; // Operation timed out -pub const ECONNREFUSED = 61; // Connection refused - -pub const ELOOP = 62; // Too many levels of symbolic links -pub const ENAMETOOLONG = 63; // File name too long - -// should be rearranged -pub const EHOSTDOWN = 64; // Host is down -pub const EHOSTUNREACH = 65; // No route to host -pub const ENOTEMPTY = 66; // Directory not empty - -// quotas & mush -pub const EPROCLIM = 67; // Too many processes -pub const EUSERS = 68; // Too many users -pub const EDQUOT = 69; // Disc quota exceeded - -// Network File System -pub const ESTALE = 70; // Stale NFS file handle -pub const EREMOTE = 71; // Too many levels of remote in path -pub const EBADRPC = 72; // RPC struct is bad -pub const ERPCMISMATCH = 73; // RPC version wrong -pub const EPROGUNAVAIL = 74; // RPC prog. not avail -pub const EPROGMISMATCH = 75; // Program version wrong -pub const EPROCUNAVAIL = 76; // Bad procedure for program - -pub const ENOLCK = 77; // No locks available -pub const ENOSYS = 78; // Function not implemented - -pub const EFTYPE = 79; // Inappropriate file type or format -pub const EAUTH = 80; // Authentication error -pub const ENEEDAUTH = 81; // Need authenticator - -// SystemV IPC -pub const EIDRM = 82; // Identifier removed -pub const ENOMSG = 83; // No message of desired type -pub const EOVERFLOW = 84; // Value too large to be stored in data type - -// Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 -pub const EILSEQ = 85; // Illegal byte sequence - -// From IEEE Std 1003.1-2001 -// Base, Realtime, Threads or Thread Priority Scheduling option errors -pub const ENOTSUP = 86; // Not supported - -// Realtime option errors -pub const ECANCELED = 87; // Operation canceled - -// Realtime, XSI STREAMS option errors -pub const EBADMSG = 88; // Bad or Corrupt message - -// XSI STREAMS option errors -pub const ENODATA = 89; // No message available -pub const ENOSR = 90; // No STREAM resources -pub const ENOSTR = 91; // Not a STREAM -pub const ETIME = 92; // STREAM ioctl timeout - -// File system extended attribute errors -pub const ENOATTR = 93; // Attribute not found - -// Realtime, XSI STREAMS option errors -pub const EMULTIHOP = 94; // Multihop attempted -pub const ENOLINK = 95; // Link has been severed -pub const EPROTO = 96; // Protocol error - -pub const ELAST = 96; // Must equal largest errno diff --git a/std/os/test.zig b/std/os/test.zig index 0ee6fc1f26..d4d662e97f 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -1,8 +1,12 @@ const std = @import("../std.zig"); const os = std.os; +const testing = std.testing; const expect = std.testing.expect; const io = std.io; +const fs = std.fs; const mem = std.mem; +const File = std.fs.File; +const Thread = std.Thread; const a = std.debug.global_allocator; @@ -11,11 +15,11 @@ const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; test "makePath, put some files in it, deleteTree" { - try os.makePath(a, "os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "c"); - try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "c" ++ os.path.sep_str ++ "file.txt", "nonsense"); - try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "file2.txt", "blah"); - try os.deleteTree(a, "os_test_tmp"); - if (os.Dir.open(a, "os_test_tmp")) |dir| { + try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c"); + try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); + try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); + try fs.deleteTree(a, "os_test_tmp"); + if (fs.Dir.open(a, "os_test_tmp")) |dir| { @panic("expected error"); } else |err| { expect(err == error.FileNotFound); @@ -23,40 +27,37 @@ test "makePath, put some files in it, deleteTree" { } test "access file" { - try os.makePath(a, "os_test_tmp"); - if (os.File.access("os_test_tmp" ++ os.path.sep_str ++ "file.txt")) |ok| { + try fs.makePath(a, "os_test_tmp"); + if (File.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt")) |ok| { @panic("expected error"); } else |err| { expect(err == error.FileNotFound); } - try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "file.txt", ""); - try os.File.access("os_test_tmp" ++ os.path.sep_str ++ "file.txt"); - try os.deleteTree(a, "os_test_tmp"); + try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", ""); + try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK); + try fs.deleteTree(a, "os_test_tmp"); } -fn testThreadIdFn(thread_id: *os.Thread.Id) void { - thread_id.* = os.Thread.getCurrentId(); +fn testThreadIdFn(thread_id: *Thread.Id) void { + thread_id.* = Thread.getCurrentId(); } -test "std.os.Thread.getCurrentId" { +test "std.Thread.getCurrentId" { if (builtin.single_threaded) return error.SkipZigTest; - var thread_current_id: os.Thread.Id = undefined; - const thread = try os.spawnThread(&thread_current_id, testThreadIdFn); + var thread_current_id: Thread.Id = undefined; + const thread = try Thread.spawn(&thread_current_id, testThreadIdFn); const thread_id = thread.handle(); thread.wait(); - if (os.Thread.use_pthreads) { + if (Thread.use_pthreads) { expect(thread_current_id == thread_id); + } else if (os.windows.is_the_target) { + expect(Thread.getCurrentId() != thread_current_id); } else { - switch (builtin.os) { - builtin.Os.windows => expect(os.Thread.getCurrentId() != thread_current_id), - else => { - // If the thread completes very quickly, then thread_id can be 0. See the - // documentation comments for `std.os.Thread.handle`. - expect(thread_id == 0 or thread_current_id == thread_id); - }, - } + // If the thread completes very quickly, then thread_id can be 0. See the + // documentation comments for `std.Thread.handle`. + expect(thread_id == 0 or thread_current_id == thread_id); } } @@ -65,10 +66,10 @@ test "spawn threads" { var shared_ctx: i32 = 1; - const thread1 = try std.os.spawnThread({}, start1); - const thread2 = try std.os.spawnThread(&shared_ctx, start2); - const thread3 = try std.os.spawnThread(&shared_ctx, start2); - const thread4 = try std.os.spawnThread(&shared_ctx, start2); + const thread1 = try Thread.spawn({}, start1); + const thread2 = try Thread.spawn(&shared_ctx, start2); + const thread3 = try Thread.spawn(&shared_ctx, start2); + const thread4 = try Thread.spawn(&shared_ctx, start2); thread1.wait(); thread2.wait(); @@ -88,7 +89,7 @@ fn start2(ctx: *i32) u8 { } test "cpu count" { - const cpu_count = try std.os.cpuCount(a); + const cpu_count = try Thread.cpuCount(); expect(cpu_count >= 1); } @@ -101,7 +102,7 @@ test "AtomicFile" { \\ this is a test file ; { - var af = try os.AtomicFile.init(test_out_file, os.File.default_mode); + var af = try fs.AtomicFile.init(test_out_file, File.default_mode); defer af.deinit(); try af.file.write(test_content); try af.finish(); @@ -109,13 +110,13 @@ test "AtomicFile" { const content = try io.readFileAlloc(allocator, test_out_file); expect(mem.eql(u8, content, test_content)); - try os.deleteFile(test_out_file); + try fs.deleteFile(test_out_file); } test "thread local storage" { if (builtin.single_threaded) return error.SkipZigTest; - const thread1 = try std.os.spawnThread({}, testTls); - const thread2 = try std.os.spawnThread({}, testTls); + const thread1 = try Thread.spawn({}, testTls); + const thread2 = try Thread.spawn({}, testTls); testTls({}); thread1.wait(); thread2.wait(); @@ -127,3 +128,24 @@ fn testTls(context: void) void { x += 1; if (x != 1235) @panic("bad end value"); } + +test "getrandom" { + var buf_a: [50]u8 = undefined; + var buf_b: [50]u8 = undefined; + try os.getrandom(&buf_a); + try os.getrandom(&buf_b); + // If this test fails the chance is significantly higher that there is a bug than + // that two sets of 50 bytes were equal. + expect(!mem.eql(u8, buf_a, buf_b)); +} + +test "getcwd" { + // at least call it so it gets compiled + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + _ = os.getcwd(&buf) catch undefined; +} + +test "realpath" { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + testing.expectError(error.FileNotFound, fs.realpath("definitely_bogus_does_not_exist1234", &buf)); +} diff --git a/std/os/time.zig b/std/os/time.zig deleted file mode 100644 index abb6412843..0000000000 --- a/std/os/time.zig +++ /dev/null @@ -1,307 +0,0 @@ -const std = @import("../std.zig"); -const builtin = @import("builtin"); -const Os = builtin.Os; -const debug = std.debug; -const testing = std.testing; -const math = std.math; - -const windows = std.os.windows; -const linux = std.os.linux; -const darwin = std.os.darwin; -const wasi = std.os.wasi; -const posix = std.os.posix; - -pub const epoch = @import("epoch.zig"); - -/// Spurious wakeups are possible and no precision of timing is guaranteed. -pub fn sleep(nanoseconds: u64) void { - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const s = nanoseconds / ns_per_s; - const ns = nanoseconds % ns_per_s; - posixSleep(s, ns); - }, - Os.windows => { - const ns_per_ms = ns_per_s / ms_per_s; - const milliseconds = nanoseconds / ns_per_ms; - const ms_that_will_fit = std.math.cast(windows.DWORD, milliseconds) catch std.math.maxInt(windows.DWORD); - windows.Sleep(ms_that_will_fit); - }, - else => @compileError("Unsupported OS"), - } -} - -/// Spurious wakeups are possible and no precision of timing is guaranteed. -pub fn posixSleep(seconds: u64, nanoseconds: u64) void { - var req = posix.timespec{ - .tv_sec = std.math.cast(isize, seconds) catch std.math.maxInt(isize), - .tv_nsec = std.math.cast(isize, nanoseconds) catch std.math.maxInt(isize), - }; - var rem: posix.timespec = undefined; - while (true) { - const ret_val = posix.nanosleep(&req, &rem); - const err = posix.getErrno(ret_val); - switch (err) { - posix.EFAULT => unreachable, - posix.EINVAL => { - // Sometimes Darwin returns EINVAL for no reason. - // We treat it as a spurious wakeup. - return; - }, - posix.EINTR => { - req = rem; - continue; - }, - // This prong handles success as well as unexpected errors. - else => return, - } - } -} - -/// Get the posix timestamp, UTC, in seconds -pub fn timestamp() u64 { - return @divFloor(milliTimestamp(), ms_per_s); -} - -/// Get the posix timestamp, UTC, in milliseconds -pub const milliTimestamp = switch (builtin.os) { - Os.windows => milliTimestampWindows, - Os.linux, Os.freebsd, Os.netbsd => milliTimestampPosix, - Os.macosx, Os.ios => milliTimestampDarwin, - Os.wasi => milliTimestampWasi, - else => @compileError("Unsupported OS"), -}; - -fn milliTimestampWasi() u64 { - var ns: wasi.timestamp_t = undefined; - - // TODO: Verify that precision is ignored - const err = wasi.clock_time_get(wasi.CLOCK_REALTIME, 1, &ns); - debug.assert(err == wasi.ESUCCESS); - - const ns_per_ms = 1000; - return @divFloor(ns, ns_per_ms); -} - -fn milliTimestampWindows() u64 { - //FileTime has a granularity of 100 nanoseconds - // and uses the NTFS/Windows epoch - var ft: windows.FILETIME = undefined; - windows.GetSystemTimeAsFileTime(&ft); - const hns_per_ms = (ns_per_s / 100) / ms_per_s; - const epoch_adj = epoch.windows * ms_per_s; - - const ft64 = (u64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime; - return @divFloor(ft64, hns_per_ms) - -epoch_adj; -} - -fn milliTimestampDarwin() u64 { - var tv: darwin.timeval = undefined; - var err = darwin.gettimeofday(&tv, null); - debug.assert(err == 0); - const sec_ms = tv.tv_sec * ms_per_s; - const usec_ms = @divFloor(tv.tv_usec, us_per_s / ms_per_s); - return @intCast(u64, sec_ms + usec_ms); -} - -fn milliTimestampPosix() u64 { - //From what I can tell there's no reason clock_gettime - // should ever fail for us with CLOCK_REALTIME, - // seccomp aside. - var ts: posix.timespec = undefined; - const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts); - debug.assert(err == 0); - const sec_ms = @intCast(u64, ts.tv_sec) * ms_per_s; - const nsec_ms = @divFloor(@intCast(u64, ts.tv_nsec), ns_per_s / ms_per_s); - return sec_ms + nsec_ms; -} - -/// Multiples of a base unit (nanoseconds) -pub const nanosecond = 1; -pub const microsecond = 1000 * nanosecond; -pub const millisecond = 1000 * microsecond; -pub const second = 1000 * millisecond; -pub const minute = 60 * second; -pub const hour = 60 * minute; - -/// Divisions of a second -pub const ns_per_s = 1000000000; -pub const us_per_s = 1000000; -pub const ms_per_s = 1000; -pub const cs_per_s = 100; - -/// Common time divisions -pub const s_per_min = 60; -pub const s_per_hour = s_per_min * 60; -pub const s_per_day = s_per_hour * 24; -pub const s_per_week = s_per_day * 7; - -/// A monotonic high-performance timer. -/// Timer.start() must be called to initialize the struct, which captures -/// the counter frequency on windows and darwin, records the resolution, -/// and gives the user an opportunity to check for the existnece of -/// monotonic clocks without forcing them to check for error on each read. -/// .resolution is in nanoseconds on all platforms but .start_time's meaning -/// depends on the OS. On Windows and Darwin it is a hardware counter -/// value that requires calculation to convert to a meaninful unit. -pub const Timer = struct { - - //if we used resolution's value when performing the - // performance counter calc on windows/darwin, it would - // be less precise - frequency: switch (builtin.os) { - Os.windows => u64, - Os.macosx, Os.ios => darwin.mach_timebase_info_data, - else => void, - }, - resolution: u64, - start_time: u64, - - //At some point we may change our minds on RAW, but for now we're - // sticking with posix standard MONOTONIC. For more information, see: - // https://github.com/ziglang/zig/pull/933 - // - //const monotonic_clock_id = switch(builtin.os) { - // Os.linux => linux.CLOCK_MONOTONIC_RAW, - // else => posix.CLOCK_MONOTONIC, - //}; - const monotonic_clock_id = posix.CLOCK_MONOTONIC; - /// Initialize the timer structure. - //This gives us an opportunity to grab the counter frequency in windows. - //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000. - //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not - // supported, or if the timespec pointer is out of bounds, which should be - // impossible here barring cosmic rays or other such occurrences of - // incredibly bad luck. - //On Darwin: This cannot fail, as far as I am able to tell. - const TimerError = error{ - TimerUnsupported, - Unexpected, - }; - pub fn start() TimerError!Timer { - var self: Timer = undefined; - - switch (builtin.os) { - Os.windows => { - var freq: i64 = undefined; - var err = windows.QueryPerformanceFrequency(&freq); - if (err == windows.FALSE) return error.TimerUnsupported; - self.frequency = @intCast(u64, freq); - self.resolution = @divFloor(ns_per_s, self.frequency); - - var start_time: i64 = undefined; - err = windows.QueryPerformanceCounter(&start_time); - debug.assert(err != windows.FALSE); - self.start_time = @intCast(u64, start_time); - }, - Os.linux, Os.freebsd, Os.netbsd => { - //On Linux, seccomp can do arbitrary things to our ability to call - // syscalls, including return any errno value it wants and - // inconsistently throwing errors. Since we can't account for - // abuses of seccomp in a reasonable way, we'll assume that if - // seccomp is going to block us it will at least do so consistently - var ts: posix.timespec = undefined; - var result = posix.clock_getres(monotonic_clock_id, &ts); - var errno = posix.getErrno(result); - switch (errno) { - 0 => {}, - posix.EINVAL => return error.TimerUnsupported, - else => return std.os.unexpectedErrorPosix(errno), - } - self.resolution = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); - - result = posix.clock_gettime(monotonic_clock_id, &ts); - errno = posix.getErrno(result); - if (errno != 0) return std.os.unexpectedErrorPosix(errno); - self.start_time = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); - }, - Os.macosx, Os.ios => { - darwin.mach_timebase_info(&self.frequency); - self.resolution = @divFloor(self.frequency.numer, self.frequency.denom); - self.start_time = darwin.mach_absolute_time(); - }, - else => @compileError("Unsupported OS"), - } - return self; - } - - /// Reads the timer value since start or the last reset in nanoseconds - pub fn read(self: *Timer) u64 { - var clock = clockNative() - self.start_time; - return switch (builtin.os) { - Os.windows => @divFloor(clock * ns_per_s, self.frequency), - Os.linux, Os.freebsd, Os.netbsd => clock, - Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom), - else => @compileError("Unsupported OS"), - }; - } - - /// Resets the timer value to 0/now. - pub fn reset(self: *Timer) void { - self.start_time = clockNative(); - } - - /// Returns the current value of the timer in nanoseconds, then resets it - pub fn lap(self: *Timer) u64 { - var now = clockNative(); - var lap_time = self.read(); - self.start_time = now; - return lap_time; - } - - const clockNative = switch (builtin.os) { - Os.windows => clockWindows, - Os.linux, Os.freebsd, Os.netbsd => clockLinux, - Os.macosx, Os.ios => clockDarwin, - else => @compileError("Unsupported OS"), - }; - - fn clockWindows() u64 { - var result: i64 = undefined; - var err = windows.QueryPerformanceCounter(&result); - debug.assert(err != windows.FALSE); - return @intCast(u64, result); - } - - fn clockDarwin() u64 { - return darwin.mach_absolute_time(); - } - - fn clockLinux() u64 { - var ts: posix.timespec = undefined; - var result = posix.clock_gettime(monotonic_clock_id, &ts); - debug.assert(posix.getErrno(result) == 0); - return @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); - } -}; - -test "os.time.sleep" { - sleep(1); -} - -test "os.time.timestamp" { - const ns_per_ms = (ns_per_s / ms_per_s); - const margin = 50; - - const time_0 = milliTimestamp(); - sleep(ns_per_ms); - const time_1 = milliTimestamp(); - const interval = time_1 - time_0; - testing.expect(interval > 0 and interval < margin); -} - -test "os.time.Timer" { - const ns_per_ms = (ns_per_s / ms_per_s); - const margin = ns_per_ms * 150; - - var timer = try Timer.start(); - sleep(10 * ns_per_ms); - const time_0 = timer.read(); - testing.expect(time_0 > 0 and time_0 < margin); - - const time_1 = timer.lap(); - testing.expect(time_1 >= time_0); - - timer.reset(); - testing.expect(timer.read() < time_1); -} diff --git a/std/os/wasi.zig b/std/os/wasi.zig index 2118db9a2a..adfe9e821d 100644 --- a/std/os/wasi.zig +++ b/std/os/wasi.zig @@ -1,42 +1,80 @@ -pub use @import("wasi/core.zig"); +// Based on https://github.com/CraneStation/wasi-sysroot/blob/wasi/libc-bottom-half/headers/public/wasi/core.h +// and https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md +const builtin = @import("builtin"); +const std = @import("std"); +const assert = std.debug.assert; -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; +pub const is_the_target = builtin.os == .wasi; +pub use @import("bits.zig"); -pub fn getErrno(r: usize) usize { - const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; +comptime { + assert(@alignOf(i8) == 1); + assert(@alignOf(u8) == 1); + assert(@alignOf(i16) == 2); + assert(@alignOf(u16) == 2); + assert(@alignOf(i32) == 4); + assert(@alignOf(u32) == 4); + assert(@alignOf(i64) == 8); + assert(@alignOf(u64) == 8); } -pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - var nwritten: usize = undefined; +pub const iovec_t = iovec; +pub const ciovec_t = iovec_const; - const ciovs = ciovec_t{ - .buf = buf, - .buf_len = count, - }; +pub extern "wasi_unstable" fn args_get(argv: [*][*]u8, argv_buf: [*]u8) errno_t; +pub extern "wasi_unstable" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t; - const err = fd_write(@bitCast(fd_t, isize(fd)), &ciovs, 1, &nwritten); - if (err == ESUCCESS) { - return nwritten; - } else { - return @bitCast(usize, -isize(err)); - } -} +pub extern "wasi_unstable" fn clock_res_get(clock_id: clockid_t, resolution: *timestamp_t) errno_t; +pub extern "wasi_unstable" fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t; -pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { - var nread: usize = undefined; +pub extern "wasi_unstable" fn environ_get(environ: [*]?[*]u8, environ_buf: [*]u8) errno_t; +pub extern "wasi_unstable" fn environ_sizes_get(environ_count: *usize, environ_buf_size: *usize) errno_t; - const iovs = iovec_t{ - .buf = buf, - .buf_len = nbyte, - }; +pub extern "wasi_unstable" fn fd_advise(fd: fd_t, offset: filesize_t, len: filesize_t, advice: advice_t) errno_t; +pub extern "wasi_unstable" fn fd_allocate(fd: fd_t, offset: filesize_t, len: filesize_t) errno_t; +pub extern "wasi_unstable" fn fd_close(fd: fd_t) errno_t; +pub extern "wasi_unstable" fn fd_datasync(fd: fd_t) errno_t; +pub extern "wasi_unstable" fn fd_pread(fd: fd_t, iovs: [*]const iovec_t, iovs_len: usize, offset: filesize_t, nread: *usize) errno_t; +pub extern "wasi_unstable" fn fd_pwrite(fd: fd_t, iovs: [*]const ciovec_t, iovs_len: usize, offset: filesize_t, nwritten: *usize) errno_t; +pub extern "wasi_unstable" fn fd_read(fd: fd_t, iovs: [*]const iovec_t, iovs_len: usize, nread: *usize) errno_t; +pub extern "wasi_unstable" fn fd_readdir(fd: fd_t, buf: [*]u8, buf_len: usize, cookie: dircookie_t, bufused: *usize) errno_t; +pub extern "wasi_unstable" fn fd_renumber(from: fd_t, to: fd_t) errno_t; +pub extern "wasi_unstable" fn fd_seek(fd: fd_t, offset: filedelta_t, whence: whence_t, newoffset: *filesize_t) errno_t; +pub extern "wasi_unstable" fn fd_sync(fd: fd_t) errno_t; +pub extern "wasi_unstable" fn fd_tell(fd: fd_t, newoffset: *filesize_t) errno_t; +pub extern "wasi_unstable" fn fd_write(fd: fd_t, iovs: [*]const ciovec_t, iovs_len: usize, nwritten: *usize) errno_t; - const err = fd_read(@bitCast(fd_t, isize(fd)), &iovs, 1, &nread); - if (err == ESUCCESS) { - return nread; - } else { - return @bitCast(usize, -isize(err)); - } -} +pub extern "wasi_unstable" fn fd_fdstat_get(fd: fd_t, buf: *fdstat_t) errno_t; +pub extern "wasi_unstable" fn fd_fdstat_set_flags(fd: fd_t, flags: fdflags_t) errno_t; +pub extern "wasi_unstable" fn fd_fdstat_set_rights(fd: fd_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t) errno_t; + +pub extern "wasi_unstable" fn fd_filestat_get(fd: fd_t, buf: *filestat_t) errno_t; +pub extern "wasi_unstable" fn fd_filestat_set_size(fd: fd_t, st_size: filesize_t) errno_t; +pub extern "wasi_unstable" fn fd_filestat_set_times(fd: fd_t, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; + +pub extern "wasi_unstable" fn fd_prestat_get(fd: fd_t, buf: *prestat_t) errno_t; +pub extern "wasi_unstable" fn fd_prestat_dir_name(fd: fd_t, path: [*]u8, path_len: usize) errno_t; + +pub extern "wasi_unstable" fn path_create_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_filestat_get(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, buf: *filestat_t) errno_t; +pub extern "wasi_unstable" fn path_filestat_set_times(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; +pub extern "wasi_unstable" fn path_link(old_fd: fd_t, old_flags: lookupflags_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_open(dirfd: fd_t, dirflags: lookupflags_t, path: [*]const u8, path_len: usize, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, fs_flags: fdflags_t, fd: *fd_t) errno_t; +pub extern "wasi_unstable" fn path_readlink(fd: fd_t, path: [*]const u8, path_len: usize, buf: [*]u8, buf_len: usize, bufused: *usize) errno_t; +pub extern "wasi_unstable" fn path_remove_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_rename(old_fd: fd_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_symlink(old_path: [*]const u8, old_path_len: usize, fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_unlink_file(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; + +pub extern "wasi_unstable" fn poll_oneoff(in: *const subscription_t, out: *event_t, nsubscriptions: usize, nevents: *usize) errno_t; + +pub extern "wasi_unstable" fn proc_exit(rval: exitcode_t) noreturn; +pub extern "wasi_unstable" fn proc_raise(sig: signal_t) errno_t; + +pub extern "wasi_unstable" fn random_get(buf: [*]u8, buf_len: usize) errno_t; + +pub extern "wasi_unstable" fn sched_yield() errno_t; + +pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t; +pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; +pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; diff --git a/std/os/windows.zig b/std/os/windows.zig index e134d87eae..526ac1cfd6 100644 --- a/std/os/windows.zig +++ b/std/os/windows.zig @@ -1,430 +1,767 @@ +// This file contains thin wrappers around Windows-specific APIs, with these +// specific goals in mind: +// * Convert "errno"-style error codes into Zig errors. +// * When null-terminated or UTF16LE byte buffers are required, provide APIs which accept +// slices as well as APIs which accept null-terminated UTF16LE byte buffers. + +const builtin = @import("builtin"); const std = @import("../std.zig"); +const mem = std.mem; const assert = std.debug.assert; +const math = std.math; const maxInt = std.math.maxInt; -pub use @import("windows/advapi32.zig"); -pub use @import("windows/kernel32.zig"); -pub use @import("windows/ntdll.zig"); -pub use @import("windows/ole32.zig"); -pub use @import("windows/shell32.zig"); - -test "import" { - _ = @import("windows/util.zig"); -} - -pub const ERROR = @import("windows/error.zig"); - -pub const SHORT = c_short; -pub const BOOL = c_int; -pub const BOOLEAN = BYTE; -pub const BYTE = u8; -pub const CHAR = u8; -pub const DWORD = u32; -pub const FLOAT = f32; -pub const HANDLE = *c_void; -pub const HCRYPTPROV = ULONG_PTR; -pub const HINSTANCE = *@OpaqueType(); -pub const HMODULE = *@OpaqueType(); -pub const FARPROC = *@OpaqueType(); -pub const INT = c_int; -pub const LPBYTE = *BYTE; -pub const LPCH = *CHAR; -pub const LPCSTR = [*]const CHAR; -pub const LPCTSTR = [*]const TCHAR; -pub const LPCVOID = *const c_void; -pub const LPDWORD = *DWORD; -pub const LPSTR = [*]CHAR; -pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; -pub const LPVOID = *c_void; -pub const LPWSTR = [*]WCHAR; -pub const LPCWSTR = [*]const WCHAR; -pub const PVOID = *c_void; -pub const PWSTR = [*]WCHAR; -pub const SIZE_T = usize; -pub const TCHAR = if (UNICODE) WCHAR else u8; -pub const UINT = c_uint; -pub const ULONG_PTR = usize; -pub const DWORD_PTR = ULONG_PTR; -pub const UNICODE = false; -pub const WCHAR = u16; -pub const WORD = u16; -pub const LARGE_INTEGER = i64; -pub const ULONG = u32; -pub const LONG = i32; -pub const ULONGLONG = u64; -pub const LONGLONG = i64; - -pub const TRUE = 1; -pub const FALSE = 0; - -/// The standard input device. Initially, this is the console input buffer, CONIN$. -pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; - -/// The standard output device. Initially, this is the active console screen buffer, CONOUT$. -pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; - -/// The standard error device. Initially, this is the active console screen buffer, CONOUT$. -pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; - -pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, maxInt(usize)); - -pub const INVALID_FILE_ATTRIBUTES = DWORD(maxInt(DWORD)); - -pub const OVERLAPPED = extern struct { - Internal: ULONG_PTR, - InternalHigh: ULONG_PTR, - Offset: DWORD, - OffsetHigh: DWORD, - hEvent: ?HANDLE, -}; -pub const LPOVERLAPPED = *OVERLAPPED; - -pub const MAX_PATH = 260; - -// TODO issue #305 -pub const FILE_INFO_BY_HANDLE_CLASS = u32; -pub const FileBasicInfo = 0; -pub const FileStandardInfo = 1; -pub const FileNameInfo = 2; -pub const FileRenameInfo = 3; -pub const FileDispositionInfo = 4; -pub const FileAllocationInfo = 5; -pub const FileEndOfFileInfo = 6; -pub const FileStreamInfo = 7; -pub const FileCompressionInfo = 8; -pub const FileAttributeTagInfo = 9; -pub const FileIdBothDirectoryInfo = 10; -pub const FileIdBothDirectoryRestartInfo = 11; -pub const FileIoPriorityHintInfo = 12; -pub const FileRemoteProtocolInfo = 13; -pub const FileFullDirectoryInfo = 14; -pub const FileFullDirectoryRestartInfo = 15; -pub const FileStorageInfo = 16; -pub const FileAlignmentInfo = 17; -pub const FileIdInfo = 18; -pub const FileIdExtdDirectoryInfo = 19; -pub const FileIdExtdDirectoryRestartInfo = 20; - -pub const FILE_NAME_INFO = extern struct { - FileNameLength: DWORD, - FileName: [1]WCHAR, +pub const is_the_target = builtin.os == .windows; +pub const advapi32 = @import("windows/advapi32.zig"); +pub const kernel32 = @import("windows/kernel32.zig"); +pub const ntdll = @import("windows/ntdll.zig"); +pub const ole32 = @import("windows/ole32.zig"); +pub const shell32 = @import("windows/shell32.zig"); + +pub use @import("windows/bits.zig"); + +pub const CreateFileError = error{ + SharingViolation, + PathAlreadyExists, + + /// When any of the path components can not be found or the file component can not + /// be found. Some operating systems distinguish between path components not found and + /// file components not found, but they are collapsed into FileNotFound to gain + /// consistency across operating systems. + FileNotFound, + + AccessDenied, + PipeBusy, + NameTooLong, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, + + /// On Windows, file paths cannot contain these characters: + /// '/', '*', '?', '"', '<', '>', '|' + BadPathName, + + Unexpected, }; -/// Return the normalized drive name. This is the default. -pub const FILE_NAME_NORMALIZED = 0x0; +pub fn CreateFile( + file_path: []const u8, + desired_access: DWORD, + share_mode: DWORD, + lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, + creation_disposition: DWORD, + flags_and_attrs: DWORD, + hTemplateFile: ?HANDLE, +) CreateFileError!HANDLE { + const file_path_w = try sliceToPrefixedFileW(file_path); + return CreateFileW(&file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile); +} + +pub fn CreateFileW( + file_path_w: [*]const u16, + desired_access: DWORD, + share_mode: DWORD, + lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, + creation_disposition: DWORD, + flags_and_attrs: DWORD, + hTemplateFile: ?HANDLE, +) CreateFileError!HANDLE { + const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile); + + if (result == INVALID_HANDLE_VALUE) { + switch (kernel32.GetLastError()) { + ERROR.SHARING_VIOLATION => return error.SharingViolation, + ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, + ERROR.FILE_EXISTS => return error.PathAlreadyExists, + ERROR.FILE_NOT_FOUND => return error.FileNotFound, + ERROR.PATH_NOT_FOUND => return error.FileNotFound, + ERROR.ACCESS_DENIED => return error.AccessDenied, + ERROR.PIPE_BUSY => return error.PipeBusy, + ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, + else => |err| return unexpectedError(err), + } + } + + return result; +} + +pub const CreatePipeError = error{Unexpected}; -/// Return the opened file name (not normalized). -pub const FILE_NAME_OPENED = 0x8; +pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void { + if (kernel32.CreatePipe(rd, wr, sattr, 0) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} -/// Return the path with the drive letter. This is the default. -pub const VOLUME_NAME_DOS = 0x0; +pub const SetHandleInformationError = error{Unexpected}; -/// Return the path with a volume GUID path instead of the drive name. -pub const VOLUME_NAME_GUID = 0x1; +pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void { + if (kernel32.SetHandleInformation(h, mask, flags) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} -/// Return the path with no drive information. -pub const VOLUME_NAME_NONE = 0x4; +pub const RtlGenRandomError = error{Unexpected}; -/// Return the path with the volume device path. -pub const VOLUME_NAME_NT = 0x2; +/// Call RtlGenRandom() instead of CryptGetRandom() on Windows +/// https://github.com/rust-lang-nursery/rand/issues/111 +/// https://bugzilla.mozilla.org/show_bug.cgi?id=504270 +pub fn RtlGenRandom(output: []u8) RtlGenRandomError!void { + if (advapi32.RtlGenRandom(output.ptr, output.len) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} -pub const SECURITY_ATTRIBUTES = extern struct { - nLength: DWORD, - lpSecurityDescriptor: ?*c_void, - bInheritHandle: BOOL, +pub const WaitForSingleObjectError = error{ + WaitAbandoned, + WaitTimeOut, + Unexpected, }; -pub const PSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; -pub const LPSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; - -pub const GENERIC_READ = 0x80000000; -pub const GENERIC_WRITE = 0x40000000; -pub const GENERIC_EXECUTE = 0x20000000; -pub const GENERIC_ALL = 0x10000000; - -pub const FILE_SHARE_DELETE = 0x00000004; -pub const FILE_SHARE_READ = 0x00000001; -pub const FILE_SHARE_WRITE = 0x00000002; - -pub const CREATE_ALWAYS = 2; -pub const CREATE_NEW = 1; -pub const OPEN_ALWAYS = 4; -pub const OPEN_EXISTING = 3; -pub const TRUNCATE_EXISTING = 5; - -pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; -pub const FILE_ATTRIBUTE_COMPRESSED = 0x800; -pub const FILE_ATTRIBUTE_DEVICE = 0x40; -pub const FILE_ATTRIBUTE_DIRECTORY = 0x10; -pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; -pub const FILE_ATTRIBUTE_HIDDEN = 0x2; -pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000; -pub const FILE_ATTRIBUTE_NORMAL = 0x80; -pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000; -pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000; -pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; -pub const FILE_ATTRIBUTE_READONLY = 0x1; -pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000; -pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000; -pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400; -pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200; -pub const FILE_ATTRIBUTE_SYSTEM = 0x4; -pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; -pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; - -pub const PROCESS_INFORMATION = extern struct { - hProcess: HANDLE, - hThread: HANDLE, - dwProcessId: DWORD, - dwThreadId: DWORD, + +pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void { + switch (kernel32.WaitForSingleObject(handle, milliseconds)) { + WAIT_ABANDONED => return error.WaitAbandoned, + WAIT_OBJECT_0 => return, + WAIT_TIMEOUT => return error.WaitTimeOut, + WAIT_FAILED => switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + }, + else => return error.Unexpected, + } +} + +pub const FindFirstFileError = error{ + FileNotFound, + InvalidUtf8, + BadPathName, + NameTooLong, + Unexpected, }; -pub const STARTUPINFOW = extern struct { - cb: DWORD, - lpReserved: ?LPWSTR, - lpDesktop: ?LPWSTR, - lpTitle: ?LPWSTR, - dwX: DWORD, - dwY: DWORD, - dwXSize: DWORD, - dwYSize: DWORD, - dwXCountChars: DWORD, - dwYCountChars: DWORD, - dwFillAttribute: DWORD, - dwFlags: DWORD, - wShowWindow: WORD, - cbReserved2: WORD, - lpReserved2: ?LPBYTE, - hStdInput: ?HANDLE, - hStdOutput: ?HANDLE, - hStdError: ?HANDLE, +pub fn FindFirstFile(dir_path: []const u8, find_file_data: *WIN32_FIND_DATAW) FindFirstFileError!HANDLE { + const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{ '\\', '*', 0 }); + const handle = kernel32.FindFirstFileW(&dir_path_w, find_file_data); + + if (handle == INVALID_HANDLE_VALUE) { + switch (kernel32.GetLastError()) { + ERROR.FILE_NOT_FOUND => return error.FileNotFound, + ERROR.PATH_NOT_FOUND => return error.FileNotFound, + else => |err| return unexpectedError(err), + } + } + + return handle; +} + +pub const FindNextFileError = error{Unexpected}; + +/// Returns `true` if there was another file, `false` otherwise. +pub fn FindNextFile(handle: HANDLE, find_file_data: *WIN32_FIND_DATAW) FindNextFileError!bool { + if (kernel32.FindNextFileW(handle, find_file_data) == 0) { + switch (kernel32.GetLastError()) { + ERROR.NO_MORE_FILES => return false, + else => |err| return unexpectedError(err), + } + } + return true; +} + +pub const CreateIoCompletionPortError = error{Unexpected}; + +pub fn CreateIoCompletionPort( + file_handle: HANDLE, + existing_completion_port: ?HANDLE, + completion_key: usize, + concurrent_thread_count: DWORD, +) CreateIoCompletionPortError!HANDLE { + const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse { + switch (kernel32.GetLastError()) { + ERROR.INVALID_PARAMETER => unreachable, + else => |err| return unexpectedError(err), + } + }; + return handle; +} + +pub const PostQueuedCompletionStatusError = error{Unexpected}; + +pub fn PostQueuedCompletionStatus( + completion_port: HANDLE, + bytes_transferred_count: DWORD, + completion_key: usize, + lpOverlapped: ?*OVERLAPPED, +) PostQueuedCompletionStatusError!void { + if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} + +pub const GetQueuedCompletionStatusResult = enum { + Normal, + Aborted, + Cancelled, + EOF, }; -pub const STARTF_FORCEONFEEDBACK = 0x00000040; -pub const STARTF_FORCEOFFFEEDBACK = 0x00000080; -pub const STARTF_PREVENTPINNING = 0x00002000; -pub const STARTF_RUNFULLSCREEN = 0x00000020; -pub const STARTF_TITLEISAPPID = 0x00001000; -pub const STARTF_TITLEISLINKNAME = 0x00000800; -pub const STARTF_UNTRUSTEDSOURCE = 0x00008000; -pub const STARTF_USECOUNTCHARS = 0x00000008; -pub const STARTF_USEFILLATTRIBUTE = 0x00000010; -pub const STARTF_USEHOTKEY = 0x00000200; -pub const STARTF_USEPOSITION = 0x00000004; -pub const STARTF_USESHOWWINDOW = 0x00000001; -pub const STARTF_USESIZE = 0x00000002; -pub const STARTF_USESTDHANDLES = 0x00000100; - -pub const INFINITE = 4294967295; - -pub const WAIT_ABANDONED = 0x00000080; -pub const WAIT_OBJECT_0 = 0x00000000; -pub const WAIT_TIMEOUT = 0x00000102; -pub const WAIT_FAILED = 0xFFFFFFFF; - -pub const HANDLE_FLAG_INHERIT = 0x00000001; -pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002; - -pub const MOVEFILE_COPY_ALLOWED = 2; -pub const MOVEFILE_CREATE_HARDLINK = 16; -pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4; -pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32; -pub const MOVEFILE_REPLACE_EXISTING = 1; -pub const MOVEFILE_WRITE_THROUGH = 8; - -pub const FILE_BEGIN = 0; -pub const FILE_CURRENT = 1; -pub const FILE_END = 2; - -pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; -pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; -pub const HEAP_NO_SERIALIZE = 0x00000001; - -// AllocationType values -pub const MEM_COMMIT = 0x1000; -pub const MEM_RESERVE = 0x2000; -pub const MEM_RESET = 0x80000; -pub const MEM_RESET_UNDO = 0x1000000; -pub const MEM_LARGE_PAGES = 0x20000000; -pub const MEM_PHYSICAL = 0x400000; -pub const MEM_TOP_DOWN = 0x100000; -pub const MEM_WRITE_WATCH = 0x200000; - -// Protect values -pub const PAGE_EXECUTE = 0x10; -pub const PAGE_EXECUTE_READ = 0x20; -pub const PAGE_EXECUTE_READWRITE = 0x40; -pub const PAGE_EXECUTE_WRITECOPY = 0x80; -pub const PAGE_NOACCESS = 0x01; -pub const PAGE_READONLY = 0x02; -pub const PAGE_READWRITE = 0x04; -pub const PAGE_WRITECOPY = 0x08; -pub const PAGE_TARGETS_INVALID = 0x40000000; -pub const PAGE_TARGETS_NO_UPDATE = 0x40000000; // Same as PAGE_TARGETS_INVALID -pub const PAGE_GUARD = 0x100; -pub const PAGE_NOCACHE = 0x200; -pub const PAGE_WRITECOMBINE = 0x400; - -// FreeType values -pub const MEM_COALESCE_PLACEHOLDERS = 0x1; -pub const MEM_RESERVE_PLACEHOLDERS = 0x2; -pub const MEM_DECOMMIT = 0x4000; -pub const MEM_RELEASE = 0x8000; - -pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; -pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; - -pub const WIN32_FIND_DATAW = extern struct { - dwFileAttributes: DWORD, - ftCreationTime: FILETIME, - ftLastAccessTime: FILETIME, - ftLastWriteTime: FILETIME, - nFileSizeHigh: DWORD, - nFileSizeLow: DWORD, - dwReserved0: DWORD, - dwReserved1: DWORD, - cFileName: [260]u16, - cAlternateFileName: [14]u16, +pub fn GetQueuedCompletionStatus( + completion_port: HANDLE, + bytes_transferred_count: *DWORD, + lpCompletionKey: *usize, + lpOverlapped: *?*OVERLAPPED, + dwMilliseconds: DWORD, +) GetQueuedCompletionStatusResult { + if (kernel32.GetQueuedCompletionStatus( + completion_port, + bytes_transferred_count, + lpCompletionKey, + lpOverlapped, + dwMilliseconds, + ) == FALSE) { + switch (kernel32.GetLastError()) { + ERROR.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted, + ERROR.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Cancelled, + ERROR.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF, + else => |err| { + if (std.debug.runtime_safety) { + std.debug.panic("unexpected error: {}\n", err); + } + }, + } + } + return GetQueuedCompletionStatusResult.Normal; +} + +pub fn CloseHandle(hObject: HANDLE) void { + assert(kernel32.CloseHandle(hObject) != 0); +} + +pub fn FindClose(hFindFile: HANDLE) void { + assert(kernel32.FindClose(hFindFile) != 0); +} + +pub const ReadFileError = error{Unexpected}; + +pub fn ReadFile(in_hFile: HANDLE, buffer: []u8) ReadFileError!usize { + var index: usize = 0; + while (index < buffer.len) { + const want_read_count = @intCast(DWORD, math.min(DWORD(maxInt(DWORD)), buffer.len - index)); + var amt_read: DWORD = undefined; + if (kernel32.ReadFile(in_hFile, buffer.ptr + index, want_read_count, &amt_read, null) == 0) { + switch (kernel32.GetLastError()) { + ERROR.OPERATION_ABORTED => continue, + ERROR.BROKEN_PIPE => return index, + else => |err| return unexpectedError(err), + } + } + if (amt_read == 0) return index; + index += amt_read; + } + return index; +} + +pub const WriteFileError = error{ + SystemResources, + OperationAborted, + BrokenPipe, + Unexpected, }; -pub const FILETIME = extern struct { - dwLowDateTime: DWORD, - dwHighDateTime: DWORD, +/// This function is for blocking file descriptors only. For non-blocking, see +/// `WriteFileAsync`. +pub fn WriteFile(handle: HANDLE, bytes: []const u8) WriteFileError!void { + var bytes_written: DWORD = undefined; + // TODO replace this @intCast with a loop that writes all the bytes + if (kernel32.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), &bytes_written, null) == 0) { + switch (kernel32.GetLastError()) { + ERROR.INVALID_USER_BUFFER => return error.SystemResources, + ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources, + ERROR.OPERATION_ABORTED => return error.OperationAborted, + ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources, + ERROR.IO_PENDING => unreachable, // this function is for blocking files only + ERROR.BROKEN_PIPE => return error.BrokenPipe, + else => |err| return unexpectedError(err), + } + } +} + +pub const GetCurrentDirectoryError = error{ + NameTooLong, + Unexpected, }; -pub const SYSTEM_INFO = extern struct { - anon1: extern union { - dwOemId: DWORD, - anon2: extern struct { - wProcessorArchitecture: WORD, - wReserved: WORD, - }, - }, - dwPageSize: DWORD, - lpMinimumApplicationAddress: LPVOID, - lpMaximumApplicationAddress: LPVOID, - dwActiveProcessorMask: DWORD_PTR, - dwNumberOfProcessors: DWORD, - dwProcessorType: DWORD, - dwAllocationGranularity: DWORD, - wProcessorLevel: WORD, - wProcessorRevision: WORD, +/// The result is a slice of `buffer`, indexed from 0. +pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 { + var utf16le_buf: [PATH_MAX_WIDE]u16 = undefined; + const result = kernel32.GetCurrentDirectoryW(utf16le_buf.len, &utf16le_buf); + if (result == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } + assert(result <= utf16le_buf.len); + const utf16le_slice = utf16le_buf[0..result]; + // Trust that Windows gives us valid UTF-16LE. + var end_index: usize = 0; + var it = std.unicode.Utf16LeIterator.init(utf16le_slice); + while (it.nextCodepoint() catch unreachable) |codepoint| { + const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable; + if (end_index + seq_len >= buffer.len) + return error.NameTooLong; + end_index += std.unicode.utf8Encode(codepoint, buffer[end_index..]) catch unreachable; + } + return buffer[0..end_index]; +} + +pub const CreateSymbolicLinkError = error{Unexpected}; + +pub fn CreateSymbolicLink( + sym_link_path: []const u8, + target_path: []const u8, + flags: DWORD, +) CreateSymbolicLinkError!void { + const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path); + const target_path_w = try sliceToPrefixedFileW(target_path); + return CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, flags); +} + +pub fn CreateSymbolicLinkW( + sym_link_path: [*]const u16, + target_path: [*]const u16, + flags: DWORD, +) CreateSymbolicLinkError!void { + if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} + +pub const DeleteFileError = error{ + FileNotFound, + AccessDenied, + NameTooLong, + Unexpected, }; -pub const HRESULT = c_long; +pub fn DeleteFile(filename: []const u8) DeleteFileError!void { + const filename_w = try sliceToPrefixedFileW(filename); + return DeleteFileW(&filename_w); +} -pub const KNOWNFOLDERID = GUID; -pub const GUID = extern struct { - Data1: c_ulong, - Data2: c_ushort, - Data3: c_ushort, - Data4: [8]u8, +pub fn DeleteFileW(filename: [*]const u16) DeleteFileError!void { + if (kernel32.DeleteFileW(filename) == 0) { + switch (kernel32.GetLastError()) { + ERROR.FILE_NOT_FOUND => return error.FileNotFound, + ERROR.ACCESS_DENIED => return error.AccessDenied, + ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, + ERROR.INVALID_PARAMETER => return error.NameTooLong, + else => |err| return unexpectedError(err), + } + } +} - pub fn parse(str: []const u8) GUID { - var guid: GUID = undefined; - var index: usize = 0; - assert(str[index] == '{'); - index += 1; +pub const MoveFileError = error{Unexpected}; - guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable; - index += 8; +pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void { + const old_path_w = try sliceToPrefixedFileW(old_path); + const new_path_w = try sliceToPrefixedFileW(new_path); + return MoveFileExW(&old_path_w, &new_path_w, flags); +} - assert(str[index] == '-'); - index += 1; +pub fn MoveFileExW(old_path: [*]const u16, new_path: [*]const u16, flags: DWORD) MoveFileError!void { + if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} - guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; - index += 4; +pub const CreateDirectoryError = error{ + PathAlreadyExists, + FileNotFound, + Unexpected, +}; - assert(str[index] == '-'); - index += 1; +pub fn CreateDirectory(pathname: []const u8, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void { + const pathname_w = try sliceToPrefixedFileW(pathname); + return CreateDirectoryW(&pathname_w, attrs); +} - guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; - index += 4; +pub fn CreateDirectoryW(pathname: [*]const u16, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void { + if (kernel32.CreateDirectoryW(pathname, attrs) == 0) { + switch (kernel32.GetLastError()) { + ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, + ERROR.PATH_NOT_FOUND => return error.FileNotFound, + else => |err| return unexpectedError(err), + } + } +} - assert(str[index] == '-'); - index += 1; +pub const RemoveDirectoryError = error{ + FileNotFound, + DirNotEmpty, + Unexpected, +}; - guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; - index += 2; - guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; - index += 2; +pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void { + const dir_path_w = try sliceToPrefixedFileW(dir_path); + return RemoveDirectoryW(&dir_path_w); +} - assert(str[index] == '-'); - index += 1; +pub fn RemoveDirectoryW(dir_path_w: [*]const u16) RemoveDirectoryError!void { + if (kernel32.RemoveDirectoryW(dir_path_w) == 0) { + switch (kernel32.GetLastError()) { + ERROR.PATH_NOT_FOUND => return error.FileNotFound, + ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, + else => |err| return unexpectedError(err), + } + } +} - var i: usize = 2; - while (i < guid.Data4.len) : (i += 1) { - guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; - index += 2; +pub const GetStdHandleError = error{ + NoStandardHandleAttached, + Unexpected, +}; + +pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE { + const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached; + if (handle == INVALID_HANDLE_VALUE) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } + return handle; +} + +pub const SetFilePointerError = error{Unexpected}; + +/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`. +pub fn SetFilePointerEx_BEGIN(handle: HANDLE, offset: u64) SetFilePointerError!void { + // "The starting point is zero or the beginning of the file. If [FILE_BEGIN] + // is specified, then the liDistanceToMove parameter is interpreted as an unsigned value." + // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex + const ipos = @bitCast(LARGE_INTEGER, offset); + if (kernel32.SetFilePointerEx(handle, ipos, null, FILE_BEGIN) == 0) { + switch (kernel32.GetLastError()) { + ERROR.INVALID_PARAMETER => unreachable, + ERROR.INVALID_HANDLE => unreachable, + else => |err| return unexpectedError(err), + } + } +} + +/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`. +pub fn SetFilePointerEx_CURRENT(handle: HANDLE, offset: i64) SetFilePointerError!void { + if (kernel32.SetFilePointerEx(handle, offset, null, FILE_CURRENT) == 0) { + switch (kernel32.GetLastError()) { + ERROR.INVALID_PARAMETER => unreachable, + ERROR.INVALID_HANDLE => unreachable, + else => |err| return unexpectedError(err), + } + } +} + +/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`. +pub fn SetFilePointerEx_END(handle: HANDLE, offset: i64) SetFilePointerError!void { + if (kernel32.SetFilePointerEx(handle, offset, null, FILE_END) == 0) { + switch (kernel32.GetLastError()) { + ERROR.INVALID_PARAMETER => unreachable, + ERROR.INVALID_HANDLE => unreachable, + else => |err| return unexpectedError(err), } + } +} - assert(str[index] == '}'); - index += 1; - return guid; +/// The SetFilePointerEx function with parameters to get the current offset. +pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { + var result: LARGE_INTEGER = undefined; + if (kernel32.SetFilePointerEx(handle, 0, &result, FILE_CURRENT) == 0) { + switch (kernel32.GetLastError()) { + ERROR.INVALID_PARAMETER => unreachable, + ERROR.INVALID_HANDLE => unreachable, + else => |err| return unexpectedError(err), + } } + // Based on the docs for FILE_BEGIN, it seems that the returned signed integer + // should be interpreted as an unsigned integer. + return @bitCast(u64, result); +} + +pub const GetFinalPathNameByHandleError = error{ + FileNotFound, + SystemResources, + NameTooLong, + Unexpected, }; -pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"); - -pub const KF_FLAG_DEFAULT = 0; -pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536; -pub const KF_FLAG_CREATE = 32768; -pub const KF_FLAG_DONT_VERIFY = 16384; -pub const KF_FLAG_DONT_UNEXPAND = 8192; -pub const KF_FLAG_NO_ALIAS = 4096; -pub const KF_FLAG_INIT = 2048; -pub const KF_FLAG_DEFAULT_PATH = 1024; -pub const KF_FLAG_NOT_PARENT_RELATIVE = 512; -pub const KF_FLAG_SIMPLE_IDLIST = 256; -pub const KF_FLAG_ALIAS_ONLY = -2147483648; - -pub const S_OK = 0; -pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001)); -pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002)); -pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003)); -pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004)); -pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005)); -pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF)); -pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); -pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); -pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); -pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); - -pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; -pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; -pub const FILE_FLAG_NO_BUFFERING = 0x20000000; -pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000; -pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; -pub const FILE_FLAG_OVERLAPPED = 0x40000000; -pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000; -pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; -pub const FILE_FLAG_SESSION_AWARE = 0x00800000; -pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; -pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; - -pub const SMALL_RECT = extern struct { - Left: SHORT, - Top: SHORT, - Right: SHORT, - Bottom: SHORT, +pub fn GetFinalPathNameByHandleW( + hFile: HANDLE, + buf_ptr: [*]u16, + buf_len: DWORD, + flags: DWORD, +) GetFinalPathNameByHandleError!DWORD { + const rc = kernel32.GetFinalPathNameByHandleW(hFile, buf_ptr, buf_len, flags); + if (rc == 0) { + switch (kernel32.GetLastError()) { + ERROR.FILE_NOT_FOUND => return error.FileNotFound, + ERROR.PATH_NOT_FOUND => return error.FileNotFound, + ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources, + ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, + ERROR.INVALID_PARAMETER => unreachable, + else => |err| return unexpectedError(err), + } + } + return rc; +} + +pub const GetFileSizeError = error{Unexpected}; + +pub fn GetFileSizeEx(hFile: HANDLE) GetFileSizeError!u64 { + var file_size: LARGE_INTEGER = undefined; + if (kernel32.GetFileSizeEx(hFile, &file_size) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } + return @bitCast(u64, file_size); +} + +pub const GetFileAttributesError = error{ + FileNotFound, + PermissionDenied, + Unexpected, +}; + +pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD { + const filename_w = try sliceToPrefixedFileW(filename); + return GetFileAttributesW(&filename_w); +} + +pub fn GetFileAttributesW(lpFileName: [*]const u16) GetFileAttributesError!DWORD { + const rc = kernel32.GetFileAttributesW(lpFileName); + if (rc == INVALID_FILE_ATTRIBUTES) { + switch (kernel32.GetLastError()) { + ERROR.FILE_NOT_FOUND => return error.FileNotFound, + ERROR.PATH_NOT_FOUND => return error.FileNotFound, + ERROR.ACCESS_DENIED => return error.PermissionDenied, + else => |err| return unexpectedError(err), + } + } + return rc; +} + +const GetModuleFileNameError = error{Unexpected}; + +pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![]u16 { + const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len); + if (rc == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } + return buf_ptr[0..rc]; +} + +pub const TerminateProcessError = error{Unexpected}; + +pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) TerminateProcessError!void { + if (kernel32.TerminateProcess(hProcess, uExitCode) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} + +pub const VirtualAllocError = error{Unexpected}; + +pub fn VirtualAlloc(addr: ?LPVOID, size: usize, alloc_type: DWORD, flProtect: DWORD) VirtualAllocError!LPVOID { + return kernel32.VirtualAlloc(addr, size, alloc_type, flProtect) orelse { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + }; +} + +pub fn VirtualFree(lpAddress: ?LPVOID, dwSize: usize, dwFreeType: DWORD) void { + assert(kernel32.VirtualFree(lpAddress, dwSize, dwFreeType) != 0); +} + +pub const SetConsoleTextAttributeError = error{Unexpected}; + +pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void { + if (kernel32.SetConsoleTextAttribute(hConsoleOutput, wAttributes) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} + +pub const GetEnvironmentStringsError = error{OutOfMemory}; + +pub fn GetEnvironmentStringsW() GetEnvironmentStringsError![*]u16 { + return kernel32.GetEnvironmentStringsW() orelse return error.OutOfMemory; +} + +pub fn FreeEnvironmentStringsW(penv: [*]u16) void { + assert(kernel32.FreeEnvironmentStringsW(penv) != 0); +} + +pub const GetEnvironmentVariableError = error{ + EnvironmentVariableNotFound, + Unexpected, }; -pub const COORD = extern struct { - X: SHORT, - Y: SHORT, +pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) GetEnvironmentVariableError!DWORD { + const rc = kernel32.GetEnvironmentVariableW(lpName, lpBuffer, nSize); + if (rc == 0) { + switch (kernel32.GetLastError()) { + ERROR.ENVVAR_NOT_FOUND => return error.EnvironmentVariableNotFound, + else => |err| return unexpectedError(err), + } + } + return rc; +} + +pub const CreateProcessError = error{ + FileNotFound, + InvalidName, + Unexpected, }; -pub const CREATE_UNICODE_ENVIRONMENT = 1024; +pub fn CreateProcessW( + lpApplicationName: ?LPWSTR, + lpCommandLine: LPWSTR, + lpProcessAttributes: ?*SECURITY_ATTRIBUTES, + lpThreadAttributes: ?*SECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: ?*c_void, + lpCurrentDirectory: ?LPWSTR, + lpStartupInfo: *STARTUPINFOW, + lpProcessInformation: *PROCESS_INFORMATION, +) CreateProcessError!void { + if (kernel32.CreateProcessW( + lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation, + ) == 0) { + switch (kernel32.GetLastError()) { + ERROR.FILE_NOT_FOUND => return error.FileNotFound, + ERROR.PATH_NOT_FOUND => return error.FileNotFound, + ERROR.INVALID_PARAMETER => unreachable, + ERROR.INVALID_NAME => return error.InvalidName, + else => |err| return unexpectedError(err), + } + } +} -pub const TLS_OUT_OF_INDEXES = 4294967295; -pub const IMAGE_TLS_DIRECTORY = extern struct { - StartAddressOfRawData: usize, - EndAddressOfRawData: usize, - AddressOfIndex: usize, - AddressOfCallBacks: usize, - SizeOfZeroFill: u32, - Characteristics: u32, +pub const LoadLibraryError = error{ + FileNotFound, + Unexpected, }; -pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; -pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; -pub const PIMAGE_TLS_CALLBACK = ?extern fn (PVOID, DWORD, PVOID) void; +pub fn LoadLibraryW(lpLibFileName: [*]const u16) LoadLibraryError!HMODULE { + return kernel32.LoadLibraryW(lpLibFileName) orelse { + switch (kernel32.GetLastError()) { + ERROR.FILE_NOT_FOUND => return error.FileNotFound, + ERROR.PATH_NOT_FOUND => return error.FileNotFound, + ERROR.MOD_NOT_FOUND => return error.FileNotFound, + else => |err| return unexpectedError(err), + } + }; +} + +pub fn FreeLibrary(hModule: HMODULE) void { + assert(kernel32.FreeLibrary(hModule) != 0); +} + +pub fn QueryPerformanceFrequency() u64 { + // "On systems that run Windows XP or later, the function will always succeed" + // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency + var result: LARGE_INTEGER = undefined; + assert(kernel32.QueryPerformanceFrequency(&result) != 0); + // The kernel treats this integer as unsigned. + return @bitCast(u64, result); +} + +pub fn QueryPerformanceCounter() u64 { + // "On systems that run Windows XP or later, the function will always succeed" + // https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter + var result: LARGE_INTEGER = undefined; + assert(kernel32.QueryPerformanceCounter(&result) != 0); + // The kernel treats this integer as unsigned. + return @bitCast(u64, result); +} + +pub fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) void { + assert(kernel32.InitOnceExecuteOnce(InitOnce, InitFn, Parameter, Context) != 0); +} + +pub fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) void { + assert(kernel32.HeapFree(hHeap, dwFlags, lpMem) != 0); +} + +pub fn HeapDestroy(hHeap: HANDLE) void { + assert(kernel32.HeapDestroy(hHeap) != 0); +} + +pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { + return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); +} + +pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { + return sliceToPrefixedSuffixedFileW(s, []u16{0}); +} + +pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { + // TODO well defined copy elision + var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; + + // > File I/O functions in the Windows API convert "/" to "\" as part of + // > converting the name to an NT-style name, except when using the "\\?\" + // > prefix as detailed in the following sections. + // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation + // Because we want the larger maximum path length for absolute paths, we + // disallow forward slashes in zig std lib file functions on Windows. + for (s) |byte| { + switch (byte) { + '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, + else => {}, + } + } + const start_index = if (mem.startsWith(u8, s, "\\\\") or !std.fs.path.isAbsolute(s)) 0 else blk: { + const prefix = []u16{ '\\', '\\', '?', '\\' }; + mem.copy(u16, result[0..], prefix); + break :blk prefix.len; + }; + const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); + assert(end_index <= result.len); + if (end_index + suffix.len > result.len) return error.NameTooLong; + mem.copy(u16, result[end_index..], suffix); + return result; +} + +/// Call this when you made a windows DLL call or something that does SetLastError +/// and you get an unexpected error. +pub fn unexpectedError(err: DWORD) std.os.UnexpectedError { + if (std.os.unexpected_error_tracing) { + std.debug.warn("unexpected GetLastError(): {}\n", err); + std.debug.dumpCurrentStackTrace(null); + } + return error.Unexpected; +} diff --git a/std/os/windows/advapi32.zig b/std/os/windows/advapi32.zig index 9672767500..df3220e24c 100644 --- a/std/os/windows/advapi32.zig +++ b/std/os/windows/advapi32.zig @@ -1,15 +1,4 @@ -use @import("../windows.zig"); - -pub const PROV_RSA_FULL = 1; - -pub const REGSAM = ACCESS_MASK; -pub const ACCESS_MASK = DWORD; -pub const PHKEY = *HKEY; -pub const HKEY = *HKEY__; -pub const HKEY__ = extern struct { - unused: c_int, -}; -pub const LSTATUS = LONG; +use @import("bits.zig"); pub extern "advapi32" stdcallcc fn RegOpenKeyExW( hKey: HKEY, diff --git a/std/os/windows/bits.zig b/std/os/windows/bits.zig new file mode 100644 index 0000000000..0bf2991903 --- /dev/null +++ b/std/os/windows/bits.zig @@ -0,0 +1,527 @@ +// Platform-dependent types and values that are used along with OS-specific APIs. + +const builtin = @import("builtin"); +const std = @import("../../std.zig"); +const assert = std.debug.assert; +const maxInt = std.math.maxInt; + +pub const ERROR = @import("error.zig"); + +/// The standard input device. Initially, this is the console input buffer, CONIN$. +pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; + +/// The standard output device. Initially, this is the active console screen buffer, CONOUT$. +pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; + +/// The standard error device. Initially, this is the active console screen buffer, CONOUT$. +pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; + +pub const SHORT = c_short; +pub const BOOL = c_int; +pub const BOOLEAN = BYTE; +pub const BYTE = u8; +pub const CHAR = u8; +pub const DWORD = u32; +pub const FLOAT = f32; +pub const HANDLE = *c_void; +pub const HCRYPTPROV = ULONG_PTR; +pub const HINSTANCE = *@OpaqueType(); +pub const HMODULE = *@OpaqueType(); +pub const FARPROC = *@OpaqueType(); +pub const INT = c_int; +pub const LPBYTE = *BYTE; +pub const LPCH = *CHAR; +pub const LPCSTR = [*]const CHAR; +pub const LPCTSTR = [*]const TCHAR; +pub const LPCVOID = *const c_void; +pub const LPDWORD = *DWORD; +pub const LPSTR = [*]CHAR; +pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; +pub const LPVOID = *c_void; +pub const LPWSTR = [*]WCHAR; +pub const LPCWSTR = [*]const WCHAR; +pub const PVOID = *c_void; +pub const PWSTR = [*]WCHAR; +pub const SIZE_T = usize; +pub const TCHAR = if (UNICODE) WCHAR else u8; +pub const UINT = c_uint; +pub const ULONG_PTR = usize; +pub const DWORD_PTR = ULONG_PTR; +pub const UNICODE = false; +pub const WCHAR = u16; +pub const WORD = u16; +pub const LARGE_INTEGER = i64; +pub const ULONG = u32; +pub const LONG = i32; +pub const ULONGLONG = u64; +pub const LONGLONG = i64; + +pub const TRUE = 1; +pub const FALSE = 0; + +pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, maxInt(usize)); + +pub const INVALID_FILE_ATTRIBUTES = DWORD(maxInt(DWORD)); + +pub const OVERLAPPED = extern struct { + Internal: ULONG_PTR, + InternalHigh: ULONG_PTR, + Offset: DWORD, + OffsetHigh: DWORD, + hEvent: ?HANDLE, +}; +pub const LPOVERLAPPED = *OVERLAPPED; + +pub const MAX_PATH = 260; + +// TODO issue #305 +pub const FILE_INFO_BY_HANDLE_CLASS = u32; +pub const FileBasicInfo = 0; +pub const FileStandardInfo = 1; +pub const FileNameInfo = 2; +pub const FileRenameInfo = 3; +pub const FileDispositionInfo = 4; +pub const FileAllocationInfo = 5; +pub const FileEndOfFileInfo = 6; +pub const FileStreamInfo = 7; +pub const FileCompressionInfo = 8; +pub const FileAttributeTagInfo = 9; +pub const FileIdBothDirectoryInfo = 10; +pub const FileIdBothDirectoryRestartInfo = 11; +pub const FileIoPriorityHintInfo = 12; +pub const FileRemoteProtocolInfo = 13; +pub const FileFullDirectoryInfo = 14; +pub const FileFullDirectoryRestartInfo = 15; +pub const FileStorageInfo = 16; +pub const FileAlignmentInfo = 17; +pub const FileIdInfo = 18; +pub const FileIdExtdDirectoryInfo = 19; +pub const FileIdExtdDirectoryRestartInfo = 20; + +pub const FILE_NAME_INFO = extern struct { + FileNameLength: DWORD, + FileName: [1]WCHAR, +}; + +/// Return the normalized drive name. This is the default. +pub const FILE_NAME_NORMALIZED = 0x0; + +/// Return the opened file name (not normalized). +pub const FILE_NAME_OPENED = 0x8; + +/// Return the path with the drive letter. This is the default. +pub const VOLUME_NAME_DOS = 0x0; + +/// Return the path with a volume GUID path instead of the drive name. +pub const VOLUME_NAME_GUID = 0x1; + +/// Return the path with no drive information. +pub const VOLUME_NAME_NONE = 0x4; + +/// Return the path with the volume device path. +pub const VOLUME_NAME_NT = 0x2; + +pub const SECURITY_ATTRIBUTES = extern struct { + nLength: DWORD, + lpSecurityDescriptor: ?*c_void, + bInheritHandle: BOOL, +}; +pub const PSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; +pub const LPSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; + +pub const GENERIC_READ = 0x80000000; +pub const GENERIC_WRITE = 0x40000000; +pub const GENERIC_EXECUTE = 0x20000000; +pub const GENERIC_ALL = 0x10000000; + +pub const FILE_SHARE_DELETE = 0x00000004; +pub const FILE_SHARE_READ = 0x00000001; +pub const FILE_SHARE_WRITE = 0x00000002; + +pub const CREATE_ALWAYS = 2; +pub const CREATE_NEW = 1; +pub const OPEN_ALWAYS = 4; +pub const OPEN_EXISTING = 3; +pub const TRUNCATE_EXISTING = 5; + +pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; +pub const FILE_ATTRIBUTE_COMPRESSED = 0x800; +pub const FILE_ATTRIBUTE_DEVICE = 0x40; +pub const FILE_ATTRIBUTE_DIRECTORY = 0x10; +pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; +pub const FILE_ATTRIBUTE_HIDDEN = 0x2; +pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000; +pub const FILE_ATTRIBUTE_NORMAL = 0x80; +pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000; +pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000; +pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; +pub const FILE_ATTRIBUTE_READONLY = 0x1; +pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000; +pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000; +pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400; +pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200; +pub const FILE_ATTRIBUTE_SYSTEM = 0x4; +pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; +pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; + +pub const PROCESS_INFORMATION = extern struct { + hProcess: HANDLE, + hThread: HANDLE, + dwProcessId: DWORD, + dwThreadId: DWORD, +}; + +pub const STARTUPINFOW = extern struct { + cb: DWORD, + lpReserved: ?LPWSTR, + lpDesktop: ?LPWSTR, + lpTitle: ?LPWSTR, + dwX: DWORD, + dwY: DWORD, + dwXSize: DWORD, + dwYSize: DWORD, + dwXCountChars: DWORD, + dwYCountChars: DWORD, + dwFillAttribute: DWORD, + dwFlags: DWORD, + wShowWindow: WORD, + cbReserved2: WORD, + lpReserved2: ?LPBYTE, + hStdInput: ?HANDLE, + hStdOutput: ?HANDLE, + hStdError: ?HANDLE, +}; + +pub const STARTF_FORCEONFEEDBACK = 0x00000040; +pub const STARTF_FORCEOFFFEEDBACK = 0x00000080; +pub const STARTF_PREVENTPINNING = 0x00002000; +pub const STARTF_RUNFULLSCREEN = 0x00000020; +pub const STARTF_TITLEISAPPID = 0x00001000; +pub const STARTF_TITLEISLINKNAME = 0x00000800; +pub const STARTF_UNTRUSTEDSOURCE = 0x00008000; +pub const STARTF_USECOUNTCHARS = 0x00000008; +pub const STARTF_USEFILLATTRIBUTE = 0x00000010; +pub const STARTF_USEHOTKEY = 0x00000200; +pub const STARTF_USEPOSITION = 0x00000004; +pub const STARTF_USESHOWWINDOW = 0x00000001; +pub const STARTF_USESIZE = 0x00000002; +pub const STARTF_USESTDHANDLES = 0x00000100; + +pub const INFINITE = 4294967295; + +pub const WAIT_ABANDONED = 0x00000080; +pub const WAIT_OBJECT_0 = 0x00000000; +pub const WAIT_TIMEOUT = 0x00000102; +pub const WAIT_FAILED = 0xFFFFFFFF; + +pub const HANDLE_FLAG_INHERIT = 0x00000001; +pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002; + +pub const MOVEFILE_COPY_ALLOWED = 2; +pub const MOVEFILE_CREATE_HARDLINK = 16; +pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4; +pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32; +pub const MOVEFILE_REPLACE_EXISTING = 1; +pub const MOVEFILE_WRITE_THROUGH = 8; + +pub const FILE_BEGIN = 0; +pub const FILE_CURRENT = 1; +pub const FILE_END = 2; + +pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; +pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; +pub const HEAP_NO_SERIALIZE = 0x00000001; + +// AllocationType values +pub const MEM_COMMIT = 0x1000; +pub const MEM_RESERVE = 0x2000; +pub const MEM_RESET = 0x80000; +pub const MEM_RESET_UNDO = 0x1000000; +pub const MEM_LARGE_PAGES = 0x20000000; +pub const MEM_PHYSICAL = 0x400000; +pub const MEM_TOP_DOWN = 0x100000; +pub const MEM_WRITE_WATCH = 0x200000; + +// Protect values +pub const PAGE_EXECUTE = 0x10; +pub const PAGE_EXECUTE_READ = 0x20; +pub const PAGE_EXECUTE_READWRITE = 0x40; +pub const PAGE_EXECUTE_WRITECOPY = 0x80; +pub const PAGE_NOACCESS = 0x01; +pub const PAGE_READONLY = 0x02; +pub const PAGE_READWRITE = 0x04; +pub const PAGE_WRITECOPY = 0x08; +pub const PAGE_TARGETS_INVALID = 0x40000000; +pub const PAGE_TARGETS_NO_UPDATE = 0x40000000; // Same as PAGE_TARGETS_INVALID +pub const PAGE_GUARD = 0x100; +pub const PAGE_NOCACHE = 0x200; +pub const PAGE_WRITECOMBINE = 0x400; + +// FreeType values +pub const MEM_COALESCE_PLACEHOLDERS = 0x1; +pub const MEM_RESERVE_PLACEHOLDERS = 0x2; +pub const MEM_DECOMMIT = 0x4000; +pub const MEM_RELEASE = 0x8000; + +pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; +pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; + +pub const WIN32_FIND_DATAW = extern struct { + dwFileAttributes: DWORD, + ftCreationTime: FILETIME, + ftLastAccessTime: FILETIME, + ftLastWriteTime: FILETIME, + nFileSizeHigh: DWORD, + nFileSizeLow: DWORD, + dwReserved0: DWORD, + dwReserved1: DWORD, + cFileName: [260]u16, + cAlternateFileName: [14]u16, +}; + +pub const FILETIME = extern struct { + dwLowDateTime: DWORD, + dwHighDateTime: DWORD, +}; + +pub const SYSTEM_INFO = extern struct { + anon1: extern union { + dwOemId: DWORD, + anon2: extern struct { + wProcessorArchitecture: WORD, + wReserved: WORD, + }, + }, + dwPageSize: DWORD, + lpMinimumApplicationAddress: LPVOID, + lpMaximumApplicationAddress: LPVOID, + dwActiveProcessorMask: DWORD_PTR, + dwNumberOfProcessors: DWORD, + dwProcessorType: DWORD, + dwAllocationGranularity: DWORD, + wProcessorLevel: WORD, + wProcessorRevision: WORD, +}; + +pub const HRESULT = c_long; + +pub const KNOWNFOLDERID = GUID; +pub const GUID = extern struct { + Data1: c_ulong, + Data2: c_ushort, + Data3: c_ushort, + Data4: [8]u8, + + pub fn parse(str: []const u8) GUID { + var guid: GUID = undefined; + var index: usize = 0; + assert(str[index] == '{'); + index += 1; + + guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable; + index += 8; + + assert(str[index] == '-'); + index += 1; + + guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; + index += 4; + + assert(str[index] == '-'); + index += 1; + + guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; + index += 4; + + assert(str[index] == '-'); + index += 1; + + guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; + index += 2; + guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; + index += 2; + + assert(str[index] == '-'); + index += 1; + + var i: usize = 2; + while (i < guid.Data4.len) : (i += 1) { + guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; + index += 2; + } + + assert(str[index] == '}'); + index += 1; + return guid; + } +}; + +pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"); + +pub const KF_FLAG_DEFAULT = 0; +pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536; +pub const KF_FLAG_CREATE = 32768; +pub const KF_FLAG_DONT_VERIFY = 16384; +pub const KF_FLAG_DONT_UNEXPAND = 8192; +pub const KF_FLAG_NO_ALIAS = 4096; +pub const KF_FLAG_INIT = 2048; +pub const KF_FLAG_DEFAULT_PATH = 1024; +pub const KF_FLAG_NOT_PARENT_RELATIVE = 512; +pub const KF_FLAG_SIMPLE_IDLIST = 256; +pub const KF_FLAG_ALIAS_ONLY = -2147483648; + +pub const S_OK = 0; +pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001)); +pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002)); +pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003)); +pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004)); +pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005)); +pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF)); +pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); +pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); +pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); +pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); + +pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; +pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; +pub const FILE_FLAG_NO_BUFFERING = 0x20000000; +pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000; +pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; +pub const FILE_FLAG_OVERLAPPED = 0x40000000; +pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000; +pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; +pub const FILE_FLAG_SESSION_AWARE = 0x00800000; +pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; +pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; + +pub const SMALL_RECT = extern struct { + Left: SHORT, + Top: SHORT, + Right: SHORT, + Bottom: SHORT, +}; + +pub const COORD = extern struct { + X: SHORT, + Y: SHORT, +}; + +pub const CREATE_UNICODE_ENVIRONMENT = 1024; + +pub const TLS_OUT_OF_INDEXES = 4294967295; +pub const IMAGE_TLS_DIRECTORY = extern struct { + StartAddressOfRawData: usize, + EndAddressOfRawData: usize, + AddressOfIndex: usize, + AddressOfCallBacks: usize, + SizeOfZeroFill: u32, + Characteristics: u32, +}; +pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; +pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; + +pub const PIMAGE_TLS_CALLBACK = ?extern fn (PVOID, DWORD, PVOID) void; + +pub const PROV_RSA_FULL = 1; + +pub const REGSAM = ACCESS_MASK; +pub const ACCESS_MASK = DWORD; +pub const PHKEY = *HKEY; +pub const HKEY = *HKEY__; +pub const HKEY__ = extern struct { + unused: c_int, +}; +pub const LSTATUS = LONG; + +pub const FILE_NOTIFY_INFORMATION = extern struct { + NextEntryOffset: DWORD, + Action: DWORD, + FileNameLength: DWORD, + FileName: [1]WCHAR, +}; + +pub const FILE_ACTION_ADDED = 0x00000001; +pub const FILE_ACTION_REMOVED = 0x00000002; +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 FILE_LIST_DIRECTORY = 1; + +pub const FILE_NOTIFY_CHANGE_CREATION = 64; +pub const FILE_NOTIFY_CHANGE_SIZE = 8; +pub const FILE_NOTIFY_CHANGE_SECURITY = 256; +pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32; +pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16; +pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2; +pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1; +pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; + +pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct { + dwSize: COORD, + dwCursorPosition: COORD, + wAttributes: WORD, + srWindow: SMALL_RECT, + dwMaximumWindowSize: COORD, +}; + +pub const FOREGROUND_BLUE = 1; +pub const FOREGROUND_GREEN = 2; +pub const FOREGROUND_RED = 4; +pub const FOREGROUND_INTENSITY = 8; + +pub const LIST_ENTRY = extern struct { + Flink: *LIST_ENTRY, + Blink: *LIST_ENTRY, +}; + +pub const RTL_CRITICAL_SECTION_DEBUG = extern struct { + Type: WORD, + CreatorBackTraceIndex: WORD, + CriticalSection: *RTL_CRITICAL_SECTION, + ProcessLocksList: LIST_ENTRY, + EntryCount: DWORD, + ContentionCount: DWORD, + Flags: DWORD, + CreatorBackTraceIndexHigh: WORD, + SpareWORD: WORD, +}; + +pub const RTL_CRITICAL_SECTION = extern struct { + DebugInfo: *RTL_CRITICAL_SECTION_DEBUG, + LockCount: LONG, + RecursionCount: LONG, + OwningThread: HANDLE, + LockSemaphore: HANDLE, + SpinCount: ULONG_PTR, +}; + +pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; +pub const INIT_ONCE = RTL_RUN_ONCE; +pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; +pub const INIT_ONCE_FN = extern fn (InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; + +pub const RTL_RUN_ONCE = extern struct { + Ptr: ?*c_void, +}; + +pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null }; + +pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED; +pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED; +pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE; +pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY; +pub const COINIT = extern enum { + COINIT_APARTMENTTHREADED = 2, + COINIT_MULTITHREADED = 0, + COINIT_DISABLE_OLE1DDE = 4, + COINIT_SPEED_OVER_MEMORY = 8, +}; + +/// > The maximum path of 32,767 characters is approximate, because the "\\?\" +/// > prefix may be expanded to a longer string by the system at run time, and +/// > this expansion applies to the total length. +/// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation +pub const PATH_MAX_WIDE = 32767; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 7e99f7eda9..2bd1824620 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -1,4 +1,4 @@ -use @import("../windows.zig"); +use @import("bits.zig"); pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL; @@ -189,86 +189,9 @@ pub extern "kernel32" stdcallcc fn GetProcAddress(hModule: HMODULE, lpProcName: pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; -pub const FILE_NOTIFY_INFORMATION = extern struct { - NextEntryOffset: DWORD, - Action: DWORD, - FileNameLength: DWORD, - FileName: [1]WCHAR, -}; - -pub const FILE_ACTION_ADDED = 0x00000001; -pub const FILE_ACTION_REMOVED = 0x00000002; -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 FILE_LIST_DIRECTORY = 1; - -pub const FILE_NOTIFY_CHANGE_CREATION = 64; -pub const FILE_NOTIFY_CHANGE_SIZE = 8; -pub const FILE_NOTIFY_CHANGE_SECURITY = 256; -pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32; -pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16; -pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2; -pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1; -pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; - -pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct { - dwSize: COORD, - dwCursorPosition: COORD, - wAttributes: WORD, - srWindow: SMALL_RECT, - dwMaximumWindowSize: COORD, -}; - -pub const FOREGROUND_BLUE = 1; -pub const FOREGROUND_GREEN = 2; -pub const FOREGROUND_RED = 4; -pub const FOREGROUND_INTENSITY = 8; - pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub const LIST_ENTRY = extern struct { - Flink: *LIST_ENTRY, - Blink: *LIST_ENTRY, -}; - -pub const RTL_CRITICAL_SECTION_DEBUG = extern struct { - Type: WORD, - CreatorBackTraceIndex: WORD, - CriticalSection: *RTL_CRITICAL_SECTION, - ProcessLocksList: LIST_ENTRY, - EntryCount: DWORD, - ContentionCount: DWORD, - Flags: DWORD, - CreatorBackTraceIndexHigh: WORD, - SpareWORD: WORD, -}; - -pub const RTL_CRITICAL_SECTION = extern struct { - DebugInfo: *RTL_CRITICAL_SECTION_DEBUG, - LockCount: LONG, - RecursionCount: LONG, - OwningThread: HANDLE, - LockSemaphore: HANDLE, - SpinCount: ULONG_PTR, -}; - -pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; -pub const INIT_ONCE = RTL_RUN_ONCE; -pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; - pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; - -pub const INIT_ONCE_FN = extern fn (InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; - -pub const RTL_RUN_ONCE = extern struct { - Ptr: ?*c_void, -}; - -pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null }; diff --git a/std/os/windows/ntdll.zig b/std/os/windows/ntdll.zig index 1b63c1e7ec..60c03ffc07 100644 --- a/std/os/windows/ntdll.zig +++ b/std/os/windows/ntdll.zig @@ -1,3 +1,3 @@ -use @import("../windows.zig"); +use @import("bits.zig"); pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD; diff --git a/std/os/windows/ole32.zig b/std/os/windows/ole32.zig index 1ecfecba3c..80f5bebc36 100644 --- a/std/os/windows/ole32.zig +++ b/std/os/windows/ole32.zig @@ -1,17 +1,6 @@ -use @import("../windows.zig"); +use @import("bits.zig"); pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; pub extern "ole32" stdcallcc fn CoUninitialize() void; pub extern "ole32" stdcallcc fn CoGetCurrentProcess() DWORD; pub extern "ole32" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT; - -pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED; -pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED; -pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE; -pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY; -pub const COINIT = extern enum { - COINIT_APARTMENTTHREADED = 2, - COINIT_MULTITHREADED = 0, - COINIT_DISABLE_OLE1DDE = 4, - COINIT_SPEED_OVER_MEMORY = 8, -}; diff --git a/std/os/windows/shell32.zig b/std/os/windows/shell32.zig index 84accecb70..9f24acc5c4 100644 --- a/std/os/windows/shell32.zig +++ b/std/os/windows/shell32.zig @@ -1,3 +1,3 @@ -use @import("../windows.zig"); +use @import("bits.zig"); pub extern "shell32" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig deleted file mode 100644 index 72c84502e3..0000000000 --- a/std/os/windows/util.zig +++ /dev/null @@ -1,316 +0,0 @@ -const std = @import("../../std.zig"); -const builtin = @import("builtin"); -const os = std.os; -const unicode = std.unicode; -const windows = std.os.windows; -const assert = std.debug.assert; -const mem = std.mem; -const BufMap = std.BufMap; -const cstr = std.cstr; - -// > The maximum path of 32,767 characters is approximate, because the "\\?\" -// > prefix may be expanded to a longer string by the system at run time, and -// > this expansion applies to the total length. -// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation -pub const PATH_MAX_WIDE = 32767; - -pub const WaitError = error{ - WaitAbandoned, - WaitTimeOut, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitError!void { - const result = windows.WaitForSingleObject(handle, milliseconds); - return switch (result) { - windows.WAIT_ABANDONED => error.WaitAbandoned, - windows.WAIT_OBJECT_0 => {}, - windows.WAIT_TIMEOUT => error.WaitTimeOut, - windows.WAIT_FAILED => x: { - const err = windows.GetLastError(); - break :x switch (err) { - else => os.unexpectedErrorWindows(err), - }; - }, - else => error.Unexpected, - }; -} - -pub fn windowsClose(handle: windows.HANDLE) void { - assert(windows.CloseHandle(handle) != 0); -} - -pub const ReadError = error{ - OperationAborted, - BrokenPipe, - Unexpected, -}; - -pub const WriteError = error{ - SystemResources, - OperationAborted, - BrokenPipe, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - var bytes_written: windows.DWORD = undefined; - if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), &bytes_written, null) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, - windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources, - windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted, - windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources, - windows.ERROR.IO_PENDING => unreachable, - windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe, - else => os.unexpectedErrorWindows(err), - }; - } -} - -pub fn windowsIsTty(handle: windows.HANDLE) bool { - if (windowsIsCygwinPty(handle)) - return true; - - var out: windows.DWORD = undefined; - return windows.GetConsoleMode(handle, &out) != 0; -} - -pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { - const size = @sizeOf(windows.FILE_NAME_INFO); - var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH); - - if (windows.GetFileInformationByHandleEx( - handle, - windows.FileNameInfo, - @ptrCast(*c_void, &name_info_bytes[0]), - @intCast(u32, name_info_bytes.len), - ) == 0) { - return false; - } - - const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); - const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; - const name_wide = @bytesToSlice(u16, name_bytes); - return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or - mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null; -} - -pub const OpenError = error{ - SharingViolation, - PathAlreadyExists, - - /// When any of the path components can not be found or the file component can not - /// be found. Some operating systems distinguish between path components not found and - /// file components not found, but they are collapsed into FileNotFound to gain - /// consistency across operating systems. - FileNotFound, - - AccessDenied, - PipeBusy, - NameTooLong, - - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, - - /// On Windows, file paths cannot contain these characters: - /// '/', '*', '?', '"', '<', '>', '|' - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn windowsOpenW( - file_path_w: [*]const u16, - desired_access: windows.DWORD, - share_mode: windows.DWORD, - creation_disposition: windows.DWORD, - flags_and_attrs: windows.DWORD, -) OpenError!windows.HANDLE { - const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); - - if (result == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.SHARING_VIOLATION => return OpenError.SharingViolation, - windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists, - windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists, - windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return OpenError.FileNotFound, - windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied, - windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy, - else => return os.unexpectedErrorWindows(err), - } - } - - return result; -} - -pub fn windowsOpen( - 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 file_path_w = try sliceToPrefixedFileW(file_path); - return windowsOpenW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs); -} - -/// Caller must free result. -pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 { - // count bytes needed - const max_chars_needed = x: { - var max_chars_needed: usize = 4; // 4 for the final 4 null bytes - var it = env_map.iterator(); - while (it.next()) |pair| { - // +1 for '=' - // +1 for null byte - max_chars_needed += pair.key.len + pair.value.len + 2; - } - break :x max_chars_needed; - }; - const result = try allocator.alloc(u16, max_chars_needed); - errdefer allocator.free(result); - - var it = env_map.iterator(); - var i: usize = 0; - while (it.next()) |pair| { - i += try unicode.utf8ToUtf16Le(result[i..], pair.key); - result[i] = '='; - i += 1; - i += try unicode.utf8ToUtf16Le(result[i..], pair.value); - result[i] = 0; - i += 1; - } - result[i] = 0; - i += 1; - result[i] = 0; - i += 1; - result[i] = 0; - i += 1; - result[i] = 0; - i += 1; - return allocator.shrink(result, i); -} - -pub fn windowsFindFirstFile( - dir_path: []const u8, - find_file_data: *windows.WIN32_FIND_DATAW, -) !windows.HANDLE { - const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{ '\\', '*', 0 }); - const handle = windows.FindFirstFileW(&dir_path_w, find_file_data); - - if (handle == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - else => return os.unexpectedErrorWindows(err), - } - } - - return handle; -} - -/// Returns `true` if there was another file, `false` otherwise. -pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool { - if (windows.FindNextFileW(handle, find_file_data) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.NO_MORE_FILES => false, - else => os.unexpectedErrorWindows(err), - }; - } - return true; -} - -pub const WindowsCreateIoCompletionPortError = error{Unexpected}; - -pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE { - const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.INVALID_PARAMETER => unreachable, - else => return os.unexpectedErrorWindows(err), - } - }; - return handle; -} - -pub const WindowsPostQueuedCompletionStatusError = error{Unexpected}; - -pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void { - if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return os.unexpectedErrorWindows(err), - } - } -} - -pub const WindowsWaitResult = enum { - Normal, - Aborted, - Cancelled, - EOF, -}; - -pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult { - if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted, - windows.ERROR.OPERATION_ABORTED => return WindowsWaitResult.Cancelled, - windows.ERROR.HANDLE_EOF => return WindowsWaitResult.EOF, - else => { - if (std.debug.runtime_safety) { - std.debug.panic("unexpected error: {}\n", err); - } - }, - } - } - return WindowsWaitResult.Normal; -} - -pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { - return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); -} - -pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { - return sliceToPrefixedSuffixedFileW(s, []u16{0}); -} - -pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { - // TODO well defined copy elision - var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; - - // > File I/O functions in the Windows API convert "/" to "\" as part of - // > converting the name to an NT-style name, except when using the "\\?\" - // > prefix as detailed in the following sections. - // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation - // Because we want the larger maximum path length for absolute paths, we - // disallow forward slashes in zig std lib file functions on Windows. - for (s) |byte| { - switch (byte) { - '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, - else => {}, - } - } - const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { - const prefix = []u16{ '\\', '\\', '?', '\\' }; - mem.copy(u16, result[0..], prefix); - break :blk prefix.len; - }; - const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); - assert(end_index <= result.len); - if (end_index + suffix.len > result.len) return error.NameTooLong; - mem.copy(u16, result[end_index..], suffix); - return result; -} diff --git a/std/os/zen.zig b/std/os/zen.zig index 0564956b24..8d2f963486 100644 --- a/std/os/zen.zig +++ b/std/os/zen.zig @@ -80,7 +80,7 @@ pub const STDOUT_FILENO = 1; pub const STDERR_FILENO = 2; // FIXME: let's borrow Linux's error numbers for now. -use @import("linux/errno.zig"); +use @import("bits/linux/errno.zig"); // Get the errno from a syscall return value, or 0 for no error. pub fn getErrno(r: usize) usize { const signed_r = @bitCast(isize, r); diff --git a/std/packed_int_array.zig b/std/packed_int_array.zig index 065f1becd8..35cffec218 100644 --- a/std/packed_int_array.zig +++ b/std/packed_int_array.zig @@ -619,7 +619,7 @@ test "PackedIntArray at end of available memory" { const PackedArray = PackedIntArray(u3, 8); const Padded = struct { - _: [std.os.page_size - @sizeOf(PackedArray)]u8, + _: [std.mem.page_size - @sizeOf(PackedArray)]u8, p: PackedArray, }; @@ -641,9 +641,9 @@ test "PackedIntSlice at end of available memory" { var da = std.heap.DirectAllocator.init(); const allocator = &da.allocator; - var page = try allocator.alloc(u8, std.os.page_size); + var page = try allocator.alloc(u8, std.mem.page_size); defer allocator.free(page); - var p = PackedSlice.init(page[std.os.page_size - 2 ..], 1); + var p = PackedSlice.init(page[std.mem.page_size - 2 ..], 1); p.set(0, std.math.maxInt(u11)); } diff --git a/std/pdb.zig b/std/pdb.zig index f3b73663e8..ffe2120296 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -6,6 +6,7 @@ const mem = std.mem; const os = std.os; const warn = std.debug.warn; const coff = std.coff; +const File = std.fs.File; const ArrayList = std.ArrayList; @@ -459,7 +460,7 @@ pub const PDBStringTableHeader = packed struct { }; pub const Pdb = struct { - in_file: os.File, + in_file: File, allocator: *mem.Allocator, coff: *coff.Coff, string_table: *MsfStream, @@ -468,7 +469,7 @@ pub const Pdb = struct { msf: Msf, pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void { - self.in_file = try os.File.openRead(file_name); + self.in_file = try File.openRead(file_name); self.allocator = coff_ptr.allocator; self.coff = coff_ptr; @@ -492,7 +493,7 @@ const Msf = struct { directory: MsfStream, streams: []MsfStream, - fn openFile(self: *Msf, allocator: *mem.Allocator, file: os.File) !void { + fn openFile(self: *Msf, allocator: *mem.Allocator, file: File) !void { var file_stream = file.inStream(); const in = &file_stream.stream; @@ -587,7 +588,7 @@ const SuperBlock = packed struct { }; const MsfStream = struct { - in_file: os.File, + in_file: File, pos: u64, blocks: []u32, block_size: u32, @@ -598,7 +599,7 @@ const MsfStream = struct { pub const Error = @typeOf(read).ReturnType.ErrorSet; pub const Stream = io.InStream(Error); - fn init(block_size: u32, block_count: u32, pos: u64, file: os.File, allocator: *mem.Allocator) !MsfStream { + fn init(block_size: u32, block_count: u32, pos: u64, file: File, allocator: *mem.Allocator) !MsfStream { var stream = MsfStream{ .in_file = file, .pos = 0, @@ -660,9 +661,8 @@ const MsfStream = struct { return size; } - // XXX: The `len` parameter should be signed - fn seekForward(self: *MsfStream, len: u64) !void { - self.pos += len; + fn seekBy(self: *MsfStream, len: i64) !void { + self.pos = @intCast(u64, @intCast(i64, self.pos) + len); if (self.pos >= self.blocks.len * self.block_size) return error.EOF; } diff --git a/std/process.zig b/std/process.zig new file mode 100644 index 0000000000..7c5e7fbb07 --- /dev/null +++ b/std/process.zig @@ -0,0 +1,617 @@ +const builtin = @import("builtin"); +const std = @import("std.zig"); +const os = std.os; +const fs = std.fs; +const BufMap = std.BufMap; +const Buffer = std.Buffer; +const mem = std.mem; +const math = std.math; +const Allocator = mem.Allocator; +const assert = std.debug.assert; +const testing = std.testing; + +pub const abort = os.abort; +pub const exit = os.exit; +pub const changeCurDir = os.chdir; +pub const changeCurDirC = os.chdirC; + +/// The result is a slice of `out_buffer`, from index `0`. +pub fn getCwd(out_buffer: *[fs.MAX_PATH_BYTES]u8) ![]u8 { + return os.getcwd(out_buffer); +} + +/// Caller must free the returned memory. +pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { + var buf: [fs.MAX_PATH_BYTES]u8 = undefined; + return mem.dupe(allocator, u8, try os.getcwd(&buf)); +} + +test "getCwdAlloc" { + // at least call it so it gets compiled + var buf: [1000]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(&buf).allocator; + _ = getCwdAlloc(allocator) catch undefined; +} + +/// Caller must free result when done. +/// TODO make this go through libc when we have it +pub fn getEnvMap(allocator: *Allocator) !BufMap { + var result = BufMap.init(allocator); + errdefer result.deinit(); + + if (os.windows.is_the_target) { + const ptr = try os.windows.GetEnvironmentStringsW(); + defer os.windows.FreeEnvironmentStringsW(ptr); + + var i: usize = 0; + while (true) { + if (ptr[i] == 0) return result; + + const key_start = i; + + while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} + const key_w = ptr[key_start..i]; + const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); + errdefer allocator.free(key); + + if (ptr[i] == '=') i += 1; + + const value_start = i; + while (ptr[i] != 0) : (i += 1) {} + const value_w = ptr[value_start..i]; + const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); + errdefer allocator.free(value); + + i += 1; // skip over null byte + + try result.setMove(key, value); + } + } else if (builtin.os == .wasi) { + var environ_count: usize = undefined; + var environ_buf_size: usize = undefined; + + const environ_sizes_get_ret = os.wasi.environ_sizes_get(&environ_count, &environ_buf_size); + if (environ_sizes_get_ret != os.wasi.ESUCCESS) { + return os.unexpectedErrno(environ_sizes_get_ret); + } + + // TODO: Verify that the documentation is incorrect + // https://github.com/WebAssembly/WASI/issues/27 + var environ = try allocator.alloc(?[*]u8, environ_count + 1); + defer allocator.free(environ); + var environ_buf = try std.heap.wasm_allocator.alloc(u8, environ_buf_size); + defer allocator.free(environ_buf); + + const environ_get_ret = os.wasi.environ_get(environ.ptr, environ_buf.ptr); + if (environ_get_ret != os.wasi.ESUCCESS) { + return os.unexpectedErrno(environ_get_ret); + } + + for (environ) |env| { + if (env) |ptr| { + const pair = mem.toSlice(u8, ptr); + var parts = mem.separate(pair, "="); + const key = parts.next().?; + const value = parts.next().?; + try result.set(key, value); + } + } + return result; + } else { + for (os.environ) |ptr| { + var line_i: usize = 0; + while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} + const key = ptr[0..line_i]; + + var end_i: usize = line_i; + while (ptr[end_i] != 0) : (end_i += 1) {} + const value = ptr[line_i + 1 .. end_i]; + + try result.set(key, value); + } + return result; + } +} + +test "os.getEnvMap" { + var env = try getEnvMap(std.debug.global_allocator); + defer env.deinit(); +} + +pub const GetEnvVarOwnedError = error{ + OutOfMemory, + EnvironmentVariableNotFound, + + /// See https://github.com/ziglang/zig/issues/1774 + InvalidUtf8, +}; + +/// Caller must free returned memory. +/// TODO make this go through libc when we have it +pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { + if (os.windows.is_the_target) { + const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); + defer allocator.free(key_with_null); + + var buf = try allocator.alloc(u16, 256); + defer allocator.free(buf); + + while (true) { + const windows_buf_len = math.cast(os.windows.DWORD, buf.len) catch return error.OutOfMemory; + const result = os.windows.GetEnvironmentVariableW( + key_with_null.ptr, + buf.ptr, + windows_buf_len, + ) catch |err| switch (err) { + error.Unexpected => return error.EnvironmentVariableNotFound, + else => |e| return e, + }; + + if (result > buf.len) { + buf = try allocator.realloc(buf, result); + continue; + } + + return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) { + error.DanglingSurrogateHalf => return error.InvalidUtf8, + error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, + error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, + else => |e| return e, + }; + } + } else { + const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound; + return mem.dupe(allocator, u8, result); + } +} + +test "os.getEnvVarOwned" { + var ga = std.debug.global_allocator; + testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); +} + +pub const ArgIteratorPosix = struct { + index: usize, + count: usize, + + pub fn init() ArgIteratorPosix { + return ArgIteratorPosix{ + .index = 0, + .count = os.argv.len, + }; + } + + pub fn next(self: *ArgIteratorPosix) ?[]const u8 { + if (self.index == self.count) return null; + + const s = os.argv[self.index]; + self.index += 1; + return mem.toSlice(u8, s); + } + + pub fn skip(self: *ArgIteratorPosix) bool { + if (self.index == self.count) return false; + + self.index += 1; + return true; + } +}; + +pub const ArgIteratorWindows = struct { + index: usize, + cmd_line: [*]const u8, + in_quote: bool, + quote_count: usize, + seen_quote_count: usize, + + pub const NextError = error{OutOfMemory}; + + pub fn init() ArgIteratorWindows { + return initWithCmdLine(os.windows.kernel32.GetCommandLineA()); + } + + pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows { + return ArgIteratorWindows{ + .index = 0, + .cmd_line = cmd_line, + .in_quote = false, + .quote_count = countQuotes(cmd_line), + .seen_quote_count = 0, + }; + } + + /// You must free the returned memory when done. + pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) { + // march forward over whitespace + while (true) : (self.index += 1) { + const byte = self.cmd_line[self.index]; + switch (byte) { + 0 => return null, + ' ', '\t' => continue, + else => break, + } + } + + return self.internalNext(allocator); + } + + pub fn skip(self: *ArgIteratorWindows) bool { + // march forward over whitespace + while (true) : (self.index += 1) { + const byte = self.cmd_line[self.index]; + switch (byte) { + 0 => return false, + ' ', '\t' => continue, + else => break, + } + } + + var backslash_count: usize = 0; + while (true) : (self.index += 1) { + const byte = self.cmd_line[self.index]; + switch (byte) { + 0 => return true, + '"' => { + const quote_is_real = backslash_count % 2 == 0; + if (quote_is_real) { + self.seen_quote_count += 1; + } + }, + '\\' => { + backslash_count += 1; + }, + ' ', '\t' => { + if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { + return true; + } + backslash_count = 0; + }, + else => { + backslash_count = 0; + continue; + }, + } + } + } + + fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 { + var buf = try Buffer.initSize(allocator, 0); + defer buf.deinit(); + + var backslash_count: usize = 0; + while (true) : (self.index += 1) { + const byte = self.cmd_line[self.index]; + switch (byte) { + 0 => return buf.toOwnedSlice(), + '"' => { + const quote_is_real = backslash_count % 2 == 0; + try self.emitBackslashes(&buf, backslash_count / 2); + backslash_count = 0; + + if (quote_is_real) { + self.seen_quote_count += 1; + if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) { + try buf.appendByte('"'); + } + } else { + try buf.appendByte('"'); + } + }, + '\\' => { + backslash_count += 1; + }, + ' ', '\t' => { + try self.emitBackslashes(&buf, backslash_count); + backslash_count = 0; + if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { + try buf.appendByte(byte); + } else { + return buf.toOwnedSlice(); + } + }, + else => { + try self.emitBackslashes(&buf, backslash_count); + backslash_count = 0; + try buf.appendByte(byte); + }, + } + } + } + + fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void { + var i: usize = 0; + while (i < emit_count) : (i += 1) { + try buf.appendByte('\\'); + } + } + + fn countQuotes(cmd_line: [*]const u8) usize { + var result: usize = 0; + var backslash_count: usize = 0; + var index: usize = 0; + while (true) : (index += 1) { + const byte = cmd_line[index]; + switch (byte) { + 0 => return result, + '\\' => backslash_count += 1, + '"' => { + result += 1 - (backslash_count % 2); + backslash_count = 0; + }, + else => { + backslash_count = 0; + }, + } + } + } +}; + +pub const ArgIterator = struct { + const InnerType = if (builtin.os == .windows) ArgIteratorWindows else ArgIteratorPosix; + + inner: InnerType, + + pub fn init() ArgIterator { + if (builtin.os == .wasi) { + // TODO: Figure out a compatible interface accomodating WASI + @compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead."); + } + + return ArgIterator{ .inner = InnerType.init() }; + } + + pub const NextError = ArgIteratorWindows.NextError; + + /// You must free the returned memory when done. + pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { + if (builtin.os == .windows) { + return self.inner.next(allocator); + } else { + return mem.dupe(allocator, u8, self.inner.next() orelse return null); + } + } + + /// If you only are targeting posix you can call this and not need an allocator. + pub fn nextPosix(self: *ArgIterator) ?[]const u8 { + return self.inner.next(); + } + + /// Parse past 1 argument without capturing it. + /// Returns `true` if skipped an arg, `false` if we are at the end. + pub fn skip(self: *ArgIterator) bool { + return self.inner.skip(); + } +}; + +pub fn args() ArgIterator { + return ArgIterator.init(); +} + +/// Caller must call argsFree on result. +pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { + if (builtin.os == .wasi) { + var count: usize = undefined; + var buf_size: usize = undefined; + + const args_sizes_get_ret = os.wasi.args_sizes_get(&count, &buf_size); + if (args_sizes_get_ret != os.wasi.ESUCCESS) { + return os.unexpectedErrno(args_sizes_get_ret); + } + + var argv = try allocator.alloc([*]u8, count); + defer allocator.free(argv); + + var argv_buf = try allocator.alloc(u8, buf_size); + const args_get_ret = os.wasi.args_get(argv.ptr, argv_buf.ptr); + if (args_get_ret != os.wasi.ESUCCESS) { + return os.unexpectedErrno(args_get_ret); + } + + var result_slice = try allocator.alloc([]u8, count); + + var i: usize = 0; + while (i < count) : (i += 1) { + result_slice[i] = mem.toSlice(u8, argv[i]); + } + + return result_slice; + } + + // TODO refactor to only make 1 allocation. + var it = args(); + var contents = try Buffer.initSize(allocator, 0); + defer contents.deinit(); + + var slice_list = std.ArrayList(usize).init(allocator); + defer slice_list.deinit(); + + while (it.next(allocator)) |arg_or_err| { + const arg = try arg_or_err; + defer allocator.free(arg); + try contents.append(arg); + try slice_list.append(arg.len); + } + + const contents_slice = contents.toSliceConst(); + const slice_sizes = slice_list.toSliceConst(); + const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len); + const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len); + const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes); + errdefer allocator.free(buf); + + const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]); + const result_contents = buf[slice_list_bytes..]; + mem.copy(u8, result_contents, contents_slice); + + var contents_index: usize = 0; + for (slice_sizes) |len, i| { + const new_index = contents_index + len; + result_slice_list[i] = result_contents[contents_index..new_index]; + contents_index = new_index; + } + + return result_slice_list; +} + +pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { + if (builtin.os == .wasi) { + const last_item = args_alloc[args_alloc.len - 1]; + const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated + const first_item_ptr = args_alloc[0].ptr; + const len = last_byte_addr - @ptrToInt(first_item_ptr); + allocator.free(first_item_ptr[0..len]); + + return allocator.free(args_alloc); + } + + var total_bytes: usize = 0; + for (args_alloc) |arg| { + total_bytes += @sizeOf([]u8) + arg.len; + } + const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes]; + const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); + return allocator.free(aligned_allocated_buf); +} + +test "windows arg parsing" { + testWindowsCmdLine(c"a b\tc d", [][]const u8{ "a", "b", "c", "d" }); + testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" }); + testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" }); + testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" }); + testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" }); + testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" }); + + testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{ + ".\\..\\zig-cache\\build", + "bin\\zig.exe", + ".\\..", + ".\\..\\zig-cache", + "--help", + }); +} + +fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { + var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); + for (expected_args) |expected_arg| { + const arg = it.next(std.debug.global_allocator).? catch unreachable; + testing.expectEqualSlices(u8, expected_arg, arg); + } + testing.expect(it.next(std.debug.global_allocator) == null); +} + +pub const UserInfo = struct { + uid: u32, + gid: u32, +}; + +/// POSIX function which gets a uid from username. +pub fn getUserInfo(name: []const u8) !UserInfo { + return switch (builtin.os) { + .linux, .macosx, .watchos, .tvos, .ios, .freebsd, .netbsd => posixGetUserInfo(name), + else => @compileError("Unsupported OS"), + }; +} + +/// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else +/// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`. +pub fn posixGetUserInfo(name: []const u8) !UserInfo { + var in_stream = try io.InStream.open("/etc/passwd", null); + defer in_stream.close(); + + const State = enum { + Start, + WaitForNextLine, + SkipPassword, + ReadUserId, + ReadGroupId, + }; + + var buf: [std.mem.page_size]u8 = undefined; + var name_index: usize = 0; + var state = State.Start; + var uid: u32 = 0; + var gid: u32 = 0; + + while (true) { + const amt_read = try in_stream.read(buf[0..]); + for (buf[0..amt_read]) |byte| { + switch (state) { + .Start => switch (byte) { + ':' => { + state = if (name_index == name.len) State.SkipPassword else State.WaitForNextLine; + }, + '\n' => return error.CorruptPasswordFile, + else => { + if (name_index == name.len or name[name_index] != byte) { + state = .WaitForNextLine; + } + name_index += 1; + }, + }, + .WaitForNextLine => switch (byte) { + '\n' => { + name_index = 0; + state = .Start; + }, + else => continue, + }, + .SkipPassword => switch (byte) { + '\n' => return error.CorruptPasswordFile, + ':' => { + state = .ReadUserId; + }, + else => continue, + }, + .ReadUserId => switch (byte) { + ':' => { + state = .ReadGroupId; + }, + '\n' => return error.CorruptPasswordFile, + else => { + const digit = switch (byte) { + '0'...'9' => byte - '0', + else => return error.CorruptPasswordFile, + }; + if (@mulWithOverflow(u32, uid, 10, *uid)) return error.CorruptPasswordFile; + if (@addWithOverflow(u32, uid, digit, *uid)) return error.CorruptPasswordFile; + }, + }, + .ReadGroupId => switch (byte) { + '\n', ':' => { + return UserInfo{ + .uid = uid, + .gid = gid, + }; + }, + else => { + const digit = switch (byte) { + '0'...'9' => byte - '0', + else => return error.CorruptPasswordFile, + }; + if (@mulWithOverflow(u32, gid, 10, *gid)) return error.CorruptPasswordFile; + if (@addWithOverflow(u32, gid, digit, *gid)) return error.CorruptPasswordFile; + }, + }, + } + } + if (amt_read < buf.len) return error.UserNotFound; + } +} + +pub fn getBaseAddress() usize { + switch (builtin.os) { + .linux => { + const base = os.system.getauxval(std.elf.AT_BASE); + if (base != 0) { + return base; + } + const phdr = os.system.getauxval(std.elf.AT_PHDR); + return phdr - @sizeOf(std.elf.Ehdr); + }, + .macosx, .freebsd, .netbsd => { + return @ptrToInt(&std.c._mh_execute_header); + }, + .windows => return @ptrToInt(os.windows.kernel32.GetModuleHandleW(null)), + else => @compileError("Unsupported OS"), + } +} diff --git a/std/rand.zig b/std/rand.zig index 4a6563f65a..770ae1b0a8 100644 --- a/std/rand.zig +++ b/std/rand.zig @@ -1,10 +1,10 @@ -// The engines provided here should be initialized from an external source. For now, getRandomBytes -// from the os package is the most suitable. Be sure to use a CSPRNG when required, otherwise using +// The engines provided here should be initialized from an external source. For now, randomBytes +// from the crypto package is the most suitable. Be sure to use a CSPRNG when required, otherwise using // a normal PRNG will be faster and use substantially less stack space. // // ``` // var buf: [8]u8 = undefined; -// try std.os.getRandomBytes(buf[0..]); +// try std.crypto.randomBytes(buf[0..]); // const seed = mem.readIntSliceLittle(u64, buf[0..8]); // // var r = DefaultPrng.init(seed); diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index a619a74b0f..45ce23c00f 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -62,7 +62,7 @@ extern fn WinMainCRTStartup() noreturn { if (!builtin.single_threaded) { _ = @import("bootstrap_windows_tls.zig"); } - std.os.windows.ExitProcess(callMain()); + std.os.windows.kernel32.ExitProcess(callMain()); } // TODO https://github.com/ziglang/zig/issues/265 @@ -78,10 +78,10 @@ fn posixCallMainAndExit() noreturn { while (envp_optional[envp_count]) |_| : (envp_count += 1) {} const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count]; - if (builtin.os == builtin.Os.linux) { + if (builtin.os == .linux) { // Find the beginning of the auxiliary vector const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1); - std.os.linux_elf_aux_maybe = auxv; + std.os.linux.elf_aux_maybe = auxv; // Initialize the TLS area std.os.linux.tls.initTLS(); @@ -92,14 +92,14 @@ fn posixCallMainAndExit() noreturn { } } - std.os.posix.exit(callMainWithArgs(argc, argv, envp)); + std.os.exit(callMainWithArgs(argc, argv, envp)); } // This is marked inline because for some reason LLVM in release mode fails to inline it, // and we want fewer call frames in stack traces. inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { - std.os.ArgIteratorPosix.raw = argv[0..argc]; - std.os.posix_environ_raw = envp; + std.os.argv = argv[0..argc]; + std.os.environ = envp; return callMain(); } diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index dfc3838577..a0a18d21d2 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -3,14 +3,15 @@ const std = @import("std"); const builtin = @import("builtin"); const io = std.io; const fmt = std.fmt; -const os = std.os; const Builder = std.build.Builder; const mem = std.mem; +const process = std.process; const ArrayList = std.ArrayList; const warn = std.debug.warn; +const File = std.fs.File; pub fn main() !void { - var arg_it = os.args(); + var arg_it = process.args(); // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, // one shot program. We don't need to waste time freeing memory and finding places to squish @@ -48,14 +49,14 @@ pub fn main() !void { var prefix: ?[]const u8 = null; var stderr_file = io.getStdErr(); - var stderr_file_stream: os.File.OutStream = undefined; + var stderr_file_stream: File.OutStream = undefined; var stderr_stream = if (stderr_file) |f| x: { stderr_file_stream = f.outStream(); break :x &stderr_file_stream.stream; } else |err| err; var stdout_file = io.getStdOut(); - var stdout_file_stream: os.File.OutStream = undefined; + var stdout_file_stream: File.OutStream = undefined; var stdout_stream = if (stdout_file) |f| x: { stdout_file_stream = f.outStream(); break :x &stdout_file_stream.stream; @@ -138,7 +139,7 @@ pub fn main() !void { error.InvalidStepName => { return usageAndErr(&builder, true, try stderr_stream); }, - error.UncleanExit => os.exit(1), + error.UncleanExit => process.exit(1), else => return err, } }; @@ -214,7 +215,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: var) void { usage(builder, already_ran_build, out_stream) catch {}; - os.exit(1); + process.exit(1); } const UnwrapArgError = error{OutOfMemory}; diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig index cfcaf036d3..9c8b91c632 100644 --- a/std/statically_initialized_mutex.zig +++ b/std/statically_initialized_mutex.zig @@ -23,7 +23,7 @@ pub const StaticallyInitializedMutex = switch (builtin.os) { mutex: *StaticallyInitializedMutex, pub fn release(self: Held) void { - windows.LeaveCriticalSection(&self.mutex.lock); + windows.kernel32.LeaveCriticalSection(&self.mutex.lock); } }; @@ -40,20 +40,20 @@ pub const StaticallyInitializedMutex = switch (builtin.os) { Context: ?*c_void, ) windows.BOOL { const lock = @ptrCast(*windows.CRITICAL_SECTION, @alignCast(@alignOf(windows.CRITICAL_SECTION), Parameter)); - windows.InitializeCriticalSection(lock); + windows.kernel32.InitializeCriticalSection(lock); return windows.TRUE; } /// TODO: once https://github.com/ziglang/zig/issues/287 is solved and std.Mutex has a better /// implementation of a runtime initialized mutex, remove this function. pub fn deinit(self: *StaticallyInitializedMutex) void { - assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); - windows.DeleteCriticalSection(&self.lock); + windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null); + windows.kernel32.DeleteCriticalSection(&self.lock); } pub fn acquire(self: *StaticallyInitializedMutex) Held { - assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); - windows.EnterCriticalSection(&self.lock); + windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null); + windows.kernel32.EnterCriticalSection(&self.lock); return Held{ .mutex = self }; } }, @@ -96,9 +96,9 @@ test "std.StaticallyInitializedMutex" { expect(context.data == TestContext.incr_count); } else { const thread_count = 10; - var threads: [thread_count]*std.os.Thread = undefined; + var threads: [thread_count]*std.Thread = undefined; for (threads) |*t| { - t.* = try std.os.spawnThread(&context, TestContext.worker); + t.* = try std.Thread.spawn(&context, TestContext.worker); } for (threads) |t| t.wait(); diff --git a/std/std.zig b/std/std.zig index 8ec042fdb8..a621af1e84 100644 --- a/std/std.zig +++ b/std/std.zig @@ -17,6 +17,8 @@ pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const SpinLock = @import("spinlock.zig").SpinLock; +pub const ChildProcess = @import("child_process.zig").ChildProcess; +pub const Thread = @import("thread.zig").Thread; pub const atomic = @import("atomic.zig"); pub const base64 = @import("base64.zig"); @@ -30,6 +32,7 @@ pub const dwarf = @import("dwarf.zig"); pub const elf = @import("elf.zig"); pub const event = @import("event.zig"); pub const fmt = @import("fmt.zig"); +pub const fs = @import("fs.zig"); pub const hash = @import("hash.zig"); pub const hash_map = @import("hash_map.zig"); pub const heap = @import("heap.zig"); @@ -43,11 +46,13 @@ pub const meta = @import("meta.zig"); pub const net = @import("net.zig"); pub const os = @import("os.zig"); pub const pdb = @import("pdb.zig"); +pub const process = @import("process.zig"); pub const rand = @import("rand.zig"); pub const rb = @import("rb.zig"); pub const sort = @import("sort.zig"); pub const ascii = @import("ascii.zig"); pub const testing = @import("testing.zig"); +pub const time = @import("time.zig"); pub const unicode = @import("unicode.zig"); pub const valgrind = @import("valgrind.zig"); pub const zig = @import("zig.zig"); @@ -65,6 +70,7 @@ test "std" { _ = @import("statically_initialized_mutex.zig"); _ = @import("segmented_list.zig"); _ = @import("spinlock.zig"); + _ = @import("child_process.zig"); _ = @import("ascii.zig"); _ = @import("base64.zig"); @@ -79,6 +85,7 @@ test "std" { _ = @import("elf.zig"); _ = @import("event.zig"); _ = @import("fmt.zig"); + _ = @import("fs.zig"); _ = @import("hash.zig"); _ = @import("heap.zig"); _ = @import("io.zig"); @@ -91,11 +98,14 @@ test "std" { _ = @import("net.zig"); _ = @import("os.zig"); _ = @import("pdb.zig"); + _ = @import("process.zig"); _ = @import("packed_int_array.zig"); _ = @import("priority_queue.zig"); _ = @import("rand.zig"); _ = @import("sort.zig"); _ = @import("testing.zig"); + _ = @import("thread.zig"); + _ = @import("time.zig"); _ = @import("unicode.zig"); _ = @import("valgrind.zig"); _ = @import("zig.zig"); diff --git a/std/thread.zig b/std/thread.zig new file mode 100644 index 0000000000..49b2decb09 --- /dev/null +++ b/std/thread.zig @@ -0,0 +1,349 @@ +const builtin = @import("builtin"); +const std = @import("std.zig"); +const os = std.os; +const mem = std.mem; +const windows = std.os.windows; +const c = std.c; +const assert = std.debug.assert; + +pub const Thread = struct { + data: Data, + + pub const use_pthreads = !windows.is_the_target and builtin.link_libc; + + /// Represents a kernel thread handle. + /// May be an integer or a pointer depending on the platform. + /// On Linux and POSIX, this is the same as Id. + pub const Handle = if (use_pthreads) + c.pthread_t + else switch (builtin.os) { + .linux => i32, + .windows => windows.HANDLE, + else => @compileError("Unsupported OS"), + }; + + /// Represents a unique ID per thread. + /// May be an integer or pointer depending on the platform. + /// On Linux and POSIX, this is the same as Handle. + pub const Id = switch (builtin.os) { + .windows => windows.DWORD, + else => Handle, + }; + + pub const Data = if (use_pthreads) + struct { + handle: Thread.Handle, + memory: []align(mem.page_size) u8, + } + else switch (builtin.os) { + .linux => struct { + handle: Thread.Handle, + memory: []align(mem.page_size) u8, + }, + .windows => struct { + handle: Thread.Handle, + alloc_start: *c_void, + heap_handle: windows.HANDLE, + }, + else => @compileError("Unsupported OS"), + }; + + /// Returns the ID of the calling thread. + /// Makes a syscall every time the function is called. + /// On Linux and POSIX, this Id is the same as a Handle. + pub fn getCurrentId() Id { + if (use_pthreads) { + return c.pthread_self(); + } else + return switch (builtin.os) { + .linux => os.linux.gettid(), + .windows => windows.kernel32.GetCurrentThreadId(), + else => @compileError("Unsupported OS"), + }; + } + + /// Returns the handle of this thread. + /// On Linux and POSIX, this is the same as Id. + /// On Linux, it is possible that the thread spawned with `spawn` + /// finishes executing entirely before the clone syscall completes. In this + /// case, this function will return 0 rather than the no-longer-existing thread's + /// pid. + pub fn handle(self: Thread) Handle { + return self.data.handle; + } + + pub fn wait(self: *const Thread) void { + if (use_pthreads) { + const err = c.pthread_join(self.data.handle, null); + switch (err) { + 0 => {}, + os.EINVAL => unreachable, + os.ESRCH => unreachable, + os.EDEADLK => unreachable, + else => unreachable, + } + os.munmap(self.data.memory); + } else switch (builtin.os) { + .linux => { + while (true) { + const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst); + if (pid_value == 0) break; + const rc = os.linux.futex_wait(&self.data.handle, os.linux.FUTEX_WAIT, pid_value, null); + switch (os.linux.getErrno(rc)) { + 0 => continue, + os.EINTR => continue, + os.EAGAIN => continue, + else => unreachable, + } + } + os.munmap(self.data.memory); + }, + .windows => { + windows.WaitForSingleObject(self.data.handle, windows.INFINITE) catch unreachable; + windows.CloseHandle(self.data.handle); + windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start); + }, + else => @compileError("Unsupported OS"), + } + } + + pub const SpawnError = error{ + /// A system-imposed limit on the number of threads was encountered. + /// There are a number of limits that may trigger this error: + /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), + /// which limits the number of processes and threads for a real + /// user ID, was reached; + /// * the kernel's system-wide limit on the number of processes and + /// threads, /proc/sys/kernel/threads-max, was reached (see + /// proc(5)); + /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was + /// reached (see proc(5)); or + /// * the PID limit (pids.max) imposed by the cgroup "process num‐ + /// ber" (PIDs) controller was reached. + ThreadQuotaExceeded, + + /// The kernel cannot allocate sufficient memory to allocate a task structure + /// for the child, or to copy those parts of the caller's context that need to + /// be copied. + SystemResources, + + /// Not enough userland memory to spawn the thread. + OutOfMemory, + + /// `mlockall` is enabled, and the memory needed to spawn the thread + /// would exceed the limit. + LockedMemoryLimitExceeded, + + Unexpected, + }; + + /// caller must call wait on the returned thread + /// fn startFn(@typeOf(context)) T + /// where T is u8, noreturn, void, or !void + /// caller must call wait on the returned thread + pub fn spawn(context: var, comptime startFn: var) SpawnError!*Thread { + if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode"); + // TODO compile-time call graph analysis to determine stack upper bound + // https://github.com/ziglang/zig/issues/157 + const default_stack_size = 8 * 1024 * 1024; + + const Context = @typeOf(context); + comptime assert(@ArgType(@typeOf(startFn), 0) == Context); + + if (builtin.os == builtin.Os.windows) { + const WinThread = struct { + const OuterContext = struct { + thread: Thread, + inner: Context, + }; + extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD { + const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; + switch (@typeId(@typeOf(startFn).ReturnType)) { + .Int => { + return startFn(arg); + }, + .Void => { + startFn(arg); + return 0; + }, + else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), + } + } + }; + + const heap_handle = windows.kernel32.GetProcessHeap() orelse return error.OutOfMemory; + const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); + const bytes_ptr = windows.kernel32.HeapAlloc(heap_handle, 0, byte_count) orelse return error.OutOfMemory; + errdefer assert(windows.kernel32.HeapFree(heap_handle, 0, bytes_ptr) != 0); + const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; + const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; + outer_context.* = WinThread.OuterContext{ + .thread = Thread{ + .data = Thread.Data{ + .heap_handle = heap_handle, + .alloc_start = bytes_ptr, + .handle = undefined, + }, + }, + .inner = context, + }; + + const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); + outer_context.thread.data.handle = windows.kernel32.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { + switch (windows.kernel32.GetLastError()) { + else => |err| return windows.unexpectedError(err), + } + }; + return &outer_context.thread; + } + + const MainFuncs = struct { + extern fn linuxThreadMain(ctx_addr: usize) u8 { + const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; + + switch (@typeId(@typeOf(startFn).ReturnType)) { + .Int => { + return startFn(arg); + }, + .Void => { + startFn(arg); + return 0; + }, + else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), + } + } + extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { + if (@sizeOf(Context) == 0) { + _ = startFn({}); + return null; + } else { + _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*); + return null; + } + } + }; + + const MAP_GROWSDOWN = if (os.linux.is_the_target) os.linux.MAP_GROWSDOWN else 0; + + var stack_end_offset: usize = undefined; + var thread_start_offset: usize = undefined; + var context_start_offset: usize = undefined; + var tls_start_offset: usize = undefined; + const mmap_len = blk: { + // First in memory will be the stack, which grows downwards. + var l: usize = mem.alignForward(default_stack_size, mem.page_size); + stack_end_offset = l; + // Above the stack, so that it can be in the same mmap call, put the Thread object. + l = mem.alignForward(l, @alignOf(Thread)); + thread_start_offset = l; + l += @sizeOf(Thread); + // Next, the Context object. + if (@sizeOf(Context) != 0) { + l = mem.alignForward(l, @alignOf(Context)); + context_start_offset = l; + l += @sizeOf(Context); + } + // Finally, the Thread Local Storage, if any. + if (!Thread.use_pthreads) { + if (os.linux.tls.tls_image) |tls_img| { + l = mem.alignForward(l, @alignOf(usize)); + tls_start_offset = l; + l += tls_img.alloc_size; + } + } + break :blk l; + }; + const mmap_slice = os.mmap( + null, + mem.alignForward(mmap_len, mem.page_size), + os.PROT_READ | os.PROT_WRITE, + os.MAP_PRIVATE | os.MAP_ANONYMOUS | MAP_GROWSDOWN, + -1, + 0, + ) catch |err| switch (err) { + error.MemoryMappingNotSupported => unreachable, // no file descriptor + error.AccessDenied => unreachable, // no file descriptor + error.PermissionDenied => unreachable, // no file descriptor + else => |e| return e, + }; + errdefer os.munmap(mmap_slice); + const mmap_addr = @ptrToInt(mmap_slice.ptr); + + const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); + thread_ptr.data.memory = mmap_slice; + + var arg: usize = undefined; + if (@sizeOf(Context) != 0) { + arg = mmap_addr + context_start_offset; + const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg)); + context_ptr.* = context; + } + + if (Thread.use_pthreads) { + // use pthreads + var attr: c.pthread_attr_t = undefined; + if (c.pthread_attr_init(&attr) != 0) return error.SystemResources; + defer assert(c.pthread_attr_destroy(&attr) == 0); + + assert(c.pthread_attr_setstack(&attr, mmap_slice.ptr, stack_end_offset) == 0); + + const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); + switch (err) { + 0 => return thread_ptr, + os.EAGAIN => return error.SystemResources, + os.EPERM => unreachable, + os.EINVAL => unreachable, + else => return os.unexpectedErrno(@intCast(usize, err)), + } + } else if (os.linux.is_the_target) { + var flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES | os.CLONE_SIGHAND | + os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID | + os.CLONE_DETACHED; + var newtls: usize = undefined; + if (os.linux.tls.tls_image) |tls_img| { + newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset); + flags |= os.CLONE_SETTLS; + } + const rc = os.linux.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); + switch (os.errno(rc)) { + 0 => return thread_ptr, + os.EAGAIN => return error.ThreadQuotaExceeded, + os.EINVAL => unreachable, + os.ENOMEM => return error.SystemResources, + os.ENOSPC => unreachable, + os.EPERM => unreachable, + os.EUSERS => unreachable, + else => |err| return os.unexpectedErrno(err), + } + } else { + @compileError("Unsupported OS"); + } + } + + pub const CpuCountError = error{ + OutOfMemory, + PermissionDenied, + SystemResources, + Unexpected, + }; + + pub fn cpuCount() CpuCountError!usize { + if (os.linux.is_the_target) { + const cpu_set = try os.sched_getaffinity(0); + return usize(os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast + } + if (os.windows.is_the_target) { + var system_info: windows.SYSTEM_INFO = undefined; + windows.kernel32.GetSystemInfo(&system_info); + return @intCast(usize, system_info.dwNumberOfProcessors); + } + var count: c_int = undefined; + var count_len: usize = @sizeOf(c_int); + const name = if (os.darwin.is_the_target) c"hw.logicalcpu" else c"hw.ncpu"; + os.sysctlbynameC(name, &count, &count_len, null, 0) catch |err| switch (err) { + error.NameTooLong => unreachable, + else => |e| return e, + }; + return @intCast(usize, count); + } +}; diff --git a/std/time.zig b/std/time.zig new file mode 100644 index 0000000000..caf6c31b31 --- /dev/null +++ b/std/time.zig @@ -0,0 +1,221 @@ +const builtin = @import("builtin"); +const std = @import("std.zig"); +const assert = std.debug.assert; +const testing = std.testing; +const os = std.os; +const math = std.math; + +pub const epoch = @import("time/epoch.zig"); + +/// Spurious wakeups are possible and no precision of timing is guaranteed. +pub fn sleep(nanoseconds: u64) void { + if (os.windows.is_the_target) { + const ns_per_ms = ns_per_s / ms_per_s; + const big_ms_from_ns = nanoseconds / ns_per_ms; + const ms = math.cast(os.windows.DWORD, big_ms_from_ns) catch math.maxInt(os.windows.DWORD); + os.windows.kernel32.Sleep(ms); + return; + } + const s = nanoseconds / ns_per_s; + const ns = nanoseconds % ns_per_s; + std.os.nanosleep(s, ns); +} + +/// Get the posix timestamp, UTC, in seconds +/// TODO audit this function. is it possible to return an error? +pub fn timestamp() u64 { + return @divFloor(milliTimestamp(), ms_per_s); +} + +/// Get the posix timestamp, UTC, in milliseconds +/// TODO audit this function. is it possible to return an error? +pub fn milliTimestamp() u64 { + if (os.windows.is_the_target) { + //FileTime has a granularity of 100 nanoseconds + // and uses the NTFS/Windows epoch + var ft: os.windows.FILETIME = undefined; + os.windows.kernel32.GetSystemTimeAsFileTime(&ft); + const hns_per_ms = (ns_per_s / 100) / ms_per_s; + const epoch_adj = epoch.windows * ms_per_s; + + const ft64 = (u64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + return @divFloor(ft64, hns_per_ms) - -epoch_adj; + } + if (os.wasi.is_the_target and !builtin.link_libc) { + var ns: os.wasi.timestamp_t = undefined; + + // TODO: Verify that precision is ignored + const err = os.wasi.clock_time_get(os.wasi.CLOCK_REALTIME, 1, &ns); + assert(err == os.wasi.ESUCCESS); + + const ns_per_ms = 1000; + return @divFloor(ns, ns_per_ms); + } + if (os.darwin.is_the_target) { + var tv: os.darwin.timeval = undefined; + var err = os.darwin.gettimeofday(&tv, null); + assert(err == 0); + const sec_ms = tv.tv_sec * ms_per_s; + const usec_ms = @divFloor(tv.tv_usec, us_per_s / ms_per_s); + return @intCast(u64, sec_ms + usec_ms); + } + var ts: os.timespec = undefined; + //From what I can tell there's no reason clock_gettime + // should ever fail for us with CLOCK_REALTIME, + // seccomp aside. + os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable; + const sec_ms = @intCast(u64, ts.tv_sec) * ms_per_s; + const nsec_ms = @divFloor(@intCast(u64, ts.tv_nsec), ns_per_s / ms_per_s); + return sec_ms + nsec_ms; +} + +/// Multiples of a base unit (nanoseconds) +pub const nanosecond = 1; +pub const microsecond = 1000 * nanosecond; +pub const millisecond = 1000 * microsecond; +pub const second = 1000 * millisecond; +pub const minute = 60 * second; +pub const hour = 60 * minute; + +/// Divisions of a second +pub const ns_per_s = 1000000000; +pub const us_per_s = 1000000; +pub const ms_per_s = 1000; +pub const cs_per_s = 100; + +/// Common time divisions +pub const s_per_min = 60; +pub const s_per_hour = s_per_min * 60; +pub const s_per_day = s_per_hour * 24; +pub const s_per_week = s_per_day * 7; + +/// A monotonic high-performance timer. +/// Timer.start() must be called to initialize the struct, which captures +/// the counter frequency on windows and darwin, records the resolution, +/// and gives the user an opportunity to check for the existnece of +/// monotonic clocks without forcing them to check for error on each read. +/// .resolution is in nanoseconds on all platforms but .start_time's meaning +/// depends on the OS. On Windows and Darwin it is a hardware counter +/// value that requires calculation to convert to a meaninful unit. +pub const Timer = struct { + ///if we used resolution's value when performing the + /// performance counter calc on windows/darwin, it would + /// be less precise + frequency: switch (builtin.os) { + .windows => u64, + .macosx, .ios, .tvos, .watchos => os.darwin.mach_timebase_info_data, + else => void, + }, + resolution: u64, + start_time: u64, + + const Error = error{TimerUnsupported}; + + ///At some point we may change our minds on RAW, but for now we're + /// sticking with posix standard MONOTONIC. For more information, see: + /// https://github.com/ziglang/zig/pull/933 + const monotonic_clock_id = os.CLOCK_MONOTONIC; + /// Initialize the timer structure. + //This gives us an opportunity to grab the counter frequency in windows. + //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000. + //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not + // supported, or if the timespec pointer is out of bounds, which should be + // impossible here barring cosmic rays or other such occurrences of + // incredibly bad luck. + //On Darwin: This cannot fail, as far as I am able to tell. + pub fn start() Error!Timer { + var self: Timer = undefined; + + if (os.windows.is_the_target) { + self.frequency = os.windows.QueryPerformanceFrequency(); + self.resolution = @divFloor(ns_per_s, self.frequency); + self.start_time = os.windows.QueryPerformanceCounter(); + } else if (os.darwin.is_the_target) { + os.darwin.mach_timebase_info(&self.frequency); + self.resolution = @divFloor(self.frequency.numer, self.frequency.denom); + self.start_time = os.darwin.mach_absolute_time(); + } else { + //On Linux, seccomp can do arbitrary things to our ability to call + // syscalls, including return any errno value it wants and + // inconsistently throwing errors. Since we can't account for + // abuses of seccomp in a reasonable way, we'll assume that if + // seccomp is going to block us it will at least do so consistently + var ts: os.timespec = undefined; + os.clock_getres(monotonic_clock_id, &ts) catch return error.TimerUnsupported; + self.resolution = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); + + os.clock_gettime(monotonic_clock_id, &ts) catch return error.TimerUnsupported; + self.start_time = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); + } + + return self; + } + + /// Reads the timer value since start or the last reset in nanoseconds + pub fn read(self: *Timer) u64 { + var clock = clockNative() - self.start_time; + if (os.windows.is_the_target) { + return @divFloor(clock * ns_per_s, self.frequency); + } + if (os.darwin.is_the_target) { + return @divFloor(clock * self.frequency.numer, self.frequency.denom); + } + return clock; + } + + /// Resets the timer value to 0/now. + pub fn reset(self: *Timer) void { + self.start_time = clockNative(); + } + + /// Returns the current value of the timer in nanoseconds, then resets it + pub fn lap(self: *Timer) u64 { + var now = clockNative(); + var lap_time = self.read(); + self.start_time = now; + return lap_time; + } + + fn clockNative() u64 { + if (os.windows.is_the_target) { + return os.windows.QueryPerformanceCounter(); + } + if (os.darwin.is_the_target) { + return os.darwin.mach_absolute_time(); + } + var ts: os.timespec = undefined; + os.clock_gettime(monotonic_clock_id, &ts) catch unreachable; + return @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); + } +}; + +test "sleep" { + sleep(1); +} + +test "timestamp" { + const ns_per_ms = (ns_per_s / ms_per_s); + const margin = 50; + + const time_0 = milliTimestamp(); + sleep(ns_per_ms); + const time_1 = milliTimestamp(); + const interval = time_1 - time_0; + testing.expect(interval > 0 and interval < margin); +} + +test "Timer" { + const ns_per_ms = (ns_per_s / ms_per_s); + const margin = ns_per_ms * 150; + + var timer = try Timer.start(); + sleep(10 * ns_per_ms); + const time_0 = timer.read(); + testing.expect(time_0 > 0 and time_0 < margin); + + const time_1 = timer.lap(); + testing.expect(time_1 >= time_0); + + timer.reset(); + testing.expect(timer.read() < time_1); +} diff --git a/std/os/epoch.zig b/std/time/epoch.zig index fc031521a5..fc031521a5 100644 --- a/std/os/epoch.zig +++ b/std/time/epoch.zig diff --git a/std/zig/bench.zig b/std/zig/bench.zig index ed6ae9a128..b43c491c97 100644 --- a/std/zig/bench.zig +++ b/std/zig/bench.zig @@ -10,7 +10,7 @@ var fixed_buffer_mem: [10 * 1024 * 1024]u8 = undefined; pub fn main() !void { var i: usize = 0; - var timer = try std.os.time.Timer.start(); + var timer = try std.time.Timer.start(); const start = timer.lap(); const iterations = 100; var memory_used: usize = 0; @@ -19,7 +19,7 @@ pub fn main() !void { } const end = timer.read(); memory_used /= iterations; - const elapsed_s = @intToFloat(f64, end - start) / std.os.time.ns_per_s; + const elapsed_s = @intToFloat(f64, end - start) / std.time.ns_per_s; const bytes_per_sec = @intToFloat(f64, source.len * iterations) / elapsed_s; const mb_per_sec = bytes_per_sec / (1024 * 1024); |
