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(matcher_t *matcher, char *string) {
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(matcher_t *matcher,
337 int use_regexp, int case_sensitive,
338 const char *pattern) {
340 char *t, *last_pattern_start;
344 matcher->nb_patterns = -1;
345 matcher->regexp_error = regcomp(&matcher->preg, pattern,
346 case_sensitive ? 0 : REG_ICASE);
348 matcher->regexp_error = 0;
349 matcher->nb_patterns = 1;
350 matcher->case_sensitive = case_sensitive;
352 for(s = pattern; *s; s++) {
353 if(*s == pattern_separator) {
354 matcher->nb_patterns++;
358 matcher->splitted_patterns =
359 safe_malloc((strlen(pattern) + 1) * sizeof(char));
362 safe_malloc(matcher->nb_patterns * sizeof(char *));
364 strcpy(matcher->splitted_patterns, pattern);
367 last_pattern_start = matcher->splitted_patterns;
368 for(t = matcher->splitted_patterns; n < matcher->nb_patterns; t++) {
369 if(*t == pattern_separator || *t == '\0') {
371 matcher->patterns[n++] = last_pattern_start;
372 last_pattern_start = t + 1;
378 /*********************************************************************
381 void delete_char(char *buffer, int *position) {
382 if(buffer[*position]) {
384 while(c < BUFFER_SIZE && buffer[c]) {
385 buffer[c] = buffer[c+1];
388 } else error_feedback();
391 void backspace_char(char *buffer, int *position) {
393 if(buffer[*position]) {
394 int c = *position - 1;
396 buffer[c] = buffer[c+1];
400 buffer[*position - 1] = '\0';
404 } else error_feedback();
407 void insert_char(char *buffer, int *position, char character) {
408 if(strlen(buffer) < BUFFER_SIZE - 1) {
410 char t = buffer[c], u;
419 buffer[(*position)++] = character;
420 } else error_feedback();
423 void kill_before_cursor(char *buffer, int *position) {
425 while(buffer[*position + s]) {
426 buffer[s] = buffer[*position + s];
433 void kill_after_cursor(char *buffer, int *position) {
434 buffer[*position] = '\0';
437 /*********************************************************************/
439 int previous_visible(int current_line, char **lines, matcher_t *matcher) {
440 int line = current_line - 1;
441 while(line >= 0 && !match(matcher, lines[line])) line--;
445 int next_visible(int current_line, int nb_lines, char **lines,
446 matcher_t *matcher) {
447 int line = current_line + 1;
448 while(line < nb_lines && !match(matcher, lines[line])) line++;
456 /*********************************************************************/
458 /* The line highlighted is the first one matching the matcher in that
459 order: (1) current_focus_line after motion, if it does not match,
460 then (2) the first with a greater index, if none matches, then (3)
461 the first with a lesser index.
463 The index of the line actually shown highlighted is written in
464 displayed_focus_line (it can be -1 if no line at all matches the
467 If there is a motion and a line is actually shown highlighted, its
468 value is written in current_focus_line. */
470 void update_screen(int *current_focus_line, int *displayed_focus_line,
472 int nb_lines, char **lines,
476 char buffer[BUFFER_SIZE];
479 int console_width, console_height;
480 int nb_printed_lines = 0;
483 initialize_matcher(&matcher, use_regexp, case_sensitive, pattern);
485 console_width = getmaxx(stdscr);
486 console_height = getmaxy(stdscr);
488 use_default_colors();
490 /* Add an empty line where we will print the modeline at the end */
494 /* If the regexp is erroneous, print a message saying so */
496 if(matcher.regexp_error) {
498 addnstr("Regexp syntax error", console_width);
502 /* Else, and we do have lines to select from, find a visible line. */
504 else if(nb_lines > 0) {
506 if(match(&matcher, lines[*current_focus_line])) {
507 new_focus_line = *current_focus_line;
509 new_focus_line = next_visible(*current_focus_line, nb_lines, lines,
511 if(new_focus_line < 0) {
512 new_focus_line = previous_visible(*current_focus_line, lines, &matcher);
516 /* If we found a visible line and we should move, let's move */
518 if(new_focus_line >= 0 && motion != 0) {
519 int l = new_focus_line;
521 /* We want to go down, let's find the first visible line below */
522 for(m = 0; l >= 0 && m < motion; m++) {
523 l = next_visible(l, nb_lines, lines, &matcher);
529 /* We want to go up, let's find the first visible line above */
530 for(m = 0; l >= 0 && m < -motion; m++) {
531 l = previous_visible(l, lines, &matcher);
539 /* Here new_focus_line is either a line number matching the
542 if(new_focus_line >= 0) {
544 int first_line = new_focus_line, last_line = new_focus_line;
547 /* We find the first and last lines to show, so that the total
548 of visible lines between them (them included) is
551 while(nb_match < console_height-1 &&
552 (first_line > 0 || last_line < nb_lines - 1)) {
556 while(first_line > 0 && !match(&matcher, lines[first_line])) {
559 if(match(&matcher, lines[first_line])) {
564 if(nb_match < console_height - 1 && last_line < nb_lines - 1) {
566 while(last_line < nb_lines - 1 && !match(&matcher, lines[last_line])) {
570 if(match(&matcher, lines[last_line])) {
576 /* Now we display them */
578 for(l = first_line; l <= last_line; l++) {
579 if(match(&matcher, lines[l])) {
582 while(lines[l][k] && k < BUFFER_SIZE - 2 && k < console_width - 2) {
583 buffer[k] = lines[l][k];
587 /* We fill the rest of the line with blanks if this is the
590 if(l == new_focus_line) {
591 while(k < console_width) {
601 /* Highlight the highlighted line ... */
603 if(l == new_focus_line) {
604 attron(attr_focus_line);
605 addnstr(buffer, console_width);
606 attroff(attr_focus_line);
608 addnstr(buffer, console_width);
615 /* If we are on a focused line and we moved, this become the new
619 *current_focus_line = new_focus_line;
623 *displayed_focus_line = new_focus_line;
625 if(nb_printed_lines == 0) {
627 addnstr("No selection", console_width);
632 /* Else, print a message saying that there are no lines to select from */
636 addnstr("Empty choice", console_width);
642 /* Draw the modeline */
646 attron(attr_modeline);
648 for(k = 0; k < console_width; k++) buffer[k] = ' ';
649 buffer[console_width] = '\0';
650 addnstr(buffer, console_width);
654 /* There must be a more elegant way of moving the cursor at a
655 location met during display */
662 cursor_x += strlen(title) + 1;
665 sprintf(buffer, "%d/%d ", nb_printed_lines, nb_lines);
667 cursor_x += strlen(buffer);
669 addnstr(pattern, cursor_position);
670 cursor_x += cursor_position;
672 if(pattern[cursor_position]) {
673 addstr(pattern + cursor_position);
678 /* Add a few info about the mode we are in (regexp and/or case
681 if(use_regexp || case_sensitive) {
698 attroff(attr_modeline);
703 free_matcher(&matcher);
706 /*********************************************************************/
708 void store_line(struct hash_table_t *hash_table,
709 const char *new_line,
710 int *nb_lines, char **lines) {
713 /* Remove the zsh history prefix */
715 if(zsh_history && *new_line == ':') {
716 while(*new_line && *new_line != ';') new_line++;
717 if(*new_line == ';') new_line++;
720 /* Remove the bash history prefix */
723 while(*new_line == ' ') new_line++;
724 while(*new_line >= '0' && *new_line <= '9') new_line++;
725 while(*new_line == ' ') new_line++;
728 /* Check for duplicates with the hash table and insert the line in
729 the list if necessary */
732 dup = add_and_get_previous_index(hash_table,
733 new_line, *nb_lines, lines);
739 lines[*nb_lines] = safe_malloc((strlen(new_line) + 1) * sizeof(char));
740 strcpy(lines[*nb_lines], new_line);
742 /* The string was already in there, so we do not allocate a new
743 string but use the pointer to the first occurence of it */
744 lines[*nb_lines] = lines[dup];
751 void read_file(struct hash_table_t *hash_table,
752 const char *input_filename,
753 int nb_lines_max, int *nb_lines, char **lines) {
755 char raw_line[BUFFER_SIZE];
756 int start, end, eol, k;
759 file = fopen(input_filename, "r");
762 fprintf(stderr, "selector: Can not open `%s'.\n", input_filename);
769 while(*nb_lines < nb_lines_max && (end > start || !feof(file))) {
772 /* Look for the end of a line in what is already in the buffer */
773 while(eol < end && raw_line[eol] != '\n') eol++;
775 /* if we did not find the of a line, move what has not been
776 processed and is in the buffer to the beginning of the buffer,
777 fill the buffer with new data from the file, and look for the
780 for(k = 0; k < end - start; k++) {
781 raw_line[k] = raw_line[k + start];
786 end += fread(raw_line + end, sizeof(char), BUFFER_SIZE - end, file);
787 while(eol < end && raw_line[eol] != '\n') eol++;
790 /* The end of the line is the buffer size, which means the line is
793 if(eol == BUFFER_SIZE) {
794 raw_line[BUFFER_SIZE - 1] = '\0';
795 fprintf(stderr, "selector: Line too long (max is %d characters):\n",
797 fprintf(stderr, raw_line);
798 fprintf(stderr, "\n");
802 /* If we got a line, we replace the carriage return by a \0 to
805 raw_line[eol] = '\0';
807 store_line(hash_table, raw_line + start,
816 /*********************************************************************/
818 /* For long options that have no equivalent short option, use a
819 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
822 OPT_BASH_MODE = CHAR_MAX + 1
825 static struct option long_options[] = {
826 { "output-file", 1, 0, 'o' },
827 { "pattern-separator", 1, 0, 's' },
828 { "label-separator", 1, 0, 'x' },
829 { "inject-in-tty", no_argument, 0, 'v' },
830 { "add-control-qs", no_argument, 0, 'w' },
831 { "monochrome", no_argument, 0, 'm' },
832 { "no-beep", no_argument, 0, 'q' },
833 { "revert-order", no_argument, 0, 'i' },
834 { "remove-bash-prefix", no_argument, 0, 'b' },
835 { "remove-zsh-prefix", no_argument, 0, 'z' },
836 { "remove-duplicates", no_argument, 0, 'd' },
837 { "regexp", no_argument, 0, 'e' },
838 { "case-sensitive", no_argument, 0, 'a' },
839 { "title", 1, 0, 't' },
840 { "number-of-lines", 1, 0, 'l' },
841 { "colors", 1, 0, 'c' },
842 { "rest-are-files", no_argument, 0, '-' },
843 { "bash", no_argument, 0, OPT_BASH_MODE },
844 { "help", no_argument, 0, 'h' },
848 int main(int argc, char **argv) {
850 char output_filename[BUFFER_SIZE];
851 char pattern[BUFFER_SIZE];
854 int error = 0, show_help = 0;
855 int rest_are_files = 0;
857 int current_focus_line, displayed_focus_line;
860 int color_fg_modeline, color_bg_modeline;
861 int color_fg_highlight, color_bg_highlight;
863 char **lines, **labels;
865 struct hash_table_t *hash_table;
868 if(!isatty(STDIN_FILENO)) {
869 fprintf(stderr, "selector: The standard input is not a tty.\n");
873 color_fg_modeline = COLOR_WHITE;
874 color_bg_modeline = COLOR_BLACK;
875 color_fg_highlight = COLOR_BLACK;
876 color_bg_highlight = COLOR_YELLOW;
878 setlocale(LC_ALL, "");
880 strcpy(output_filename, "");
882 while (!rest_are_files &&
883 (c = getopt_long(argc, argv, "o:s:x:vwmqf:ibzdeat:l:c:-h",
884 long_options, NULL)) != -1) {
889 strncpy(output_filename, optarg, BUFFER_SIZE);
893 pattern_separator = optarg[0];
897 label_separator = optarg[0];
901 output_to_vt_buffer = 1;
929 remove_duplicates = 1;
942 title = safe_malloc((strlen(optarg) + 1) * sizeof(char));
943 strcpy(title, optarg);
947 str_to_positive_integers(optarg, &nb_lines_max, 1);
951 str_to_positive_integers(optarg, colors, 4);
952 color_fg_modeline = colors[0];
953 color_bg_modeline = colors[1];
954 color_fg_highlight = colors[2];
955 color_bg_highlight = colors[3];
967 /* Same as -c 7,4,0,3 -q */
968 /* color_fg_modeline = 7; */
969 /* color_bg_modeline = 4; */
970 /* color_fg_highlight = 0; */
971 /* color_bg_highlight = 3; */
972 /* error_flash = 1; */
973 /* Same as -b -i -d -v -w */
976 remove_duplicates = 1;
977 output_to_vt_buffer = 1;
979 bash_histsize = getenv("HISTSIZE");
981 str_to_positive_integers(bash_histsize, &nb_lines_max, 1);
1001 lines = safe_malloc(nb_lines_max * sizeof(char *));
1005 if(remove_duplicates) {
1006 hash_table = new_hash_table(nb_lines_max * 10);
1011 while(optind < argc) {
1012 read_file(hash_table,
1014 nb_lines_max, &nb_lines, lines);
1019 free_hash_table(hash_table);
1022 /* Now remove the null strings */
1025 for(k = 0; k < nb_lines; k++) {
1027 lines[n++] = lines[k];
1034 for(l = 0; l < nb_lines / 2; l++) {
1035 char *s = lines[nb_lines - 1 - l];
1036 lines[nb_lines - 1 - l] = lines[l];
1041 /* Build the labels from the strings, take only the part before the
1042 label_separator and transform control characters to printable
1045 labels = safe_malloc(nb_lines * sizeof(char *));
1047 for(l = 0; l < nb_lines; l++) {
1053 while(*t && *t != label_separator) {
1058 labels[l] = safe_malloc((e + 1) * sizeof(char));
1061 while(*t && *t != label_separator) {
1063 while(*u) { *s++ = *u++; }
1070 cursor_position = 0;
1072 /* Here we start to display with curse */
1077 intrflush(stdscr, FALSE);
1079 /* So that the arrow keys work */
1080 keypad(stdscr, TRUE);
1082 attr_error = A_STANDOUT;
1083 attr_modeline = A_REVERSE;
1084 attr_focus_line = A_STANDOUT;
1086 if(with_colors && has_colors()) {
1090 if(color_fg_modeline < 0 || color_fg_modeline >= COLORS ||
1091 color_bg_modeline < 0 || color_bg_modeline >= COLORS ||
1092 color_fg_highlight < 0 || color_bg_highlight >= COLORS ||
1093 color_bg_highlight < 0 || color_bg_highlight >= COLORS) {
1096 fprintf(stderr, "selector: Color numbers have to be between 0 and %d.\n",
1101 init_pair(1, color_fg_modeline, color_bg_modeline);
1102 attr_modeline = COLOR_PAIR(1);
1104 init_pair(2, color_fg_highlight, color_bg_highlight);
1105 attr_focus_line = COLOR_PAIR(2);
1107 init_pair(3, COLOR_WHITE, COLOR_RED);
1108 attr_error = COLOR_PAIR(3);
1112 current_focus_line = 0;
1113 displayed_focus_line = 0;
1115 update_screen(¤t_focus_line, &displayed_focus_line,
1117 nb_lines, labels, cursor_position, pattern);
1124 if(key >= ' ' && key <= '~') { /* Insert character */
1125 insert_char(pattern, &cursor_position, key);
1128 else if(key == KEY_BACKSPACE ||
1129 key == '\010' || /* ^H */
1130 key == '\177') { /* ^? */
1131 backspace_char(pattern, &cursor_position);
1134 else if(key == KEY_DC ||
1135 key == '\004') { /* ^D */
1136 delete_char(pattern, &cursor_position);
1139 else if(key == KEY_HOME) {
1140 current_focus_line = 0;
1143 else if(key == KEY_END) {
1144 current_focus_line = nb_lines - 1;
1147 else if(key == KEY_NPAGE) {
1151 else if(key == KEY_PPAGE) {
1155 else if(key == KEY_DOWN ||
1156 key == '\016') { /* ^N */
1160 else if(key == KEY_UP ||
1161 key == '\020') { /* ^P */
1165 else if(key == KEY_LEFT ||
1166 key == '\002') { /* ^B */
1167 if(cursor_position > 0) cursor_position--;
1168 else error_feedback();
1171 else if(key == KEY_RIGHT ||
1172 key == '\006') { /* ^F */
1173 if(pattern[cursor_position]) cursor_position++;
1174 else error_feedback();
1177 else if(key == '\001') { /* ^A */
1178 cursor_position = 0;
1181 else if(key == '\005') { /* ^E */
1182 cursor_position = strlen(pattern);
1185 else if(key == '\022') { /* ^R */
1186 use_regexp = !use_regexp;
1189 else if(key == '\011') { /* ^I */
1190 case_sensitive = !case_sensitive;
1193 else if(key == '\025') { /* ^U */
1194 kill_before_cursor(pattern, &cursor_position);
1197 else if(key == '\013') { /* ^K */
1198 kill_after_cursor(pattern, &cursor_position);
1201 else if(key == '\014') { /* ^L */
1202 /* I suspect that we may sometime mess up the display, so ^L is
1203 here to force a full refresh */
1207 update_screen(¤t_focus_line, &displayed_focus_line,
1209 nb_lines, labels, cursor_position, pattern);
1211 } while(key != '\007' && /* ^G */
1212 key != '\033' && /* ^[ (escape) */
1219 /* Here we come back to standard display */
1221 if((key == KEY_ENTER || key == '\n')) {
1225 if(displayed_focus_line >= 0 && displayed_focus_line < nb_lines) {
1226 t = lines[displayed_focus_line];
1227 if(label_separator) {
1228 while(*t && *t != label_separator) t++;
1235 if(output_to_vt_buffer && t) {
1236 inject_into_tty_buffer(t, add_control_qs);
1239 if(output_filename[0]) {
1240 FILE *out = fopen(output_filename, "w");
1248 "selector: Can not open %s for writing.\n",
1256 printf("Aborted.\n");
1259 for(l = 0; l < nb_lines; l++) {