Last active
October 13, 2023 18:13
-
-
Save Tetralux/25d1fd01d1a8997b8aa2dac710ef3758 to your computer and use it in GitHub Desktop.
A simple example program that runs a program with the given arguments by searching the PATH environment variable.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// A simple example program that runs a program with the given arguments by searching the PATH environment variable. | |
// | |
// Tetralux, 2023-10-13. | |
// | |
const std = @import("std"); | |
const builtin = @import("builtin"); | |
const string = []const u8; | |
const Allocator = std.mem.Allocator; | |
const print = std.debug.print; | |
fn find_program_in_path(name: string, ally: Allocator) error{OutOfMemory}!?string { | |
var arena_state = std.heap.ArenaAllocator.init(ally); | |
defer arena_state.deinit(); | |
const arena = arena_state.allocator(); | |
const path_env = std.process.getEnvVarOwned(arena, "PATH") catch return null; | |
const sep = switch (builtin.os.tag) { | |
.windows => ';', | |
else => ':', | |
}; | |
const search_name = if (builtin.os.tag == .windows) | |
try std.fmt.allocPrint(arena, "{s}.exe", .{ name }) | |
else | |
name; | |
var iteration_arena_state = std.heap.ArenaAllocator.init(arena); | |
const iteration_arena = iteration_arena_state.allocator(); | |
var splitted = std.mem.splitScalar(u8, path_env, sep); | |
while (splitted.next()) |path| { | |
defer _ = iteration_arena_state.reset(.{ .retain_with_limit = 4096 }); | |
const full_path = try std.fs.path.join(iteration_arena, &.{ path, search_name }); | |
std.os.access(full_path, 0o644) catch continue; | |
return try ally.dupe(u8, full_path); | |
} | |
return null; | |
} | |
fn run(program: string, args: []const string, ally: Allocator) !bool { | |
const full_program_path = try find_program_in_path(program, ally) orelse return error.CannotFindProgramInPath; | |
defer ally.free(full_program_path); | |
var argv = try std.ArrayList(string).initCapacity(ally, args.len + 1); | |
defer argv.deinit(); | |
try argv.append(full_program_path); | |
try argv.appendSlice(args); | |
print("Cmdline: {s}\n", .{ argv.items }); // NOTE: For debugging and demonstration purposes | |
var p = std.ChildProcess.init(argv.items, ally); | |
try p.spawn(); | |
switch (try p.wait()) { | |
.Exited => |code| return code == 0, | |
else => return false, | |
} | |
} | |
fn fatalf(msg: string) noreturn { | |
print("error: {s}\n\n", .{ msg }); | |
print("usage: {s} <program_name> [argv...]\n", .{ cmdline_args[0] }); | |
std.os.exit(1); | |
} | |
// NOTE: Global so that fatalf() doesn't need it passed to it | |
var cmdline_args: []const [:0]u8 = undefined; | |
pub fn main() !void { | |
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | |
defer _ = gpa.deinit(); | |
const ally = gpa.allocator(); | |
cmdline_args = try std.process.argsAlloc(ally); | |
defer std.process.argsFree(ally, cmdline_args); | |
if (cmdline_args.len == 1) fatalf("Missing 'program' argument."); | |
const program = cmdline_args[1]; | |
const argv: []const string = if (cmdline_args.len > 2) cmdline_args[2..] else &.{}; | |
const ok = try run(program, argv, ally); | |
print("ok = {}\n", .{ ok }); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment