/** * Insert 'ch' at position 'pos' * * Returns 1 if the line needs to be refreshed, 2 if not * and 0 if nothing was inserted (no room) */ static int insert_char(struct current *current, int pos, int ch) { char buf[3]; int n = utf8_getchars(buf, ch); if (has_room(current, n) && pos >= 0 && pos <= current->chars) { int p1, p2; int ret = 1; p1 = utf8_index(current->buf, pos); p2 = p1 + n; #ifdef USE_TERMIOS /* optimise the case where adding a single char to the end and no scrolling is needed */ if (current->pos == pos && current->chars == pos) { if (ch >= ' ' && utf8_strlen(current->prompt, -1) + utf8_strlen(current->buf, current->len) < current->cols - 1) { IGNORE_RC(write(current->fd, buf, n)); ret = 2; } } #endif memmove(current->buf + p2, current->buf + p1, current->len - p1); memcpy(current->buf + p1, buf, n); current->len += n; current->chars++; if (current->pos >= pos) { current->pos++; } return ret; } return 0; }
static int linenoiseEdit(struct current *current) { int history_index = 0; /* The latest history entry is always our current buffer, that * initially is just an empty string. */ linenoiseHistoryAdd(""); set_current(current, ""); refreshLine(current->prompt, current); while(1) { int dir = -1; int c = fd_read(current); #ifndef NO_COMPLETION /* Only autocomplete when the callback is set. It returns < 0 when * there was an error reading from fd. Otherwise it will return the * character that should be handled next. */ if (c == '\t' && current->pos == current->chars && completionCallback != NULL) { c = completeLine(current); /* Return on errors */ if (c < 0) return current->len; /* Read next character when 0 */ if (c == 0) continue; } #endif process_char: if (c == -1) return current->len; #ifdef USE_TERMIOS if (c == 27) { /* escape sequence */ c = check_special(current->fd); } #endif switch(c) { case '\r': /* enter */ history_len--; free(history[history_len]); return current->len; case ctrl('C'): /* ctrl-c */ errno = EAGAIN; return -1; case 127: /* backspace */ case ctrl('H'): if (remove_char(current, current->pos - 1) == 1) { refreshLine(current->prompt, current); } break; case ctrl('D'): /* ctrl-d */ if (current->len == 0) { /* Empty line, so EOF */ history_len--; free(history[history_len]); return -1; } /* Otherwise fall through to delete char to right of cursor */ case SPECIAL_DELETE: if (remove_char(current, current->pos) == 1) { refreshLine(current->prompt, current); } break; case SPECIAL_INSERT: /* Ignore. Expansion Hook. * Future possibility: Toggle Insert/Overwrite Modes */ break; case ctrl('W'): /* ctrl-w, delete word at left. save deleted chars */ /* eat any spaces on the left */ { int pos = current->pos; while (pos > 0 && get_char(current, pos - 1) == ' ') { pos--; } /* now eat any non-spaces on the left */ while (pos > 0 && get_char(current, pos - 1) != ' ') { pos--; } if (remove_chars(current, pos, current->pos - pos)) { refreshLine(current->prompt, current); } } break; case ctrl('R'): /* ctrl-r */ { /* Display the reverse-i-search prompt and process chars */ char rbuf[50]; char rprompt[80]; int rchars = 0; int rlen = 0; int searchpos = history_len - 1; rbuf[0] = 0; while (1) { int n = 0; const char *p = NULL; int skipsame = 0; int searchdir = -1; snprintf(rprompt, sizeof(rprompt), "(reverse-i-search)'%s': ", rbuf); refreshLine(rprompt, current); c = fd_read(current); if (c == ctrl('H') || c == 127) { if (rchars) { int p = utf8_index(rbuf, --rchars); rbuf[p] = 0; rlen = strlen(rbuf); } continue; } #ifdef USE_TERMIOS if (c == 27) { c = check_special(current->fd); } #endif if (c == ctrl('P') || c == SPECIAL_UP) { /* Search for the previous (earlier) match */ if (searchpos > 0) { searchpos--; } skipsame = 1; } else if (c == ctrl('N') || c == SPECIAL_DOWN) { /* Search for the next (later) match */ if (searchpos < history_len) { searchpos++; } searchdir = 1; skipsame = 1; } else if (c >= ' ') { if (rlen >= (int)sizeof(rbuf) + 3) { continue; } n = utf8_getchars(rbuf + rlen, c); rlen += n; rchars++; rbuf[rlen] = 0; /* Adding a new char resets the search location */ searchpos = history_len - 1; } else { /* Exit from incremental search mode */ break; } /* Now search through the history for a match */ for (; searchpos >= 0 && searchpos < history_len; searchpos += searchdir) { p = strstr(history[searchpos], rbuf); if (p) { /* Found a match */ if (skipsame && strcmp(history[searchpos], current->buf) == 0) { /* But it is identical, so skip it */ continue; } /* Copy the matching line and set the cursor position */ set_current(current,history[searchpos]); current->pos = utf8_strlen(history[searchpos], p - history[searchpos]); break; } } if (!p && n) { /* No match, so don't add it */ rchars--; rlen -= n; rbuf[rlen] = 0; } } if (c == ctrl('G') || c == ctrl('C')) { /* ctrl-g terminates the search with no effect */ set_current(current, ""); c = 0; } else if (c == ctrl('J')) { /* ctrl-j terminates the search leaving the buffer in place */ c = 0; } /* Go process the char normally */ refreshLine(current->prompt, current); goto process_char; } break; case ctrl('T'): /* ctrl-t */ if (current->pos > 0 && current->pos <= current->chars) { /* If cursor is at end, transpose the previous two chars */ int fixer = (current->pos == current->chars); c = get_char(current, current->pos - fixer); remove_char(current, current->pos - fixer); insert_char(current, current->pos - 1, c); refreshLine(current->prompt, current); } break; case ctrl('V'): /* ctrl-v */ if (has_room(current, 3)) { /* Insert the ^V first */ if (insert_char(current, current->pos, c)) { refreshLine(current->prompt, current); /* Now wait for the next char. Can insert anything except \0 */ c = fd_read(current); /* Remove the ^V first */ remove_char(current, current->pos - 1); if (c != -1) { /* Insert the actual char */ insert_char(current, current->pos, c); } refreshLine(current->prompt, current); } } break; case ctrl('B'): case SPECIAL_LEFT: if (current->pos > 0) { current->pos--; refreshLine(current->prompt, current); } break; case ctrl('F'): case SPECIAL_RIGHT: if (current->pos < current->chars) { current->pos++; refreshLine(current->prompt, current); } break; case SPECIAL_PAGE_UP: dir = history_len - history_index - 1; /* move to start of history */ goto history_navigation; case SPECIAL_PAGE_DOWN: dir = -history_index; /* move to 0 == end of history, i.e. current */ goto history_navigation; case ctrl('P'): case SPECIAL_UP: dir = 1; goto history_navigation; case ctrl('N'): case SPECIAL_DOWN: history_navigation: if (history_len > 1) { /* Update the current history entry before to * overwrite it with tne next one. */ free(history[history_len - 1 - history_index]); history[history_len - 1 - history_index] = strdup(current->buf); /* Show the new entry */ history_index += dir; if (history_index < 0) { history_index = 0; break; } else if (history_index >= history_len) { history_index = history_len - 1; break; } set_current(current, history[history_len - 1 - history_index]); refreshLine(current->prompt, current); } break; case ctrl('A'): /* Ctrl+a, go to the start of the line */ case SPECIAL_HOME: current->pos = 0; refreshLine(current->prompt, current); break; case ctrl('E'): /* ctrl+e, go to the end of the line */ case SPECIAL_END: current->pos = current->chars; refreshLine(current->prompt, current); break; case ctrl('U'): /* Ctrl+u, delete to beginning of line, save deleted chars. */ if (remove_chars(current, 0, current->pos)) { refreshLine(current->prompt, current); } break; case ctrl('K'): /* Ctrl+k, delete from current to end of line, save deleted chars. */ if (remove_chars(current, current->pos, current->chars - current->pos)) { refreshLine(current->prompt, current); } break; case ctrl('Y'): /* Ctrl+y, insert saved chars at current position */ if (current->capture && insert_chars(current, current->pos, current->capture)) { refreshLine(current->prompt, current); } break; case ctrl('L'): /* Ctrl+L, clear screen */ linenoiseClearScreen(); /* Force recalc of window size for serial terminals */ current->cols = 0; refreshLine(current->prompt, current); break; default: /* Only tab is allowed without ^V */ if (c == '\t' || c >= ' ') { if (insert_char(current, current->pos, c) == 1) { refreshLine(current->prompt, current); } } break; } } return current->len; }
/* - regdump - dump a regexp onto stdout in vaguely comprehensible form */ static void regdump(regex_t *preg) { int s; int op = EXACTLY; /* Arbitrary non-END op. */ int next; char buf[MAX_UTF8_LEN + 1]; int i; for (i = 1; i < preg->p; i++) { printf("%02x ", (unsigned char)preg->program[i]); if (i % 16 == 0) { printf("\n"); } } printf("\n"); s = 1; while (op != END && s < preg->p) { /* While that wasn't END last time... */ op = OP(preg, s); printf("%3d: %s", s, regprop(op)); /* Where, what. */ next = regnext(preg, s); if (next == 0) /* Next ptr. */ printf("(0)"); else printf("(%d)", next); s += 2; if (op == REP || op == REPMIN || op == REPX || op == REPXMIN) { int max = preg->program[s]; int min = preg->program[s + 1]; if (max == 65535) { printf("{%d,*}", min); } else { printf("{%d,%d}", min, max); } printf(" %d", preg->program[s + 2]); s += 3; } else if (op == ANYOF || op == ANYBUT) { /* set of ranges */ while (preg->program[s]) { int len = preg->program[s++]; int first = preg->program[s++]; buf[utf8_getchars(buf, first)] = 0; printf("%s", buf); if (len > 1) { buf[utf8_getchars(buf, first + len - 1)] = 0; printf("-%s", buf); } } s++; } else if (op == EXACTLY) { /* Literal string, where present. */ while (preg->program[s]) { buf[utf8_getchars(buf, preg->program[s])] = 0; printf("%s", buf); s++; } s++; } putchar('\n'); } if (op == END) { /* Header fields of interest. */ if (preg->regstart) { buf[utf8_getchars(buf, preg->regstart)] = 0; printf("start '%s' ", buf); } if (preg->reganch) printf("anchored "); if (preg->regmust != 0) { int i; printf("must have:"); for (i = 0; i < preg->regmlen; i++) { putchar(preg->program[preg->regmust + i]); } putchar('\n'); } } printf("\n"); }