~leon_plickat/nfm

771b65cb4e2a49d1ea6a896130df80a2e4636473 — Leon Henrik Plickat 4 months ago a9d58fd
Fix a few bugs in the init tokenizer

Thanks novakane for reporting!
1 files changed, 120 insertions(+), 37 deletions(-)

M src/ini.zig
M src/ini.zig => src/ini.zig +120 -37
@@ 41,7 41,7 @@ fn IniTok(comptime T: type) type {
                line.* += 1;
                if (try self.reader.readUntilDelimiterOrEof(&self.stack_buffer, '\n')) |__buf| {
                    if (__buf.len == 1) {
                        if (__buf[0] == '\n') {
                        if (__buf[0] == '#') {
                            continue;
                        } else {
                            return error.InvalidLine;


@@ 69,28 69,39 @@ fn IniTok(comptime T: type) type {
                    }

                    // Is this line an assignment?
                    var it = mem.tokenize(u8, buf, "=");
                    const variable = blk: {
                        const _variable = mem.trim(u8, it.next() orelse return error.InvalidLine, &ascii.spaces);
                        if (_variable.len == 0) return error.InvalidLine;
                        break :blk _variable;
                    };
                    const value = blk: {
                        const _value = mem.trim(u8, it.next() orelse return error.InvalidLine, &ascii.spaces);
                        if (_value.len < 2 or _value[_value.len - 1] != ';') return error.InvalidLine;
                        // TODO[zig] should be inline, but that crashes the zig compiler right now
                        for ([_]u8{ '\'', '"' }) |q| {
                            if (_value[0] == q) {
                                if (_value[_value.len - 2] == q and _value.len > 3) {
                                    break :blk _value[1 .. _value.len - 2];
                                } else {
                                    return error.InvalidLine;
                                }
                    var eq_pos = blk: {
                        for (buf) |char, i| {
                            if (char == '=') {
                                if (i == buf.len - 1) return error.InvalidLine;
                                break :blk i;
                            }
                        }
                        break :blk _value[0 .. _value.len - 1];
                        return error.InvalidLine;
                    };
                    return Content{
                        .assign = .{
                            .variable = blk: {
                                const variable = mem.trim(u8, buf[0..eq_pos], &ascii.spaces);
                                if (variable.len == 0) return error.InvalidLine;
                                break :blk variable;
                            },
                            .value = blk: {
                                const value = mem.trim(u8, buf[eq_pos + 1 ..], &ascii.spaces);
                                if (value.len < 2 or value[value.len - 1] != ';') return error.InvalidLine;
                                // TODO[zig] should be inline, but that crashes the zig compiler right now
                                for ([_]u8{ '\'', '"' }) |q| {
                                    if (value[0] == q) {
                                        if (value[value.len - 2] == q and value.len > 3) {
                                            break :blk value[1 .. value.len - 2];
                                        } else {
                                            return error.InvalidLine;
                                        }
                                    }
                                }
                                break :blk value[0 .. value.len - 1];
                            },
                        },
                    };
                    return Content{ .assign = .{ .variable = variable, .value = value } };
                } else {
                    return null;
                }


@@ 115,6 126,11 @@ test "ini tokenizer good input" {
        \\
        \\[header2]
        \\[header3]
        \\#
        \\
        \\hello = this has spaces;
        \\hello = this one; is weird;
        \\hello = test=test;
        \\
    ).reader();



@@ 172,28 188,95 @@ test "ini tokenizer good input" {
        unreachable;
    }

    try std.testing.expect((try it.next(&line)) == null);
}

test "ini tokenizer bad input" {
    const reader = io.fixedBufferStream(
        \\[header]
        \\
        \\a = b # missing semicolon
        \\
    ).reader();
    if (try it.next(&line)) |a| {
        try std.testing.expect(a == .assign);
        try std.testing.expectEqualSlices(u8, "hello", a.assign.variable);
        try std.testing.expectEqualSlices(u8, "this has spaces", a.assign.value);
        try std.testing.expect(line == 11);
    } else {
        unreachable;
    }

    var it = tokenize(reader);
    var line: usize = 0;
    if (try it.next(&line)) |a| {
        try std.testing.expect(a == .assign);
        try std.testing.expectEqualSlices(u8, "hello", a.assign.variable);
        try std.testing.expectEqualSlices(u8, "this one; is weird", a.assign.value);
        try std.testing.expect(line == 12);
    } else {
        unreachable;
    }

    if (try it.next(&line)) |a| {
        try std.testing.expect(a == .section);
        try std.testing.expectEqualSlices(u8, "header", a.section);
        try std.testing.expect(line == 1);
        try std.testing.expect(a == .assign);
        try std.testing.expectEqualSlices(u8, "hello", a.assign.variable);
        try std.testing.expectEqualSlices(u8, "test=test", a.assign.value);
        try std.testing.expect(line == 13);
    } else {
        unreachable;
    }

    try std.testing.expectError(error.InvalidLine, it.next(&line));
    try std.testing.expect(line == 3);
    try std.testing.expect((try it.next(&line)) == null);
}

test "ini tokenizer bad input" {
    {
        const reader = io.fixedBufferStream(
            \\[section
            \\
        ).reader();
        var it = tokenize(reader);
        var line: usize = 0;
        try std.testing.expectError(error.InvalidLine, it.next(&line));
        try std.testing.expect(line == 1);
    }
    {
        const reader = io.fixedBufferStream(
            \\section]
            \\
        ).reader();
        var it = tokenize(reader);
        var line: usize = 0;
        try std.testing.expectError(error.InvalidLine, it.next(&line));
        try std.testing.expect(line == 1);
    }
    {
        const reader = io.fixedBufferStream(
            \\[]
            \\
        ).reader();
        var it = tokenize(reader);
        var line: usize = 0;
        try std.testing.expectError(error.InvalidLine, it.next(&line));
        try std.testing.expect(line == 1);
    }
    {
        const reader = io.fixedBufferStream(
            \\ =B;
            \\
        ).reader();
        var it = tokenize(reader);
        var line: usize = 0;
        try std.testing.expectError(error.InvalidLine, it.next(&line));
        try std.testing.expect(line == 1);
    }
    {
        const reader = io.fixedBufferStream(
            \\a =
            \\
        ).reader();
        var it = tokenize(reader);
        var line: usize = 0;
        try std.testing.expectError(error.InvalidLine, it.next(&line));
        try std.testing.expect(line == 1);
    }
    {
        const reader = io.fixedBufferStream(
            \\a =  ;
            \\
        ).reader();
        var it = tokenize(reader);
        var line: usize = 0;
        try std.testing.expectError(error.InvalidLine, it.next(&line));
        try std.testing.expect(line == 1);
    }
}