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 -q -b -i -d -v -w -l ${HISTSIZE} <(history)
41 #include <sys/ioctl.h>
47 #define VERSION "1.1.1"
49 #define BUFFER_SIZE 4096
51 /* Yeah, global variables! */
53 int nb_lines_max = 1000;
54 char pattern_separator = ';';
55 char label_separator = '\0';
56 int output_to_vt_buffer = 0;
57 int add_control_qs = 0;
61 int inverse_order = 0;
62 int remove_duplicates = 0;
64 int case_sensitive = 0;
68 int attr_modeline, attr_focus_line, attr_error;
70 /********************************************************************/
72 /* malloc with error checking. */
74 void *safe_malloc(size_t n) {
77 printf("Can not allocate memory: %s\n", strerror(errno));
83 /*********************************************************************/
85 void inject_into_tty_buffer(char *string, int add_control_qs) {
86 struct termios oldtio, newtio;
88 const char control_q = '\021';
89 tcgetattr(STDIN_FILENO, &oldtio);
90 memset(&newtio, 0, sizeof(newtio));
91 /* Set input mode (non-canonical, *no echo*,...) */
92 tcsetattr(STDIN_FILENO, TCSANOW, &newtio);
93 /* Put the selected string in the tty input buffer */
94 for(k = string; *k; k++) {
95 if(add_control_qs && !(*k >= ' ' && *k <= '~')) {
96 /* Add ^Q to quote control characters */
97 ioctl(STDIN_FILENO, TIOCSTI, &control_q);
99 ioctl(STDIN_FILENO, TIOCSTI, k);
101 /* Restore the old settings */
102 tcsetattr(STDIN_FILENO, TCSANOW, &oldtio);
105 /*********************************************************************/
107 void str_to_positive_integers(char *string, int *values, int nb) {
108 int current_value, gotone;
118 if(*s >= '0' && *s <= '9') {
119 current_value = current_value * 10 + (int) (*s - '0');
121 } else if(*s == ',' || *s == '\0') {
124 values[n++] = current_value;
130 "Selector: Missing value in `%s'.\n", string);
138 "Selector: Too many values in `%s'.\n", string);
143 "Selector: Empty value in `%s'.\n", string);
148 "Selector: Syntax error in `%s'.\n", string);
155 void error_feedback() {
163 void usage(FILE *out) {
165 fprintf(out, "Selector version %s (%s)\n", VERSION, UNAME);
166 fprintf(out, "Written by Francois Fleuret <francois@fleuret.org>.\n");
168 fprintf(out, "Usage: selector [options] [<filename1> [<filename2> ...]]\n");
170 fprintf(out, " -h, --help\n");
171 fprintf(out, " show this help\n");
172 fprintf(out, " -v, --inject-in-tty\n");
173 fprintf(out, " inject the selected line in the tty\n");
174 fprintf(out, " -w, --add-control-qs\n");
175 fprintf(out, " quote control characters with ^Qs when using -v\n");
176 fprintf(out, " -d, --remove-duplicates\n");
177 fprintf(out, " remove duplicated lines\n");
178 fprintf(out, " -b, --remove-bash-prefix\n");
179 fprintf(out, " remove the bash history line prefix\n");
180 fprintf(out, " -z, --remove-zsh-prefix\n");
181 fprintf(out, " remove the zsh history line prefix\n");
182 fprintf(out, " -i, --revert-order\n");
183 fprintf(out, " invert the order of lines\n");
184 fprintf(out, " -e, --regexp\n");
185 fprintf(out, " start in regexp mode\n");
186 fprintf(out, " -a, --case-sensitive\n");
187 fprintf(out, " start in case sensitive mode\n");
188 fprintf(out, " -m, --monochrome\n");
189 fprintf(out, " monochrome mode\n");
190 fprintf(out, " -q, --no-beep\n");
191 fprintf(out, " make a flash instead of a beep on an edition error\n");
192 fprintf(out, " --, --rest-are-files\n");
193 fprintf(out, " all following arguments are filenames\n");
194 fprintf(out, " -t <title>, --title <title>\n");
195 fprintf(out, " add a title in the modeline\n");
196 fprintf(out, " -c <colors>, --colors <colors>\n");
197 fprintf(out, " set the display colors with an argument of the form\n");
198 fprintf(out, " <fg_modeline>,<bg_modeline>,<fg_highlight>,<bg_highlight>\n");
199 fprintf(out, " -o <output filename>, --output-file <output filename>\n");
200 fprintf(out, " set a file to write the selected line to\n");
201 fprintf(out, " -s <pattern separator>, --pattern-separator <pattern separator>\n");
202 fprintf(out, " set the symbol to separate substrings in the pattern\n");
203 fprintf(out, " -x <label separator>, --label-separator <label separator>\n");
204 fprintf(out, " set the symbol to terminate the label\n");
205 fprintf(out, " -l <max number of lines>, --number-of-lines <max number of lines>\n");
206 fprintf(out, " set the maximum number of lines to take into account\n");
210 /*********************************************************************/
212 /* A quick and dirty hash table */
214 /* The table itself stores indexes of the strings taken in a char**
215 table. When a string is added, if it was already in the table, the
216 new index replaces the previous one. */
218 struct hash_table_t {
223 struct hash_table_t *new_hash_table(int size) {
225 struct hash_table_t *hash_table;
227 hash_table = safe_malloc(sizeof(struct hash_table_t));
229 hash_table->size = size;
230 hash_table->entries = safe_malloc(hash_table->size * sizeof(int));
232 for(k = 0; k < hash_table->size; k++) {
233 hash_table->entries[k] = -1;
239 void free_hash_table(struct hash_table_t *hash_table) {
240 free(hash_table->entries);
244 /* Adds new_string in the table, associated to new_index. If this
245 string was not already in the table, returns -1. Otherwise, returns
246 the previous index it had. */
248 int add_and_get_previous_index(struct hash_table_t *hash_table,
249 const char *new_string, int new_index,
252 unsigned int code = 0;
255 /* This is my recipe. I checked, it seems to work (as long as
256 hash_table->size is not a multiple of 387433 that should be
259 for(k = 0; new_string[k]; k++) {
260 code = code * 387433 + (unsigned int) (new_string[k]);
263 code = code % hash_table->size;
265 while(hash_table->entries[code] >= 0) {
266 /* There is a string with that code */
267 if(strcmp(new_string, strings[hash_table->entries[code]]) == 0) {
268 /* It is the same string, we keep a copy of the stored index */
269 int result = hash_table->entries[code];
270 /* Put the new one */
271 hash_table->entries[code] = new_index;
272 /* And return the previous one */
275 /* This collision was not the same string, let's move to the next
277 code = (code + 1) % hash_table->size;
280 /* This string was not already in there, store the index in the
281 table and return -1 */
283 hash_table->entries[code] = new_index;
287 /*********************************************************************
288 A matcher matches either with a collection of substrings, or with a
296 char *splitted_patterns, **patterns;
299 int match(char *string, matcher_t *matcher) {
301 if(matcher->nb_patterns >= 0) {
302 if(matcher->case_sensitive) {
303 for(n = 0; n < matcher->nb_patterns; n++) {
304 if(strstr(string, matcher->patterns[n]) == 0) return 0;
307 for(n = 0; n < matcher->nb_patterns; n++) {
308 if(strcasestr(string, matcher->patterns[n]) == 0) return 0;
313 return regexec(&matcher->preg, string, 0, 0, 0) == 0;
317 void free_matcher(matcher_t *matcher) {
318 if(matcher->nb_patterns < 0) {
319 if(!matcher->regexp_error) regfree(&matcher->preg);
321 free(matcher->splitted_patterns);
322 free(matcher->patterns);
326 void initialize_matcher(int use_regexp, int case_sensitive,
327 matcher_t *matcher, const char *pattern) {
329 char *t, *last_pattern_start;
333 matcher->nb_patterns = -1;
334 matcher->regexp_error = regcomp(&matcher->preg, pattern,
335 case_sensitive ? 0 : REG_ICASE);
337 matcher->regexp_error = 0;
338 matcher->nb_patterns = 1;
339 matcher->case_sensitive = case_sensitive;
341 for(s = pattern; *s; s++) {
342 if(*s == pattern_separator) {
343 matcher->nb_patterns++;
347 matcher->splitted_patterns =
348 safe_malloc((strlen(pattern) + 1) * sizeof(char));
351 safe_malloc(matcher->nb_patterns * sizeof(char *));
353 strcpy(matcher->splitted_patterns, pattern);
356 last_pattern_start = matcher->splitted_patterns;
357 for(t = matcher->splitted_patterns; n < matcher->nb_patterns; t++) {
358 if(*t == pattern_separator || *t == '\0') {
360 matcher->patterns[n++] = last_pattern_start;
361 last_pattern_start = t + 1;
367 /*********************************************************************
370 void delete_char(char *buffer, int *position) {
371 if(buffer[*position]) {
373 while(c < BUFFER_SIZE && buffer[c]) {
374 buffer[c] = buffer[c+1];
377 } else error_feedback();
380 void backspace_char(char *buffer, int *position) {
382 if(buffer[*position]) {
383 int c = *position - 1;
385 buffer[c] = buffer[c+1];
389 buffer[*position - 1] = '\0';
393 } else error_feedback();
396 void insert_char(char *buffer, int *position, char character) {
397 if(strlen(buffer) < BUFFER_SIZE - 1) {
399 char t = buffer[c], u;
408 buffer[(*position)++] = character;
409 } else error_feedback();
412 void kill_before_cursor(char *buffer, int *position) {
414 while(buffer[*position + s]) {
415 buffer[s] = buffer[*position + s];
422 void kill_after_cursor(char *buffer, int *position) {
423 buffer[*position] = '\0';
426 /*********************************************************************/
428 int previous_visible(int current_line, int nb_lines, char **lines,
429 matcher_t *matcher) {
430 int line = current_line - 1;
431 while(line >= 0 && !match(lines[line], matcher)) line--;
435 int next_visible(int current_line, int nb_lines, char **lines,
436 matcher_t *matcher) {
437 int line = current_line + 1;
438 while(line < nb_lines && !match(lines[line], matcher)) line++;
446 /*********************************************************************/
448 /* The line highlighted is the first one matching the matcher in that
449 order: (1) current_focus_line after motion, if it does not match,
450 then (2) the first with a greater index, if none matches, then (3)
451 the first with a lesser index.
453 The index of the line actually shown highlighted is written in
454 displayed_focus_line (it can be -1 if no line at all matches the
457 If there is a motion and a line is actually shown highlighted, its
458 value is written in current_focus_line. */
460 void update_screen(int *current_focus_line, int *displayed_focus_line,
462 int nb_lines, char **lines,
466 char buffer[BUFFER_SIZE];
469 int console_width, console_height;
470 int nb_printed_lines = 0;
473 initialize_matcher(use_regexp, case_sensitive, &matcher, pattern);
475 console_width = getmaxx(stdscr);
476 console_height = getmaxy(stdscr);
478 use_default_colors();
480 /* Add an empty line where we will print the modeline at the end */
484 /* If the regexp is erroneous, print a message saying so */
486 if(matcher.regexp_error) {
488 addnstr("Regexp syntax error", console_width);
492 /* Else, and we do have lines to select from, find a visible line. */
494 else if(nb_lines > 0) {
496 if(match(lines[*current_focus_line], &matcher)) {
497 new_focus_line = *current_focus_line;
499 new_focus_line = next_visible(*current_focus_line, nb_lines, lines,
501 if(new_focus_line < 0) {
502 new_focus_line = previous_visible(*current_focus_line, nb_lines, lines,
507 /* If we found a visible line and we should move, let's move */
509 if(new_focus_line >= 0 && motion != 0) {
510 int l = new_focus_line;
512 /* We want to go down, let's find the first visible line below */
513 for(m = 0; l >= 0 && m < motion; m++) {
514 l = next_visible(l, nb_lines, lines, &matcher);
520 /* We want to go up, let's find the first visible line above */
521 for(m = 0; l >= 0 && m < -motion; m++) {
522 l = previous_visible(l, nb_lines, lines, &matcher);
530 /* Here new_focus_line is either a line number matching the
533 if(new_focus_line >= 0) {
535 int first_line = new_focus_line, last_line = new_focus_line;
538 /* We find the first and last lines to show, so that the total
539 of visible lines between them (them included) is
542 while(nb_match < console_height-1 &&
543 (first_line > 0 || last_line < nb_lines - 1)) {
547 while(first_line > 0 && !match(lines[first_line], &matcher)) {
550 if(match(lines[first_line], &matcher)) {
555 if(nb_match < console_height - 1 && last_line < nb_lines - 1) {
557 while(last_line < nb_lines - 1 && !match(lines[last_line], &matcher)) {
561 if(match(lines[last_line], &matcher)) {
567 /* Now we display them */
569 for(l = first_line; l <= last_line; l++) {
570 if(match(lines[l], &matcher)) {
573 while(lines[l][k] && k < BUFFER_SIZE - 2 && k < console_width - 2) {
574 buffer[k] = lines[l][k];
578 /* We fill the rest of the line with blanks if this is the
581 if(l == new_focus_line) {
582 while(k < console_width) {
592 /* Highlight the highlighted line ... */
594 if(l == new_focus_line) {
595 attron(attr_focus_line);
596 addnstr(buffer, console_width);
597 attroff(attr_focus_line);
599 addnstr(buffer, console_width);
606 /* If we are on a focused line and we moved, this become the new
610 *current_focus_line = new_focus_line;
614 *displayed_focus_line = new_focus_line;
616 if(nb_printed_lines == 0) {
618 addnstr("No selection", console_width);
623 /* Else, print a message saying that there are no lines to select from */
627 addnstr("Empty choice", console_width);
633 /* Draw the modeline */
637 attron(attr_modeline);
639 for(k = 0; k < console_width; k++) buffer[k] = ' ';
640 buffer[console_width] = '\0';
641 addnstr(buffer, console_width);
645 /* There must be a more elegant way of moving the cursor at a
646 location met during display */
653 cursor_x += strlen(title) + 1;
656 sprintf(buffer, "%d/%d ", nb_printed_lines, nb_lines);
658 cursor_x += strlen(buffer);
660 addnstr(pattern, cursor_position);
661 cursor_x += cursor_position;
663 if(pattern[cursor_position]) {
664 addstr(pattern + cursor_position);
669 /* Add a few info about the mode we are in (regexp and/or case
672 if(use_regexp || case_sensitive) {
689 attroff(attr_modeline);
694 free_matcher(&matcher);
697 /*********************************************************************/
699 void store_line(struct hash_table_t *hash_table,
700 const char *new_line,
701 int *nb_lines, char **lines) {
704 /* Remove the zsh history prefix */
706 if(zsh_history && *new_line == ':') {
707 while(*new_line && *new_line != ';') new_line++;
708 if(*new_line == ';') new_line++;
711 /* Remove the bash history prefix */
714 while(*new_line == ' ') new_line++;
715 while(*new_line >= '0' && *new_line <= '9') new_line++;
716 while(*new_line == ' ') new_line++;
719 /* Check for duplicates with the hash table and insert the line in
720 the list if necessary */
723 dup = add_and_get_previous_index(hash_table,
724 new_line, *nb_lines, lines);
730 lines[*nb_lines] = safe_malloc((strlen(new_line) + 1) * sizeof(char));
731 strcpy(lines[*nb_lines], new_line);
733 /* The string was already in there, so we do not allocate a new
734 string but use the pointer to the first occurence of it */
735 lines[*nb_lines] = lines[dup];
742 void read_file(struct hash_table_t *hash_table,
743 const char *input_filename,
744 int nb_lines_max, int *nb_lines, char **lines) {
746 char raw_line[BUFFER_SIZE];
747 int start, end, eol, k;
750 file = fopen(input_filename, "r");
753 fprintf(stderr, "Selector: Can not open `%s'.\n", input_filename);
760 while(*nb_lines < nb_lines_max && (end > start || !feof(file))) {
763 /* Look for the end of a line in what is already in the buffer */
764 while(eol < end && raw_line[eol] != '\n') eol++;
766 /* if we did not find the of a line, move what has not been
767 processed and is in the buffer to the beginning of the buffer,
768 fill the buffer with new data from the file, and look for the
771 for(k = 0; k < end - start; k++) {
772 raw_line[k] = raw_line[k + start];
777 end += fread(raw_line + end, sizeof(char), BUFFER_SIZE - end, file);
778 while(eol < end && raw_line[eol] != '\n') eol++;
781 /* The end of the line is the buffer size, which means the line is
784 if(eol == BUFFER_SIZE) {
785 raw_line[BUFFER_SIZE - 1] = '\0';
786 fprintf(stderr, "Selector: Line too long (max is %d characters):\n",
788 fprintf(stderr, raw_line);
789 fprintf(stderr, "\n");
793 /* If we got a line, we replace the carriage return by a \0 to
796 raw_line[eol] = '\0';
798 store_line(hash_table, raw_line + start,
807 /*********************************************************************/
809 static struct option long_options[] = {
810 { "output-file", 1, 0, 'o' },
811 { "pattern-separator", 1, 0, 's' },
812 { "label-separator", 1, 0, 'x' },
813 { "inject-in-tty", no_argument, 0, 'v' },
814 { "add-control-qs", no_argument, 0, 'w' },
815 { "monochrome", no_argument, 0, 'm' },
816 { "no-beep", no_argument, 0, 'q' },
817 { "revert-order", no_argument, 0, 'i' },
818 { "remove-bash-prefix", no_argument, 0, 'b' },
819 { "remove-zsh-prefix", no_argument, 0, 'z' },
820 { "remove-duplicates", no_argument, 0, 'd' },
821 { "regexp", no_argument, 0, 'e' },
822 { "case-sensitive", no_argument, 0, 'a' },
823 { "title", 1, 0, 't' },
824 { "number-of-lines", 1, 0, 'l' },
825 { "colors", 1, 0, 'c' },
826 { "rest-are-files", no_argument, 0, '-' },
827 { "help", no_argument, 0, 'h' },
831 int main(int argc, char **argv) {
833 char output_filename[BUFFER_SIZE];
834 char pattern[BUFFER_SIZE];
837 int error = 0, show_help = 0;
838 int rest_are_files = 0;
840 int current_focus_line, displayed_focus_line;
843 int color_fg_modeline, color_bg_modeline;
844 int color_fg_highlight, color_bg_highlight;
846 char **lines, **labels;
848 struct hash_table_t *hash_table;
850 if(!isatty(STDIN_FILENO)) {
851 fprintf(stderr, "Selector: The standard input is not a tty.\n");
855 color_fg_modeline = COLOR_WHITE;
856 color_bg_modeline = COLOR_BLACK;
857 color_fg_highlight = COLOR_BLACK;
858 color_bg_highlight = COLOR_YELLOW;
860 setlocale(LC_ALL, "");
862 strcpy(output_filename, "");
864 while (!rest_are_files &&
865 (c = getopt_long(argc, argv, "o:s:x:vwmqf:ibzdeat:l:c:-h",
866 long_options, NULL)) != -1) {
871 strncpy(output_filename, optarg, BUFFER_SIZE);
875 pattern_separator = optarg[0];
879 label_separator = optarg[0];
883 output_to_vt_buffer = 1;
911 remove_duplicates = 1;
924 title = safe_malloc((strlen(optarg) + 1) * sizeof(char));
925 strcpy(title, optarg);
929 str_to_positive_integers(optarg, &nb_lines_max, 1);
933 str_to_positive_integers(optarg, colors, 4);
934 color_fg_modeline = colors[0];
935 color_bg_modeline = colors[1];
936 color_fg_highlight = colors[2];
937 color_bg_highlight = colors[3];
954 if(show_help || error) {
964 lines = safe_malloc(nb_lines_max * sizeof(char *));
968 if(remove_duplicates) {
969 hash_table = new_hash_table(nb_lines_max * 10);
974 while(optind < argc) {
975 read_file(hash_table,
977 nb_lines_max, &nb_lines, lines);
982 free_hash_table(hash_table);
985 /* Now remove the null strings */
988 for(k = 0; k < nb_lines; k++) {
990 lines[n++] = lines[k];
997 for(l = 0; l < nb_lines / 2; l++) {
998 char *s = lines[nb_lines - 1 - l];
999 lines[nb_lines - 1 - l] = lines[l];
1004 /* Build the labels from the strings, take only the part before the
1005 label_separator and transform control characters to printable
1008 labels = safe_malloc(nb_lines * sizeof(char *));
1010 for(l = 0; l < nb_lines; l++) {
1016 while(*t && *t != label_separator) {
1021 labels[l] = safe_malloc((e + 1) * sizeof(char));
1024 while(*t && *t != label_separator) {
1026 while(*u) { *s++ = *u++; }
1033 cursor_position = 0;
1035 /* Here we start to display with curse */
1040 intrflush(stdscr, FALSE);
1042 /* So that the arrow keys work */
1043 keypad(stdscr, TRUE);
1045 attr_error = A_STANDOUT;
1046 attr_modeline = A_REVERSE;
1047 attr_focus_line = A_STANDOUT;
1049 if(with_colors && has_colors()) {
1053 if(color_fg_modeline < 0 || color_fg_modeline >= COLORS ||
1054 color_bg_modeline < 0 || color_bg_modeline >= COLORS ||
1055 color_fg_highlight < 0 || color_bg_highlight >= COLORS ||
1056 color_bg_highlight < 0 || color_bg_highlight >= COLORS) {
1059 fprintf(stderr, "Selector: Color numbers have to be between 0 and %d.\n",
1064 init_pair(1, color_fg_modeline, color_bg_modeline);
1065 attr_modeline = COLOR_PAIR(1);
1067 init_pair(2, color_fg_highlight, color_bg_highlight);
1068 attr_focus_line = COLOR_PAIR(2);
1070 init_pair(3, COLOR_WHITE, COLOR_RED);
1071 attr_error = COLOR_PAIR(3);
1075 current_focus_line = 0;
1076 displayed_focus_line = 0;
1078 update_screen(¤t_focus_line, &displayed_focus_line,
1080 nb_lines, labels, cursor_position, pattern);
1087 if(key >= ' ' && key <= '~') { /* Insert character */
1088 insert_char(pattern, &cursor_position, key);
1091 else if(key == KEY_BACKSPACE ||
1092 key == '\010' || /* ^H */
1093 key == '\177') { /* ^? */
1094 backspace_char(pattern, &cursor_position);
1097 else if(key == KEY_DC ||
1098 key == '\004') { /* ^D */
1099 delete_char(pattern, &cursor_position);
1102 else if(key == KEY_HOME) {
1103 current_focus_line = 0;
1106 else if(key == KEY_END) {
1107 current_focus_line = nb_lines - 1;
1110 else if(key == KEY_NPAGE) {
1114 else if(key == KEY_PPAGE) {
1118 else if(key == KEY_DOWN ||
1119 key == '\016') { /* ^N */
1123 else if(key == KEY_UP ||
1124 key == '\020') { /* ^P */
1128 else if(key == KEY_LEFT ||
1129 key == '\002') { /* ^B */
1130 if(cursor_position > 0) cursor_position--;
1131 else error_feedback();
1134 else if(key == KEY_RIGHT ||
1135 key == '\006') { /* ^F */
1136 if(pattern[cursor_position]) cursor_position++;
1137 else error_feedback();
1140 else if(key == '\001') { /* ^A */
1141 cursor_position = 0;
1144 else if(key == '\005') { /* ^E */
1145 cursor_position = strlen(pattern);
1148 else if(key == '\022') { /* ^R */
1149 use_regexp = !use_regexp;
1152 else if(key == '\011') { /* ^I */
1153 case_sensitive = !case_sensitive;
1156 else if(key == '\025') { /* ^U */
1157 kill_before_cursor(pattern, &cursor_position);
1160 else if(key == '\013') { /* ^K */
1161 kill_after_cursor(pattern, &cursor_position);
1164 else if(key == '\014') { /* ^L */
1165 /* I suspect that we may sometime mess up the display, so ^L is
1166 here to force a full refresh */
1170 update_screen(¤t_focus_line, &displayed_focus_line,
1172 nb_lines, labels, cursor_position, pattern);
1174 } while(key != '\007' && /* ^G */
1175 key != '\033' && /* ^[ (escape) */
1182 /* Here we come back to standard display */
1184 if((key == KEY_ENTER || key == '\n')) {
1188 if(displayed_focus_line >= 0 && displayed_focus_line < nb_lines) {
1189 t = lines[displayed_focus_line];
1190 if(label_separator) {
1191 while(*t && *t != label_separator) t++;
1198 if(output_to_vt_buffer && t) {
1199 inject_into_tty_buffer(t, add_control_qs);
1202 if(output_filename[0]) {
1203 FILE *out = fopen(output_filename, "w");
1211 "Selector: Can not open %s for writing.\n",
1219 printf("Aborted.\n");
1222 for(l = 0; l < nb_lines; l++) {