Skip to content

Instantly share code, notes, and snippets.

@Tetralux
Last active October 13, 2023 18:13
Show Gist options
  • Save Tetralux/25d1fd01d1a8997b8aa2dac710ef3758 to your computer and use it in GitHub Desktop.
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.
//
// 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