static void fuzz_back() { const int orig_entries = rl.cur_entries; const int n0 = PXY2N(page,x,y); const char * const p0 = rl.entries[PXY2N(page,x,y)]; int n1 = n0; if (fuzz_len == 0) return; if (prune) { if (orig_entries == rl0->cur_entries) return; while (rl.cur_entries == orig_entries) { fuzz_len = max(0,fuzz_len-1); int i; for (int j = n1 = i = 0; j < rl0->cur_entries; j++) { char * const p1 = rl0->entries[j]; if ( ! strncasecmp(p0, p1, fuzz_len) ) { if (p1 == p0) n1 = i; rl.entries[i++] = p1; } } rl.cur_entries = i; } } else { fuzz_len--; int shiftsize = rl.alloc_entries - rl.cur_entries; if (shiftsize) { /* shift our current entries to the end of their buffer */ memmove(rl.entries + shiftsize, rl.entries, rl.cur_entries * sizeof(char *)); int rldest, rl0src, rlsrc, n0shifted = n0 + shiftsize; for (rldest = rl0src = 0, rlsrc = shiftsize; rl0src < rl0->cur_entries; rl0src++) { if (rl0->entries[rl0src] == rl.entries[rlsrc]) { /* This was in our old list, so we keep it */ if (n0shifted == rlsrc) n1 = rldest; /* it was our marked entry */ rl.entries[rldest++] = rl.entries[rlsrc++]; } else if ( ! strncasecmp(p0, rl0->entries[rl0src], fuzz_len) ) { /* Wasn't in our old list due to prior purning, but should be */ rl.entries[rldest++] = rl0->entries[rl0src]; } } rl.cur_entries = rldest; } } page = -1; /* causes normalize() to call print_strings() */ normalize(n1); }
/* Reorder (i.e. swap) the current entry n with entry n+dir. dir should be either 1 or -1. */ static bool request_reorder(const int dir) { if (! rl0->allow_reorder || rl.cur_entries < 2) return false; const int n0 = PXY2N(page,x,y); const int n1 = (n0 + dir + rl.cur_entries ) % rl.cur_entries; /* Allows wrap around. */ char * const p0 = rl.entries[n0]; char * const p1 = rl.entries[n1]; int i0, i1, i; for (i=0, i0=-1, i1=-1; i<rl0->cur_entries && (i0<0 || i1<0); i++) { if (i0 < 0 && p0 == rl0->entries[i] ) { i0 = i; } if (i1 < 0 && p1 == rl0->entries[i] ) { i1 = i; } } i = rl0->orig_order[i0]; rl0->orig_order[i0] = rl0->orig_order[i1]; rl0->orig_order[i1] = i; rl.entries[n0] = p1; rl.entries[n1] = p0; rl0->entries[i0] = p1; rl0->entries[i1] = p0; page = -1; /* causes normalize() to call print_strings() */ normalize(n1); return true; }
static void request_move_left(void) { if ( x == 0 && y + page > 0 ) { request_move_up(); request_move_to_eol(); } else { normalize(PXY2N(page,x,y) - DX(page)); } }
static void print_strings() { const int dx = rl.max_entry_len + 1 + (rl.suffix ? 1 : 0); set_attr(0); for(int row = 0; row < max_names_per_col; row++) { move_cursor(row, 0); clear_to_eol(); if (row < NAMES_PER_COL(page)) { for(int col = 0; col < NAMES_PER_LINE(page); col++) { if (PXY2N(page,col,row) < rl.cur_entries) { move_cursor(row, col * dx); const char * const p = rl.entries[PXY2N(page,col,row)]; if (rl.suffix) set_attr(p[strlen(p) - 1] == rl.suffix ? BOLD : 0); output_string(p, io_utf8); } } } } }
static void print_strings() { int row,col; const char *p; set_attr(0); for(row = 0; row < max_names_per_col; row++) { move_cursor(row, 0); clear_to_eol(); if (row < NAMES_PER_COL(page)) { for(col = 0; col < NAMES_PER_LINE(page); col++) { if (PXY2N(page,col,row) < num_entries) { move_cursor(row, col * max_name_len); p = entries[PXY2N(page,col,row)]; if (mark_char) set_attr(p[strlen(p) - 1] == mark_char ? BOLD : 0); output_string(p, io_utf8); } } } } }
static int request_strings_cleanup(bool reordered) { int n = PXY2N(page,x,y); const char * const p0 = rl.entries[n]; for (int i = 0; i<rl0->cur_entries; i++) { if (rl0->entries[i] == p0) { n = i; break; } } if (rl.entries) free(rl.entries); rl.entries = NULL; rl0->reordered = reordered; return n; }
static void request_move_right(void) { if (y < NAMES_PER_COL(page) - 1 && PXY2N(page,0,y+1) < rl.cur_entries && (x == NAMES_PER_LINE(page) - 1 || PXY2N(page,x+1,y) > rl.cur_entries -1) ) { request_move_to_sol(); request_move_down(); } else if (y == NAMES_PER_COL(page) - 1 && x == NAMES_PER_LINE(page) - 1 && PXY2N(page+1,0,0) < rl.cur_entries) { normalize(PXY2N(page+1,0,0)); } else if (PXY2N(page,x,y) + DX(page) < rl.cur_entries ) { normalize(PXY2N(page,x,y) + DX(page)); } }
static void fuzz_forward(const int c) { const int n0 = PXY2N(page,x,y); const char * const p0 = rl.entries[n0]; assert(fuzz_len >= 0); if (prune) { int i = 0, n1 = 0; for (int j = 0; j < rl.cur_entries; j++) { char * const p1 = rl.entries[j]; const int cmp = strncasecmp(p0, p1, fuzz_len); if (! cmp && strlen(p1) > fuzz_len && localised_up_case[(unsigned char)p1[fuzz_len]] == c) { if (p1 == p0) n1 = i; rl.entries[i++] = p1; } } if (i) { rl.cur_entries = i; fuzz_len = common_prefix_len(&rl); page = -1; /* causes normalize() to call print_strings() */ normalize(n1); } } else { /* find the next matching string, possibly wrapping around */ for (int n=n0, i=rl.cur_entries; i; i--, n=(n+1)%rl.cur_entries) { char * const p1 = rl.entries[n]; const int cmp = strncasecmp(p0, p1, fuzz_len); if (!cmp && strlen(p1) > fuzz_len && localised_up_case[(unsigned char)p1[fuzz_len]] == c) { fuzz_len++; page = -1; normalize(n); break; } } } }
int request_strings(req_list *rlp0, int n) { assert(rlp0->cur_entries > 0); int ne_lines0 = 0, ne_columns0 = 0; bool reordered = false; max_names_per_line = max_names_per_col = x = y = page = fuzz_len = 0; if ( ! request_strings_init(rlp0) ) return ERROR; const int dx = rl.max_entry_len + 1 + (rl.suffix ? 1 : 0); while(true) { if (ne_lines0 != ne_lines || ne_columns0 != ne_columns) { if (ne_lines0 && ne_columns0 ) n = PXY2N(page,x,y); if (!(max_names_per_line = ne_columns / dx)) max_names_per_line = 1; max_names_per_col = ne_lines - 1; names_per_page = max_names_per_line * max_names_per_col; ne_lines0 = ne_lines; ne_columns0 = ne_columns; page = N2P(n); x = N2X(n); y = N2Y(n); print_strings(); print_message(NULL); } n = PXY2N(page,x,y); assert(fuzz_len >= 0); fuzz_len = min(fuzz_len, strlen(rl.entries[n])); move_cursor(y, x * dx + fuzz_len); int c; input_class ic; do c = get_key_code(); while((ic = CHAR_CLASS(c)) == IGNORE || ic == INVALID); switch(ic) { case ALPHA: if (n >= rl.cur_entries) n = rl.cur_entries - 1; c = localised_up_case[(unsigned char)c]; fuzz_forward( c ); break; case TAB: if (! rlp0->ignore_tab) { n = request_strings_cleanup(reordered); if (n >= rlp0->cur_entries) return ERROR; else return -n - 2; } break; case RETURN: n = request_strings_cleanup(reordered); if (n >= rlp0->cur_entries) return ERROR; else return n; case COMMAND: if (c < 0) c = -c - 1; const int a = parse_command_line(key_binding[c], NULL, NULL, false); if (a >= 0) { switch(a) { case BACKSPACE_A: fuzz_back(); break; case MOVERIGHT_A: request_move_right(); break; case MOVELEFT_A: request_move_left(); break; case MOVESOL_A: request_move_to_sol(); break; case MOVEEOL_A: request_move_to_eol(); break; case TOGGLESEOL_A: if (x != 0) x = 0; else request_move_to_eol(); break; case LINEUP_A: request_move_up(); break; case LINEDOWN_A: request_move_down(); break; case MOVEINCUP_A: request_move_inc_up(); break; case MOVEINCDOWN_A: request_move_inc_down(); break; case PAGEUP_A: case PREVPAGE_A: request_prev_page(); break; case PAGEDOWN_A: case NEXTPAGE_A: request_next_page(); break; case MOVESOF_A: request_move_to_sof(); break; case MOVEEOF_A: request_move_to_eof(); break; case TOGGLESEOF_A: request_toggle_seof(); break; case NEXTWORD_A: request_move_next(); break; case PREVWORD_A: request_move_previous(); break; case NEXTDOC_A: reordered |= request_reorder(1); break; case PREVDOC_A: reordered |= request_reorder(-1); break; case INSERT_A: case DELETECHAR_A: prune = !prune; break; case CLOSEDOC_A: case ESCAPE_A: case QUIT_A: case SELECTDOC_A: request_strings_cleanup(reordered); return -1; } } break; default: break; } } }
static void request_move_to_eol(void) { while (x < NAMES_PER_LINE(page) - 1 && PXY2N(page,x+1,y) < rl.cur_entries) { x++; } }
static void request_prev_page(void) { if (page == 0 ) normalize(PXY2N(page,0,0)); else normalize(PXY2N(page-1,x,y)); }
static void request_move_previous(void) { normalize(PXY2N(page,x,y)-1); }
static void request_move_next(void) { normalize(PXY2N(page,x,y)+1); }
/* If mark_char is not '\0', we bold names ending with it. */ int request_strings(const char * const * const _entries, const int _num_entries, int n, const int _max_name_len, int _mark_char) { action a; input_class ic; int c, i, ne_lines0, ne_columns0; assert(_num_entries > 0); ne_lines0 = ne_columns0 = max_names_per_line = max_names_per_col = x = y = page = 0; entries = _entries; num_entries = _num_entries; max_name_len = _max_name_len + 1; mark_char = _mark_char; while(TRUE) { if (ne_lines0 != ne_lines || ne_columns0 != ne_columns) { if (ne_lines0 && ne_columns0 ) n = PXY2N(page,x,y); if (!(max_names_per_line = ne_columns / (max_name_len))) max_names_per_line = 1; max_names_per_col = ne_lines - 1; names_per_page = max_names_per_line * max_names_per_col; ne_lines0 = ne_lines; ne_columns0 = ne_columns; page = N2P(n); x = N2X(n); y = N2Y(n); print_strings(); print_message(NULL); } move_cursor(y, x * max_name_len); do c = get_key_code(); while((ic = CHAR_CLASS(c)) == IGNORE || ic == INVALID); n = PXY2N(page,x,y); switch(ic) { case ALPHA: if (n >= num_entries) n = num_entries - 1; c = localised_up_case[(unsigned char)c]; for(i = 1; i < num_entries; i++) if (localised_up_case[(unsigned char)entries[(n + i) % num_entries][0]] == c) { normalize((n + i) % num_entries); break; } break; case TAB: if (n >= num_entries) return ERROR; else return -n - 2; case RETURN: if (n >= num_entries) return ERROR; else return n; case COMMAND: if (c < 0) c = -c - 1; if ((a = parse_command_line(key_binding[c], NULL, NULL, FALSE))>=0) { switch(a) { case MOVERIGHT_A: request_move_right(); break; case MOVELEFT_A: request_move_left(); break; case MOVESOL_A: request_move_to_sol(); break; case MOVEEOL_A: request_move_to_eol(); break; case TOGGLESEOL_A: if (x != 0) x = 0; else request_move_to_eol(); break; case LINEUP_A: request_move_up(); break; case LINEDOWN_A: request_move_down(); break; case MOVEINCUP_A: request_move_inc_up(); break; case MOVEINCDOWN_A: request_move_inc_down(); break; case PAGEUP_A: case PREVPAGE_A: request_prev_page(); break; case PAGEDOWN_A: case NEXTPAGE_A: request_next_page(); break; case MOVESOF_A: request_move_to_sof(); break; case MOVEEOF_A: request_move_to_eof(); break; case TOGGLESEOF_A: request_toggle_seof(); break; case ESCAPE_A: return -1; } } break; default: break; } } }
static void request_move_down(void) { normalize(PXY2N(page,x,y) + DY); }
static void request_move_up(void) { normalize(PXY2N(page,x,y) - DY); }
static void request_next_page(void) { normalize(PXY2N(page+1,x,y)); }