void rl_init(line_process_callback cb) { struct termios settings; /* disable echo */ tcgetattr(0, &settings); /* read settings from stdin (0) */ settings.c_lflag &= ~(ICANON | ECHO); /* disable canonical and echo flags */ tcsetattr(0, TCSANOW, &settings); /* store new settings */ rl_clear(); line_cb = cb; }
/* * NAME: parse_grammar() * DESCRIPTION: check the grammar, return a pre-processed version */ string *parse_grammar(string *gram) { char buffer[STRINGSZ]; hashtab *ruletab, *strtab; rschunk *rschunks; rlchunk *rlchunks; rule *rgxlist, *strlist, *estrlist, *prodlist, *tmplist, *rr, *rrl; int token, ruleno, nrgx, nstr, nestr, nprod; ssizet glen; unsigned int buflen; bool nomatch; rulesym **rs; rule *rl, **r; long size; unsigned int len; # if MAX_STRLEN > 0xffffffL if (gram->len > 0xffffffL) { error("Grammar string too large"); } # endif /* initialize */ ruletab = ht_new(PARSERULTABSZ, PARSERULHASHSZ, FALSE); strtab = ht_new(PARSERULTABSZ, PARSERULHASHSZ, FALSE); rschunks = (rschunk *) NULL; rlchunks = (rlchunk *) NULL; rgxlist = strlist = estrlist = prodlist = tmplist = (rule *) NULL; nrgx = nstr = nestr = nprod = 0; size = 17 + 8; /* size of header + start rule */ glen = gram->len; nomatch = FALSE; token = gramtok(gram, &glen, buffer, &buflen); for (ruleno = 1; ; ruleno++) { switch (token) { case TOK_TOKSYM: /* * token rule definition */ r = (rule **) ht_lookup(ruletab, buffer, TRUE); if (*r != (rule *) NULL) { if ((*r)->type == RULE_UNKNOWN) { /* replace unknown rule */ rl = *r; rl->type = RULE_REGEXP; size += 4; nrgx++; if (rl->alt != (rule *) NULL) { rl->alt->next = rl->next; } else { tmplist = rl->next; } if (rl->next != (rule *) NULL) { rl->next->alt = rl->alt; } rl->alt = (rule *) NULL; rl->next = rgxlist; rgxlist = rl; } else if ((*r)->type == RULE_REGEXP) { /* new alternative regexp */ rl = rl_new(&rlchunks, RULE_REGEXP); *((*r)->last) = rl; (*r)->last = &rl->alt; } else { sprintf(buffer, "Rule %d previously defined as production rule", ruleno); goto err; } } else { /* new rule */ rl = rl_new(&rlchunks, RULE_REGEXP); str_ref(rl->symb = str_new(buffer, (long) buflen)); rl->chain.name = rl->symb->text; rl->chain.next = (hte *) *r; *r = rl; size += 4; nrgx++; rl->next = rgxlist; rgxlist = rl; } switch (gramtok(gram, &glen, buffer, &buflen)) { case TOK_REGEXP: str_ref(rl->u.rgx = str_new(buffer, (long) buflen)); (*r)->num++; (*r)->len += buflen; size += buflen + 1; break; case TOK_BADREGEXP: sprintf(buffer, "Rule %d: malformed regular expression", ruleno); goto err; case TOK_TOOBIGRGX: sprintf(buffer, "Rule %d: regular expression too large", ruleno); goto err; case TOK_SYMBOL: if (buflen == 7 && strcmp(buffer, "nomatch") == 0) { if (nomatch) { sprintf(buffer, "Rule %d: extra nomatch rule", ruleno); goto err; } nomatch = TRUE; rl->u.rgx = (string *) NULL; break; } /* fall through */ default: sprintf(buffer, "Rule %d: regular expression expected", ruleno); goto err; } /* next token */ token = gramtok(gram, &glen, buffer, &buflen); break; case TOK_PRODSYM: /* * production rule definition */ r = (rule **) ht_lookup(ruletab, buffer, TRUE); if (*r != (rule *) NULL) { if ((*r)->type == RULE_UNKNOWN) { /* replace unknown rule */ rl = *r; rl->type = RULE_PROD; size += 4; nprod++; if (rl->alt != (rule *) NULL) { rl->alt->next = rl->next; } else { tmplist = rl->next; } if (rl->next != (rule *) NULL) { rl->next->alt = rl->alt; } rl->alt = (rule *) NULL; rl->next = prodlist; prodlist = rl; } else if ((*r)->type == RULE_PROD) { /* new alternative production */ rl = rl_new(&rlchunks, RULE_PROD); *((*r)->last) = rl; (*r)->last = &rl->alt; } else { sprintf(buffer, "Rule %d previously defined as token rule", ruleno); goto err; } } else { /* new rule */ rl = rl_new(&rlchunks, RULE_PROD); str_ref(rl->symb = str_new(buffer, (long) buflen)); rl->chain.name = rl->symb->text; rl->chain.next = (hte *) *r; *r = rl; size += 4; nprod++; rl->next = prodlist; prodlist = rl; } rr = *r; rrl = rl; rs = &rl->u.syms; len = 0; for (;;) { switch (token = gramtok(gram, &glen, buffer, &buflen)) { case TOK_SYMBOL: /* * symbol */ r = (rule **) ht_lookup(ruletab, buffer, TRUE); if (*r == (rule *) NULL) { /* new unknown rule */ rl = rl_new(&rlchunks, RULE_UNKNOWN); str_ref(rl->symb = str_new(buffer, (long) buflen)); rl->chain.name = rl->symb->text; rl->chain.next = (hte *) *r; *r = rl; rl->next = tmplist; if (tmplist != (rule *) NULL) { tmplist->alt = rl; } tmplist = rl; } else { /* previously known rule */ rl = *r; } *rs = rs_new(&rschunks, rl); rs = &(*rs)->next; len += 2; continue; case TOK_STRING: case TOK_ESTRING: /* * string */ r = (rule **) ht_lookup(strtab, buffer, FALSE); while (*r != (rule *) NULL) { if ((*r)->symb->len == buflen && memcmp((*r)->symb->text, buffer, buflen) == 0) { break; } r = (rule **) &(*r)->chain.next; } if (*r == (rule *) NULL) { /* new string rule */ rl = rl_new(&rlchunks, RULE_STRING); str_ref(rl->symb = str_new(buffer, (long) buflen)); rl->chain.name = rl->symb->text; rl->chain.next = (hte *) *r; *r = rl; if (token == TOK_STRING) { size += 4; nstr++; rl->len = gram->len - glen - buflen - 1; rl->next = strlist; strlist = rl; } else { size += 3 + buflen; nestr++; rl->next = estrlist; estrlist = rl; } } else { /* existing string rule */ rl = *r; } *rs = rs_new(&rschunks, rl); rs = &(*rs)->next; len += 2; continue; case TOK_QUEST: /* * ? function */ if (gramtok(gram, &glen, buffer, &buflen) != TOK_SYMBOL) { sprintf(buffer, "Rule %d: function name expected", ruleno); goto err; } str_ref(rrl->func = str_new(buffer, (long) buflen)); len += buflen + 1; token = gramtok(gram, &glen, buffer, &buflen); /* fall through */ default: break; } break; } if (len > 255) { sprintf(buffer, "Rule %d is too long", ruleno); goto err; } rr->num++; rr->len += len; size += len + 2; break; case TOK_NULL: /* * end of grammar */ if (tmplist != (rule *) NULL) { sprintf(buffer, "Undefined symbol %s", tmplist->symb->text); goto err; } if (rgxlist == (rule *) NULL) { strcpy(buffer, "No tokens"); goto err; } if (prodlist == (rule *) NULL) { strcpy(buffer, "No starting rule"); goto err; } if (size > (long) USHRT_MAX) { strcpy(buffer, "Grammar too large"); goto err; } gram = make_grammar(rgxlist, strlist, estrlist, prodlist, nrgx, nstr, nestr, nprod, size); rs_clear(rschunks); rl_clear(rlchunks); ht_del(strtab); ht_del(ruletab); return gram; case TOK_ERROR: sprintf(buffer, "Rule %d: bad token", ruleno); goto err; case TOK_BADREGEXP: sprintf(buffer, "Rule %d: malformed regular expression", ruleno); goto err; case TOK_TOOBIGRGX: sprintf(buffer, "Rule %d: regular expression too large", ruleno); goto err; case TOK_BADSTRING: sprintf(buffer, "Rule %d: malformed string constant", ruleno); goto err; case TOK_TOOBIGSTR: sprintf(buffer, "Rule %d: string too long", ruleno); goto err; case TOK_TOOBIGSYM: sprintf(buffer, "Rule %d: symbol too long", ruleno); goto err; default: sprintf(buffer, "Rule %d: unexpected token", ruleno); goto err; } } err: rs_clear(rschunks); rl_clear(rlchunks); ht_del(strtab); ht_del(ruletab); error(buffer); return NULL; }
bool rl_feed(int c) { if (rl_parse_seq(&c)) return true; switch (c) { case K_EOT: putchar('\n'); return false; case K_TAB: if (tab_completer_cb) { const char *str = tab_completer_cb(lnbuf, pos); if (str) { int len = strlen(str); memmove(lnbuf + pos + len, lnbuf + pos, sizeof(lnbuf) - pos - len); strncpy(lnbuf + pos, str, len); pos += len; rl_reprint_prompt(); } } break; case '\r': case '\n': if (strlen(lnbuf) > 0) { char *dup = strdup(lnbuf); /* alloc space in history buffer to new string pointer */ hs_len++; history = realloc(history, hs_len * sizeof(history[0])); history[hs_len - 1] = strdup(lnbuf); putchar('\n'); line_cb(dup); /* send a copy, so we can change it */ free(dup); } else /* don't parse empty lines */ putchar('\n'); hs_cur = hs_len; rl_clear(); rl_reprint_prompt(); break; case K_ESC: break; case K_BACKSPACE: if (pos > 0) { memmove(lnbuf + pos - 1, lnbuf + pos, sizeof(lnbuf) - pos); pos--; rl_reprint_prompt(); } break; case K_UP: if (hs_cur > 0) { /* we have more history commands up */ hs_cur--; strcpy(lnbuf, history[hs_cur]); pos = strlen(history[hs_cur]); rl_reprint_prompt(); } break; case K_DOWN: if (hs_cur < hs_len - 1) { /* we have more history commands down */ hs_cur++; strcpy(lnbuf, history[hs_cur]); pos = strlen(history[hs_cur]); rl_reprint_prompt(); } else if (hs_cur == hs_len - 1) { /* we don't have more commands down, let's clear the prompt */ hs_cur++; rl_clear(); rl_reprint_prompt(); } break; case K_RIGHT: if (pos < strlen(lnbuf)) { pos++; rl_reprint_prompt(); } break; case K_LEFT: if (pos > 0) { pos--; rl_reprint_prompt(); } break; case K_END: pos = strlen(lnbuf); rl_reprint_prompt(); break; case K_HOME: pos = 0; rl_reprint_prompt(); break; case K_DELETE: memmove(lnbuf + pos, lnbuf + pos + 1, sizeof(lnbuf) - pos - 1); rl_reprint_prompt(); break; default: if (isprint(c)) { if (pos < (sizeof(lnbuf) - 1)) { /* shift everything to right and insert char at pos */ memmove(lnbuf + pos + 1, lnbuf + pos, sizeof(lnbuf) - pos - 1); lnbuf[pos++] = c; rl_reprint_prompt(); } } else printf(" %x ", c); break; } return true; }
void rl_quit() { rl_clear(); rl_clear_line(); }