static edit_status editor_delete_char (rp_input_line *line) { size_t diff = 0; if (line->position == line->length) return EDIT_NO_OP; if (isu8start (line->buffer[line->position])) { do diff++; while (isu8cont (line->buffer[line->position + diff])); } else diff++; memmove (&line->buffer[line->position], &line->buffer[line->position + diff], line->length - line->position + diff + 1); line->length -= diff; return EDIT_DELETE; }
static edit_status editor_forward_char (rp_input_line *line) { if (line->position == line->length) return EDIT_NO_OP; if (isu8start (line->buffer[line->position])) { do line->position++; while (isu8cont (line->buffer[line->position])); } else line->position++; return EDIT_MOVE; }
int get_key(char *buf, size_t size, size_t *nread) { static struct { union { const char *s; char c; } input; size_t length; int key; } keys[] = { { { (char *)8 }, 1, DEL }, { { (char *)10 }, 1, ENTER }, { { (char *)127 }, 1, DEL }, { { (char *)CTRL('A') }, 1, CTRL_A }, { { (char *)CTRL('B') }, 1, LEFT }, { { (char *)CTRL('D') }, 1, CTRL_D }, { { (char *)CTRL('E') }, 1, CTRL_E }, { { (char *)CTRL('F') }, 1, RIGHT }, { { (char *)CTRL('K') }, 1, CTRL_K }, { { (char *)CTRL('N') }, 1, DOWN }, { { (char *)CTRL('P') }, 1, UP }, { { (char *)CTRL('U') }, 1, CTRL_U }, { { (char *)CTRL('W') }, 1, CTRL_W }, { { "\033\n" }, 2, ALT_ENTER }, { { "\033[A" }, 3, UP }, { { "\033OA" }, 3, UP }, { { "\033[B" }, 3, DOWN }, { { "\033OB" }, 3, DOWN }, { { "\033[C" }, 3, RIGHT }, { { "\033OC" }, 3, RIGHT }, { { "\033[D" }, 3, LEFT }, { { "\033OD" }, 3, LEFT }, { { NULL }, 0, 0 }, }; const char *input; int i; *nread = 0; getc: buf[(*nread)++] = tty_getc(); size--; for (i = 0; keys[i].input.s; i++) { input = keys[i].length > 1 ? keys[i].input.s : &keys[i].input.c; if (*nread > keys[i].length || strncmp(buf, input, *nread)) continue; if (*nread == keys[i].length) return keys[i].key; /* Partial match found, continue reading. */ if (size > 0) goto getc; } if (*nread > 1 && buf[0] == '\033' && (buf[1] == '[' || buf[1] == 'O')) /* * A escape sequence which is not a supported key is being read. * Ensure the whole sequence is read. */ while ((buf[*nread - 1] < '@' || buf[*nread - 1] > '~') && size-- > 0) buf[(*nread)++] = tty_getc(); if (!isu8start(buf[0])) return UNKNOWN; /* * Ensure a whole Unicode character is read. The number of MSBs in the * first octet of a Unicode character is equal to the number of octets * the character consists of, followed by a zero. Therefore, as long as * the MSB is not zero there is still bytes left to read. */ while (((buf[0] << *nread) & 0x80) == 0x80 && size-- > 0) buf[(*nread)++] = tty_getc(); return UNKNOWN; }
const struct choice * selected_choice(void) { char buf[6]; int key, selection = 0, visible_choices_count; int word_position; size_t cursor_position, length, query_length, scroll; cursor_position = query_length = strlen(query); filter_choices(); init_tty(); if (cursor_position >= (size_t)columns) scroll = cursor_position - columns + 1; else scroll = 0; visible_choices_count = print_choices(selection); print_query(query, query_length, cursor_position, scroll); tty_putp(cursor_normal); for (;;) { fflush(tty_out); memset(buf, 0, sizeof(buf)); key = get_key(buf, sizeof(buf), &length); switch (key) { case ENTER: if (visible_choices_count > 0) { restore_tty(); if (selection >= 0 && selection < (ssize_t)choices.length) return &choices.v[selection]; else return NULL; } break; case ALT_ENTER: restore_tty(); choices.v[choices.length].string = query; choices.v[choices.length].description = ""; return &choices.v[choices.length]; case DEL: if (cursor_position > 0) { for (length = 1; isu8cont(query[cursor_position - length]); length++); delete_between( query, query_length, cursor_position - length, cursor_position); cursor_position -= length; query_length -= length; filter_choices(); selection = 0; } break; case CTRL_D: if (cursor_position < query_length) { for (length = 1; isu8cont(query[cursor_position + length]); length++); delete_between( query, query_length, cursor_position, cursor_position + length); query_length -= length; filter_choices(); selection = 0; } break; case CTRL_U: delete_between( query, query_length, 0, cursor_position); query_length -= cursor_position; cursor_position = 0; filter_choices(); selection = 0; break; case CTRL_K: delete_between( query, query_length, cursor_position, query_length); query_length = cursor_position; filter_choices(); selection = 0; break; case CTRL_W: if (cursor_position == 0) break; for (word_position = cursor_position;;) { while (isu8cont(query[--word_position])); if (word_position < 1) break; if (query[word_position] != ' ' && query[word_position - 1] == ' ') break; } delete_between( query, query_length, word_position, cursor_position); query_length -= cursor_position - word_position; cursor_position = word_position; filter_choices(); selection = 0; break; case CTRL_A: cursor_position = 0; break; case CTRL_E: cursor_position = query_length; break; case DOWN: if (selection < visible_choices_count - 1) ++selection; break; case UP: if (selection > 0) --selection; break; case LEFT: while (cursor_position > 0 && isu8cont(query[--cursor_position])); break; case RIGHT: while (cursor_position < query_length && isu8cont(query[++cursor_position])); break; default: if (!isu8start(buf[0]) && !isprint(buf[0])) continue; if (query_size < query_length + length) { query_size = 2*query_length + length; if ((query = reallocarray(query, query_size, sizeof(char))) == NULL) err(1, NULL); } if (cursor_position < query_length) memmove(query + cursor_position + length, query + cursor_position, query_length - cursor_position); memcpy(query + cursor_position, buf, length); cursor_position += length; query_length += length; query[query_length] = '\0'; filter_choices(); selection = 0; break; } tty_putp(cursor_invisible); visible_choices_count = print_choices(selection); if (cursor_position >= scroll + columns) scroll = cursor_position - columns + 1; if (cursor_position < scroll) scroll = cursor_position; print_query(query, query_length, cursor_position, scroll); tty_putp(cursor_normal); } }