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 -l ${HISTSIZE} <(history)
41 #include <sys/ioctl.h>
48 #define VERSION "1.1.1"
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\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;
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;
268 while(hash_table->entries[code] >= 0) {
269 /* There is a string with that code */
270 if(strcmp(new_string, strings[hash_table->entries[code]]) == 0) {
271 /* It is the same string, we keep a copy of the stored index */
272 int result = hash_table->entries[code];
273 /* Put the new one */
274 hash_table->entries[code] = new_index;
275 /* And return the previous one */
278 /* This collision was not the same string, let's move to the next
280 code = (code + 1) % hash_table->size;
283 /* This string was not already in there, store the index in the
284 table and return -1 */
286 hash_table->entries[code] = new_index;
290 /*********************************************************************
291 A matcher matches either with a collection of substrings, or with a
299 char *splitted_patterns, **patterns;
302 int match(char *string, matcher_t *matcher) {
304 if(matcher->nb_patterns >= 0) {
305 if(matcher->case_sensitive) {
306 for(n = 0; n < matcher->nb_patterns; n++) {
307 if(strstr(string, matcher->patterns[n]) == 0) return 0;
310 for(n = 0; n < matcher->nb_patterns; n++) {
311 if(strcasestr(string, matcher->patterns[n]) == 0) return 0;
316 return regexec(&matcher->preg, string, 0, 0, 0) == 0;
320 void free_matcher(matcher_t *matcher) {
321 if(matcher->nb_patterns < 0) {
322 if(!matcher->regexp_error) regfree(&matcher->preg);
324 free(matcher->splitted_patterns);
325 free(matcher->patterns);
329 void initialize_matcher(int use_regexp, int case_sensitive,
330 matcher_t *matcher, const char *pattern) {
332 char *t, *last_pattern_start;
336 matcher->nb_patterns = -1;
337 matcher->regexp_error = regcomp(&matcher->preg, pattern,
338 case_sensitive ? 0 : REG_ICASE);
340 matcher->regexp_error = 0;
341 matcher->nb_patterns = 1;
342 matcher->case_sensitive = case_sensitive;
344 for(s = pattern; *s; s++) {
345 if(*s == pattern_separator) {
346 matcher->nb_patterns++;
350 matcher->splitted_patterns =
351 safe_malloc((strlen(pattern) + 1) * sizeof(char));
354 safe_malloc(matcher->nb_patterns * sizeof(char *));
356 strcpy(matcher->splitted_patterns, pattern);
359 last_pattern_start = matcher->splitted_patterns;
360 for(t = matcher->splitted_patterns; n < matcher->nb_patterns; t++) {
361 if(*t == pattern_separator || *t == '\0') {
363 matcher->patterns[n++] = last_pattern_start;
364 last_pattern_start = t + 1;
370 /*********************************************************************
373 void delete_char(char *buffer, int *position) {
374 if(buffer[*position]) {
376 while(c < BUFFER_SIZE && buffer[c]) {
377 buffer[c] = buffer[c+1];
380 } else error_feedback();
383 void backspace_char(char *buffer, int *position) {
385 if(buffer[*position]) {
386 int c = *position - 1;
388 buffer[c] = buffer[c+1];
392 buffer[*position - 1] = '\0';
396 } else error_feedback();
399 void insert_char(char *buffer, int *position, char character) {
400 if(strlen(buffer) < BUFFER_SIZE - 1) {
402 char t = buffer[c], u;
411 buffer[(*position)++] = character;
412 } else error_feedback();
415 void kill_before_cursor(char *buffer, int *position) {
417 while(buffer[*position + s]) {
418 buffer[s] = buffer[*position + s];
425 void kill_after_cursor(char *buffer, int *position) {
426 buffer[*position] = '\0';
429 /*********************************************************************/
431 int previous_visible(int current_line, int nb_lines, char **lines,
432 matcher_t *matcher) {
433 int line = current_line - 1;
434 while(line >= 0 && !match(lines[line], matcher)) line--;
438 int next_visible(int current_line, int nb_lines, char **lines,
439 matcher_t *matcher) {
440 int line = current_line + 1;
441 while(line < nb_lines && !match(lines[line], matcher)) line++;
449 /*********************************************************************/
451 /* The line highlighted is the first one matching the matcher in that
452 order: (1) current_focus_line after motion, if it does not match,
453 then (2) the first with a greater index, if none matches, then (3)
454 the first with a lesser index.
456 The index of the line actually shown highlighted is written in
457 displayed_focus_line (it can be -1 if no line at all matches the
460 If there is a motion and a line is actually shown highlighted, its
461 value is written in current_focus_line. */
463 void update_screen(int *current_focus_line, int *displayed_focus_line,
465 int nb_lines, char **lines,
469 char buffer[BUFFER_SIZE];
472 int console_width, console_height;
473 int nb_printed_lines = 0;
476 initialize_matcher(use_regexp, case_sensitive, &matcher, pattern);
478 console_width = getmaxx(stdscr);
479 console_height = getmaxy(stdscr);
481 use_default_colors();
483 /* Add an empty line where we will print the modeline at the end */
487 /* If the regexp is erroneous, print a message saying so */
489 if(matcher.regexp_error) {
491 addnstr("Regexp syntax error", console_width);
495 /* Else, and we do have lines to select from, find a visible line. */
497 else if(nb_lines > 0) {
499 if(match(lines[*current_focus_line], &matcher)) {
500 new_focus_line = *current_focus_line;
502 new_focus_line = next_visible(*current_focus_line, nb_lines, lines,
504 if(new_focus_line < 0) {
505 new_focus_line = previous_visible(*current_focus_line, nb_lines, lines,
510 /* If we found a visible line and we should move, let's move */
512 if(new_focus_line >= 0 && motion != 0) {
513 int l = new_focus_line;
515 /* We want to go down, let's find the first visible line below */
516 for(m = 0; l >= 0 && m < motion; m++) {
517 l = next_visible(l, nb_lines, lines, &matcher);
523 /* We want to go up, let's find the first visible line above */
524 for(m = 0; l >= 0 && m < -motion; m++) {
525 l = previous_visible(l, nb_lines, lines, &matcher);
533 /* Here new_focus_line is either a line number matching the
536 if(new_focus_line >= 0) {
538 int first_line = new_focus_line, last_line = new_focus_line;
541 /* We find the first and last lines to show, so that the total
542 of visible lines between them (them included) is
545 while(nb_match < console_height-1 &&
546 (first_line > 0 || last_line < nb_lines - 1)) {
550 while(first_line > 0 && !match(lines[first_line], &matcher)) {
553 if(match(lines[first_line], &matcher)) {
558 if(nb_match < console_height - 1 && last_line < nb_lines - 1) {
560 while(last_line < nb_lines - 1 && !match(lines[last_line], &matcher)) {
564 if(match(lines[last_line], &matcher)) {
570 /* Now we display them */
572 for(l = first_line; l <= last_line; l++) {
573 if(match(lines[l], &matcher)) {
576 while(lines[l][k] && k < BUFFER_SIZE - 2 && k < console_width - 2) {
577 buffer[k] = lines[l][k];
581 /* We fill the rest of the line with blanks if this is the
584 if(l == new_focus_line) {
585 while(k < console_width) {
595 /* Highlight the highlighted line ... */
597 if(l == new_focus_line) {
598 attron(attr_focus_line);
599 addnstr(buffer, console_width);
600 attroff(attr_focus_line);
602 addnstr(buffer, console_width);
609 /* If we are on a focused line and we moved, this become the new
613 *current_focus_line = new_focus_line;
617 *displayed_focus_line = new_focus_line;
619 if(nb_printed_lines == 0) {
621 addnstr("No selection", console_width);
626 /* Else, print a message saying that there are no lines to select from */
630 addnstr("Empty choice", console_width);
636 /* Draw the modeline */
640 attron(attr_modeline);
642 for(k = 0; k < console_width; k++) buffer[k] = ' ';
643 buffer[console_width] = '\0';
644 addnstr(buffer, console_width);
648 /* There must be a more elegant way of moving the cursor at a
649 location met during display */
656 cursor_x += strlen(title) + 1;
659 sprintf(buffer, "%d/%d ", nb_printed_lines, nb_lines);
661 cursor_x += strlen(buffer);
663 addnstr(pattern, cursor_position);
664 cursor_x += cursor_position;
666 if(pattern[cursor_position]) {
667 addstr(pattern + cursor_position);
672 /* Add a few info about the mode we are in (regexp and/or case
675 if(use_regexp || case_sensitive) {
692 attroff(attr_modeline);
697 free_matcher(&matcher);
700 /*********************************************************************/
702 void store_line(struct hash_table_t *hash_table,
703 const char *new_line,
704 int *nb_lines, char **lines) {
707 /* Remove the zsh history prefix */
709 if(zsh_history && *new_line == ':') {
710 while(*new_line && *new_line != ';') new_line++;
711 if(*new_line == ';') new_line++;
714 /* Remove the bash history prefix */
717 while(*new_line == ' ') new_line++;
718 while(*new_line >= '0' && *new_line <= '9') new_line++;
719 while(*new_line == ' ') new_line++;
722 /* Check for duplicates with the hash table and insert the line in
723 the list if necessary */
726 dup = add_and_get_previous_index(hash_table,
727 new_line, *nb_lines, lines);
733 lines[*nb_lines] = safe_malloc((strlen(new_line) + 1) * sizeof(char));
734 strcpy(lines[*nb_lines], new_line);
736 /* The string was already in there, so we do not allocate a new
737 string but use the pointer to the first occurence of it */
738 lines[*nb_lines] = lines[dup];
745 void read_file(struct hash_table_t *hash_table,
746 const char *input_filename,
747 int nb_lines_max, int *nb_lines, char **lines) {
749 char raw_line[BUFFER_SIZE];
750 int start, end, eol, k;
753 file = fopen(input_filename, "r");
756 fprintf(stderr, "Selector: Can not open `%s'.\n", input_filename);
763 while(*nb_lines < nb_lines_max && (end > start || !feof(file))) {
766 /* Look for the end of a line in what is already in the buffer */
767 while(eol < end && raw_line[eol] != '\n') eol++;
769 /* if we did not find the of a line, move what has not been
770 processed and is in the buffer to the beginning of the buffer,
771 fill the buffer with new data from the file, and look for the
774 for(k = 0; k < end - start; k++) {
775 raw_line[k] = raw_line[k + start];
780 end += fread(raw_line + end, sizeof(char), BUFFER_SIZE - end, file);
781 while(eol < end && raw_line[eol] != '\n') eol++;
784 /* The end of the line is the buffer size, which means the line is
787 if(eol == BUFFER_SIZE) {
788 raw_line[BUFFER_SIZE - 1] = '\0';
789 fprintf(stderr, "Selector: Line too long (max is %d characters):\n",
791 fprintf(stderr, raw_line);
792 fprintf(stderr, "\n");
796 /* If we got a line, we replace the carriage return by a \0 to
799 raw_line[eol] = '\0';
801 store_line(hash_table, raw_line + start,
810 /*********************************************************************/
812 /* For long options that have no equivalent short option, use a
813 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
816 OPT_BASH_MODE = CHAR_MAX + 1
819 static struct option long_options[] = {
820 { "output-file", 1, 0, 'o' },
821 { "pattern-separator", 1, 0, 's' },
822 { "label-separator", 1, 0, 'x' },
823 { "inject-in-tty", no_argument, 0, 'v' },
824 { "add-control-qs", no_argument, 0, 'w' },
825 { "monochrome", no_argument, 0, 'm' },
826 { "no-beep", no_argument, 0, 'q' },
827 { "revert-order", no_argument, 0, 'i' },
828 { "remove-bash-prefix", no_argument, 0, 'b' },
829 { "remove-zsh-prefix", no_argument, 0, 'z' },
830 { "remove-duplicates", no_argument, 0, 'd' },
831 { "regexp", no_argument, 0, 'e' },
832 { "case-sensitive", no_argument, 0, 'a' },
833 { "title", 1, 0, 't' },
834 { "number-of-lines", 1, 0, 'l' },
835 { "colors", 1, 0, 'c' },
836 { "rest-are-files", no_argument, 0, '-' },
837 { "bash", no_argument, 0, OPT_BASH_MODE },
838 { "help", no_argument, 0, 'h' },
842 int main(int argc, char **argv) {
844 char output_filename[BUFFER_SIZE];
845 char pattern[BUFFER_SIZE];
848 int error = 0, show_help = 0;
849 int rest_are_files = 0;
851 int current_focus_line, displayed_focus_line;
854 int color_fg_modeline, color_bg_modeline;
855 int color_fg_highlight, color_bg_highlight;
857 char **lines, **labels;
859 struct hash_table_t *hash_table;
861 if(!isatty(STDIN_FILENO)) {
862 fprintf(stderr, "Selector: The standard input is not a tty.\n");
866 color_fg_modeline = COLOR_WHITE;
867 color_bg_modeline = COLOR_BLACK;
868 color_fg_highlight = COLOR_BLACK;
869 color_bg_highlight = COLOR_YELLOW;
871 setlocale(LC_ALL, "");
873 strcpy(output_filename, "");
875 while (!rest_are_files &&
876 (c = getopt_long(argc, argv, "o:s:x:vwmqf:ibzdeat:l:c:-h",
877 long_options, NULL)) != -1) {
882 strncpy(output_filename, optarg, BUFFER_SIZE);
886 pattern_separator = optarg[0];
890 label_separator = optarg[0];
894 output_to_vt_buffer = 1;
922 remove_duplicates = 1;
935 title = safe_malloc((strlen(optarg) + 1) * sizeof(char));
936 strcpy(title, optarg);
940 str_to_positive_integers(optarg, &nb_lines_max, 1);
944 str_to_positive_integers(optarg, colors, 4);
945 color_fg_modeline = colors[0];
946 color_bg_modeline = colors[1];
947 color_fg_highlight = colors[2];
948 color_bg_highlight = colors[3];
960 /* Same as -c 7,4,0,3 -q */
961 /* color_fg_modeline = 7; */
962 /* color_bg_modeline = 4; */
963 /* color_fg_highlight = 0; */
964 /* color_bg_highlight = 3; */
965 /* error_flash = 1; */
966 /* Same as -b -i -d -v -w */
969 remove_duplicates = 1;
970 output_to_vt_buffer = 1;
980 if(show_help || error) {
990 lines = safe_malloc(nb_lines_max * sizeof(char *));
994 if(remove_duplicates) {
995 hash_table = new_hash_table(nb_lines_max * 10);
1000 while(optind < argc) {
1001 read_file(hash_table,
1003 nb_lines_max, &nb_lines, lines);
1008 free_hash_table(hash_table);
1011 /* Now remove the null strings */
1014 for(k = 0; k < nb_lines; k++) {
1016 lines[n++] = lines[k];
1023 for(l = 0; l < nb_lines / 2; l++) {
1024 char *s = lines[nb_lines - 1 - l];
1025 lines[nb_lines - 1 - l] = lines[l];
1030 /* Build the labels from the strings, take only the part before the
1031 label_separator and transform control characters to printable
1034 labels = safe_malloc(nb_lines * sizeof(char *));
1036 for(l = 0; l < nb_lines; l++) {
1042 while(*t && *t != label_separator) {
1047 labels[l] = safe_malloc((e + 1) * sizeof(char));
1050 while(*t && *t != label_separator) {
1052 while(*u) { *s++ = *u++; }
1059 cursor_position = 0;
1061 /* Here we start to display with curse */
1066 intrflush(stdscr, FALSE);
1068 /* So that the arrow keys work */
1069 keypad(stdscr, TRUE);
1071 attr_error = A_STANDOUT;
1072 attr_modeline = A_REVERSE;
1073 attr_focus_line = A_STANDOUT;
1075 if(with_colors && has_colors()) {
1079 if(color_fg_modeline < 0 || color_fg_modeline >= COLORS ||
1080 color_bg_modeline < 0 || color_bg_modeline >= COLORS ||
1081 color_fg_highlight < 0 || color_bg_highlight >= COLORS ||
1082 color_bg_highlight < 0 || color_bg_highlight >= COLORS) {
1085 fprintf(stderr, "Selector: Color numbers have to be between 0 and %d.\n",
1090 init_pair(1, color_fg_modeline, color_bg_modeline);
1091 attr_modeline = COLOR_PAIR(1);
1093 init_pair(2, color_fg_highlight, color_bg_highlight);
1094 attr_focus_line = COLOR_PAIR(2);
1096 init_pair(3, COLOR_WHITE, COLOR_RED);
1097 attr_error = COLOR_PAIR(3);
1101 current_focus_line = 0;
1102 displayed_focus_line = 0;
1104 update_screen(¤t_focus_line, &displayed_focus_line,
1106 nb_lines, labels, cursor_position, pattern);
1113 if(key >= ' ' && key <= '~') { /* Insert character */
1114 insert_char(pattern, &cursor_position, key);
1117 else if(key == KEY_BACKSPACE ||
1118 key == '\010' || /* ^H */
1119 key == '\177') { /* ^? */
1120 backspace_char(pattern, &cursor_position);
1123 else if(key == KEY_DC ||
1124 key == '\004') { /* ^D */
1125 delete_char(pattern, &cursor_position);
1128 else if(key == KEY_HOME) {
1129 current_focus_line = 0;
1132 else if(key == KEY_END) {
1133 current_focus_line = nb_lines - 1;
1136 else if(key == KEY_NPAGE) {
1140 else if(key == KEY_PPAGE) {
1144 else if(key == KEY_DOWN ||
1145 key == '\016') { /* ^N */
1149 else if(key == KEY_UP ||
1150 key == '\020') { /* ^P */
1154 else if(key == KEY_LEFT ||
1155 key == '\002') { /* ^B */
1156 if(cursor_position > 0) cursor_position--;
1157 else error_feedback();
1160 else if(key == KEY_RIGHT ||
1161 key == '\006') { /* ^F */
1162 if(pattern[cursor_position]) cursor_position++;
1163 else error_feedback();
1166 else if(key == '\001') { /* ^A */
1167 cursor_position = 0;
1170 else if(key == '\005') { /* ^E */
1171 cursor_position = strlen(pattern);
1174 else if(key == '\022') { /* ^R */
1175 use_regexp = !use_regexp;
1178 else if(key == '\011') { /* ^I */
1179 case_sensitive = !case_sensitive;
1182 else if(key == '\025') { /* ^U */
1183 kill_before_cursor(pattern, &cursor_position);
1186 else if(key == '\013') { /* ^K */
1187 kill_after_cursor(pattern, &cursor_position);
1190 else if(key == '\014') { /* ^L */
1191 /* I suspect that we may sometime mess up the display, so ^L is
1192 here to force a full refresh */
1196 update_screen(¤t_focus_line, &displayed_focus_line,
1198 nb_lines, labels, cursor_position, pattern);
1200 } while(key != '\007' && /* ^G */
1201 key != '\033' && /* ^[ (escape) */
1208 /* Here we come back to standard display */
1210 if((key == KEY_ENTER || key == '\n')) {
1214 if(displayed_focus_line >= 0 && displayed_focus_line < nb_lines) {
1215 t = lines[displayed_focus_line];
1216 if(label_separator) {
1217 while(*t && *t != label_separator) t++;
1224 if(output_to_vt_buffer && t) {
1225 inject_into_tty_buffer(t, add_control_qs);
1228 if(output_filename[0]) {
1229 FILE *out = fopen(output_filename, "w");
1237 "Selector: Can not open %s for writing.\n",
1245 printf("Aborted.\n");
1248 for(l = 0; l < nb_lines; l++) {