diff --git a/README.org b/README.org index 6403aff..72cd528 100644 --- a/README.org +++ b/README.org @@ -1,6 +1,6 @@ #+TITLE: Zigcli #+DATE: 2022-09-20T22:55:17+0800 -#+LASTMOD: 2022-09-20T22:55:17+0800 +#+LASTMOD: 2023-06-28T20:22:53+0800 #+AUTHOR: Jiacai Liu #+EMAIL: dev@liujiacai.net #+OPTIONS: toc:nil num:nil @@ -13,6 +13,7 @@ Command line programs written in Zig. Currently there are: - =loc=, lines of code. - =tree=, list contents of directories in a tree-like format. - =yes=, output a string repeatedly until killed. +- =pidof=, like [[https://man7.org/linux/man-pages/man1/pidof.1.html][pidof]], but for macOS. Prebuilt binaries can be found in [[https://github.com/jiacai2050/loc/actions/workflows/binary.yml][CI's artifacts]], or you can build from source: #+begin_src bash diff --git a/build.zig b/build.zig index 63cf554..86d35ad 100644 --- a/build.zig +++ b/build.zig @@ -17,22 +17,27 @@ pub fn build(b: *Build) void { buildLoc(b, optimize, target, simargs_dep), "loc", }, + .{ + buildPidof(b, optimize, target, simargs_dep), + "pidof", + }, .{ buildYes(b, optimize, target), "yes", }, }) |prog| { - const exe = prog.@"0"; - const name = prog.@"1"; - b.installArtifact(exe); - const run_cmd = b.addRunArtifact(exe); - if (b.args) |args| { - run_cmd.addArgs(args); - } - const run_step = b.step("run-" ++ name, "Run " ++ name); - run_step.dependOn(&run_cmd.step); + if (prog.@"0") |exe| { + const name = prog.@"1"; + b.installArtifact(exe); + const run_cmd = b.addRunArtifact(exe); + if (b.args) |args| { + run_cmd.addArgs(args); + } + b.step("run-" ++ name, "Run " ++ name) + .dependOn(&run_cmd.step); - all_tests.append(buildTestStep(b, name, target)) catch @panic("OOM"); + all_tests.append(buildTestStep(b, name, target)) catch @panic("OOM"); + } } const test_all_step = b.step("test", "Run all tests"); @@ -52,7 +57,7 @@ fn buildTestStep(b: *std.Build, comptime name: []const u8, target: std.zig.Cross return test_step; } -fn buildTree(b: *std.Build, optimize: std.builtin.Mode, target: std.zig.CrossTarget, simargs_dep: *std.build.Dependency) *Build.CompileStep { +fn buildTree(b: *std.Build, optimize: std.builtin.Mode, target: std.zig.CrossTarget, simargs_dep: *std.build.Dependency) ?*Build.CompileStep { const exe = b.addExecutable(.{ .name = "tree", .root_source_file = FileSource.relative("src/tree.zig"), @@ -64,7 +69,7 @@ fn buildTree(b: *std.Build, optimize: std.builtin.Mode, target: std.zig.CrossTar return exe; } -fn buildLoc(b: *std.Build, optimize: std.builtin.Mode, target: std.zig.CrossTarget, simargs_dep: *std.build.Dependency) *Build.CompileStep { +fn buildLoc(b: *std.Build, optimize: std.builtin.Mode, target: std.zig.CrossTarget, simargs_dep: *std.build.Dependency) ?*Build.CompileStep { const exe = b.addExecutable(.{ .name = "loc", .root_source_file = FileSource.relative("src/loc.zig"), @@ -78,7 +83,7 @@ fn buildLoc(b: *std.Build, optimize: std.builtin.Mode, target: std.zig.CrossTarg return exe; } -fn buildYes(b: *std.build.Builder, optimize: std.builtin.Mode, target: std.zig.CrossTarget) *std.build.CompileStep { +fn buildYes(b: *std.build.Builder, optimize: std.builtin.Mode, target: std.zig.CrossTarget) ?*Build.CompileStep { const exe = b.addExecutable(.{ .name = "yes", .root_source_file = FileSource.relative("src/yes.zig"), @@ -88,3 +93,19 @@ fn buildYes(b: *std.build.Builder, optimize: std.builtin.Mode, target: std.zig.C return exe; } + +fn buildPidof(b: *std.Build, optimize: std.builtin.Mode, target: std.zig.CrossTarget, simargs_dep: *std.build.Dependency) ?*Build.CompileStep { + if (target.getOsTag() != .macos) { + return null; + } + + const exe = b.addExecutable(.{ + .name = "pidof", + .root_source_file = FileSource.relative("src/pidof.zig"), + .target = target, + .optimize = optimize, + }); + exe.addModule("simargs", simargs_dep.module("simargs")); + + return exe; +} diff --git a/build.zig.zon b/build.zig.zon index 5826f15..0f92359 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,10 +1,10 @@ .{ - .name = "loc", + .name = "zigcli", .version = "0.1.0", .dependencies = .{ .simargs = .{ - .url = "https://github.com/jiacai2050/simargs/archive/2ac9368.tar.gz", - .hash = "1220c7f602de51855b241b771058255d5023e156c2d8df93c3cf0f6ec3248e484f95", + .url = "https://github.com/jiacai2050/simargs/archive/77eb191.tar.gz", + .hash = "1220eef7753fccb692bbebae6b4ee2bab7900e954b5598ea25649ade4c062481cb49", }, .@"table-helper" = .{ .url = "https://github.com/jiacai2050/table-helper/archive/f0c8eb9.tar.gz", diff --git a/src/loc.zig b/src/loc.zig index b740726..291b64d 100644 --- a/src/loc.zig +++ b/src/loc.zig @@ -300,7 +300,7 @@ fn populateLoc(allocator: std.mem.Allocator, loc_map: *LocMap, dir: fs.Dir, base loc_entry.files += 1; const metadata = try file.metadata(); - const file_size = @truncate(usize, metadata.size()); + const file_size: usize = @truncate(metadata.size()); if (file_size == 0) { return; } diff --git a/src/pidof.zig b/src/pidof.zig new file mode 100644 index 0000000..3c6d4b6 --- /dev/null +++ b/src/pidof.zig @@ -0,0 +1,95 @@ +//! Pidof for macOS +//! +//! https://man7.org/linux/man-pages/man1/pidof.1.html + +const std = @import("std"); +const simargs = @import("simargs"); +const c = @cImport({ + @cInclude("sys/sysctl.h"); +}); + +pub const Options = struct { + single: bool = false, + separator: []const u8 = " ", + help: bool = false, + + pub const __shorts__ = .{ + .single = .s, + .separator = .S, + .help = .h, + }; + pub const __messages__ = .{ + .single = "Single shot - this instructs the program to only return one pid.", + .separator = "Use separator as a separator put between pids.", + .help = "Print help message.", + }; +}; + +pub fn findPids(allocator: std.mem.Allocator, opt: Options, program: []const u8) !std.ArrayList(c.pid_t) { + var mib = [_]c_int{ + c.CTL_KERN, + c.KERN_PROC, + c.KERN_PROC_ALL, + }; + var procSize: usize = 0; + var rc = c.sysctl(&mib, mib.len, null, &procSize, null, 0); + if (rc != 0) { + std.debug.print("get proc size, err:{any}", .{std.c.getErrno(rc)}); + return error.sysctl; + } + + var procList = try allocator.alloc(c.struct_kinfo_proc, procSize / @sizeOf(c.struct_kinfo_proc)); + rc = c.sysctl(&mib, mib.len, @ptrCast(procList), &procSize, null, 0); + if (rc != 0) { + std.debug.print("get proc list failed, err:{any}", .{std.c.getErrno(rc)}); + return error.sysctl; + } + + // procSize may change between two calls of sysctl, so we cannot iterate + // procList directly with for(procList) |proc|. + var pids = std.ArrayList(c.pid_t).init(allocator); + for (0..procSize / @sizeOf(c.struct_kinfo_proc)) |i| { + if (opt.single and pids.items.len == 1) { + break; + } + const proc = procList[i]; + // p_comm is [17]u8 + const name = std.mem.sliceTo(&proc.kp_proc.p_comm, 0); + if (program.len >= name.len) { + if (std.mem.eql(u8, name, program[0..name.len])) { + try pids.append(proc.kp_proc.p_pid); + } + } + } + + return pids; +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + const opt = try simargs.parse(allocator, Options, "[program]"); + defer opt.deinit(); + + if (opt.positional_args.items.len == 0) { + std.debug.print("program is not given", .{}); + std.os.exit(1); + } + + const program = opt.positional_args.items[0]; + + const pids = try findPids(allocator, opt.args, program); + if (pids.items.len == 0) { + std.os.exit(1); + } + + var stdout = std.io.getStdOut().writer(); + for (pids.items, 0..) |pid, i| { + if (i > 0) { + try stdout.writeAll(opt.args.separator); + } + try stdout.print("{d}", .{pid}); + } +} diff --git a/src/util.zig b/src/util.zig index cd43cd4..6042206 100644 --- a/src/util.zig +++ b/src/util.zig @@ -5,7 +5,7 @@ pub const StringUtil = struct { const SIZE_UNIT = [_][]const u8{ "B", "K", "M", "G", "T" }; pub fn humanSize(allocator: mem.Allocator, n: u64) ![]const u8 { - var remaining = @floatFromInt(f64, n); + var remaining: f64 = @floatFromInt(n); var i: usize = 0; while (remaining > 1024) { remaining /= 1024;