aboutsummaryrefslogtreecommitdiffhomepage
path: root/io.c
blob: dcfbd0882503f4a117016eb66fb6518341953528 (plain)
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
struct SourceLocation {
    const char* filename;
    int line;
};
typedef struct SourceLocation SourceLocation;

struct InFile {
    const char* buf;
    int pos;
    SourceLocation loc;
};
typedef struct InFile InFile;

InFile* infile_open(const char* filename) {
    FILE* in;
    if (strcmp(filename, "-") == 0) {
        in = stdin;
    } else {
        in = fopen(filename, "rb");
    }
    if (!in) {
        return NULL;
    }

    size_t buf_size = 1024 * 10;
    char* buf = calloc(buf_size, sizeof(char));
    char* cur = buf;
    char* tmp = calloc(1024, sizeof(char));

    while (fgets(tmp, 1024, in)) {
        size_t len = strlen(tmp);
        size_t used_size = cur - buf;

        if (buf_size <= used_size + len) {
            size_t old_size = buf_size;
            buf_size *= 2;
            buf = realloc(buf, buf_size);
            memset(buf + old_size, 0, buf_size - old_size);
            cur = buf + used_size;
        }

        memcpy(cur, tmp, len);
        cur += len;
    }
    fclose(in);

    InFile* in_file = calloc(1, sizeof(InFile));
    in_file->buf = buf;
    in_file->loc.filename = filename;
    in_file->loc.line = 1;
    return in_file;
}

BOOL infile_eof(InFile* f) {
    return f->buf[f->pos] == '\0';
}

char infile_peek_char(InFile* f) {
    char c = f->buf[f->pos];

    // Skip a pair of backslash and new-line.
    if (c == '\\') {
        char c2 = f->buf[f->pos + 1];
        // C23: 5.1.1.2
        // A source file that is not empty shall end in a new-line character,
        // which shall not be immediately preceded by a backslash character
        // before any such splicing takes place.
        if (c2 == '\0') {
            fatal_error("%s:%d: <new-line> expected, but got <eof>", f->loc.filename, f->loc.line);
        }
        // TODO: crlf
        if (c2 == '\r' || c2 == '\n') {
            f->pos += 2;
            return infile_peek_char(f);
        }
    }

    // Normalize new-line.
    // TODO: crlf
    if (c == '\r')
        c = '\n';
    return c;
}

char infile_next_char(InFile* f) {
    char c = infile_peek_char(f);
    ++f->pos;
    if (c == '\n')
        ++f->loc.line;
    return c;
}

BOOL infile_consume_if(InFile* f, char expected) {
    if (infile_peek_char(f) == expected) {
        infile_next_char(f);
        return TRUE;
    } else {
        return FALSE;
    }
}