-
-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathini.zig
More file actions
135 lines (112 loc) · 4.76 KB
/
ini.zig
File metadata and controls
135 lines (112 loc) · 4.76 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
const std = @import("std");
/// An entry in a ini file. Each line that contains non-whitespace text can
/// be categorized into a record type.
pub const Record = union(enum) {
/// A section heading enclosed in `[` and `]`. The brackets are not included.
section: [:0]const u8,
/// A line that contains a key-value pair separated by `=`.
/// Both key and value have the excess whitespace trimmed.
/// Both key and value allow escaping with C string syntax.
property: KeyValue,
/// A line that is either escaped as a C string or contains no `=`
enumeration: [:0]const u8,
};
pub const KeyValue = struct {
key: [:0]const u8,
value: [:0]const u8,
};
const whitespace = " \r\t\x00";
/// WARNING:
/// This function is not a general purpose function but
/// requires to be executed on slices of the line_buffer *after*
/// the NUL terminator appendix.
/// This function will override the character after the slice end,
/// so make sure there is one available!
fn insertNulTerminator(slice: []const u8) [:0]const u8 {
const mut_ptr = @as([*]u8, @ptrFromInt(@intFromPtr(slice.ptr)));
mut_ptr[slice.len] = 0;
return mut_ptr[0..slice.len :0];
}
pub const Parser = struct {
const Self = @This();
allocator: std.mem.Allocator,
line_buffer: std.ArrayList(u8),
reader: *std.Io.Reader,
comment_characters: []const u8,
pub fn deinit(self: *Self) void {
self.line_buffer.deinit(self.allocator);
self.* = undefined;
}
pub fn next(self: *Self) !?Record {
self.line_buffer.clearRetainingCapacity();
while (true) {
{
var list_writer = std.Io.Writer.Allocating.fromArrayList(self.allocator, &self.line_buffer);
defer self.line_buffer = list_writer.toArrayList();
_ = try self.reader.streamDelimiterLimit(&list_writer.writer, '\n', .limited(4096));
try list_writer.writer.flush();
}
const discarded = self.reader.discard(.limited(1)) catch |e| blk: {
switch (e) {
error.EndOfStream => {
if (self.line_buffer.items.len == 0)
return null;
break :blk 0;
},
else => return e,
}
};
if (self.line_buffer.items.len == 0 and discarded == 0)
return null;
try self.line_buffer.append(self.allocator, 0); // append guaranteed space for sentinel
var line: []const u8 = self.line_buffer.items;
var last_index: usize = 0;
// handle comments and escaping
while (last_index < line.len) {
if (std.mem.indexOfAnyPos(u8, line, last_index, self.comment_characters)) |index| {
// escape character if needed, then skip it (it's not a comment)
if (index > 0) {
const previous_index = index - 1;
const previous_char = line[previous_index];
if (previous_char == '\\') {
_ = self.line_buffer.orderedRemove(previous_index);
line = self.line_buffer.items;
last_index = index + 1;
continue;
}
}
line = std.mem.trim(u8, line[0..index], whitespace);
} else {
line = std.mem.trim(u8, line, whitespace);
}
break;
}
if (line.len == 0) {
self.line_buffer.clearRetainingCapacity();
continue;
}
if (std.mem.startsWith(u8, line, "[") and std.mem.endsWith(u8, line, "]")) {
return Record{ .section = insertNulTerminator(line[1 .. line.len - 1]) };
}
if (std.mem.indexOfScalar(u8, line, '=')) |index| {
return Record{
.property = KeyValue{
// note: the key *might* replace the '=' in the slice with 0!
.key = insertNulTerminator(std.mem.trim(u8, line[0..index], whitespace)),
.value = insertNulTerminator(std.mem.trim(u8, line[index + 1 ..], whitespace)),
},
};
}
return Record{ .enumeration = insertNulTerminator(line) };
}
}
};
/// Returns a new parser that can read the ini structure
pub fn parse(allocator: std.mem.Allocator, reader: *std.Io.Reader, comment_characters: []const u8) Parser {
return Parser{
.allocator = allocator,
.line_buffer = .empty,
.reader = reader,
.comment_characters = comment_characters,
};
}