git.y1.nz

rvis

Minimal terminal colorizer.
download: https://git.y1.nz/archives/rvis.tar.gz
README | Files | Log | Refs

vt.c

      1 /* See vt.h for documentation. */
      2 
      3 #include <stdio.h> /* for printf in init_callbacks */
      4 #include <stdlib.h> /* for malloc/free */
      5 
      6 #include "vt.h"
      7 
      8 /* state machine nodes */
      9 #define GROUND  0
     10 #define ESCAPE  1
     11 #define ESCAPE_INTERMEDIATE  2
     12 #define DCS_ENTRY  3
     13 #define DCS_INTERMEDIATE  4
     14 #define DCS_IGNORE  5
     15 #define DCS_PARAM  6
     16 #define DCS_PASSTHROUGH  7
     17 #define CSI_IGNORE  8
     18 #define CSI_PARAM  9
     19 #define CSI_INTERMEDIATE  10
     20 #define CSI_ENTRY  11
     21 #define SOS_PM_APC_STRING  12
     22 #define OSC_STRING  13
     23 
     24 const char buffer[1000];
     25 
     26 ParserState *
     27 init_parser_state()
     28 {
     29 	ParserState *state = malloc(sizeof(ParserState));
     30 	state->node = GROUND;
     31 	state->intermediate_char_count = 0;
     32 	state->intermediate_chars = buffer;
     33 	return state;
     34 }
     35 
     36 void
     37 free_parser_state(ParserState *state)
     38 {
     39 	free((void *) state->intermediate_chars);
     40 	free((void *) state);
     41 }
     42 
     43 void
     44 init_parser_callbacks(ParserCallbacks *cbs, void (*pass)(ParserState *))
     45 {
     46 	/* set all callbacks to pass by default */
     47 	cbs->ignore = cbs->print = cbs->execute =
     48 	cbs->clear = cbs->collect = cbs->param =
     49 	cbs->esc_dispatch = cbs->csi_dispatch = cbs->hook =
     50 	cbs->put = cbs->unhook = cbs->osc_start =
     51 	cbs->osc_put = cbs->osc_end = cbs->unhandled = pass;
     52 }
     53 
     54 /* state machine actions (just for shortening's sake) */
     55 #define IGNORE  callbacks->ignore(state)
     56 #define PRINT  callbacks->print(state)
     57 #define EXECUTE  callbacks->execute(state)
     58 #define CLEAR  callbacks->clear(state)
     59 #define COLLECT callbacks->collect(state)
     60 #define PARAM  callbacks->param(state)
     61 #define ESC_DISPATCH  callbacks->esc_dispatch(state)
     62 #define CSI_DISPATCH  callbacks->csi_dispatch(state)
     63 #define HOOK  callbacks->hook(state)
     64 #define PUT  callbacks->put(state)
     65 #define UNHOOK  callbacks->unhook(state)
     66 #define OSC_START  callbacks->osc_start(state)
     67 #define OSC_PUT  callbacks->osc_put(state)
     68 #define OSC_END  callbacks->osc_end(state)
     69 #define UNHANDLED  callbacks->unhandled(state)
     70 
     71 /* helper macros (for brevity) */
     72 #define IS(value)  (ch == (value))
     73 #define ISR(lo, hi)  ((lo) <= ch && ch <= (hi))
     74 #define TO(new_node)  state->node = new_node
     75 #define ACTION(condition, actions)  \
     76 	if (condition) { actions; return; }
     77 
     78 /* special handler, for when we are moving states. */
     79 /* the variadic arg should always end with a call to TO(...). */
     80 #define EDGE(condition, ...)  { \
     81 	if (condition) { \
     82 		/* on_exit behaviors */ \
     83 		switch(state->node) { \
     84 		case DCS_PASSTHROUGH: UNHOOK; break; \
     85 		case OSC_STRING: OSC_END; break; \
     86 		} \
     87 		__VA_ARGS__; \
     88 		/* on_entry behaviors */ \
     89 		switch(state->node) { \
     90 		case ESCAPE: CLEAR; break; \
     91 		case DCS_ENTRY: CLEAR; break; \
     92 		case CSI_ENTRY: CLEAR; break; \
     93 		case DCS_PASSTHROUGH: HOOK; break; \
     94 		case OSC_STRING: OSC_START; break; \
     95 		} \
     96 		return; \
     97 	} \
     98 }
     99 
    100 void
    101 handle(ParserCallbacks *callbacks, ParserState *state, char ch)
    102 {
    103 	state->current_char = ch;
    104 
    105 	/* universal transitions */
    106 	EDGE(IS(0x1B), TO(ESCAPE));
    107 	EDGE(
    108 		IS(0x18) || IS(0x1A) || ISR(0x80, 0x8F) ||
    109 		ISR(0x91, 0x97) || IS(0x99) || IS(0x9A),
    110 		EXECUTE, TO(GROUND)
    111 	);
    112 	EDGE(IS(0x9C), TO(GROUND));
    113 	EDGE(IS(0x98) || IS(0x9E) || IS(0x9F), TO(SOS_PM_APC_STRING));
    114 	EDGE(IS(0x90), TO(DCS_ENTRY));
    115 	EDGE(IS(0x9B), TO(CSI_ENTRY));
    116 	EDGE(IS(0x9D), TO(OSC_STRING));
    117 
    118 	/* state-specific actions + transitions (in that order) */
    119 	switch (state->node) {
    120 	case GROUND:
    121 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
    122 		ACTION(ISR(0x20, 0x7F), PRINT);
    123 		break;
    124 
    125 	case ESCAPE: 
    126 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
    127 		ACTION(IS(0x7F), IGNORE);
    128 		EDGE(
    129 			ISR(0x30, 0x4F) || ISR(0x51, 0x57) ||
    130 			IS(0x59) || IS(0x5A) || IS(0x5C) ||
    131 			ISR(0x60, 0x7E),
    132 			ESC_DISPATCH, TO(GROUND)
    133 		);
    134 		EDGE(ISR(0x20, 0x2F), COLLECT, TO(ESCAPE_INTERMEDIATE));
    135 		EDGE(IS(0x58) || IS(0x5E) || IS(0x5F), TO(SOS_PM_APC_STRING));
    136 		EDGE(IS(0x50), TO(DCS_ENTRY));
    137 		EDGE(IS(0x5D), TO(OSC_STRING));
    138 		EDGE(IS(0x5B), TO(CSI_ENTRY));
    139 		break;
    140 
    141 	case ESCAPE_INTERMEDIATE:
    142 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
    143 		ACTION(ISR(0x20, 0x2F), COLLECT);
    144 		ACTION(IS(0x7F), IGNORE);
    145 		EDGE(ISR(0x30, 0x7E), ESC_DISPATCH, TO(GROUND));
    146 		break;
    147 
    148 	case DCS_ENTRY:
    149 		ACTION(
    150 			ISR(0x00, 0x17) || IS(0x19) ||
    151 			ISR(0x1C, 0x1F) || IS(0x7F),
    152 			IGNORE
    153 		);
    154 		EDGE(IS(0x3A), TO(DCS_IGNORE));
    155 		EDGE(ISR(0x20, 0x2F), TO(DCS_INTERMEDIATE));
    156 		EDGE(ISR(0x30, 0x39) || IS(0x3B), PARAM, TO(DCS_PARAM));
    157 		EDGE(ISR(0x3C, 0x3F), COLLECT, TO(DCS_PARAM));
    158 		EDGE(ISR(0x40, 0x7E), TO(DCS_PASSTHROUGH));
    159 		break;
    160 
    161 	case DCS_INTERMEDIATE:
    162 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), IGNORE);
    163 		ACTION(ISR(0x20, 0x2F), COLLECT);
    164 		ACTION(IS(0x7F), IGNORE);
    165 		EDGE(ISR(0x30, 0x3F), TO(DCS_IGNORE));
    166 		EDGE(ISR(0x40, 0x7E), TO(DCS_PASSTHROUGH));
    167 		break;
    168 
    169 	case DCS_PARAM:
    170 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), IGNORE);
    171 		ACTION(ISR(0x30, 0x39) || IS(0x3B), PARAM);
    172 		ACTION(IS(0x7F), IGNORE);
    173 		EDGE(IS(0x3A) || ISR(0x3C, 0x3F), TO(DCS_IGNORE));
    174 		EDGE(ISR(0x20, 0x2F), COLLECT, TO(DCS_INTERMEDIATE));
    175 		EDGE(ISR(0x40, 0x7E), TO(DCS_PASSTHROUGH));
    176 		break;
    177 
    178 	case DCS_IGNORE:
    179 		ACTION(
    180 			ISR(0x00, 0x17) || IS(0x19) ||
    181 			ISR(0x1C, 0x1F) || ISR(0x20, 0x7F),
    182 			IGNORE
    183 		);
    184 		EDGE(IS(0x9C), TO(GROUND));
    185 		break;
    186 
    187 	case DCS_PASSTHROUGH: 
    188 		ACTION(
    189 			ISR(0x00, 0x17) || IS(0x19) || 
    190 			ISR(0x1C, 0x1F) || ISR(0x20, 0x7E),
    191 			PUT
    192 		);
    193 		ACTION(IS(0x7F), IGNORE);
    194 		EDGE(IS(0x9C), TO(GROUND));
    195 		break;
    196 
    197 	case CSI_ENTRY:
    198 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
    199 		ACTION(IS(0x7F), IGNORE);
    200 		EDGE(ISR(0x30, 0x39) || IS(0x3B), PARAM, TO(CSI_PARAM));
    201 		EDGE(ISR(0x3C, 0x3F), COLLECT, TO(CSI_PARAM));
    202 		EDGE(IS(0x3A), TO(CSI_IGNORE));
    203 		EDGE(ISR(0x20, 0x2F), COLLECT, TO(CSI_INTERMEDIATE));
    204 		EDGE(ISR(0x40, 0x7E), CSI_DISPATCH, TO(GROUND));
    205 		break;
    206 
    207 	case CSI_INTERMEDIATE:
    208 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
    209 		ACTION(ISR(0x20, 0x2F), COLLECT);
    210 		ACTION(IS(0x7F), IGNORE);
    211 		EDGE(ISR(0x40, 0x7E), CSI_DISPATCH, TO(GROUND));
    212 		EDGE(ISR(0x30, 0x3F), TO(CSI_IGNORE));
    213 		break;
    214 
    215 	case CSI_PARAM:
    216 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
    217 		ACTION(ISR(0x30, 0x39) || IS(0x3B), PARAM);
    218 		ACTION(IS(0x7F), IGNORE);
    219 		EDGE(ISR(0x40, 0x7E), CSI_DISPATCH, TO(GROUND));
    220 		EDGE(ISR(0x20, 0x2F), COLLECT, TO(CSI_INTERMEDIATE));
    221 		EDGE(IS(0x3A) || ISR(0x3C, 0x3F), TO(CSI_IGNORE));
    222 		break;
    223 
    224 	case CSI_IGNORE:
    225 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), EXECUTE);
    226 		ACTION(ISR(0x20, 0x3F) || IS(0x7F), IGNORE);
    227 		EDGE(ISR(0x40, 0x7E), TO(GROUND));
    228 		break;
    229 
    230 	case SOS_PM_APC_STRING:
    231 		ACTION(
    232 			ISR(0x00, 0x17) || IS(0x19) ||
    233 			ISR(0x1C, 0x1F) || ISR(0x20, 0x7F),
    234 			IGNORE
    235 		);
    236 		EDGE(IS(0x9C), TO(GROUND));
    237 		break;
    238 
    239 	case OSC_STRING: 
    240 		ACTION(ISR(0x00, 0x17) || IS(0x19) || ISR(0x1C, 0x1F), IGNORE);
    241 		ACTION(ISR(0x20, 0x7F), OSC_PUT);
    242 		EDGE(IS(0x9C), TO(GROUND));
    243 		break;
    244 	}
    245 
    246 	UNHANDLED;
    247 }

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