Notcurses 3.0.13
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
automaton.c
Go to the documentation of this file.
1#include "automaton.h"
2#include "internal.h"
3
4// the input automaton, walked for all escape sequences. an escape sequence is
5// everything from an escape through recognized termination of that escape, or
6// abort of the sequence via another escape, save the case of DCS sequences
7// (those beginning with Escape-P), which are terminated by the ST sequence
8// Escape-\. in the case of an aborted sequence, the sequence in its entirety
9// is replayed as regular input. regular input is not driven through this
10// automaton.
11//
12// one complication is that the user can just press escape themselves, followed
13// by arbitrary other keypresses. when input is redirected from some source
14// other than the connected terminal, this is no problem: we know control
15// sequences to be coming in from the connected terminal, and everything else
16// is bulk input.
17
18// we assumed escapes can only be composed of 7-bit chars
19typedef struct esctrie {
20 // if non-NULL, this is the next level of radix-128 trie. it is NULL on
21 // accepting nodes, since no valid control sequence is a prefix of another
22 // valid control sequence. links are 1-biased (0 is NULL).
23 unsigned* trie;
24 enum {
25 NODE_SPECIAL, // an accepting node, or pure transit (if ni.id == 0)
26 NODE_NUMERIC, // accumulates a number
27 NODE_STRING, // accumulates a string
28 NODE_FUNCTION, // invokes a function
30 ncinput ni; // composed key terminating here
31 triefunc fxn; // function to call on match
32 unsigned kleene; // idx of kleene match
34
35// get node corresponding to 1-biased index
36static inline esctrie*
37esctrie_from_idx(const automaton* a, unsigned idx){
38 if(idx == 0){
39 return NULL;
40 }
41 return a->nodepool + (idx - 1);
42}
43
44// return 1-biased index of node in pool
45static inline unsigned
46esctrie_idx(const automaton* a, const esctrie* e){
47 return e - a->nodepool + 1;
48}
49
50uint32_t esctrie_id(const esctrie* e){
51 return e->ni.id;
52}
53
54// returns the idx of the new node, or 0 on failure (idx is 1-biased).
55// *invalidates any existing escnode pointers!*
56static unsigned
57create_esctrie_node(automaton* a, int special){
58 if(a->poolused == a->poolsize){
59 unsigned newsize = a->poolsize ? a->poolsize * 2 : 512;
60 esctrie* tmp = realloc(a->nodepool, sizeof(*a->nodepool) * newsize);
61 if(tmp == NULL){
62 return 0;
63 }
64 a->nodepool = tmp;
65 a->poolsize = newsize;
66 }
67 esctrie* e = &a->nodepool[a->poolused++];
68 memset(e, 0, sizeof(*e));
69 e->ntype = NODE_SPECIAL;
70 if((e->ni.id = special) == 0){
71 const size_t tsize = sizeof(*e->trie) * 0x80;
72 if((e->trie = malloc(tsize)) == NULL){
73 --a->poolused;
74 return 0;
75 }
76 memset(e->trie, 0, tsize);
77 }
78 return esctrie_idx(a, e);
79}
80
82 a->escapes = 0;
83 a->poolsize = 0;
84 for(unsigned i = 0 ; i < a->poolused ; ++i){
85 free(a->nodepool[i].trie);
86 }
87 free(a->nodepool);
88 a->poolused = 0;
89 a->nodepool = NULL;
90}
91
92static int
93esctrie_make_kleene(automaton* a, esctrie* e, unsigned follow, esctrie* term){
94 if(e->ntype != NODE_SPECIAL){
95 logerror("can't make node type %d string", e->ntype);
96 return -1;
97 }
98 for(unsigned i = 0 ; i < 0x80 ; ++i){
99 if(i == follow){
100 e->trie[i] = esctrie_idx(a, term);
101 }else if(e->trie[i] == 0){
102 e->trie[i] = esctrie_idx(a, e);
103 }
104 }
105 return 0;
106}
107
108static int
109esctrie_make_function(esctrie* e, triefunc fxn){
110 if(e->ntype != NODE_SPECIAL){
111 logerror("can't make node type %d function", e->ntype);
112 return -1;
113 }
114 if(e->trie){
115 logerror("can't make followed function");
116 return -1;
117 }
118 e->ntype = NODE_FUNCTION;
119 e->fxn = fxn;
120 return 0;
121}
122
123static esctrie*
124esctrie_make_string(automaton* a, esctrie* e, unsigned rxvtstyle){
125 if(e->ntype == NODE_STRING){
126 logerror("repeated string node");
127 return NULL;
128 }
129 if(e->ntype != NODE_SPECIAL){
130 logerror("can't make node type %d string", e->ntype);
131 return NULL;
132 }
133 for(int i = 0 ; i < 0x80 ; ++i){
134 if(!isprint(i)){
135 continue;
136 }
137 if(e->trie[i]){
138 logerror("can't make %c-followed string", i);
139 return NULL;
140 }
141 }
142 esctrie* newe = esctrie_from_idx(a, create_esctrie_node(a, 0));
143 if(newe == NULL){
144 return NULL;
145 }
146 for(int i = 0 ; i < 0x80 ; ++i){
147 if(!isprint(i)){
148 continue;
149 }
150 e->trie[i] = esctrie_idx(a, newe);
151 }
152 e = newe;
153 e->ntype = NODE_STRING;
154 for(int i = 0 ; i < 0x80 ; ++i){
155 if(!isprint(i)){
156 continue;
157 }
158 e->trie[i] = esctrie_idx(a, newe);
159 }
160 if(rxvtstyle){ // ends with bare ESC, not BEL/ST
161 if((e->trie[0x1b] = create_esctrie_node(a, 0)) == 0){
162 return NULL;
163 }
164 e = esctrie_from_idx(a, e->trie[0x1b]);
165 e->ni.id = 0;
166 e->ntype = NODE_SPECIAL;
167 }else{
168 if((e->trie[0x07] = create_esctrie_node(a, NCKEY_INVALID)) == 0){
169 return NULL;
170 }
171 esctrie* term = esctrie_from_idx(a, e->trie[0x07]);
172 if((e->trie[0x1b] = create_esctrie_node(a, 0)) == 0){
173 return NULL;
174 }
175 e = esctrie_from_idx(a, e->trie[0x1b]);
176 e->trie['\\'] = esctrie_idx(a, term);
177 term->ni.id = 0;
178 term->ntype = NODE_SPECIAL;
179 e = term;
180 }
181 logdebug("made string: %u", esctrie_idx(a, e));
182 return e;
183}
184
185static esctrie*
186link_kleene(automaton* a, esctrie* e, unsigned follow){
187 unsigned eidx = esctrie_idx(a, e);
188 if(e->kleene){
189 return a->nodepool + e->kleene;
190 }
191 // invalidates e
192 unsigned termidx = create_esctrie_node(a, 0);
193 unsigned targidx = create_esctrie_node(a, 0);
194 esctrie* term = esctrie_from_idx(a, termidx);
195 esctrie* targ = esctrie_from_idx(a, targidx);
196 if(targ == NULL){
197 return NULL;
198 }
199 if(term == NULL){
200 return NULL;
201 }
202 if(esctrie_make_kleene(a, targ, follow, term)){
203 return NULL;
204 }
205 e = esctrie_from_idx(a, eidx);
206 // fill in all NULL numeric links with the new target
207 for(unsigned int i = 0 ; i < 0x80 ; ++i){
208 if(i == follow){
209 if(e->trie[i]){
210 logerror("drain terminator already registered");
211 return NULL;
212 }
213 e->trie[follow] = esctrie_idx(a, term);
214 }else if(e->trie[i] == 0){
215 e->trie[i] = esctrie_idx(a, targ);
216 }
217 }
218 targ->kleene = esctrie_idx(a, targ);
219 return esctrie_from_idx(a, e->trie[follow]);
220}
221
222// phase 1 of the numeric algorithm; find a φ node on e. not sure what
223// to do if we have non-φ links at every digit...punt for now FIXME.
224static unsigned
225get_phi_node(automaton* a, esctrie* e){
226 // find a linked NODE_NUMERIC, if one exists. we'll want to reuse it.
227 int nonphis = 0;
228 esctrie* targ;
229 for(int i = '0' ; i <= '9' ; ++i){
230 if( (targ = esctrie_from_idx(a, e->trie[i])) ){
231 if(targ->ntype == NODE_NUMERIC){
232 logtrace("found existing phi node %u[%c]->%u", esctrie_idx(a, e), i, esctrie_idx(a, targ));
233 break;
234 }else{
235 ++nonphis;
236 targ = NULL;
237 }
238 }
239 }
240 // we either have a numeric target, or will make one now. if we create a new
241 // one, be sure to mark it numeric, and add all digit links back to itself.
242 if(targ == NULL){
243 if(nonphis == 10){
244 logerror("ten non-phi links from %u", esctrie_idx(a, e));
245 return 0;
246 }
247 if((targ = esctrie_from_idx(a, create_esctrie_node(a, 0))) == 0){
248 return 0;
249 }
250 targ->ntype = NODE_NUMERIC;
251 for(int i = '0' ; i <= '9' ; ++i){
252 targ->trie[i] = esctrie_idx(a, targ);
253 }
254 }
255 assert(NODE_NUMERIC == targ->ntype);
256 return esctrie_idx(a, targ);
257}
258
259// phase 2 of the numeric algorithm; find a ή node for |successor| on |phi|.
260static unsigned
261get_eta_node(automaton* a, esctrie* phi, unsigned successor){
262 unsigned phiidx = esctrie_idx(a, phi);
263 unsigned etaidx = phi->trie[successor];
264 esctrie* eta = esctrie_from_idx(a, etaidx);
265 if(eta == NULL){
266 // invalidates phi
267 if((eta = esctrie_from_idx(a, create_esctrie_node(a, 0))) == NULL){
268 return 0;
269 }
270 phi = esctrie_from_idx(a, phiidx);
271 phi->trie[successor] = esctrie_idx(a, eta);
272 }
273 return esctrie_idx(a, eta);
274}
275
276// |e| is a known-standard node reached by our prefix; go ahead and prep both
277// phi and eta links from it.
278static void
279add_phi_and_eta_chain(const automaton *a, esctrie* e, unsigned phi,
280 unsigned follow, unsigned eta){
281//logtrace("working with %u phi: %u follow: %u eta: %u", esctrie_idx(a, e), phi, follow, eta);
282 for(int i = '0' ; i <= '9' ; ++i){
283 esctrie* chain = esctrie_from_idx(a, e->trie[i]);
284 if(chain == NULL){
285 //logdebug("linking %u[%d] to %u", esctrie_idx(a, e), i, phi);
286 e->trie[i] = phi;
287 }else if(chain->ntype == NODE_SPECIAL){
288//logdebug("propagating along %u[%c]", e->trie[i], i);
289 add_phi_and_eta_chain(a, esctrie_from_idx(a, e->trie[i]), phi, follow, eta);
290 }
291 }
292 if(e->trie[follow] == 0){
293 //logdebug("linking %u[%u] to %u", esctrie_idx(a, e), follow, eta);
294 e->trie[follow] = eta;
295 }
296}
297
298// phase 3 of the numeric algorithm: walk the automaton, finding all nodes
299// which are prefixes of phi (all nodes matching the prefix, and all numeric
300// non-phi chains from those nodes) and linking them to phi, and finding all
301// nodes which are prefixes of eta (all numeric non-phi chains from the
302// previous set) and linking them to eta. |e| is the path thus far.
303static void
304add_phi_and_eta_recurse(automaton* a, esctrie* e, const char* prefix,
305 int pfxlen, esctrie* phi, unsigned follow,
306 esctrie* eta, unsigned inphi){
307//logtrace("working with %u %d prefix [%*.*s]", esctrie_idx(a, e), pfxlen, pfxlen, pfxlen, prefix);
308 // if pfxlen == 0, we found a match for our fixed prefix. start adding phi
309 // links whereever we can. where we find chained numerics, add an eta link.
310 if(pfxlen == 0){
311 add_phi_and_eta_chain(a, e, esctrie_idx(a, phi), follow, esctrie_idx(a, eta));
312 return;
313 }
314 // when we hit a \N in the prefix, we must recurse along all digit links
315 if(*prefix == '\\'){
316 ++prefix;
317 --pfxlen;
318 if(*prefix != 'N'){
319 logerror("illegal wildcard in prefix %c", *prefix);
320 return;
321 }
322 ++prefix;
323 --pfxlen;
324 // Optimization: get_phi_node will set the trie[i] for i='0'..'9' to the exact
325 // same linked tri index. If that happens, there is no need to to the (expensive)
326 // add_phi_and_eta_recurse call ten times, only the first time is enough.
327 unsigned linked_tri_seen_last = UINT_MAX;
328 for(int i = '0' ; i <= '9' ; ++i){
329 if(e->trie[i] == 0){
330 //logdebug("linking %u[%d] to %u", esctrie_idx(a, e), i, esctrie_idx(a, phi));
331 e->trie[i] = esctrie_idx(a, phi);
332 }else{
333 if(e->trie[i] != linked_tri_seen_last){
334 add_phi_and_eta_recurse(a, esctrie_from_idx(a, e->trie[i]),
335 prefix, pfxlen, phi, follow, eta, 1);
336 linked_tri_seen_last = e->trie[i];
337 }
338 }
339 }
340 }else{
341 if(inphi){
342 //same optimization as above
343 unsigned linked_tri_seen_last = UINT_MAX;
344 for(int i = '0' ; i <= '9' ; ++i){
345 if(e->trie[i] == 0){
346 //logdebug("linking %u[%d] to %u", esctrie_idx(a, e), i, esctrie_idx(a, phi));
347 e->trie[i] = esctrie_idx(a, phi);
348 }else if(e->trie[i] != esctrie_idx(a, e) && e->trie[i] != linked_tri_seen_last){
349 add_phi_and_eta_recurse(a, esctrie_from_idx(a, e->trie[i]),
350 prefix, pfxlen, phi, follow, eta, 1);
351 linked_tri_seen_last = e->trie[i];
352 }
353 }
354 }
355 unsigned char p = *prefix;
356 if(e->trie[p]){
357 add_phi_and_eta_recurse(a, esctrie_from_idx(a, e->trie[p]),
358 prefix + 1, pfxlen - 1, phi, follow, eta, 0);
359 }
360 }
361}
362
363// |prefix| does *not* lead with an escape, and does not include the numeric.
364static void
365add_phi_and_eta(automaton* a, const char* prefix, size_t pfxlen,
366 esctrie* phi, unsigned follow, esctrie* eta){
367 esctrie* esc = esctrie_from_idx(a, a->escapes);
368 if(esc == NULL){
369 return;
370 }
371 add_phi_and_eta_recurse(a, esc, prefix, pfxlen, phi, follow, eta, 0);
372}
373
374// accept any digit and transition to a numeric node. |e| is the culmination of
375// the prefix before the numeric. |follow| is the successor of the numeric.
376// here's our approach:
377// - find a link to a numeric from e. there can only be one node (though it
378// might have many links), so we can use the first one we find.
379// - if there is no such numeric node linked from e, create one.
380// (FIXME if all ten digits are occupied, what would we do?)
381// - chosen numeric node is φ.
382// - if an appropriate follow node exists linked from φ, choose it as ή.
383// - otherwise, create a new ή and link it from φ.
384// - walk from the top, finding all possible prefixes of φ.
385// - at each, link all unused digits to φ.
386// - from each that is also a possible prefix of ή, link ή.
387static esctrie*
388link_numeric(automaton* a, const char* prefix, int pfxlen,
389 esctrie* e, unsigned char follow){
390 logdebug("adding numeric with follow %c following %*.*s", follow, pfxlen, pfxlen, prefix);
391 unsigned phiidx = get_phi_node(a, e);
392 if(phiidx == 0){
393 return NULL;
394 }
395 esctrie* phi = esctrie_from_idx(a, phiidx);
396 // invalidates phi
397 unsigned etaidx = get_eta_node(a, phi, follow);
398 if(etaidx == 0){
399 return NULL;
400 }
401 phi = esctrie_from_idx(a, phiidx);
402 esctrie* eta = esctrie_from_idx(a, etaidx);
403 logtrace("phi node: %u->%u", esctrie_idx(a, e), esctrie_idx(a, phi));
404 logtrace("eta node: %u philink[%c]: %u", esctrie_idx(a, eta), follow, phi->trie[follow]);
405 // eta is now bound to phi, and phi links something at all digits, but no
406 // other links are guaranteed. walk the automaton, finding all possible
407 // prefixes of φ (and linking to φ) and all possible prefixes of ή (and
408 // linking them to ή).
409 add_phi_and_eta(a, prefix, pfxlen, phi, follow, eta);
410 return eta;
411}
412
413static esctrie*
414insert_path(automaton* a, const char* seq){
415 if(a->escapes == 0){
416 if((a->escapes = create_esctrie_node(a, 0)) == 0){
417 return NULL;
418 }
419 }
420 esctrie* eptr = esctrie_from_idx(a, a->escapes);
421 bool inescape = false;
422 const char* seqstart = seq;
423 unsigned char c;
424 while( (c = *seq++) ){
425 if(c == '\\'){
426 if(inescape){
427 logerror("illegal escape: \\");
428 return NULL;
429 }
430 inescape = true;
431 }else if(inescape){
432 if(c == 'N'){
433 // a numeric must be followed by some terminator
434 if(!*seq){
435 logerror("illegal numeric terminator");
436 return NULL;
437 }
438 c = *seq++;
439 eptr = link_numeric(a, seqstart, seq - 3 - seqstart, eptr, c);
440 if(eptr == NULL){
441 return NULL;
442 }
443 }else if(c == 'S' || c == 'R'){
444 // strings always end with ST ("\e\\") or at least ("\e")
445 if((eptr = esctrie_make_string(a, eptr, c == 'R')) == NULL){
446 return NULL;
447 }
448 return eptr;
449 }else if(c == 'D'){ // drain (kleene closure)
450 // a kleene must be followed by some terminator
451 if(!*seq){
452 logerror("illegal kleene terminator");
453 return NULL;
454 }
455 c = *seq++;
456 eptr = link_kleene(a, eptr, c);
457 if(eptr == NULL){
458 return NULL;
459 }
460 }else{
461 logerror("illegal escape: %u", c);
462 return NULL;
463 }
464 inescape = false;
465 }else{ // fixed character
466 unsigned eidx = esctrie_idx(a, eptr);
467 // invalidates eptr
468 if(eptr->trie[c] == 0 || eptr->trie[c] == eptr->kleene){
469 unsigned tidx = create_esctrie_node(a, 0);
470 if(tidx == 0){
471 return NULL;
472 }
473 eptr = esctrie_from_idx(a, eidx);
474 eptr->trie[c] = tidx;
475 }else if(esctrie_from_idx(a, eptr->trie[c])->ntype == NODE_NUMERIC){
476 // punch a hole through the numeric loop. create a new one, and fill
477 // it in with the existing target.
478 struct esctrie* newe;
479 // invalidates eptr
480 if((newe = esctrie_from_idx(a, create_esctrie_node(a, 0))) == 0){
481 return NULL;
482 }
483 eptr = esctrie_from_idx(a, eidx);
484 for(int i = 0 ; i < 0x80 ; ++i){
485 newe->trie[i] = esctrie_from_idx(a, eptr->trie[c])->trie[i];
486 }
487 eptr->trie[c] = esctrie_idx(a, newe);
488 }
489 eptr = esctrie_from_idx(a, eidx);
490 eptr = esctrie_from_idx(a, eptr->trie[c]);
491 logtrace("added fixed %c %u as %u", c, c, esctrie_idx(a, eptr));
492 }
493 }
494 if(inescape){
495 logerror("illegal escape at end of line");
496 return NULL;
497 }
498 return eptr;
499}
500
501// add a cflow path to the automaton
502int inputctx_add_cflow(automaton* a, const char* seq, triefunc fxn){
503 esctrie* eptr = insert_path(a, seq);
504 if(eptr == NULL){
505 return -1;
506 }
507 free(eptr->trie);
508 eptr->trie = NULL;
509 return esctrie_make_function(eptr, fxn);
510}
511
512// multiple input escapes might map to the same input
513int inputctx_add_input_escape(automaton* a, const char* esc, uint32_t special,
514 unsigned modifiers){
515 if(esc[0] != NCKEY_ESC || strlen(esc) < 2){ // assume ESC prefix + content
516 logerror("not an escape (0x%x)", special);
517 return -1;
518 }
519 esctrie* eptr = insert_path(a, esc + 1);
520 if(eptr == NULL){
521 return -1;
522 }
523 // it appears that multiple keys can be mapped to the same escape string. as
524 // an example, see "kend" and "kc1" in st ("simple term" from suckless) :/.
525 if(eptr->ni.id){ // already had one here!
526 if(eptr->ni.id != special){
527 logwarn("already added escape (got 0x%x, wanted 0x%x)", eptr->ni.id, special);
528 }
529 }else{
530 eptr->ni.id = special;
531 eptr->ni.shift = modifiers & NCKEY_MOD_SHIFT;
532 eptr->ni.ctrl = modifiers & NCKEY_MOD_CTRL;
533 eptr->ni.alt = modifiers & NCKEY_MOD_ALT;
534 eptr->ni.y = 0;
535 eptr->ni.x = 0;
536 eptr->ni.modifiers = modifiers;
537 logdebug("added 0x%08x to %u", special, esctrie_idx(a, eptr));
538 }
539 return 0;
540}
541
542// returns -1 for non-match, 0 for match, 1 for acceptance. if we are in the
543// middle of a sequence, and receive an escape, *do not call this*, but
544// instead call reset_automaton() after replaying the used characters to the
545// bulk input buffer, and *then* call this with the escape.
546int walk_automaton(automaton* a, struct inputctx* ictx, unsigned candidate,
547 ncinput* ni){
548 if(candidate >= 0x80){
549 logerror("eight-bit char %u in control sequence", candidate);
550 return -1;
551 }
552 esctrie* e = esctrie_from_idx(a, a->state);
553 // we ought not have been called for an escape with any state!
554 if(candidate == 0x1b && !a->instring){
555 assert(NULL == e);
556 a->state = a->escapes;
557 return 0;
558 }
559 if(e->ntype == NODE_STRING){
560 if(candidate == 0x1b || candidate == 0x07){
561 a->state = e->trie[candidate];
562 a->instring = 0;
563 }
564 e = esctrie_from_idx(a, a->state);
565 if(e->ntype == NODE_FUNCTION){ // for the 0x07s of the world
566 if(e->fxn == NULL){
567 return 2;
568 }
569 return e->fxn(ictx);
570 }
571 return 0;
572 }
573 if((a->state = e->trie[candidate]) == 0){
574 if(esctrie_idx(a, e) == a->escapes){
575 memset(ni, 0, sizeof(*ni));
576 ni->id = candidate;
577 ni->alt = true;
578 return 1;
579 }
580 loginfo("unexpected transition on %u[%u]",
581 esctrie_idx(a, e), candidate);
582 return -1;
583 }
584 e = esctrie_from_idx(a, a->state);
585 // initialize any node we've just stepped into
586 switch(e->ntype){
587 case NODE_NUMERIC:
588 break;
589 case NODE_STRING:
590 a->instring = 1;
591 break;
592 case NODE_SPECIAL:
593 if(e->ni.id){
594 memcpy(ni, &e->ni, sizeof(*ni));
595 return 1;
596 }
597 break;
598 case NODE_FUNCTION:
599 if(e->fxn == NULL){
600 return 2;
601 }
602 return e->fxn(ictx);
603 break;
604 }
605 return 0;
606}
int walk_automaton(automaton *a, struct inputctx *ictx, unsigned candidate, ncinput *ni)
Definition automaton.c:546
int inputctx_add_input_escape(automaton *a, const char *esc, uint32_t special, unsigned modifiers)
Definition automaton.c:513
uint32_t esctrie_id(const esctrie *e)
Definition automaton.c:50
void input_free_esctrie(automaton *a)
Definition automaton.c:81
int inputctx_add_cflow(automaton *a, const char *seq, triefunc fxn)
Definition automaton.c:502
int(* triefunc)(struct inputctx *)
Definition automaton.h:14
assert(false)
const nccell * c
Definition egcpool.h:296
uint32_t idx
Definition egcpool.h:298
free(duplicated)
#define logerror(fmt,...)
Definition logging.h:32
#define logtrace(fmt,...)
Definition logging.h:57
#define loginfo(fmt,...)
Definition logging.h:42
#define logdebug(fmt,...)
Definition logging.h:52
#define logwarn(fmt,...)
Definition logging.h:37
#define NCKEY_MOD_SHIFT
Definition nckeys.h:219
#define NCKEY_MOD_CTRL
Definition nckeys.h:221
#define NCKEY_INVALID
Definition nckeys.h:35
#define NCKEY_ESC
Definition nckeys.h:196
#define NCKEY_MOD_ALT
Definition nckeys.h:220
struct esctrie * nodepool
Definition automaton.h:30
unsigned state
Definition automaton.h:23
unsigned escapes
Definition automaton.h:20
unsigned poolsize
Definition automaton.h:28
unsigned poolused
Definition automaton.h:29
int instring
Definition automaton.h:22
unsigned kleene
Definition automaton.c:32
@ NODE_SPECIAL
Definition automaton.c:25
@ NODE_FUNCTION
Definition automaton.c:28
@ NODE_STRING
Definition automaton.c:27
@ NODE_NUMERIC
Definition automaton.c:26
triefunc fxn
Definition automaton.c:31
ncinput ni
Definition automaton.c:30
unsigned * trie
Definition automaton.c:23
enum esctrie::@0 ntype
Definition in.c:52
bool alt
Definition notcurses.h:1214
bool ctrl
Definition notcurses.h:1216
uint32_t id
Definition notcurses.h:1210
bool shift
Definition notcurses.h:1215
unsigned modifiers
Definition notcurses.h:1219
return NULL
Definition termdesc.h:229
static escape_e e
Definition termdesc.h:224