Automatic commit
[selector.git] / selector.cc
1
2 ///////////////////////////////////////////////////////////////////////////
3 // START_IP_HEADER                                                       //
4 //                                                                       //
5 // This program is free software: you can redistribute it and/or modify  //
6 // it under the terms of the version 3 of the GNU General Public License //
7 // as published by the Free Software Foundation.                         //
8 //                                                                       //
9 // This program is distributed in the hope that it will be useful, but   //
10 // WITHOUT ANY WARRANTY; without even the implied warranty of            //
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      //
12 // General Public License for more details.                              //
13 //                                                                       //
14 // You should have received a copy of the GNU General Public License     //
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.  //
16 //                                                                       //
17 // Written by and Copyright (C) Francois Fleuret                         //
18 // Contact <francois.fleuret@idiap.ch> for comments & bug reports        //
19 //                                                                       //
20 // END_IP_HEADER                                                         //
21 ///////////////////////////////////////////////////////////////////////////
22
23 #include <stdio.h>
24 #include <ncurses.h>
25 #include <iostream>
26 #include <fstream>
27 #include <string.h>
28
29 using namespace std;
30
31 ostream *log;
32
33 const int buffer_size = 1024;
34 const int nb_lines_max = 100000;
35
36 int match(char *string, char *regexp) {
37   return strstr(string, regexp) != 0;
38 }
39
40 void update_screen(int *current_line, int motion,
41                    int nb_lines, char **lines,
42                    char *regexp, int noblink) {
43
44   char buffer[buffer_size];
45
46   int console_width = getmaxx(stdscr);
47   int console_height = getmaxy(stdscr);
48
49   if(!noblink) {
50     clear();
51   }
52
53   use_default_colors();
54
55   printw("\n");
56
57   int nb_printed_lines = 1, last_printer_line = -1;
58
59   int new_line;
60   if(motion >= 0) {
61     new_line = *current_line + motion;
62     while(new_line < nb_lines && !match(lines[new_line], regexp)) {
63       new_line++;
64     }
65     if(new_line == nb_lines) {
66       new_line = *current_line;
67       while(new_line >= 0 && ! match(lines[new_line], regexp)) {
68         new_line--;
69       }
70     }
71   } else {
72
73     new_line = *current_line - 1;
74     while(new_line >= 0 && ! match(lines[new_line], regexp)) {
75       new_line--;
76     }
77
78     if(new_line < 0) {
79       new_line = *current_line;
80       while(new_line < nb_lines && !match(lines[new_line], regexp)) {
81         new_line++;
82       }
83       if(new_line == nb_lines) {
84         new_line = -1;
85       }
86     }
87   }
88
89   // Here new_line is either a line number matching the regexp, or -1
90
91   if(new_line >= 0) {
92     int first_line = new_line, last_line = new_line, nb_match = 1;
93
94     while(nb_match < console_height && (first_line > 0 || last_line < nb_lines - 1)) {
95
96       if(first_line > 0) {
97         first_line--;
98         while(first_line > 0 && !match(lines[first_line], regexp)) {
99           first_line--;
100         }
101         if(match(lines[first_line], regexp)) {
102           nb_match++;
103         }
104       }
105
106       if(last_line < nb_lines - 1) {
107         last_line++;
108         while(last_line < nb_lines - 1 && !match(lines[last_line], regexp)) {
109           last_line++;
110         }
111
112         if(match(lines[last_line], regexp)) {
113           nb_match++;
114         }
115       }
116     }
117
118     for(int l = first_line; l <= last_line; l++) {
119       if(match(lines[l], regexp)) {
120         int k = 0;
121
122         while(lines[l][k] && k < buffer_size - 2 && k < console_width - 1) {
123           buffer[k] = lines[l][k];
124           k++;
125         }
126
127         if(noblink) {
128           while(k < console_width - 1) {
129             buffer[k++] = ' ';
130           }
131         }
132         buffer[k++] = '\n';
133         buffer[k++] = '\0';
134
135         if(l == new_line) {
136           attron(COLOR_PAIR(2));
137           printw(buffer);
138           attroff(COLOR_PAIR(2));
139         } else {
140           printw(buffer);
141         }
142
143         last_printer_line = l;
144         nb_printed_lines++;
145       }
146     }
147
148     *current_line = new_line;
149   }
150
151   if(noblink) { // Erase the rest of the window. That's slightly ugly.
152     int k = 0;
153     while(k < console_width - 1) {
154       buffer[k++] = ' ';
155     }
156     buffer[k++] = '\n';
157     buffer[k++] = '\0';
158     for(int l = nb_printed_lines; l < console_height; l++) {
159       printw(buffer);
160     }
161   }
162
163   // Draw the modeline
164
165   move(0, 0);
166   attron(COLOR_PAIR(1));
167   sprintf(buffer, "%d/%d pattern: %s", nb_printed_lines - 1, nb_lines, regexp);
168   for(int k = strlen(buffer); k < console_width - 1; k++) buffer[k] = ' ';
169   buffer[console_width-1] = '\0';
170   printw(buffer);
171   attroff(COLOR_PAIR(1));
172
173   refresh();       // After doing something on the display, we refresh it
174 }
175
176 int main(int argc, char **argv) {
177   char buffer[buffer_size];
178   char *lines[nb_lines_max];
179   int noblink = 0;
180
181   log = new fstream("/tmp/selector.log");
182
183   char *file_name;
184   char stdin_name[] = "/dev/stdin";
185
186   if(argc == 2 && strcmp(argv[1], "-")) {
187     file_name = argv[1];
188   } else {
189     file_name = stdin_name;
190   }
191
192   ifstream file(file_name);
193
194   if(file.fail()) {
195     cerr << "Can not open \"" << file_name << "\"" << endl;
196     return 1;
197   }
198
199   int nb_lines = 0;
200   while(nb_lines < nb_lines_max && !file.eof()) {
201     file.getline(buffer, buffer_size);
202     lines[nb_lines] = new char[strlen(buffer) + 1];
203     strcpy(lines[nb_lines], buffer);
204     nb_lines++;
205   }
206
207   char regexp[buffer_size]="";
208   int regexp_point;
209   regexp_point = 0;
210
211   initscr();
212
213   if(!has_colors()) {
214     cerr << "No colors." << endl;
215     return 1;
216   }
217
218   noecho();
219   curs_set(0);
220   keypad(stdscr, TRUE);
221
222   start_color();
223   init_pair(1, COLOR_WHITE, COLOR_BLACK);
224   init_pair(2, COLOR_BLACK, COLOR_YELLOW);
225
226   int key;
227
228   int line = 0;
229
230   update_screen(&line, 0,
231                 nb_lines, lines,
232                 regexp, noblink);
233
234   do {
235
236     key = getch();
237
238     int motion = 0;
239
240     if(key >= ' ' && key <= 'z') {
241       regexp[regexp_point++] = key;
242       regexp[regexp_point] = '\0';
243     }
244
245     else if(key == KEY_BACKSPACE || key == KEY_DC) {
246       if(regexp_point > 0) {
247         regexp_point--;
248         regexp[regexp_point] = '\0';
249       }
250     }
251
252     else if(key == KEY_UP || key == '\10') {
253       motion = -1;
254     }
255
256     else if(key == KEY_DOWN || key == '\ e') {
257       motion = 1;
258     }
259
260     update_screen(&line, motion, nb_lines, lines, regexp, noblink);
261   } while(key != '\n' && key != KEY_ENTER && key != '\a');
262
263   echo();
264   curs_set(1);
265   endwin();
266
267   ofstream out("/tmp/selector.out");
268   if((key == KEY_ENTER || key == '\n') && line >= 0 && line < nb_lines) {
269     out << lines[line] << endl;
270   } else {
271     out << endl;
272   }
273   out.flush();
274
275   for(int l = 0; l < nb_lines; l++) {
276     delete[] lines[l];
277   }
278
279   log->flush();
280   delete log;
281
282   return 0;
283 }