3 * selector is a simple command line utility for selection of strings
4 * with a dynamic pattern-matching.
6 * Copyright (c) 2009, 2010 Francois Fleuret
7 * Written by Francois Fleuret <francois@fleuret.org>
9 * This file is part of selector.
11 * selector is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 3 as
13 * published by the Free Software Foundation.
15 * selector is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with selector. If not, see <http://www.gnu.org/licenses/>.
27 To use it as a super-history-search for bash:
28 selector --bash <(history)
41 #include <sys/ioctl.h>
48 #define VERSION "1.1.2"
50 #define BUFFER_SIZE 4096
52 /* Yeah, global variables! */
54 int nb_lines_max = 1000;
55 char pattern_separator = ';';
56 char label_separator = '\0';
57 int output_to_vt_buffer = 0;
58 int add_control_qs = 0;
62 int inverse_order = 0;
63 int remove_duplicates = 0;
65 int case_sensitive = 0;
69 int attr_modeline, attr_focus_line, attr_error;
71 /********************************************************************/
73 /* malloc with error checking. */
75 void *safe_malloc(size_t n) {
78 printf("Can not allocate memory: %s\n", strerror(errno));
84 /*********************************************************************/
86 void inject_into_tty_buffer(char *string, int add_control_qs) {
87 struct termios oldtio, newtio;
89 const char control_q = '\021';
90 tcgetattr(STDIN_FILENO, &oldtio);
91 memset(&newtio, 0, sizeof(newtio));
92 /* Set input mode (non-canonical, *no echo*,...) */
93 tcsetattr(STDIN_FILENO, TCSANOW, &newtio);
94 /* Put the selected string in the tty input buffer */
95 for(k = string; *k; k++) {
96 if(add_control_qs && !(*k >= ' ' && *k <= '~')) {
97 /* Add ^Q to quote control characters */
98 ioctl(STDIN_FILENO, TIOCSTI, &control_q);
100 ioctl(STDIN_FILENO, TIOCSTI, k);
102 /* Restore the old settings */
103 tcsetattr(STDIN_FILENO, TCSANOW, &oldtio);
106 /*********************************************************************/
108 void str_to_positive_integers(char *string, int *values, int nb) {
109 int current_value, gotone;
119 if(*s >= '0' && *s <= '9') {
120 current_value = current_value * 10 + (int) (*s - '0');
122 } else if(*s == ',' || *s == '\0') {
125 values[n++] = current_value;
131 "selector: Missing value in `%s'.\n", string);
139 "selector: Too many values in `%s'.\n", string);
144 "selector: Empty value in `%s'.\n", string);
149 "selector: Syntax error in `%s'.\n", string);
156 void error_feedback() {
164 void usage(FILE *out) {
166 fprintf(out, "Selector version %s (%s)\n", VERSION, UNAME);
167 fprintf(out, "Written by Francois Fleuret <francois@fleuret.org>.\n");
169 fprintf(out, "Usage: selector [options] [<filename1> [<filename2> ...]]\n");
171 fprintf(out, " -h, --help\n");
172 fprintf(out, " show this help\n");
173 fprintf(out, " -v, --inject-in-tty\n");
174 fprintf(out, " inject the selected line in the tty\n");
175 fprintf(out, " -w, --add-control-qs\n");
176 fprintf(out, " quote control characters with ^Qs when using -v\n");
177 fprintf(out, " -d, --remove-duplicates\n");
178 fprintf(out, " remove duplicated lines\n");
179 fprintf(out, " -b, --remove-bash-prefix\n");
180 fprintf(out, " remove the bash history line prefix\n");
181 fprintf(out, " -z, --remove-zsh-prefix\n");
182 fprintf(out, " remove the zsh history line prefix\n");
183 fprintf(out, " -i, --revert-order\n");
184 fprintf(out, " invert the order of lines\n");
185 fprintf(out, " -e, --regexp\n");
186 fprintf(out, " start in regexp mode\n");
187 fprintf(out, " -a, --case-sensitive\n");
188 fprintf(out, " start in case sensitive mode\n");
189 fprintf(out, " -m, --monochrome\n");
190 fprintf(out, " monochrome mode\n");
191 fprintf(out, " -q, --no-beep\n");
192 fprintf(out, " make a flash instead of a beep on an edition error\n");
193 fprintf(out, " --bash\n");
194 fprintf(out, " setting for bash history search, same as -b -i -d -v -w -l ${HISTSIZE}\n");
195 fprintf(out, " --, --rest-are-files\n");
196 fprintf(out, " all following arguments are filenames\n");
197 fprintf(out, " -t <title>, --title <title>\n");
198 fprintf(out, " add a title in the modeline\n");
199 fprintf(out, " -c <colors>, --colors <colors>\n");
200 fprintf(out, " set the display colors with an argument of the form\n");
201 fprintf(out, " <fg_modeline>,<bg_modeline>,<fg_highlight>,<bg_highlight>\n");
202 fprintf(out, " -o <output filename>, --output-file <output filename>\n");
203 fprintf(out, " set a file to write the selected line to\n");
204 fprintf(out, " -s <pattern separator>, --pattern-separator <pattern separator>\n");
205 fprintf(out, " set the symbol to separate substrings in the pattern\n");
206 fprintf(out, " -x <label separator>, --label-separator <label separator>\n");
207 fprintf(out, " set the symbol to terminate the label\n");
208 fprintf(out, " -l <max number of lines>, --number-of-lines <max number of lines>\n");
209 fprintf(out, " set the maximum number of lines to take into account\n");
213 /*********************************************************************/
215 /* A quick and dirty hash table */
217 /* The table itself stores indexes of the strings taken in a char**
218 table. When a string is added, if it was already in the table, the
219 new index replaces the previous one. */
221 struct hash_table_t {
226 struct hash_table_t *new_hash_table(int size) {
228 struct hash_table_t *hash_table;
230 hash_table = safe_malloc(sizeof(struct hash_table_t));
232 hash_table->size = size;
233 hash_table->entries = safe_malloc(hash_table->size * sizeof(int));
235 for(k = 0; k < hash_table->size; k++) {
236 hash_table->entries[k] = -1;
242 void free_hash_table(struct hash_table_t *hash_table) {
243 free(hash_table->entries);
247 /* Adds new_string in the table, associated to new_index. If this
248 string was not already in the table, returns -1. Otherwise, returns
249 the previous index it had. */
251 int add_and_get_previous_index(struct hash_table_t *hash_table,
252 const char *new_string, int new_index,
255 unsigned int code = 0, start;
258 /* This is my recipe. I checked, it seems to work (as long as
259 hash_table->size is not a multiple of 387433 that should be
262 for(k = 0; new_string[k]; k++) {
263 code = code * 387433 + (unsigned int) (new_string[k]);
266 code = code % hash_table->size;
269 while(hash_table->entries[code] >= 0) {
270 /* There is a string with that code */
271 if(strcmp(new_string, strings[hash_table->entries[code]]) == 0) {
272 /* It is the same string, we keep a copy of the stored index */
273 int result = hash_table->entries[code];
274 /* Put the new one */
275 hash_table->entries[code] = new_index;
276 /* And return the previous one */
279 /* This collision was not the same string, let's move to the next
281 code = (code + 1) % hash_table->size;
282 /* We came back to our original code, which means that the table
285 printf("Full hash table (that should not happen)\n");
290 /* This string was not already in there, store the index in the
291 table and return -1 */
293 hash_table->entries[code] = new_index;
297 /*********************************************************************
298 A matcher matches either with a collection of substrings, or with a
306 char *splitted_patterns, **patterns;
309 int match(char *string, matcher_t *matcher) {
311 if(matcher->nb_patterns >= 0) {
312 if(matcher->case_sensitive) {
313 for(n = 0; n < matcher->nb_patterns; n++) {
314 if(strstr(string, matcher->patterns[n]) == 0) return 0;
317 for(n = 0; n < matcher->nb_patterns; n++) {
318 if(strcasestr(string, matcher->patterns[n]) == 0) return 0;
323 return regexec(&matcher->preg, string, 0, 0, 0) == 0;
327 void free_matcher(matcher_t *matcher) {
328 if(matcher->nb_patterns < 0) {
329 if(!matcher->regexp_error) regfree(&matcher->preg);
331 free(matcher->splitted_patterns);
332 free(matcher->patterns);
336 void initialize_matcher(int use_regexp, int case_sensitive,
337 matcher_t *matcher, const char *pattern) {
339 char *t, *last_pattern_start;
343 matcher->nb_patterns = -1;
344 matcher->regexp_error = regcomp(&matcher->preg, pattern,
345 case_sensitive ? 0 : REG_ICASE);
347 matcher->regexp_error = 0;
348 matcher->nb_patterns = 1;
349 matcher->case_sensitive = case_sensitive;
351 for(s = pattern; *s; s++) {
352 if(*s == pattern_separator) {
353 matcher->nb_patterns++;
357 matcher->splitted_patterns =
358 safe_malloc((strlen(pattern) + 1) * sizeof(char));
361 safe_malloc(matcher->nb_patterns * sizeof(char *));
363 strcpy(matcher->splitted_patterns, pattern);
366 last_pattern_start = matcher->splitted_patterns;
367 for(t = matcher->splitted_patterns; n < matcher->nb_patterns; t++) {
368 if(*t == pattern_separator || *t == '\0') {
370 matcher->patterns[n++] = last_pattern_start;
371 last_pattern_start = t + 1;
377 /*********************************************************************
380 void delete_char(char *buffer, int *position) {
381 if(buffer[*position]) {
383 while(c < BUFFER_SIZE && buffer[c]) {
384 buffer[c] = buffer[c+1];
387 } else error_feedback();
390 void backspace_char(char *buffer, int *position) {
392 if(buffer[*position]) {
393 int c = *position - 1;
395 buffer[c] = buffer[c+1];
399 buffer[*position - 1] = '\0';
403 } else error_feedback();
406 void insert_char(char *buffer, int *position, char character) {
407 if(strlen(buffer) < BUFFER_SIZE - 1) {
409 char t = buffer[c], u;
418 buffer[(*position)++] = character;
419 } else error_feedback();
422 void kill_before_cursor(char *buffer, int *position) {
424 while(buffer[*position + s]) {
425 buffer[s] = buffer[*position + s];
432 void kill_after_cursor(char *buffer, int *position) {
433 buffer[*position] = '\0';
436 /*********************************************************************/
438 int previous_visible(int current_line, char **lines, matcher_t *matcher) {
439 int line = current_line - 1;
440 while(line >= 0 && !match(lines[line], matcher)) line--;
444 int next_visible(int current_line, int nb_lines, char **lines,
445 matcher_t *matcher) {
446 int line = current_line + 1;
447 while(line < nb_lines && !match(lines[line], matcher)) line++;
455 /*********************************************************************/
457 /* The line highlighted is the first one matching the matcher in that
458 order: (1) current_focus_line after motion, if it does not match,
459 then (2) the first with a greater index, if none matches, then (3)
460 the first with a lesser index.
462 The index of the line actually shown highlighted is written in
463 displayed_focus_line (it can be -1 if no line at all matches the
466 If there is a motion and a line is actually shown highlighted, its
467 value is written in current_focus_line. */
469 void update_screen(int *current_focus_line, int *displayed_focus_line,
471 int nb_lines, char **lines,
475 char buffer[BUFFER_SIZE];
478 int console_width, console_height;
479 int nb_printed_lines = 0;
482 initialize_matcher(use_regexp, case_sensitive, &matcher, pattern);
484 console_width = getmaxx(stdscr);
485 console_height = getmaxy(stdscr);
487 use_default_colors();
489 /* Add an empty line where we will print the modeline at the end */
493 /* If the regexp is erroneous, print a message saying so */
495 if(matcher.regexp_error) {
497 addnstr("Regexp syntax error", console_width);
501 /* Else, and we do have lines to select from, find a visible line. */
503 else if(nb_lines > 0) {
505 if(match(lines[*current_focus_line], &matcher)) {
506 new_focus_line = *current_focus_line;
508 new_focus_line = next_visible(*current_focus_line, nb_lines, lines,
510 if(new_focus_line < 0) {
511 new_focus_line = previous_visible(*current_focus_line, lines, &matcher);
515 /* If we found a visible line and we should move, let's move */
517 if(new_focus_line >= 0 && motion != 0) {
518 int l = new_focus_line;
520 /* We want to go down, let's find the first visible line below */
521 for(m = 0; l >= 0 && m < motion; m++) {
522 l = next_visible(l, nb_lines, lines, &matcher);
528 /* We want to go up, let's find the first visible line above */
529 for(m = 0; l >= 0 && m < -motion; m++) {
530 l = previous_visible(l, lines, &matcher);
538 /* Here new_focus_line is either a line number matching the
541 if(new_focus_line >= 0) {
543 int first_line = new_focus_line, last_line = new_focus_line;
546 /* We find the first and last lines to show, so that the total
547 of visible lines between them (them included) is
550 while(nb_match < console_height-1 &&
551 (first_line > 0 || last_line < nb_lines - 1)) {
555 while(first_line > 0 && !match(lines[first_line], &matcher)) {
558 if(match(lines[first_line], &matcher)) {
563 if(nb_match < console_height - 1 && last_line < nb_lines - 1) {
565 while(last_line < nb_lines - 1 && !match(lines[last_line], &matcher)) {
569 if(match(lines[last_line], &matcher)) {
575 /* Now we display them */
577 for(l = first_line; l <= last_line; l++) {
578 if(match(lines[l], &matcher)) {
581 while(lines[l][k] && k < BUFFER_SIZE - 2 && k < console_width - 2) {
582 buffer[k] = lines[l][k];
586 /* We fill the rest of the line with blanks if this is the
589 if(l == new_focus_line) {
590 while(k < console_width) {
600 /* Highlight the highlighted line ... */
602 if(l == new_focus_line) {
603 attron(attr_focus_line);
604 addnstr(buffer, console_width);
605 attroff(attr_focus_line);
607 addnstr(buffer, console_width);
614 /* If we are on a focused line and we moved, this become the new
618 *current_focus_line = new_focus_line;
622 *displayed_focus_line = new_focus_line;
624 if(nb_printed_lines == 0) {
626 addnstr("No selection", console_width);
631 /* Else, print a message saying that there are no lines to select from */
635 addnstr("Empty choice", console_width);
641 /* Draw the modeline */
645 attron(attr_modeline);
647 for(k = 0; k < console_width; k++) buffer[k] = ' ';
648 buffer[console_width] = '\0';
649 addnstr(buffer, console_width);
653 /* There must be a more elegant way of moving the cursor at a
654 location met during display */
661 cursor_x += strlen(title) + 1;
664 sprintf(buffer, "%d/%d ", nb_printed_lines, nb_lines);
666 cursor_x += strlen(buffer);
668 addnstr(pattern, cursor_position);
669 cursor_x += cursor_position;
671 if(pattern[cursor_position]) {
672 addstr(pattern + cursor_position);
677 /* Add a few info about the mode we are in (regexp and/or case
680 if(use_regexp || case_sensitive) {
697 attroff(attr_modeline);
702 free_matcher(&matcher);
705 /*********************************************************************/
707 void store_line(struct hash_table_t *hash_table,
708 const char *new_line,
709 int *nb_lines, char **lines) {
712 /* Remove the zsh history prefix */
714 if(zsh_history && *new_line == ':') {
715 while(*new_line && *new_line != ';') new_line++;
716 if(*new_line == ';') new_line++;
719 /* Remove the bash history prefix */
722 while(*new_line == ' ') new_line++;
723 while(*new_line >= '0' && *new_line <= '9') new_line++;
724 while(*new_line == ' ') new_line++;
727 /* Check for duplicates with the hash table and insert the line in
728 the list if necessary */
731 dup = add_and_get_previous_index(hash_table,
732 new_line, *nb_lines, lines);
738 lines[*nb_lines] = safe_malloc((strlen(new_line) + 1) * sizeof(char));
739 strcpy(lines[*nb_lines], new_line);
741 /* The string was already in there, so we do not allocate a new
742 string but use the pointer to the first occurence of it */
743 lines[*nb_lines] = lines[dup];
750 void read_file(struct hash_table_t *hash_table,
751 const char *input_filename,
752 int nb_lines_max, int *nb_lines, char **lines) {
754 char raw_line[BUFFER_SIZE];
755 int start, end, eol, k;
758 file = fopen(input_filename, "r");
761 fprintf(stderr, "selector: Can not open `%s'.\n", input_filename);
768 while(*nb_lines < nb_lines_max && (end > start || !feof(file))) {
771 /* Look for the end of a line in what is already in the buffer */
772 while(eol < end && raw_line[eol] != '\n') eol++;
774 /* if we did not find the of a line, move what has not been
775 processed and is in the buffer to the beginning of the buffer,
776 fill the buffer with new data from the file, and look for the
779 for(k = 0; k < end - start; k++) {
780 raw_line[k] = raw_line[k + start];
785 end += fread(raw_line + end, sizeof(char), BUFFER_SIZE - end, file);
786 while(eol < end && raw_line[eol] != '\n') eol++;
789 /* The end of the line is the buffer size, which means the line is
792 if(eol == BUFFER_SIZE) {
793 raw_line[BUFFER_SIZE - 1] = '\0';
794 fprintf(stderr, "selector: Line too long (max is %d characters):\n",
796 fprintf(stderr, raw_line);
797 fprintf(stderr, "\n");
801 /* If we got a line, we replace the carriage return by a \0 to
804 raw_line[eol] = '\0';
806 store_line(hash_table, raw_line + start,
815 /*********************************************************************/
817 /* For long options that have no equivalent short option, use a
818 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
821 OPT_BASH_MODE = CHAR_MAX + 1
824 static struct option long_options[] = {
825 { "output-file", 1, 0, 'o' },
826 { "pattern-separator", 1, 0, 's' },
827 { "label-separator", 1, 0, 'x' },
828 { "inject-in-tty", no_argument, 0, 'v' },
829 { "add-control-qs", no_argument, 0, 'w' },
830 { "monochrome", no_argument, 0, 'm' },
831 { "no-beep", no_argument, 0, 'q' },
832 { "revert-order", no_argument, 0, 'i' },
833 { "remove-bash-prefix", no_argument, 0, 'b' },
834 { "remove-zsh-prefix", no_argument, 0, 'z' },
835 { "remove-duplicates", no_argument, 0, 'd' },
836 { "regexp", no_argument, 0, 'e' },
837 { "case-sensitive", no_argument, 0, 'a' },
838 { "title", 1, 0, 't' },
839 { "number-of-lines", 1, 0, 'l' },
840 { "colors", 1, 0, 'c' },
841 { "rest-are-files", no_argument, 0, '-' },
842 { "bash", no_argument, 0, OPT_BASH_MODE },
843 { "help", no_argument, 0, 'h' },
847 int main(int argc, char **argv) {
849 char output_filename[BUFFER_SIZE];
850 char pattern[BUFFER_SIZE];
853 int error = 0, show_help = 0;
854 int rest_are_files = 0;
856 int current_focus_line, displayed_focus_line;
859 int color_fg_modeline, color_bg_modeline;
860 int color_fg_highlight, color_bg_highlight;
862 char **lines, **labels;
864 struct hash_table_t *hash_table;
867 if(!isatty(STDIN_FILENO)) {
868 fprintf(stderr, "selector: The standard input is not a tty.\n");
872 color_fg_modeline = COLOR_WHITE;
873 color_bg_modeline = COLOR_BLACK;
874 color_fg_highlight = COLOR_BLACK;
875 color_bg_highlight = COLOR_YELLOW;
877 setlocale(LC_ALL, "");
879 strcpy(output_filename, "");
881 while (!rest_are_files &&
882 (c = getopt_long(argc, argv, "o:s:x:vwmqf:ibzdeat:l:c:-h",
883 long_options, NULL)) != -1) {
888 strncpy(output_filename, optarg, BUFFER_SIZE);
892 pattern_separator = optarg[0];
896 label_separator = optarg[0];
900 output_to_vt_buffer = 1;
928 remove_duplicates = 1;
941 title = safe_malloc((strlen(optarg) + 1) * sizeof(char));
942 strcpy(title, optarg);
946 str_to_positive_integers(optarg, &nb_lines_max, 1);
950 str_to_positive_integers(optarg, colors, 4);
951 color_fg_modeline = colors[0];
952 color_bg_modeline = colors[1];
953 color_fg_highlight = colors[2];
954 color_bg_highlight = colors[3];
966 /* Same as -c 7,4,0,3 -q */
967 /* color_fg_modeline = 7; */
968 /* color_bg_modeline = 4; */
969 /* color_fg_highlight = 0; */
970 /* color_bg_highlight = 3; */
971 /* error_flash = 1; */
972 /* Same as -b -i -d -v -w */
975 remove_duplicates = 1;
976 output_to_vt_buffer = 1;
978 bash_histsize = getenv("HISTSIZE");
980 str_to_positive_integers(bash_histsize, &nb_lines_max, 1);
1000 lines = safe_malloc(nb_lines_max * sizeof(char *));
1004 if(remove_duplicates) {
1005 hash_table = new_hash_table(nb_lines_max * 10);
1010 while(optind < argc) {
1011 read_file(hash_table,
1013 nb_lines_max, &nb_lines, lines);
1018 free_hash_table(hash_table);
1021 /* Now remove the null strings */
1024 for(k = 0; k < nb_lines; k++) {
1026 lines[n++] = lines[k];
1033 for(l = 0; l < nb_lines / 2; l++) {
1034 char *s = lines[nb_lines - 1 - l];
1035 lines[nb_lines - 1 - l] = lines[l];
1040 /* Build the labels from the strings, take only the part before the
1041 label_separator and transform control characters to printable
1044 labels = safe_malloc(nb_lines * sizeof(char *));
1046 for(l = 0; l < nb_lines; l++) {
1052 while(*t && *t != label_separator) {
1057 labels[l] = safe_malloc((e + 1) * sizeof(char));
1060 while(*t && *t != label_separator) {
1062 while(*u) { *s++ = *u++; }
1069 cursor_position = 0;
1071 /* Here we start to display with curse */
1076 intrflush(stdscr, FALSE);
1078 /* So that the arrow keys work */
1079 keypad(stdscr, TRUE);
1081 attr_error = A_STANDOUT;
1082 attr_modeline = A_REVERSE;
1083 attr_focus_line = A_STANDOUT;
1085 if(with_colors && has_colors()) {
1089 if(color_fg_modeline < 0 || color_fg_modeline >= COLORS ||
1090 color_bg_modeline < 0 || color_bg_modeline >= COLORS ||
1091 color_fg_highlight < 0 || color_bg_highlight >= COLORS ||
1092 color_bg_highlight < 0 || color_bg_highlight >= COLORS) {
1095 fprintf(stderr, "selector: Color numbers have to be between 0 and %d.\n",
1100 init_pair(1, color_fg_modeline, color_bg_modeline);
1101 attr_modeline = COLOR_PAIR(1);
1103 init_pair(2, color_fg_highlight, color_bg_highlight);
1104 attr_focus_line = COLOR_PAIR(2);
1106 init_pair(3, COLOR_WHITE, COLOR_RED);
1107 attr_error = COLOR_PAIR(3);
1111 current_focus_line = 0;
1112 displayed_focus_line = 0;
1114 update_screen(¤t_focus_line, &displayed_focus_line,
1116 nb_lines, labels, cursor_position, pattern);
1123 if(key >= ' ' && key <= '~') { /* Insert character */
1124 insert_char(pattern, &cursor_position, key);
1127 else if(key == KEY_BACKSPACE ||
1128 key == '\010' || /* ^H */
1129 key == '\177') { /* ^? */
1130 backspace_char(pattern, &cursor_position);
1133 else if(key == KEY_DC ||
1134 key == '\004') { /* ^D */
1135 delete_char(pattern, &cursor_position);
1138 else if(key == KEY_HOME) {
1139 current_focus_line = 0;
1142 else if(key == KEY_END) {
1143 current_focus_line = nb_lines - 1;
1146 else if(key == KEY_NPAGE) {
1150 else if(key == KEY_PPAGE) {
1154 else if(key == KEY_DOWN ||
1155 key == '\016') { /* ^N */
1159 else if(key == KEY_UP ||
1160 key == '\020') { /* ^P */
1164 else if(key == KEY_LEFT ||
1165 key == '\002') { /* ^B */
1166 if(cursor_position > 0) cursor_position--;
1167 else error_feedback();
1170 else if(key == KEY_RIGHT ||
1171 key == '\006') { /* ^F */
1172 if(pattern[cursor_position]) cursor_position++;
1173 else error_feedback();
1176 else if(key == '\001') { /* ^A */
1177 cursor_position = 0;
1180 else if(key == '\005') { /* ^E */
1181 cursor_position = strlen(pattern);
1184 else if(key == '\022') { /* ^R */
1185 use_regexp = !use_regexp;
1188 else if(key == '\011') { /* ^I */
1189 case_sensitive = !case_sensitive;
1192 else if(key == '\025') { /* ^U */
1193 kill_before_cursor(pattern, &cursor_position);
1196 else if(key == '\013') { /* ^K */
1197 kill_after_cursor(pattern, &cursor_position);
1200 else if(key == '\014') { /* ^L */
1201 /* I suspect that we may sometime mess up the display, so ^L is
1202 here to force a full refresh */
1206 update_screen(¤t_focus_line, &displayed_focus_line,
1208 nb_lines, labels, cursor_position, pattern);
1210 } while(key != '\007' && /* ^G */
1211 key != '\033' && /* ^[ (escape) */
1218 /* Here we come back to standard display */
1220 if((key == KEY_ENTER || key == '\n')) {
1224 if(displayed_focus_line >= 0 && displayed_focus_line < nb_lines) {
1225 t = lines[displayed_focus_line];
1226 if(label_separator) {
1227 while(*t && *t != label_separator) t++;
1234 if(output_to_vt_buffer && t) {
1235 inject_into_tty_buffer(t, add_control_qs);
1238 if(output_filename[0]) {
1239 FILE *out = fopen(output_filename, "w");
1247 "selector: Can not open %s for writing.\n",
1255 printf("Aborted.\n");
1258 for(l = 0; l < nb_lines; l++) {