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.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 -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;
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, char **lines, matcher_t *matcher) {
432 int line = current_line - 1;
433 while(line >= 0 && !match(lines[line], matcher)) line--;
437 int next_visible(int current_line, int nb_lines, char **lines,
438 matcher_t *matcher) {
439 int line = current_line + 1;
440 while(line < nb_lines && !match(lines[line], matcher)) line++;
448 /*********************************************************************/
450 /* The line highlighted is the first one matching the matcher in that
451 order: (1) current_focus_line after motion, if it does not match,
452 then (2) the first with a greater index, if none matches, then (3)
453 the first with a lesser index.
455 The index of the line actually shown highlighted is written in
456 displayed_focus_line (it can be -1 if no line at all matches the
459 If there is a motion and a line is actually shown highlighted, its
460 value is written in current_focus_line. */
462 void update_screen(int *current_focus_line, int *displayed_focus_line,
464 int nb_lines, char **lines,
468 char buffer[BUFFER_SIZE];
471 int console_width, console_height;
472 int nb_printed_lines = 0;
475 initialize_matcher(use_regexp, case_sensitive, &matcher, pattern);
477 console_width = getmaxx(stdscr);
478 console_height = getmaxy(stdscr);
480 use_default_colors();
482 /* Add an empty line where we will print the modeline at the end */
486 /* If the regexp is erroneous, print a message saying so */
488 if(matcher.regexp_error) {
490 addnstr("Regexp syntax error", console_width);
494 /* Else, and we do have lines to select from, find a visible line. */
496 else if(nb_lines > 0) {
498 if(match(lines[*current_focus_line], &matcher)) {
499 new_focus_line = *current_focus_line;
501 new_focus_line = next_visible(*current_focus_line, nb_lines, lines,
503 if(new_focus_line < 0) {
504 new_focus_line = previous_visible(*current_focus_line, lines, &matcher);
508 /* If we found a visible line and we should move, let's move */
510 if(new_focus_line >= 0 && motion != 0) {
511 int l = new_focus_line;
513 /* We want to go down, let's find the first visible line below */
514 for(m = 0; l >= 0 && m < motion; m++) {
515 l = next_visible(l, nb_lines, lines, &matcher);
521 /* We want to go up, let's find the first visible line above */
522 for(m = 0; l >= 0 && m < -motion; m++) {
523 l = previous_visible(l, lines, &matcher);
531 /* Here new_focus_line is either a line number matching the
534 if(new_focus_line >= 0) {
536 int first_line = new_focus_line, last_line = new_focus_line;
539 /* We find the first and last lines to show, so that the total
540 of visible lines between them (them included) is
543 while(nb_match < console_height-1 &&
544 (first_line > 0 || last_line < nb_lines - 1)) {
548 while(first_line > 0 && !match(lines[first_line], &matcher)) {
551 if(match(lines[first_line], &matcher)) {
556 if(nb_match < console_height - 1 && last_line < nb_lines - 1) {
558 while(last_line < nb_lines - 1 && !match(lines[last_line], &matcher)) {
562 if(match(lines[last_line], &matcher)) {
568 /* Now we display them */
570 for(l = first_line; l <= last_line; l++) {
571 if(match(lines[l], &matcher)) {
574 while(lines[l][k] && k < BUFFER_SIZE - 2 && k < console_width - 2) {
575 buffer[k] = lines[l][k];
579 /* We fill the rest of the line with blanks if this is the
582 if(l == new_focus_line) {
583 while(k < console_width) {
593 /* Highlight the highlighted line ... */
595 if(l == new_focus_line) {
596 attron(attr_focus_line);
597 addnstr(buffer, console_width);
598 attroff(attr_focus_line);
600 addnstr(buffer, console_width);
607 /* If we are on a focused line and we moved, this become the new
611 *current_focus_line = new_focus_line;
615 *displayed_focus_line = new_focus_line;
617 if(nb_printed_lines == 0) {
619 addnstr("No selection", console_width);
624 /* Else, print a message saying that there are no lines to select from */
628 addnstr("Empty choice", console_width);
634 /* Draw the modeline */
638 attron(attr_modeline);
640 for(k = 0; k < console_width; k++) buffer[k] = ' ';
641 buffer[console_width] = '\0';
642 addnstr(buffer, console_width);
646 /* There must be a more elegant way of moving the cursor at a
647 location met during display */
654 cursor_x += strlen(title) + 1;
657 sprintf(buffer, "%d/%d ", nb_printed_lines, nb_lines);
659 cursor_x += strlen(buffer);
661 addnstr(pattern, cursor_position);
662 cursor_x += cursor_position;
664 if(pattern[cursor_position]) {
665 addstr(pattern + cursor_position);
670 /* Add a few info about the mode we are in (regexp and/or case
673 if(use_regexp || case_sensitive) {
690 attroff(attr_modeline);
695 free_matcher(&matcher);
698 /*********************************************************************/
700 void store_line(struct hash_table_t *hash_table,
701 const char *new_line,
702 int *nb_lines, char **lines) {
705 /* Remove the zsh history prefix */
707 if(zsh_history && *new_line == ':') {
708 while(*new_line && *new_line != ';') new_line++;
709 if(*new_line == ';') new_line++;
712 /* Remove the bash history prefix */
715 while(*new_line == ' ') new_line++;
716 while(*new_line >= '0' && *new_line <= '9') new_line++;
717 while(*new_line == ' ') new_line++;
720 /* Check for duplicates with the hash table and insert the line in
721 the list if necessary */
724 dup = add_and_get_previous_index(hash_table,
725 new_line, *nb_lines, lines);
731 lines[*nb_lines] = safe_malloc((strlen(new_line) + 1) * sizeof(char));
732 strcpy(lines[*nb_lines], new_line);
734 /* The string was already in there, so we do not allocate a new
735 string but use the pointer to the first occurence of it */
736 lines[*nb_lines] = lines[dup];
743 void read_file(struct hash_table_t *hash_table,
744 const char *input_filename,
745 int nb_lines_max, int *nb_lines, char **lines) {
747 char raw_line[BUFFER_SIZE];
748 int start, end, eol, k;
751 file = fopen(input_filename, "r");
754 fprintf(stderr, "selector: Can not open `%s'.\n", input_filename);
761 while(*nb_lines < nb_lines_max && (end > start || !feof(file))) {
764 /* Look for the end of a line in what is already in the buffer */
765 while(eol < end && raw_line[eol] != '\n') eol++;
767 /* if we did not find the of a line, move what has not been
768 processed and is in the buffer to the beginning of the buffer,
769 fill the buffer with new data from the file, and look for the
772 for(k = 0; k < end - start; k++) {
773 raw_line[k] = raw_line[k + start];
778 end += fread(raw_line + end, sizeof(char), BUFFER_SIZE - end, file);
779 while(eol < end && raw_line[eol] != '\n') eol++;
782 /* The end of the line is the buffer size, which means the line is
785 if(eol == BUFFER_SIZE) {
786 raw_line[BUFFER_SIZE - 1] = '\0';
787 fprintf(stderr, "selector: Line too long (max is %d characters):\n",
789 fprintf(stderr, raw_line);
790 fprintf(stderr, "\n");
794 /* If we got a line, we replace the carriage return by a \0 to
797 raw_line[eol] = '\0';
799 store_line(hash_table, raw_line + start,
808 /*********************************************************************/
810 /* For long options that have no equivalent short option, use a
811 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
814 OPT_BASH_MODE = CHAR_MAX + 1
817 static struct option long_options[] = {
818 { "output-file", 1, 0, 'o' },
819 { "pattern-separator", 1, 0, 's' },
820 { "label-separator", 1, 0, 'x' },
821 { "inject-in-tty", no_argument, 0, 'v' },
822 { "add-control-qs", no_argument, 0, 'w' },
823 { "monochrome", no_argument, 0, 'm' },
824 { "no-beep", no_argument, 0, 'q' },
825 { "revert-order", no_argument, 0, 'i' },
826 { "remove-bash-prefix", no_argument, 0, 'b' },
827 { "remove-zsh-prefix", no_argument, 0, 'z' },
828 { "remove-duplicates", no_argument, 0, 'd' },
829 { "regexp", no_argument, 0, 'e' },
830 { "case-sensitive", no_argument, 0, 'a' },
831 { "title", 1, 0, 't' },
832 { "number-of-lines", 1, 0, 'l' },
833 { "colors", 1, 0, 'c' },
834 { "rest-are-files", no_argument, 0, '-' },
835 { "bash", no_argument, 0, OPT_BASH_MODE },
836 { "help", no_argument, 0, 'h' },
840 int main(int argc, char **argv) {
842 char output_filename[BUFFER_SIZE];
843 char pattern[BUFFER_SIZE];
846 int error = 0, show_help = 0;
847 int rest_are_files = 0;
849 int current_focus_line, displayed_focus_line;
852 int color_fg_modeline, color_bg_modeline;
853 int color_fg_highlight, color_bg_highlight;
855 char **lines, **labels;
857 struct hash_table_t *hash_table;
860 if(!isatty(STDIN_FILENO)) {
861 fprintf(stderr, "selector: The standard input is not a tty.\n");
865 color_fg_modeline = COLOR_WHITE;
866 color_bg_modeline = COLOR_BLACK;
867 color_fg_highlight = COLOR_BLACK;
868 color_bg_highlight = COLOR_YELLOW;
870 setlocale(LC_ALL, "");
872 strcpy(output_filename, "");
874 while (!rest_are_files &&
875 (c = getopt_long(argc, argv, "o:s:x:vwmqf:ibzdeat:l:c:-h",
876 long_options, NULL)) != -1) {
881 strncpy(output_filename, optarg, BUFFER_SIZE);
885 pattern_separator = optarg[0];
889 label_separator = optarg[0];
893 output_to_vt_buffer = 1;
921 remove_duplicates = 1;
934 title = safe_malloc((strlen(optarg) + 1) * sizeof(char));
935 strcpy(title, optarg);
939 str_to_positive_integers(optarg, &nb_lines_max, 1);
943 str_to_positive_integers(optarg, colors, 4);
944 color_fg_modeline = colors[0];
945 color_bg_modeline = colors[1];
946 color_fg_highlight = colors[2];
947 color_bg_highlight = colors[3];
959 /* Same as -c 7,4,0,3 -q */
960 /* color_fg_modeline = 7; */
961 /* color_bg_modeline = 4; */
962 /* color_fg_highlight = 0; */
963 /* color_bg_highlight = 3; */
964 /* error_flash = 1; */
965 /* Same as -b -i -d -v -w */
968 remove_duplicates = 1;
969 output_to_vt_buffer = 1;
971 bash_histsize = getenv("HISTSIZE");
973 str_to_positive_integers(bash_histsize, &nb_lines_max, 1);
983 if(show_help || error) {
993 lines = safe_malloc(nb_lines_max * sizeof(char *));
997 if(remove_duplicates) {
998 hash_table = new_hash_table(nb_lines_max * 10);
1003 while(optind < argc) {
1004 read_file(hash_table,
1006 nb_lines_max, &nb_lines, lines);
1011 free_hash_table(hash_table);
1014 /* Now remove the null strings */
1017 for(k = 0; k < nb_lines; k++) {
1019 lines[n++] = lines[k];
1026 for(l = 0; l < nb_lines / 2; l++) {
1027 char *s = lines[nb_lines - 1 - l];
1028 lines[nb_lines - 1 - l] = lines[l];
1033 /* Build the labels from the strings, take only the part before the
1034 label_separator and transform control characters to printable
1037 labels = safe_malloc(nb_lines * sizeof(char *));
1039 for(l = 0; l < nb_lines; l++) {
1045 while(*t && *t != label_separator) {
1050 labels[l] = safe_malloc((e + 1) * sizeof(char));
1053 while(*t && *t != label_separator) {
1055 while(*u) { *s++ = *u++; }
1062 cursor_position = 0;
1064 /* Here we start to display with curse */
1069 intrflush(stdscr, FALSE);
1071 /* So that the arrow keys work */
1072 keypad(stdscr, TRUE);
1074 attr_error = A_STANDOUT;
1075 attr_modeline = A_REVERSE;
1076 attr_focus_line = A_STANDOUT;
1078 if(with_colors && has_colors()) {
1082 if(color_fg_modeline < 0 || color_fg_modeline >= COLORS ||
1083 color_bg_modeline < 0 || color_bg_modeline >= COLORS ||
1084 color_fg_highlight < 0 || color_bg_highlight >= COLORS ||
1085 color_bg_highlight < 0 || color_bg_highlight >= COLORS) {
1088 fprintf(stderr, "selector: Color numbers have to be between 0 and %d.\n",
1093 init_pair(1, color_fg_modeline, color_bg_modeline);
1094 attr_modeline = COLOR_PAIR(1);
1096 init_pair(2, color_fg_highlight, color_bg_highlight);
1097 attr_focus_line = COLOR_PAIR(2);
1099 init_pair(3, COLOR_WHITE, COLOR_RED);
1100 attr_error = COLOR_PAIR(3);
1104 current_focus_line = 0;
1105 displayed_focus_line = 0;
1107 update_screen(¤t_focus_line, &displayed_focus_line,
1109 nb_lines, labels, cursor_position, pattern);
1116 if(key >= ' ' && key <= '~') { /* Insert character */
1117 insert_char(pattern, &cursor_position, key);
1120 else if(key == KEY_BACKSPACE ||
1121 key == '\010' || /* ^H */
1122 key == '\177') { /* ^? */
1123 backspace_char(pattern, &cursor_position);
1126 else if(key == KEY_DC ||
1127 key == '\004') { /* ^D */
1128 delete_char(pattern, &cursor_position);
1131 else if(key == KEY_HOME) {
1132 current_focus_line = 0;
1135 else if(key == KEY_END) {
1136 current_focus_line = nb_lines - 1;
1139 else if(key == KEY_NPAGE) {
1143 else if(key == KEY_PPAGE) {
1147 else if(key == KEY_DOWN ||
1148 key == '\016') { /* ^N */
1152 else if(key == KEY_UP ||
1153 key == '\020') { /* ^P */
1157 else if(key == KEY_LEFT ||
1158 key == '\002') { /* ^B */
1159 if(cursor_position > 0) cursor_position--;
1160 else error_feedback();
1163 else if(key == KEY_RIGHT ||
1164 key == '\006') { /* ^F */
1165 if(pattern[cursor_position]) cursor_position++;
1166 else error_feedback();
1169 else if(key == '\001') { /* ^A */
1170 cursor_position = 0;
1173 else if(key == '\005') { /* ^E */
1174 cursor_position = strlen(pattern);
1177 else if(key == '\022') { /* ^R */
1178 use_regexp = !use_regexp;
1181 else if(key == '\011') { /* ^I */
1182 case_sensitive = !case_sensitive;
1185 else if(key == '\025') { /* ^U */
1186 kill_before_cursor(pattern, &cursor_position);
1189 else if(key == '\013') { /* ^K */
1190 kill_after_cursor(pattern, &cursor_position);
1193 else if(key == '\014') { /* ^L */
1194 /* I suspect that we may sometime mess up the display, so ^L is
1195 here to force a full refresh */
1199 update_screen(¤t_focus_line, &displayed_focus_line,
1201 nb_lines, labels, cursor_position, pattern);
1203 } while(key != '\007' && /* ^G */
1204 key != '\033' && /* ^[ (escape) */
1211 /* Here we come back to standard display */
1213 if((key == KEY_ENTER || key == '\n')) {
1217 if(displayed_focus_line >= 0 && displayed_focus_line < nb_lines) {
1218 t = lines[displayed_focus_line];
1219 if(label_separator) {
1220 while(*t && *t != label_separator) t++;
1227 if(output_to_vt_buffer && t) {
1228 inject_into_tty_buffer(t, add_control_qs);
1231 if(output_filename[0]) {
1232 FILE *out = fopen(output_filename, "w");
1240 "selector: Can not open %s for writing.\n",
1248 printf("Aborted.\n");
1251 for(l = 0; l < nb_lines; l++) {