git home / emma home
logo

mtm

Terminal Multiplexer. Emma's branch.
git clone https://git.y1.nz/archives/mtm.tar.gz
Files | Log | Refs

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 );

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.