vtparser.c
1 /* Copyright (c) 2017-2019 Rob King 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * * Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * * Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * * Neither the name of the copyright holder nor the 12 * names of contributors may be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS, 19 * COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 22 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 #include <stdbool.h> 28 #include <string.h> 29 #include "vtparser.h" 30 31 /**** DATA TYPES */ 32 #define MAXACTIONS 128 33 34 typedef struct ACTION ACTION; 35 struct ACTION{ 36 wchar_t lo, hi; 37 void (*cb)(VTPARSER *p, wchar_t w); 38 STATE *next; 39 }; 40 41 struct STATE{ 42 void (*entry)(VTPARSER *v); 43 ACTION actions[MAXACTIONS]; 44 }; 45 46 /**** GLOBALS */ 47 static STATE ground, escape, escape_intermediate, csi_entry, 48 csi_ignore, csi_param, csi_intermediate, osc_string; 49 50 /**** ACTION FUNCTIONS */ 51 static void 52 reset(VTPARSER *v) 53 { 54 v->inter = v->narg = v->nosc = 0; 55 memset(v->args, 0, sizeof(v->args)); 56 memset(v->oscbuf, 0, sizeof(v->oscbuf)); 57 } 58 59 static void 60 ignore(VTPARSER *v, wchar_t w) 61 { 62 (void)v; (void)w; /* avoid warnings */ 63 } 64 65 static void 66 collect(VTPARSER *v, wchar_t w) 67 { 68 v->inter = v->inter? v->inter : (int)w; 69 } 70 71 static void 72 collectosc(VTPARSER *v, wchar_t w) 73 { 74 if (v->nosc < MAXOSC) 75 v->oscbuf[v->nosc++] = w; 76 } 77 78 static void 79 param(VTPARSER *v, wchar_t w) 80 { 81 v->narg = v->narg? v->narg : 1; 82 83 if (w == L';') 84 v->args[v->narg++] = 0; 85 else if (v->narg < MAXPARAM && v->args[v->narg - 1] < 9999) 86 v->args[v->narg - 1] = v->args[v->narg - 1] * 10 + (w - 0x30); 87 } 88 89 #define DO(k, t, f, n, a) \ 90 static void \ 91 do ## k (VTPARSER *v, wchar_t w) \ 92 { \ 93 if (t) \ 94 f (v, v->p, w, v->inter, n, a, v->oscbuf); \ 95 } 96 97 DO(control, w < MAXCALLBACK && v->cons[w], v->cons[w], 0, NULL) 98 DO(escape, w < MAXCALLBACK && v->escs[w], v->escs[w], v->inter > 0, &v->inter) 99 DO(csi, w < MAXCALLBACK && v->csis[w], v->csis[w], v->narg, v->args) 100 DO(print, v->print, v->print, 0, NULL) 101 DO(osc, v->osc, v->osc, v->nosc, NULL) 102 103 /**** PUBLIC FUNCTIONS */ 104 VTCALLBACK 105 vtonevent(VTPARSER *vp, VtEvent t, wchar_t w, VTCALLBACK cb) 106 { 107 VTCALLBACK o = NULL; 108 if (w < MAXCALLBACK) switch (t){ 109 case VTPARSER_CONTROL: o = vp->cons[w]; vp->cons[w] = cb; break; 110 case VTPARSER_ESCAPE: o = vp->escs[w]; vp->escs[w] = cb; break; 111 case VTPARSER_CSI: o = vp->csis[w]; vp->csis[w] = cb; break; 112 case VTPARSER_PRINT: o = vp->print; vp->print = cb; break; 113 case VTPARSER_OSC: o = vp->osc; vp->osc = cb; break; 114 } 115 116 return o; 117 } 118 119 static void 120 handlechar(VTPARSER *vp, wchar_t w) 121 { 122 vp->s = vp->s? vp->s : &ground; 123 for (ACTION *a = vp->s->actions; a->cb; a++) if (w >= a->lo && w <= a->hi){ 124 a->cb(vp, w); 125 if (a->next){ 126 vp->s = a->next; 127 if (a->next->entry) 128 a->next->entry(vp); 129 } 130 return; 131 } 132 } 133 134 void 135 vtwrite(VTPARSER *vp, const char *s, size_t n) 136 { 137 wchar_t w = 0; 138 while (n){ 139 size_t r = mbrtowc(&w, s, n, &vp->ms); 140 switch (r){ 141 case -2: /* incomplete character, try again */ 142 return; 143 144 case -1: /* invalid character, skip it */ 145 w = VTPARSER_BAD_CHAR; 146 r = 1; 147 break; 148 149 case 0: /* literal zero, write it but advance */ 150 r = 1; 151 break; 152 } 153 154 n -= r; 155 s += r; 156 handlechar(vp, w); 157 } 158 } 159 160 /**** STATE DEFINITIONS 161 * This was built by consulting the excellent state chart created by 162 * Paul Flo Williams: http://vt100.net/emu/dec_ansi_parser 163 * Please note that Williams does not (AFAIK) endorse this work. 164 */ 165 #define MAKESTATE(name, onentry, ...) \ 166 static STATE name ={ \ 167 onentry , \ 168 { \ 169 {0x00, 0x00, ignore, NULL}, \ 170 {0x7f, 0x7f, ignore, NULL}, \ 171 {0x18, 0x18, docontrol, &ground}, \ 172 {0x1a, 0x1a, docontrol, &ground}, \ 173 {0x1b, 0x1b, ignore, &escape}, \ 174 {0x01, 0x06, docontrol, NULL}, \ 175 {0x08, 0x17, docontrol, NULL}, \ 176 {0x19, 0x19, docontrol, NULL}, \ 177 {0x1c, 0x1f, docontrol, NULL}, \ 178 __VA_ARGS__ , \ 179 {0x07, 0x07, docontrol, NULL}, \ 180 {0x00, 0x00, NULL, NULL} \ 181 } \ 182 } 183 184 MAKESTATE(ground, NULL, 185 {0x20, WCHAR_MAX, doprint, NULL} 186 ); 187 188 MAKESTATE(escape, reset, 189 {0x21, 0x21, ignore, &osc_string}, 190 {0x20, 0x2f, collect, &escape_intermediate}, 191 {0x30, 0x4f, doescape, &ground}, 192 {0x51, 0x57, doescape, &ground}, 193 {0x59, 0x59, doescape, &ground}, 194 {0x5a, 0x5a, doescape, &ground}, 195 {0x5c, 0x5c, doescape, &ground}, 196 {0x6b, 0x6b, ignore, &osc_string}, 197 {0x60, 0x7e, doescape, &ground}, 198 {0x5b, 0x5b, ignore, &csi_entry}, 199 {0x5d, 0x5d, ignore, &osc_string}, 200 {0x5e, 0x5e, ignore, &osc_string}, 201 {0x50, 0x50, ignore, &osc_string}, 202 {0x5f, 0x5f, ignore, &osc_string} 203 ); 204 205 MAKESTATE(escape_intermediate, NULL, 206 {0x20, 0x2f, collect, NULL}, 207 {0x30, 0x7e, doescape, &ground} 208 ); 209 210 MAKESTATE(csi_entry, reset, 211 {0x20, 0x2f, collect, &csi_intermediate}, 212 {0x3a, 0x3a, ignore, &csi_ignore}, 213 {0x30, 0x39, param, &csi_param}, 214 {0x3b, 0x3b, param, &csi_param}, 215 {0x3c, 0x3f, collect, &csi_param}, 216 {0x40, 0x7e, docsi, &ground} 217 ); 218 219 MAKESTATE(csi_ignore, NULL, 220 {0x20, 0x3f, ignore, NULL}, 221 {0x40, 0x7e, ignore, &ground} 222 ); 223 224 MAKESTATE(csi_param, NULL, 225 {0x30, 0x39, param, NULL}, 226 {0x3b, 0x3b, param, NULL}, 227 {0x3a, 0x3a, ignore, &csi_ignore}, 228 {0x3c, 0x3f, ignore, &csi_ignore}, 229 {0x20, 0x2f, collect, &csi_intermediate}, 230 {0x40, 0x7e, docsi, &ground} 231 ); 232 233 MAKESTATE(csi_intermediate, NULL, 234 {0x20, 0x2f, collect, NULL}, 235 {0x30, 0x3f, ignore, &csi_ignore}, 236 {0x40, 0x7e, docsi, &ground} 237 ); 238 239 MAKESTATE(osc_string, reset, 240 {0x07, 0x07, doosc, &ground}, 241 {0x20, 0x7f, collectosc, NULL} 242 );