summaryrefslogtreecommitdiff
path: root/src/csi.h
blob: b4609eafbe83c8243d00b34c36918c2ff7f75699 (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
#ifndef CSI_H
#define CSI_H

#include <stddef.h>
#include <stdint.h>
#include <limits.h>

#define MAX_PARAMS 16

struct csi {
    int params[MAX_PARAMS];
    int nparams;
    uint16_t subparams;
    char prefix, intermediate, final;
};

enum csi_phase { CSI_ENTRY, CSI_PARAMETERS, CSI_INTERMEDIATES, CSI_IGNORE };
enum csi_result { CSI_MORE, CSI_DISPATCH, CSI_DISCARD, CSI_CONTROL };

struct csi_parser {
    struct csi csi;
    enum csi_phase phase;
};

static inline void csi_parser_reset(struct csi_parser *p)
{
    p->csi.params[0] = 0;
    p->csi.nparams = 0;
    p->csi.subparams = 0;
    p->csi.prefix = p->csi.intermediate = p->csi.final = 0;
    p->phase = CSI_ENTRY;
}

static inline int csi_count(const struct csi *csi)
{
    return csi->params[0] > 0 ? csi->params[0] : 1;
}

static inline enum csi_result csi_read(struct csi_parser *restrict p, const char *restrict s,
                                     size_t len, size_t *restrict consumed)
{
    size_t i = 0;
    enum csi_result result = CSI_MORE;
    while (i < len) {
        unsigned char ch = (unsigned char)s[i];
        if (ch < 0x20 || ch >= 0x80) {
            result = CSI_CONTROL;
            break;
        }
        i++;
        if (ch == 0x7f) continue;
        if (ch >= 0x40) {
            p->csi.final = (char)ch;
            result = p->phase == CSI_IGNORE ? CSI_DISCARD : CSI_DISPATCH;
            break;
        }
        if (p->phase == CSI_IGNORE) continue;

        if (ch >= '0' && ch <= ';') {
            if (p->phase == CSI_INTERMEDIATES) {
                p->phase = CSI_IGNORE;
                continue;
            }
            p->phase = CSI_PARAMETERS;
            int n = p->csi.nparams ? p->csi.nparams : 1;
            int value = p->csi.params[n - 1];
            for (;;) {
                if (ch <= '9') {
                    for (;;) {
                        int digit = ch - '0';
                        value = value < 214748364 ? value * 10 + digit : INT_MAX;
                        if (i == len) break;
                        ch = (unsigned char)s[i];
                        if (ch < '0' || ch > '9') break;
                        i++;
                    }
                } else {
                    p->csi.params[n - 1] = value;
                    if (n == MAX_PARAMS) {
                        p->phase = CSI_IGNORE;
                        break;
                    }
                    if (ch == ':') p->csi.subparams |= (uint16_t)(1u << n);
                    n++;
                    value = 0;
                }
                if (i == len) break;
                ch = (unsigned char)s[i];
                if (ch < '0' || ch > ';') break;
                i++;
            }
            p->csi.params[n - 1] = value;
            p->csi.nparams = n;
        } else if (ch >= '<') {
            if (p->phase != CSI_ENTRY || p->csi.prefix) p->phase = CSI_IGNORE;
            else p->csi.prefix = (char)ch;
        } else {
            if (p->phase == CSI_INTERMEDIATES) p->phase = CSI_IGNORE;
            else {
                p->phase = CSI_INTERMEDIATES;
                p->csi.intermediate = (char)ch;
            }
        }
    }
    *consumed = i;
    return result;
}

#endif