diff --git a/src/lib/automaton.c b/src/lib/automaton.c index ca73f0bb9..bd5c6fb2b 100644 --- a/src/lib/automaton.c +++ b/src/lib/automaton.c @@ -28,7 +28,6 @@ typedef struct esctrie { NODE_FUNCTION, // invokes a function } ntype; ncinput ni; // composed key terminating here - int number; // accumulated number; reset to 0 on entry char* str; // accumulated string; reset to NULL on entry triefunc fxn; // function to call on match struct esctrie* kleene; // kleene match @@ -46,13 +45,6 @@ esctrie** esctrie_trie(esctrie* e){ return e->trie; } -int esctrie_numeric(const esctrie* e){ - if(e->ntype != NODE_NUMERIC){ - return -1; - } - return e->number; -} - static inline esctrie* create_esctrie_node(int special){ esctrie* e = malloc(sizeof(*e)); @@ -357,9 +349,6 @@ int inputctx_add_cflow(automaton* a, const char* csi, triefunc fxn){ if((eptr->trie[c] = create_esctrie_node(0)) == NULL){ return -1; } - if(isdigit(c)){ - eptr->trie[c]->number = c - '0'; - } }else if(eptr->trie[c] == eptr->kleene){ if((eptr->trie[c] = create_esctrie_node(0)) == NULL){ return -1; @@ -378,7 +367,7 @@ int inputctx_add_cflow(automaton* a, const char* csi, triefunc fxn){ } eptr = eptr->trie[c]; } - logdebug("added %c, now at %p (%d) %d (%u)\n", c, eptr, eptr->ntype, eptr->number, *csi); + logdebug("added %c, now at %p (%d) (%u)\n", c, eptr, eptr->ntype, *csi); } if(inescape){ logerror("illegal escape at end of line\n"); @@ -468,16 +457,7 @@ int walk_automaton(automaton* a, struct inputctx* ictx, unsigned candidate, a->state = a->escapes; return 0; } - if(e->ntype == NODE_NUMERIC){ - if(isdigit(candidate)){ - int digit = candidate - '0'; - if((INT_MAX - digit) / 10 < e->number){ - logwarn("digit %c will overflow %d\n", candidate, e->number); - } - e->number = e->number * 10 + digit; - return 0; - } - }else if(e->ntype == NODE_STRING){ + if(e->ntype == NODE_STRING){ if(candidate == 0x1b){ a->state = e->trie[candidate]; a->instring = 0; @@ -502,7 +482,6 @@ int walk_automaton(automaton* a, struct inputctx* ictx, unsigned candidate, // initialize any node we've just stepped into switch(e->ntype){ case NODE_NUMERIC: - e->number = candidate - '0'; break; case NODE_STRING: a->stridx = 1; @@ -514,12 +493,10 @@ int walk_automaton(automaton* a, struct inputctx* ictx, unsigned candidate, case NODE_SPECIAL: if(e->ni.id){ memcpy(ni, &e->ni, sizeof(*ni)); - a->state = NULL; // FIXME? return 1; } break; case NODE_FUNCTION: - a->state = NULL; // FIXME? if(e->fxn == NULL){ return 2; } diff --git a/src/lib/automaton.h b/src/lib/automaton.h index a74a00751..fcdfd9a0b 100644 --- a/src/lib/automaton.h +++ b/src/lib/automaton.h @@ -21,7 +21,8 @@ typedef struct automaton { int used; // bytes consumed thus far int instring; // are we in an ST-terminated string? struct esctrie* state; - unsigned stridx; // bytes of accumulating string (includes NUL) + unsigned stridx; // bytes of accumulating string (includes NUL) FIXME kill + const unsigned char* matchstart; // beginning of active match } automaton; void input_free_esctrie(automaton *a); @@ -41,7 +42,6 @@ uint32_t esctrie_id(const struct esctrie* e); const char* esctrie_string(const struct esctrie* e); // returns 128-way array of esctrie pointers struct esctrie** esctrie_trie(struct esctrie* e); -int esctrie_numeric(const struct esctrie* e); #ifdef __cplusplus } diff --git a/src/lib/in.c b/src/lib/in.c index 02761daf3..640337a05 100644 --- a/src/lib/in.c +++ b/src/lib/in.c @@ -294,6 +294,37 @@ prep_special_keys(inputctx* ictx){ return 0; } +// starting from the current amata match point, match any necessary prefix, then +// extract the numeric (possibly empty), then match the follow. as we are only +// called from a callback context, and know we've been properly matched, there +// is no error-checking per se (we do require prefix/follow matches, but if +// missed, we just return 0). indicate empty prefix with "", not NULL. +// updates ictx->amata.matchstart to be pointing past the follow. follow ought +// not be a digit nor NUL. +static unsigned +amata_next_numeric(automaton* amata, const char* prefix, char follow){ + char c; + while( (c = *prefix++) ){ + if(*amata->matchstart != c){ + logerror("matchstart didn't match prefix (%c vs %c)\n", c, *amata->matchstart); + return 0; + } + ++amata->matchstart; + } + // prefix has been matched + unsigned ret = 0; + while(isdigit(*amata->matchstart)){ + ret *= 10; + ret += *amata->matchstart - '0'; // FIXME overflow check! + ++amata->matchstart; + } + if(*amata->matchstart++ != follow){ + logerror("didn't see follow (%c)\n", follow); + return 0; + } + return ret; +} + // get a fixed CSI-anchored node from the trie static inline struct esctrie* csi_node(automaton *amata, const char* prefix){ @@ -322,28 +353,25 @@ dcs_node(automaton *amata){ // ('M' for click, 'm' for release). static void mouse_click(inputctx* ictx, unsigned release){ - struct esctrie* e = csi_node(&ictx->amata, "<0"); - const int mods = esctrie_numeric(e); - e = esctrie_trie(e)[';']; - e = esctrie_trie(e)['0']; - const int x = esctrie_numeric(e) - 1 - ictx->lmargin; - e = esctrie_trie(e)[';']; - e = esctrie_trie(e)['0']; - const int y = esctrie_numeric(e) - 1 - ictx->tmargin; + unsigned mods = amata_next_numeric(&ictx->amata, "\x1b[<", ';'); + long x = amata_next_numeric(&ictx->amata, "", ';'); + long y = amata_next_numeric(&ictx->amata, "", ';'); + x -= (1 + ictx->lmargin); + y -= (1 + ictx->tmargin); // convert from 1- to 0-indexing, and account for margins if(x < 0 || y < 0){ // click was in margins, drop it - logwarn("dropping click in margins %d/%d\n", y, x); + logwarn("dropping click in margins %ld/%ld\n", y, x); return; } pthread_mutex_lock(&ictx->ilock); if(ictx->ivalid == ictx->isize){ pthread_mutex_unlock(&ictx->ilock); - logerror("dropping mouse click 0x%02x %d %d\n", mods, y, x); + logerror("dropping mouse click 0x%02x %ld %ld\n", mods, y, x); inc_input_errors(ictx); return; } ncinput* ni = ictx->inputs + ictx->iwrite; - if(mods >= 0 && mods < 64){ + if(mods < 64){ ni->id = NCKEY_BUTTON1 + (mods % 4); }else if(mods >= 64 && mods < 128){ ni->id = NCKEY_BUTTON4 + (mods % 4); @@ -383,11 +411,8 @@ mouse_release_cb(inputctx* ictx){ static int cursor_location_cb(inputctx* ictx){ - struct esctrie* e = csi_node(&ictx->amata, "0"); - int y = esctrie_numeric(e) - 1; - e = esctrie_trie(e)[';']; - e = esctrie_trie(e)['9']; - int x = esctrie_numeric(e) - 1; + unsigned y = amata_next_numeric(&ictx->amata, "\x1b[", ';') - 1; + unsigned x = amata_next_numeric(&ictx->amata, "", 'R') - 1; // the first one doesn't go onto the queue; consume it here if(ictx->initdata){ ictx->initdata->cursory = y; @@ -397,7 +422,7 @@ cursor_location_cb(inputctx* ictx){ pthread_mutex_lock(&ictx->clock); if(ictx->cvalid == ictx->csize){ pthread_mutex_unlock(&ictx->clock); - logwarn("dropping cursor location report %d/%d\n", y, x); + logwarn("dropping cursor location report %u/%u\n", y, x); inc_input_errors(ictx); }else{ cursorloc* cloc = &ictx->csrs[ictx->cwrite]; @@ -409,21 +434,16 @@ cursor_location_cb(inputctx* ictx){ ++ictx->cvalid; pthread_mutex_unlock(&ictx->clock); pthread_cond_broadcast(&ictx->ccond); - loginfo("cursor location: %d/%d\n", y, x); + loginfo("cursor location: %u/%u\n", y, x); } return 2; } static int geom_cb(inputctx* ictx){ - struct esctrie* e = csi_node(&ictx->amata, "0"); - int kind = esctrie_numeric(e); - e = esctrie_trie(e)[';']; - e = esctrie_trie(e)['0']; - int y = esctrie_numeric(e); - e = esctrie_trie(e)[';']; - e = esctrie_trie(e)['0']; - int x = esctrie_numeric(e); + unsigned kind = amata_next_numeric(&ictx->amata, "\x1b[", ';'); + unsigned y = amata_next_numeric(&ictx->amata, "", ';'); + unsigned x = amata_next_numeric(&ictx->amata, "", 't'); if(kind == 4){ // pixel geometry if(ictx->initdata){ ictx->initdata->pixy = y; @@ -503,59 +523,50 @@ kitty_kbd(inputctx* ictx, int val, int mods){ static int kitty_cb_simple(inputctx* ictx){ - struct esctrie* e = csi_node(&ictx->amata, "0"); - int val = esctrie_numeric(e); + unsigned val = amata_next_numeric(&ictx->amata, "\x1b[", 'u'); kitty_kbd(ictx, val, 0); return 2; } static int kitty_cb(inputctx* ictx){ - struct esctrie* e = csi_node(&ictx->amata, "0"); - int val = esctrie_numeric(e); - e = esctrie_trie(e)[';']; - e = esctrie_trie(e)['0']; - int mods = esctrie_numeric(e); + unsigned val = amata_next_numeric(&ictx->amata, "\x1b[", ';'); + unsigned mods = amata_next_numeric(&ictx->amata, "", 'u'); kitty_kbd(ictx, val, mods); return 2; } static int kitty_keyboard_cb(inputctx* ictx){ - struct esctrie* e = csi_node(&ictx->amata, "?1"); - int val = esctrie_numeric(e); + unsigned level = amata_next_numeric(&ictx->amata, "\x1b[?", 'u'); if(ictx->initdata){ - ictx->initdata->kbdlevel = val; - loginfo("kitty keyboard protocol level %u\n", ictx->initdata->kbdlevel); + ictx->initdata->kbdlevel = level; } + loginfo("kitty keyboard protocol level %u\n", level); return 2; } // the only xtsmgraphics reply with a single Pv arg is color registers static int xtsmgraphics_cregs_cb(inputctx* ictx){ - struct esctrie* e = csi_node(&ictx->amata, "?1;0;0"); - int pv = esctrie_numeric(e); + unsigned pv = amata_next_numeric(&ictx->amata, "\x1b[?1;0;", 'S'); if(ictx->initdata){ ictx->initdata->color_registers = pv; - loginfo("sixel color registers: %d\n", ictx->initdata->color_registers); } + loginfo("sixel color registers: %d\n", pv); return 2; } // the only xtsmgraphics reply with a dual Pv arg we want is sixel geometry static int xtsmgraphics_sixel_cb(inputctx* ictx){ - struct esctrie* e = csi_node(&ictx->amata, "?2;0;0"); - int width = esctrie_numeric(e); - e = esctrie_trie(e)[';']; - e = esctrie_trie(e)['0']; - int height = esctrie_numeric(e); + unsigned width = amata_next_numeric(&ictx->amata, "\x1b[?2;0;", ';'); + unsigned height = amata_next_numeric(&ictx->amata, "", 'S'); if(ictx->initdata){ ictx->initdata->sixelx = width; ictx->initdata->sixely = height; - loginfo("max sixel geometry: %dx%d\n", ictx->initdata->sixely, ictx->initdata->sixelx); } + loginfo("max sixel geometry: %dx%d\n", height, width); return 2; } @@ -599,10 +610,9 @@ da2_cb(inputctx* ictx){ termname ? termname : "unset"); return 2; } - struct esctrie* e = csi_node(&ictx->amata, ">0;0"); - int pv = esctrie_numeric(e); + unsigned pv = amata_next_numeric(&ictx->amata, "\x1b[>0;", 'c'); int maj, min, patch; - if(pv <= 0){ + if(pv == 0){ return 2; } maj = pv / 10000; @@ -643,9 +653,8 @@ kittygraph_cb(inputctx* ictx){ static int decrpm_asu_cb(inputctx* ictx){ - struct esctrie* e = csi_node(&ictx->amata, "?2026;0"); - int ps = esctrie_numeric(e); - loginfo("received decrpm 2026 %d\n", ps); + unsigned ps = amata_next_numeric(&ictx->amata, "\x1b[?2026;", 'y'); + loginfo("received decrpm 2026 %u\n", ps); if(ps == 2){ if(ictx->initdata){ ictx->initdata->appsync_supported = 1; @@ -1212,7 +1221,7 @@ special_key(inputctx* ictx, const ncinput* inni){ // precondition: buflen >= 1. precondition: buf[0] == 0x1b. static int process_escape(inputctx* ictx, const unsigned char* buf, int buflen){ - assert(1 <= buflen); + assert(ictx->amata.used < buflen); while(ictx->amata.used < buflen){ unsigned char candidate = buf[ictx->amata.used++]; unsigned used = ictx->amata.used; @@ -1223,6 +1232,7 @@ process_escape(inputctx* ictx, const unsigned char* buf, int buflen){ // an escape always resets the trie (unless we're in the middle of an // ST-terminated string), as does a NULL transition. if(candidate == NCKEY_ESC && !ictx->amata.instring){ + ictx->amata.matchstart = buf + ictx->amata.used - 1; ictx->amata.state = ictx->amata.escapes; logtrace("initialized automaton to %p\n", ictx->amata.state); ictx->amata.used = 1; @@ -1302,6 +1312,7 @@ process_escapes(inputctx* ictx, unsigned char* buf, int* bufused){ // move any leftovers to the front; only happens if we fill output queue, // or ran out of input data mid-escape if(*bufused){ + ictx->amata.matchstart = buf; memmove(buf, buf + offset, *bufused); } }