aboutsummaryrefslogtreecommitdiff
path: root/utils.h
blob: 8012af16a05edb2c03603e71f62d3d2b1c8875b5 (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
101
102
103
104
105
106
107
108
109
110
/*
 * utils.h - Some helper code for debugging and error logging.
 */

#define streq(a, b) (strcmp(a, b) == 0)
// TODO: better error reporting
#define check(cond, ...) do { if (!(cond)) { fprintf(stderr, __VA_ARGS__); fwrite("\n", 1, 1, stderr); _exit(1); } } while(0)
#define debug(...) do { if (verbose) fprintf(stderr, __VA_ARGS__); } while(0)

static int verbose = 0;
static int visualize_delay = -1;

/* 
 * Helper function to skip past all spaces (and comments)
 * Returns a pointer to the first non-space character.
 */
static inline const char *after_spaces(const char *str)
{
    // Skip whitespace and comments:
  skip_whitespace:
    switch (*str) {
        case ' ': case '\r': case '\n': case '\t': {
            ++str;
            goto skip_whitespace;
        }
        case '#': {
            while (*str && *str != '\n') ++str;
            goto skip_whitespace;
        }
    }
    return str;
}

static inline int matchchar(const char **str, char c)
{
    *str = after_spaces(*str);
    if (**str == c) {
        ++(*str);
        return 1;
    } else {
        return 0;
    }
}

static void visualize(const char *source, const char *ptr, const char *msg)
{
    if (!verbose) return;
    fprintf(stderr, "\033[0;1m\r\033[2A\033[K%.*s\033[0;2m%s\033[0m\n",
            (int)(ptr-source), source, ptr);
    fprintf(stderr, "\033[0;1m");
    for (--ptr ; ptr > source; --ptr) putc(' ', stderr);
    fprintf(stderr, "^\033[K\n");
    if (msg)
        fprintf(stderr, "\033[K\033[33;1m%s\033[0m", msg);
    if (visualize_delay > 0)
        usleep(visualize_delay);
}

/*
 * Write an unescaped version of `src` to `dest` (at most bufsize-1 chars,
 * terminated by a null byte)
 */
static size_t unescape_string(char *dest, const char *src, size_t bufsize)
{
    size_t len = 0;
#define PUT(c) do { *(dest++) = (char)(c); ++len; } while (0)
    for ( ; *src && len < bufsize; ++src) {
        if (*src != '\\') {
            PUT(*src);
            continue;
        }
        ++src;
        switch (*src) {
            case 'a': PUT('\a'); break; case 'b': PUT('\b'); break;
            case 'n': PUT('\n'); break; case 'r': PUT('\r'); break;
            case 't': PUT('\t'); break; case 'v': PUT('\v'); break;
            case 'e': PUT('\033'); break;
            case 'x': { // Hex
                static const char hextable[255] = {
                    ['0']=0x10, ['1']=0x1, ['2']=0x2, ['3']=0x3, ['4']=0x4,
                    ['5']=0x5, ['6']=0x6, ['7']=0x7, ['8']=0x8, ['9']=0x9,
                    ['a']=0xa, ['b']=0xb, ['c']=0xc, ['d']=0xd, ['e']=0xe, ['f']=0xf,
                    ['A']=0xa, ['B']=0xb, ['C']=0xc, ['D']=0xd, ['E']=0xe, ['F']=0xf,
                };
                if (hextable[(int)src[1]] && hextable[(int)src[2]]) {
                    PUT((hextable[(int)src[1]] << 4) | (hextable[(int)src[2]] & 0xF));
                    src += 2;
                }
                break;
            }
            case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { // Octal
                int c = *src - '0';
                if ('0' <= src[1] && src[1] <= '7') {
                    ++src;
                    c = (c << 3) | (*src - '0');
                    if ('0' <= src[1] && src[1] <= '7' && (c << 3) < 256) {
                        ++src;
                        c = (c << 3) | (*src - '0');
                    }
                }
                PUT(c);
                break;
            }
            default: PUT(*src); break;
        }
    }
    *dest = '\0';
    return len;
#undef PUT
}