3 * selector is a simple command line utility for selection of strings
4 * with a dynamic pattern-matching.
6 * Copyright (c) 2009 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>
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 check_opt(int argc, char **argv, int n_opt, int n, const char *help) {
108 if(n_opt + n >= argc) {
109 fprintf(stderr, "Selector: Missing argument for %s, expecting %s.\n",
115 int string_to_positive_integer(char *string) {
120 printf("string_to_positive_integer string=\"%s\"\n", string);
122 for(s = string; *s && *s != ','; s++) {
123 if(*s >= '0' && *s <= '9') {
124 result = result * 10 + (int) (*s - '0');
131 "Selector: Value `%s' is not a positive integer.\n",
139 void error_feedback() {
147 void print_help(FILE *out) {
149 fprintf(out, "Selector version %s (%s)\n", VERSION, UNAME);
150 fprintf(out, "Written by Francois Fleuret <francois@fleuret.org>.\n");
152 fprintf(out, "Usage: selector [options] [<filename1> [<filename2> ...]]\n");
154 fprintf(out, " -h show this help\n");
155 fprintf(out, " -v inject the selected line in the tty\n");
156 fprintf(out, " -w quote control characters with ^Qs when using -v\n");
157 fprintf(out, " -d remove duplicated lines\n");
158 fprintf(out, " -b remove the bash history line prefix\n");
159 fprintf(out, " -z remove the zsh history line prefix\n");
160 fprintf(out, " -i invert the order of lines\n");
161 fprintf(out, " -e start in regexp mode\n");
162 fprintf(out, " -a start in case sensitive mode\n");
163 fprintf(out, " -m monochrome mode\n");
164 fprintf(out, " -q make a flash instead of a beep on an edition error\n");
165 fprintf(out, " -- all following arguments are filenames\n");
166 fprintf(out, " -t <title>\n");
167 fprintf(out, " add a title in the modeline\n");
168 fprintf(out, " -c <fg modeline> <bg modeline> <fg highlight> <bg highlight>\n");
169 fprintf(out, " set the display colors\n");
170 fprintf(out, " -o <output filename>\n");
171 fprintf(out, " set a file to write the selected line to\n");
172 fprintf(out, " -s <pattern separator>\n");
173 fprintf(out, " set the symbol to separate substrings in the pattern\n");
174 fprintf(out, " -x <label separator>\n");
175 fprintf(out, " set the symbol to terminate the label\n");
176 fprintf(out, " -l <max number of lines>\n");
177 fprintf(out, " set the maximum number of lines to take into account\n");
181 /*********************************************************************/
183 /* A quick and dirty hash table */
185 /* The table itself stores indexes of the strings taken in a char**
186 table. When a string is added, if it was already in the table, the
187 new index replaces the previous one. */
189 struct hash_table_t {
194 struct hash_table_t *new_hash_table(int size) {
196 struct hash_table_t *hash_table;
198 hash_table = safe_malloc(sizeof(struct hash_table_t));
200 hash_table->size = size;
201 hash_table->entries = safe_malloc(hash_table->size * sizeof(int));
203 for(k = 0; k < hash_table->size; k++) {
204 hash_table->entries[k] = -1;
210 void free_hash_table(struct hash_table_t *hash_table) {
211 free(hash_table->entries);
215 /* Adds new_string in the table, associated to new_index. If this
216 string was not already in the table, returns -1. Otherwise, returns
217 the previous index it had. */
219 int add_and_get_previous_index(struct hash_table_t *hash_table,
220 const char *new_string, int new_index,
223 unsigned int code = 0;
226 /* This is my recipe. I checked, it seems to work (as long as
227 hash_table->size is not a multiple of 387433 that should be
230 for(k = 0; new_string[k]; k++) {
231 code = code * 387433 + (unsigned int) (new_string[k]);
234 code = code % hash_table->size;
236 while(hash_table->entries[code] >= 0) {
237 /* There is a string with that code */
238 if(strcmp(new_string, strings[hash_table->entries[code]]) == 0) {
239 /* It is the same string, we keep a copy of the stored index */
240 int result = hash_table->entries[code];
241 /* Put the new one */
242 hash_table->entries[code] = new_index;
243 /* And return the previous one */
246 /* This collision was not the same string, let's move to the next
248 code = (code + 1) % hash_table->size;
251 /* This string was not already in there, store the index in the
252 table and return -1 */
254 hash_table->entries[code] = new_index;
258 /*********************************************************************
259 A matcher matches either with a collection of substrings, or with a
267 char *splitted_patterns, **patterns;
270 int match(char *string, matcher_t *matcher) {
272 if(matcher->nb_patterns >= 0) {
273 if(matcher->case_sensitive) {
274 for(n = 0; n < matcher->nb_patterns; n++) {
275 if(strstr(string, matcher->patterns[n]) == 0) return 0;
278 for(n = 0; n < matcher->nb_patterns; n++) {
279 if(strcasestr(string, matcher->patterns[n]) == 0) return 0;
284 return regexec(&matcher->preg, string, 0, 0, 0) == 0;
288 void free_matcher(matcher_t *matcher) {
289 if(matcher->nb_patterns < 0) {
290 if(!matcher->regexp_error) regfree(&matcher->preg);
292 free(matcher->splitted_patterns);
293 free(matcher->patterns);
297 void initialize_matcher(int use_regexp, int case_sensitive,
298 matcher_t *matcher, const char *pattern) {
300 char *t, *last_pattern_start;
304 matcher->nb_patterns = -1;
305 matcher->regexp_error = regcomp(&matcher->preg, pattern,
306 case_sensitive ? 0 : REG_ICASE);
308 matcher->regexp_error = 0;
309 matcher->nb_patterns = 1;
310 matcher->case_sensitive = case_sensitive;
312 for(s = pattern; *s; s++) {
313 if(*s == pattern_separator) {
314 matcher->nb_patterns++;
318 matcher->splitted_patterns =
319 safe_malloc((strlen(pattern) + 1) * sizeof(char));
322 safe_malloc(matcher->nb_patterns * sizeof(char *));
324 strcpy(matcher->splitted_patterns, pattern);
327 last_pattern_start = matcher->splitted_patterns;
328 for(t = matcher->splitted_patterns; n < matcher->nb_patterns; t++) {
329 if(*t == pattern_separator || *t == '\0') {
331 matcher->patterns[n++] = last_pattern_start;
332 last_pattern_start = t + 1;
338 /*********************************************************************
341 void delete_char(char *buffer, int *position) {
342 if(buffer[*position]) {
344 while(c < BUFFER_SIZE && buffer[c]) {
345 buffer[c] = buffer[c+1];
348 } else error_feedback();
351 void backspace_char(char *buffer, int *position) {
353 if(buffer[*position]) {
354 int c = *position - 1;
356 buffer[c] = buffer[c+1];
360 buffer[*position - 1] = '\0';
364 } else error_feedback();
367 void insert_char(char *buffer, int *position, char character) {
368 if(strlen(buffer) < BUFFER_SIZE - 1) {
370 char t = buffer[c], u;
379 buffer[(*position)++] = character;
380 } else error_feedback();
383 void kill_before_cursor(char *buffer, int *position) {
385 while(buffer[*position + s]) {
386 buffer[s] = buffer[*position + s];
393 void kill_after_cursor(char *buffer, int *position) {
394 buffer[*position] = '\0';
397 /*********************************************************************/
399 int previous_visible(int current_line, int nb_lines, char **lines,
400 matcher_t *matcher) {
401 int line = current_line - 1;
402 while(line >= 0 && !match(lines[line], matcher)) line--;
406 int next_visible(int current_line, int nb_lines, char **lines,
407 matcher_t *matcher) {
408 int line = current_line + 1;
409 while(line < nb_lines && !match(lines[line], matcher)) line++;
417 /*********************************************************************/
419 /* The line highlighted is the first one matching the matcher in that
420 order: (1) current_focus_line after motion, if it does not match,
421 then (2) the first with a greater index, if none matches, then (3)
422 the first with a lesser index.
424 The index of the line actually shown highlighted is written in
425 displayed_focus_line (it can be -1 if no line at all matches the
428 If there is a motion and a line is actually shown highlighted, its
429 value is written in current_focus_line. */
431 void update_screen(int *current_focus_line, int *displayed_focus_line,
433 int nb_lines, char **lines,
437 char buffer[BUFFER_SIZE];
440 int console_width, console_height;
441 int nb_printed_lines = 0;
444 initialize_matcher(use_regexp, case_sensitive, &matcher, pattern);
446 console_width = getmaxx(stdscr);
447 console_height = getmaxy(stdscr);
449 use_default_colors();
451 /* Add an empty line where we will print the modeline at the end */
455 /* If the regexp is erroneous, print a message saying so */
457 if(matcher.regexp_error) {
459 addnstr("Regexp syntax error", console_width);
463 /* Else, and we do have lines to select from, find a visible line. */
465 else if(nb_lines > 0) {
467 if(match(lines[*current_focus_line], &matcher)) {
468 new_focus_line = *current_focus_line;
470 new_focus_line = next_visible(*current_focus_line, nb_lines, lines,
472 if(new_focus_line < 0) {
473 new_focus_line = previous_visible(*current_focus_line, nb_lines, lines,
478 /* If we found a visible line and we should move, let's move */
480 if(new_focus_line >= 0 && motion != 0) {
481 int l = new_focus_line;
483 /* We want to go down, let's find the first visible line below */
484 for(m = 0; l >= 0 && m < motion; m++) {
485 l = next_visible(l, nb_lines, lines, &matcher);
491 /* We want to go up, let's find the first visible line above */
492 for(m = 0; l >= 0 && m < -motion; m++) {
493 l = previous_visible(l, nb_lines, lines, &matcher);
501 /* Here new_focus_line is either a line number matching the
504 if(new_focus_line >= 0) {
506 int first_line = new_focus_line, last_line = new_focus_line;
509 /* We find the first and last line to show, so that the total of
510 visible lines between them (them included) is
513 while(nb_match < console_height-1 &&
514 (first_line > 0 || last_line < nb_lines - 1)) {
518 while(first_line > 0 && !match(lines[first_line], &matcher)) {
521 if(match(lines[first_line], &matcher)) {
526 if(nb_match < console_height - 1 && last_line < nb_lines - 1) {
528 while(last_line < nb_lines - 1 && !match(lines[last_line], &matcher)) {
532 if(match(lines[last_line], &matcher)) {
538 /* Now we display them */
540 for(l = first_line; l <= last_line; l++) {
541 if(match(lines[l], &matcher)) {
544 while(lines[l][k] && k < BUFFER_SIZE - 2 && k < console_width - 2) {
545 buffer[k] = lines[l][k];
549 /* We fill the rest of the line with blanks if this is the
552 if(l == new_focus_line) {
553 while(k < console_width) {
563 /* Highlight the highlighted line ... */
565 if(l == new_focus_line) {
566 attron(attr_focus_line);
567 addnstr(buffer, console_width);
568 attroff(attr_focus_line);
570 addnstr(buffer, console_width);
577 /* If we are on a focused line and we moved, this become the new
581 *current_focus_line = new_focus_line;
585 *displayed_focus_line = new_focus_line;
587 if(nb_printed_lines == 0) {
589 addnstr("No selection", console_width);
594 /* Else, print a message saying that there are no lines to select from */
598 addnstr("Empty choice", console_width);
604 /* Draw the modeline */
608 attron(attr_modeline);
610 for(k = 0; k < console_width; k++) buffer[k] = ' ';
611 buffer[console_width] = '\0';
612 addnstr(buffer, console_width);
616 /* There must be a more elegant way of moving the cursor at a
617 location met during display */
624 cursor_x += strlen(title) + 1;
627 sprintf(buffer, "%d/%d ", nb_printed_lines, nb_lines);
629 cursor_x += strlen(buffer);
631 addnstr(pattern, cursor_position);
632 cursor_x += cursor_position;
634 if(pattern[cursor_position]) {
635 addstr(pattern + cursor_position);
640 /* Add a few info about the mode we are in (regexp and/or case
643 if(use_regexp || case_sensitive) {
660 attroff(attr_modeline);
665 free_matcher(&matcher);
668 /*********************************************************************/
670 void store_line(struct hash_table_t *hash_table,
671 const char *new_line,
672 int *nb_lines, char **lines) {
675 /* Remove the zsh history prefix */
677 if(zsh_history && *new_line == ':') {
678 while(*new_line && *new_line != ';') new_line++;
679 if(*new_line == ';') new_line++;
682 /* Remove the bash history prefix */
685 while(*new_line == ' ') new_line++;
686 while(*new_line >= '0' && *new_line <= '9') new_line++;
687 while(*new_line == ' ') new_line++;
690 /* Check for duplicates with the hash table and insert the line in
691 the list if necessary */
694 dup = add_and_get_previous_index(hash_table,
695 new_line, *nb_lines, lines);
701 lines[*nb_lines] = safe_malloc((strlen(new_line) + 1) * sizeof(char));
702 strcpy(lines[*nb_lines], new_line);
704 /* The string was already in there, so we do not allocate a new
705 string but use the pointer to the first occurence of it */
706 lines[*nb_lines] = lines[dup];
713 void read_file(struct hash_table_t *hash_table,
714 const char *input_filename,
715 int nb_lines_max, int *nb_lines, char **lines) {
717 char raw_line[BUFFER_SIZE];
718 int start, end, eol, k;
721 file = fopen(input_filename, "r");
724 fprintf(stderr, "Selector: Can not open `%s'.\n", input_filename);
731 while(*nb_lines < nb_lines_max && (end > start || !feof(file))) {
734 /* Look for the end of a line in what is already in the buffer */
735 while(eol < end && raw_line[eol] != '\n') eol++;
737 /* if we did not find the of a line, move what has not been
738 processed and is in the buffer to the beginning of the buffer,
739 fill the buffer with new data from the file, and look for the
742 for(k = 0; k < end - start; k++) {
743 raw_line[k] = raw_line[k + start];
748 end += fread(raw_line + end, sizeof(char), BUFFER_SIZE - end, file);
749 while(eol < end && raw_line[eol] != '\n') eol++;
752 /* The end of the line is the buffer size, which means the line is
755 if(eol == BUFFER_SIZE) {
756 raw_line[BUFFER_SIZE - 1] = '\0';
757 fprintf(stderr, "Selector: Line too long (max is %d characters):\n",
759 fprintf(stderr, raw_line);
760 fprintf(stderr, "\n");
764 /* If we got a line, we replace the carriage return by a \0 to
767 raw_line[eol] = '\0';
769 store_line(hash_table, raw_line + start,
778 /*********************************************************************/
780 static struct option long_options[] = {
781 { "output-file", 1, 0, 'o' },
782 { "pattern-separator", 1, 0, 's' },
783 { "label-separator", 1, 0, 'x' },
784 { "inject-in-tty", no_argument, 0, 'v' },
785 { "add-control-qs", no_argument, 0, 'w' },
786 { "monochrome", no_argument, 0, 'm' },
787 { "no-beep", no_argument, 0, 'q' },
788 { "input-file", 1, 0, 'f' },
789 { "revert-order", no_argument, 0, 'i' },
790 { "remove-bash-prefix", no_argument, 0, 'b' },
791 { "remove-zsh-prefix", no_argument, 0, 'z' },
792 { "remove-duplicates", no_argument, 0, 'd' },
793 { "regexp", no_argument, 0, 'e' },
794 { "case-sensitive", no_argument, 0, 'a' },
795 { "title", 1, 0, 't' },
796 { "number-of-lines", 1, 0, 'l' },
797 { "colors", 1, 0, 'c' },
798 { "rest-are-files", no_argument, 0, '-' },
799 { "help", no_argument, 0, 'h' },
803 int main(int argc, char **argv) {
805 char input_filename[BUFFER_SIZE], output_filename[BUFFER_SIZE];
807 char pattern[BUFFER_SIZE];
810 int error = 0, show_help = 0;
811 int rest_are_files = 0;
813 int current_focus_line, displayed_focus_line;
815 int color_fg_modeline, color_bg_modeline;
816 int color_fg_highlight, color_bg_highlight;
818 char **lines, **labels;
820 struct hash_table_t *hash_table;
822 if(!isatty(STDIN_FILENO)) {
823 fprintf(stderr, "Selector: The standard input is not a tty.\n");
827 color_fg_modeline = COLOR_WHITE;
828 color_bg_modeline = COLOR_BLACK;
829 color_fg_highlight = COLOR_BLACK;
830 color_bg_highlight = COLOR_YELLOW;
832 setlocale(LC_ALL, "");
834 strcpy(input_filename, "");
835 strcpy(output_filename, "");
837 while (!rest_are_files &&
838 (c = getopt_long(argc, argv, "o:s:x:vwmqf:ibzdeat:l:c:-h",
839 long_options, NULL)) != -1) {
844 strncpy(output_filename, optarg, BUFFER_SIZE);
848 pattern_separator = optarg[0];
852 label_separator = optarg[0];
856 output_to_vt_buffer = 1;
872 strncpy(input_filename, optarg, BUFFER_SIZE);
888 remove_duplicates = 1;
901 title = safe_malloc((strlen(optarg) + 1) * sizeof(char));
902 strcpy(title, optarg);
906 nb_lines_max = string_to_positive_integer(optarg);
911 color_fg_modeline = string_to_positive_integer(s);
912 while(*s && *s != ',') s++; if(*s == ',') { s++; }
913 color_bg_modeline = string_to_positive_integer(s);
914 while(*s && *s != ',') s++; if(*s == ',') { s++; }
915 color_fg_highlight = string_to_positive_integer(s);
916 while(*s && *s != ',') s++; if(*s == ',') { s++; }
917 color_bg_highlight = string_to_positive_integer(s);
934 if(show_help || error) {
944 lines = safe_malloc(nb_lines_max * sizeof(char *));
948 if(remove_duplicates) {
949 hash_table = new_hash_table(nb_lines_max * 10);
954 if(input_filename[0]) {
955 read_file(hash_table,
957 nb_lines_max, &nb_lines, lines);
960 while(optind < argc) {
961 read_file(hash_table,
963 nb_lines_max, &nb_lines, lines);
968 free_hash_table(hash_table);
971 /* Now remove the null strings */
974 for(k = 0; k < nb_lines; k++) {
976 lines[n++] = lines[k];
983 for(l = 0; l < nb_lines / 2; l++) {
984 char *s = lines[nb_lines - 1 - l];
985 lines[nb_lines - 1 - l] = lines[l];
990 /* Build the labels from the strings, take only the part before the
991 label_separator and transform control characters to printable
994 labels = safe_malloc(nb_lines * sizeof(char *));
996 for(l = 0; l < nb_lines; l++) {
1002 while(*t && *t != label_separator) {
1007 labels[l] = safe_malloc((e + 1) * sizeof(char));
1010 while(*t && *t != label_separator) {
1012 while(*u) { *s++ = *u++; }
1019 cursor_position = 0;
1021 /* Here we start to display with curse */
1026 intrflush(stdscr, FALSE);
1028 /* So that the arrow keys work */
1029 keypad(stdscr, TRUE);
1031 attr_error = A_STANDOUT;
1032 attr_modeline = A_REVERSE;
1033 attr_focus_line = A_STANDOUT;
1035 if(with_colors && has_colors()) {
1039 if(color_fg_modeline < 0 || color_fg_modeline >= COLORS ||
1040 color_bg_modeline < 0 || color_bg_modeline >= COLORS ||
1041 color_fg_highlight < 0 || color_bg_highlight >= COLORS ||
1042 color_bg_highlight < 0 || color_bg_highlight >= COLORS) {
1045 fprintf(stderr, "Selector: Color numbers have to be between 0 and %d.\n",
1050 init_pair(1, color_fg_modeline, color_bg_modeline);
1051 attr_modeline = COLOR_PAIR(1);
1053 init_pair(2, color_fg_highlight, color_bg_highlight);
1054 attr_focus_line = COLOR_PAIR(2);
1056 init_pair(3, COLOR_WHITE, COLOR_RED);
1057 attr_error = COLOR_PAIR(3);
1061 current_focus_line = 0;
1062 displayed_focus_line = 0;
1064 update_screen(¤t_focus_line, &displayed_focus_line,
1066 nb_lines, labels, cursor_position, pattern);
1073 if(key >= ' ' && key <= '~') { /* Insert character */
1074 insert_char(pattern, &cursor_position, key);
1077 else if(key == KEY_BACKSPACE ||
1078 key == '\010' || /* ^H */
1079 key == '\177') { /* ^? */
1080 backspace_char(pattern, &cursor_position);
1083 else if(key == KEY_DC ||
1084 key == '\004') { /* ^D */
1085 delete_char(pattern, &cursor_position);
1088 else if(key == KEY_HOME) {
1089 current_focus_line = 0;
1092 else if(key == KEY_END) {
1093 current_focus_line = nb_lines - 1;
1096 else if(key == KEY_NPAGE) {
1100 else if(key == KEY_PPAGE) {
1104 else if(key == KEY_DOWN ||
1105 key == '\016') { /* ^N */
1109 else if(key == KEY_UP ||
1110 key == '\020') { /* ^P */
1114 else if(key == KEY_LEFT ||
1115 key == '\002') { /* ^B */
1116 if(cursor_position > 0) cursor_position--;
1117 else error_feedback();
1120 else if(key == KEY_RIGHT ||
1121 key == '\006') { /* ^F */
1122 if(pattern[cursor_position]) cursor_position++;
1123 else error_feedback();
1126 else if(key == '\001') { /* ^A */
1127 cursor_position = 0;
1130 else if(key == '\005') { /* ^E */
1131 cursor_position = strlen(pattern);
1134 else if(key == '\022') { /* ^R */
1135 use_regexp = !use_regexp;
1138 else if(key == '\011') { /* ^I */
1139 case_sensitive = !case_sensitive;
1142 else if(key == '\025') { /* ^U */
1143 kill_before_cursor(pattern, &cursor_position);
1146 else if(key == '\013') { /* ^K */
1147 kill_after_cursor(pattern, &cursor_position);
1150 else if(key == '\014') { /* ^L */
1151 /* I suspect that we may sometime mess up the display, so ^L is
1152 here to force a full refresh */
1156 update_screen(¤t_focus_line, &displayed_focus_line,
1158 nb_lines, labels, cursor_position, pattern);
1160 } while(key != '\007' && /* ^G */
1161 key != '\033' && /* ^[ (escape) */
1168 /* Here we come back to standard display */
1170 if((key == KEY_ENTER || key == '\n')) {
1174 if(displayed_focus_line >= 0 && displayed_focus_line < nb_lines) {
1175 t = lines[displayed_focus_line];
1176 if(label_separator) {
1177 while(*t && *t != label_separator) t++;
1184 if(output_to_vt_buffer && t) {
1185 inject_into_tty_buffer(t, add_control_qs);
1188 if(output_filename[0]) {
1189 FILE *out = fopen(output_filename, "w");
1197 "Selector: Can not open %s for writing.\n",
1205 printf("Aborted.\n");
1208 for(l = 0; l < nb_lines; l++) {