+enum {
+ VI_CMDMODE_BIT = 0x40000000,
+ /* 0x80000000 bit flags KEYCODE_xxx */
+};
+
+#if ENABLE_FEATURE_REVERSE_SEARCH
+/* Mimic readline Ctrl-R reverse history search.
+ * When invoked, it shows the following prompt:
+ * (reverse-i-search)'': user_input [cursor pos unchanged by Ctrl-R]
+ * and typing results in search being performed:
+ * (reverse-i-search)'tmp': cd /tmp [cursor under t in /tmp]
+ * Search is performed by looking at progressively older lines in history.
+ * Ctrl-R again searches for the next match in history.
+ * Backspace deletes last matched char.
+ * Control keys exit search and return to normal editing (at current history line).
+ */
+static int32_t reverse_i_search(void)
+{
+ char match_buf[128]; /* for user input */
+ char read_key_buffer[KEYCODE_BUFFER_SIZE];
+ const char *matched_history_line;
+ const char *saved_prompt;
+ int32_t ic;
+
+ matched_history_line = NULL;
+ read_key_buffer[0] = 0;
+ match_buf[0] = '\0';
+
+ /* Save and replace the prompt */
+ saved_prompt = cmdedit_prompt;
+ goto set_prompt;
+
+ while (1) {
+ int h;
+ unsigned match_buf_len = strlen(match_buf);
+
+ fflush_all();
+//FIXME: correct timeout?
+ ic = lineedit_read_key(read_key_buffer, -1);
+
+ switch (ic) {
+ case CTRL('R'): /* searching for the next match */
+ break;
+
+ case '\b':
+ case '\x7f':
+ /* Backspace */
+ if (unicode_status == UNICODE_ON) {
+ while (match_buf_len != 0) {
+ uint8_t c = match_buf[--match_buf_len];
+ if ((c & 0xc0) != 0x80) /* start of UTF-8 char? */
+ break; /* yes */
+ }
+ } else {
+ if (match_buf_len != 0)
+ match_buf_len--;
+ }
+ match_buf[match_buf_len] = '\0';
+ break;
+
+ default:
+ if (ic < ' '
+ || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
+ || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
+ ) {
+ goto ret;
+ }
+
+ /* Append this char */
+#if ENABLE_UNICODE_SUPPORT
+ if (unicode_status == UNICODE_ON) {
+ mbstate_t mbstate = { 0 };
+ char buf[MB_CUR_MAX + 1];
+ int len = wcrtomb(buf, ic, &mbstate);
+ if (len > 0) {
+ buf[len] = '\0';
+ if (match_buf_len + len < sizeof(match_buf))
+ strcpy(match_buf + match_buf_len, buf);
+ }
+ } else
+#endif
+ if (match_buf_len < sizeof(match_buf) - 1) {
+ match_buf[match_buf_len] = ic;
+ match_buf[match_buf_len + 1] = '\0';
+ }
+ break;
+ } /* switch (ic) */
+
+ /* Search in history for match_buf */
+ h = state->cur_history;
+ if (ic == CTRL('R'))
+ h--;
+ while (h >= 0) {
+ if (state->history[h]) {
+ char *match = strstr(state->history[h], match_buf);
+ if (match) {
+ state->cur_history = h;
+ matched_history_line = state->history[h];
+ command_len = load_string(matched_history_line);
+ cursor = match - matched_history_line;
+//FIXME: cursor position for Unicode case
+
+ free((char*)cmdedit_prompt);
+ set_prompt:
+ cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf);
+ cmdedit_prmt_len = strlen(cmdedit_prompt);
+ goto do_redraw;
+ }
+ }
+ h--;
+ }
+
+ /* Not found */
+ match_buf[match_buf_len] = '\0';
+ beep();
+ continue;
+
+ do_redraw:
+ redraw(cmdedit_y, command_len - cursor);
+ } /* while (1) */
+
+ ret:
+ if (matched_history_line)
+ command_len = load_string(matched_history_line);
+
+ free((char*)cmdedit_prompt);
+ cmdedit_prompt = saved_prompt;
+ cmdedit_prmt_len = strlen(cmdedit_prompt);
+ redraw(cmdedit_y, command_len - cursor);
+
+ return ic;
+}
+#endif
+
+/* maxsize must be >= 2.
+ * Returns: