we will need to patch utilities.c. Quick program for to do this

This commit is contained in:
Emil Lerch 2025-09-18 10:18:17 -07:00
parent ffdf1bd0f1
commit fb9bae8525
Signed by: lobo
GPG key ID: A7B62D657EF764F8
4 changed files with 208 additions and 0 deletions

39
sed-lite/README.md Normal file
View file

@ -0,0 +1,39 @@
# sed-lite
A minimal command line program for in-place file editing with exact line substitution.
## Usage
```
sed-lite -sL "original string" "replacement string" <file>
```
## Operations
- `-sL` (substitute line): Replaces entire lines that exactly match the original string
## Features
- In-place editing using temporary files
- Exact line matching (no regex or wildcards)
- Safe atomic file replacement
- Comprehensive error handling
## Building
```bash
zig build
```
## Testing
```bash
zig build test
```
## Example
```bash
# Replace all lines containing exactly "old text" with "new text"
./sed-lite -sL "old text" "new text" myfile.txt
```

27
sed-lite/build.zig Normal file
View file

@ -0,0 +1,27 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "sed-lite",
.root_module = b.addModule("main", .{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
b.installArtifact(exe);
const tests = b.addTest(.{
.root_module = b.addModule("test", .{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
const test_step = b.step("test", "Run tests");
test_step.dependOn(&b.addRunArtifact(tests).step);
}

7
sed-lite/build.zig.zon Normal file
View file

@ -0,0 +1,7 @@
.{
.name = .sed_lite,
.version = "0.0.1",
.minimum_zig_version = "0.15.1",
.paths = .{""},
.fingerprint = 0xf728ae3f808521e3,
}

135
sed-lite/src/main.zig Normal file
View file

@ -0,0 +1,135 @@
const std = @import("std");
const print = std.debug.print;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
if (args.len < 2) {
print("Usage: {s} <operation> <operation args...> <file>\n", .{args[0]});
std.process.exit(1);
}
const operation = args[1];
const file_path = args[args.len - 1];
if (std.mem.eql(u8, operation, "-sL")) {
if (args.len != 5) {
print("Usage: {s} -sL \"original string\" \"replacement string\" <file>\n", .{args[0]});
std.process.exit(1);
}
const file = std.fs.cwd().openFile(file_path, .{}) catch |err| {
print("Error opening file '{s}': {}\n", .{ file_path, err });
std.process.exit(1);
};
defer file.close();
var buffer: [4096]u8 = undefined;
const temp_path = try std.fmt.allocPrint(allocator, "{s}.tmp", .{file_path});
defer allocator.free(temp_path);
const temp_file = std.fs.cwd().createFile(temp_path, .{}) catch |err| {
print("Error creating temp file: {}\n", .{err});
std.process.exit(1);
};
defer temp_file.close();
var output_buffer: [4096]u8 = undefined;
var reader = file.reader(&buffer).interface;
var writer = temp_file.writer(&output_buffer).interface;
try substituteLines(
&reader,
&writer,
args[2],
args[3],
);
try writer.flush();
std.fs.cwd().rename(temp_path, file_path) catch |err| {
print("Error moving temp file: {}\n", .{err});
std.process.exit(1);
};
} else {
print("Error: Unknown operation '{s}'\n", .{operation});
std.process.exit(1);
}
}
fn substituteLines(reader: *std.Io.Reader, writer: *std.Io.Writer, original: []const u8, replacement: []const u8) !void {
var line_buf: [1024]u8 = undefined;
var line = std.Io.Writer.fixed(&line_buf);
while ((try reader.streamDelimiterEnding(&line, '\n')) > 0) : (_ = line.consumeAll()) {
const line_content = line.buffered();
if (std.mem.eql(u8, line_content, original))
try writer.writeAll(replacement)
else
try writer.writeAll(line_content);
// Write our \n
if (reader.takeByte()) |b|
try writer.writeByte(b)
else |_| {} // end of stream
}
}
test "substitute lines exact match" {
const test_content = "line1\nreplace_me\nline3\nreplace_me\nline5";
const expected = "line1\nnew_line\nline3\nnew_line\nline5";
var input_stream = std.Io.Reader.fixed(test_content);
var output_buf: [1024]u8 = undefined;
var output_stream = std.Io.Writer.fixed(&output_buf);
try substituteLines(
&input_stream,
&output_stream,
"replace_me",
"new_line",
);
const result = output_stream.buffered();
try std.testing.expectEqualStrings(expected, result);
}
test "no match found" {
const test_content = "line1\nline2\nline3";
var input_stream = std.Io.Reader.fixed(test_content);
var output_buf: [1024]u8 = undefined;
var output_stream = std.Io.Writer.fixed(&output_buf);
try substituteLines(
&input_stream,
&output_stream,
"nonexistent",
"replacement",
);
const result = output_stream.buffer[0..output_stream.end];
try std.testing.expectEqualStrings(test_content, result);
}
test "partial match not replaced" {
const test_content = "prefix_match_suffix\nmatch\nline3";
const expected = "prefix_match_suffix\nreplaced\nline3";
var input_stream = std.Io.Reader.fixed(test_content);
var output_buf: [1024]u8 = undefined;
var output_stream = std.Io.Writer.fixed(&output_buf);
try substituteLines(
&input_stream,
&output_stream,
"match",
"replaced",
);
const result = output_stream.buffered();
try std.testing.expectEqualStrings(expected, result);
}