forked from natecraddock/ziglua
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpatch.zig
More file actions
141 lines (118 loc) · 5.01 KB
/
patch.zig
File metadata and controls
141 lines (118 loc) · 5.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! A simple script to apply a patch to a file
//! Does minimal validation and is just enough for patching Lua 5.1
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var threaded = Io.Threaded.init(allocator, .{});
defer threaded.deinit();
const io = threaded.io();
const args = try std.process.argsAlloc(allocator);
if (args.len != 4) @panic("Wrong number of arguments");
const file_path = args[1];
const patch_file_path = args[2];
const output_path = args[3];
const patch_file = patch_file: {
const patch_file = try Io.Dir.cwd().openFile(io, patch_file_path, .{ .mode = .read_only });
defer patch_file.close(io);
var buf: [4096]u8 = undefined;
var reader = patch_file.reader(io, &buf);
break :patch_file try reader.interface.allocRemaining(allocator, .unlimited);
};
const chunk_details = Chunk.init(allocator, patch_file, 0) orelse @panic("No chunk data found");
const file = try Io.Dir.cwd().openFile(io, file_path, .{ .mode = .read_only });
defer file.close(io);
var in_buf: [4096]u8 = undefined;
var reader = file.reader(io, &in_buf);
const output = try Io.Dir.cwd().createFile(io, output_path, .{});
defer output.close(io);
var out_buf: [4096]u8 = undefined;
var writer = output.writer(io, &out_buf);
var state: State = .copy;
var line_number: usize = 1;
while (true) : (line_number += 1) {
if (line_number == chunk_details.src) state = .chunk;
switch (state) {
.copy => {
_ = reader.interface.streamDelimiter(&writer.interface, '\n') catch |err| switch (err) {
error.EndOfStream => {
try writer.end();
return;
},
else => return err,
};
reader.interface.toss(1);
try writer.interface.writeByte('\n');
},
.chunk => {
const chunk = chunk_details.lines[line_number - chunk_details.src];
switch (chunk.action) {
.remove => {
const line = (try reader.interface.takeDelimiter('\n')).?;
if (!std.mem.eql(u8, chunk.buf, line)) @panic("Failed to apply patch");
},
.keep => {
const line = (try reader.interface.takeDelimiter('\n')).?;
if (!std.mem.eql(u8, chunk.buf, line)) @panic("Failed to apply patch");
try writer.interface.writeAll(line);
try writer.interface.writeByte('\n');
},
.add => {
try writer.interface.writeAll(chunk.buf);
try writer.interface.writeByte('\n');
},
}
if (line_number - chunk_details.src == chunk_details.lines.len - 1) state = .copy;
},
}
}
}
const State = enum { copy, chunk };
const Chunk = struct {
lines: []Line,
src: usize,
dst: usize,
const Line = struct {
const Action = enum { remove, keep, add };
action: Action,
buf: []const u8,
};
fn init(arena: std.mem.Allocator, contents: []const u8, pos: usize) ?Chunk {
var it = std.mem.tokenizeScalar(u8, contents[pos..], '\n');
while (it.next()) |line| {
if (std.mem.startsWith(u8, line, "@@")) {
const end = std.mem.indexOfPosLinear(u8, line, 3, "@@").?;
const details = line[4 .. end - 2];
const space_index = std.mem.indexOfScalar(u8, details, ' ').?;
const src = getLineNumber(details[0..space_index]);
const dst = getLineNumber(details[space_index + 1 ..]);
var lines: std.ArrayListUnmanaged(Line) = .empty;
while (true) {
const diff_line = it.next() orelse break;
if (std.mem.startsWith(u8, diff_line, "@@")) break;
const action: Line.Action = switch (diff_line[0]) {
'-' => .remove,
' ' => .keep,
'+' => .add,
else => @panic("Bad patch file"),
};
lines.append(arena, .{ .action = action, .buf = diff_line[1..] }) catch unreachable;
}
return .{
.lines = lines.toOwnedSlice(arena) catch unreachable,
.src = src,
.dst = dst,
};
}
}
return null;
}
fn getLineNumber(buf: []const u8) usize {
const comma = std.mem.indexOfScalar(u8, buf, ',').?;
return std.fmt.parseInt(usize, buf[0..comma], 10) catch unreachable;
}
};
const std = @import("std");
const Allocator = std.mem.Allocator;
const Io = std.Io;
const File = Io.File;