mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
126 lines
5.6 KiB
Zig
126 lines
5.6 KiB
Zig
//! ./runner command0 [arg0 arg1 ...] [--then command1 arg0 arg1 ...] [--path path0 path1 ...] [--cwd cwd] [--delete file0 file1 ...] [--rename old0 new0 [old1 new1 ...]] -- [replacement0 replacement1 ... replacement9]
|
|
//! Replaces $0 through $9 in all non-option arguments with replacement0 through replacement9,
|
|
//! which have native path separators replaced with posix path separators.
|
|
//! Replaces @0 through @9 in all non-option arguments with the contents of the file
|
|
//! replacement0 through replacement9.
|
|
//! Then, executes the command before any option arguments with extra paths after --path and at optional cwd after --cwd.
|
|
//! Then, executes the command after --then with extra paths after --path and at optional cwd after --cwd.
|
|
//! Then, optionally deletes files after --delete.
|
|
//! Then, optionally renomes pairs of files after --rename.
|
|
|
|
const std = @import("std");
|
|
|
|
pub fn main() !void {
|
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
const allocator = arena.allocator();
|
|
|
|
const args = try std.process.argsAlloc(allocator);
|
|
var first: [][]u8 = &.{};
|
|
var then: [][]u8 = &.{};
|
|
var cwd: [][]u8 = &.{};
|
|
var path: [][]u8 = &.{};
|
|
var delete: [][]u8 = &.{};
|
|
var rename: [][]u8 = &.{};
|
|
var replacements: [][]u8 = &.{};
|
|
|
|
var prev_separator_index: usize = 0;
|
|
var separator_index: usize = 1;
|
|
while (separator_index < args.len) : (separator_index += 1) {
|
|
if (!std.mem.startsWith(u8, args[separator_index], "--")) continue;
|
|
const separated_args = args[prev_separator_index + 1 .. separator_index];
|
|
if (prev_separator_index == 0)
|
|
first = separated_args
|
|
else if (std.mem.eql(u8, args[prev_separator_index], "--then"))
|
|
then = separated_args
|
|
else if (std.mem.eql(u8, args[prev_separator_index], "--cwd"))
|
|
cwd = separated_args
|
|
else if (std.mem.eql(u8, args[prev_separator_index], "--path"))
|
|
path = separated_args
|
|
else if (std.mem.eql(u8, args[prev_separator_index], "--delete"))
|
|
delete = separated_args
|
|
else if (std.mem.eql(u8, args[prev_separator_index], "--rename"))
|
|
rename = separated_args
|
|
else
|
|
std.debug.panic("unexpected '{s}'", .{args[prev_separator_index]});
|
|
if (args[separator_index].len == 2) {
|
|
replacements = args[separator_index + 1 ..];
|
|
break;
|
|
}
|
|
prev_separator_index = separator_index;
|
|
} else std.debug.panic("expected '--'", .{});
|
|
|
|
if (first.len == 0) std.debug.panic("expected arguments", .{});
|
|
if (cwd.len > 1) std.debug.panic("expected at most 1 cwd argument", .{});
|
|
if (rename.len % 2 != 0) std.debug.panic("expected even rename arguments", .{});
|
|
|
|
for (replacements) |replacement| std.mem.replaceScalar(
|
|
u8,
|
|
replacement,
|
|
std.fs.path.sep,
|
|
std.fs.path.sep_posix,
|
|
);
|
|
for ([_][][]u8{ first, then, cwd, path, delete, rename }) |patterns| for (patterns) |*pattern| {
|
|
var replaced = std.ArrayList(u8).init(allocator);
|
|
var pos: usize = 0;
|
|
while (std.mem.indexOfAnyPos(u8, pattern.*, pos, "$@")) |special_pos| {
|
|
try replaced.appendSlice(pattern.*[pos..special_pos]);
|
|
const special = pattern.*[special_pos..][0..2];
|
|
if (std.fmt.charToDigit(special[1], 10)) |special_index| switch (special[0]) {
|
|
'$' => try replaced.appendSlice(replacements[special_index]),
|
|
'@' => {
|
|
const special_file = try std.fs.cwd().openFile(replacements[special_index], .{});
|
|
defer special_file.close();
|
|
try special_file.reader().readAllArrayList(&replaced, 1 << 12);
|
|
replaced.shrinkRetainingCapacity(std.mem.trimRight(u8, replaced.items, &std.ascii.whitespace).len);
|
|
},
|
|
else => unreachable,
|
|
} else |_| switch (special[0]) {
|
|
'$' => try replaced.append(special[1]),
|
|
else => unreachable,
|
|
}
|
|
pos = special_pos + 2;
|
|
}
|
|
try replaced.appendSlice(pattern.*[pos..]);
|
|
pattern.* = replaced.items;
|
|
};
|
|
|
|
var path_buffer = std.ArrayList(u8).init(allocator);
|
|
for (path) |path_path| {
|
|
try path_buffer.appendSlice(path_path);
|
|
try path_buffer.append(std.fs.path.delimiter);
|
|
}
|
|
var env = try std.process.getEnvMap(allocator);
|
|
if (env.getPtr("PATH")) |env_path| {
|
|
try path_buffer.appendSlice(env_path.*);
|
|
env_path.* = try path_buffer.toOwnedSlice();
|
|
} else if (path_buffer.items.len > 0) try env.putMove(
|
|
try allocator.dupe(u8, "PATH"),
|
|
path_buffer.items[0 .. path_buffer.items.len - 1],
|
|
);
|
|
|
|
var first_child = std.process.Child.init(first, allocator);
|
|
first_child.env_map = &env;
|
|
if (cwd.len == 1) first_child.cwd = cwd[0];
|
|
switch (try first_child.spawnAndWait()) {
|
|
.Exited => |status| if (status != 0) std.process.exit(status),
|
|
else => |term| std.debug.panic("{}", .{term}),
|
|
}
|
|
|
|
if (then.len > 0) {
|
|
var then_child = std.process.Child.init(then, allocator);
|
|
then_child.env_map = &env;
|
|
if (cwd.len == 1) then_child.cwd = cwd[0];
|
|
switch (try then_child.spawnAndWait()) {
|
|
.Exited => |status| if (status != 0) std.process.exit(status),
|
|
else => |term| std.debug.panic("{}", .{term}),
|
|
}
|
|
}
|
|
|
|
for (delete) |delete_path| try std.fs.cwd().deleteFile(delete_path);
|
|
|
|
var rename_index: usize = 0;
|
|
while (rename_index < rename.len) : (rename_index += 2) try std.fs.cwd().rename(
|
|
rename[rename_index + 0],
|
|
rename[rename_index + 1],
|
|
);
|
|
}
|