1 #if HAVE_CONFIG_H
2 #include "config.h"
3 #endif /* HAVE_CONFIG_H */
4 
5 #if HAVE_STDLIB_H
6 #include <stdlib.h>
7 #endif /* HAVE_STDLIB_H */
8 
9 #if HAVE_STRING_H
10 #include <string.h>
11 #endif /* HAVE_STRING_H */
12 
13 #include <string>
14 
15 #include "fs_util.h"
16 
17 #include "sys_util.h"
18 #include "stretchy.h"
19 #include "sys_win.h"
20 #include "filedlg.h"
21 #include "cgdb.h"
22 #include "cgdbrc.h"
23 #include "highlight.h"
24 #include "sources.h"
25 #include "kui_term.h"
26 #include "highlight_groups.h"
27 
28 struct file_buffer {
29     char **files;               /* Array containing file */
30     int max_width;              /* Width of longest line in file */
31 
32     int sel_line;               /* Current line selected in file dialog */
33     int sel_col;                /* Current column selected in file dialog */
34     int sel_rline;              /* Current line used by regex */
35 };
36 
37 struct filedlg {
38     struct file_buffer *buf;    /* All of the widget's data ( files ) */
39     struct hl_regex_info *last_hlregex;
40     struct hl_regex_info *hlregex;
41     SWINDOW *win;               /* Curses window */
42     std::string G_line_number;  /* Line number user wants to 'G' to */
43 };
44 
45 static char regex_line[MAX_LINE];   /* The regex the user enters */
46 static int regex_line_pos;      /* The index into the current regex */
47 static int regex_search;        /* Currently searching text ? */
48 static int regex_direction;     /* Direction to search */
49 
50 /* print_in_middle: Prints the message 'string' centered at line in win
51  * ----------------
52  *
53  *  win:    Curses window
54  *  line:   The line to print the message at
55  *  width:  The width of the window
56  *  string: The message to print
57  */
print_in_middle(SWINDOW * win,int line,int width,const char * string)58 static void print_in_middle(SWINDOW *win, int line, int width, const char *string)
59 {
60     int x, y;
61     int j;
62     int length = strlen(string);
63 
64     y = swin_getcury(win);
65     x = swin_getcurx(win);
66 
67     x = (int) ((width - length) / 2);
68 
69     swin_wmove(win, line, 0);
70     for (j = 0; j < x; j++)
71         swin_waddch(win, ' ');
72 
73     swin_mvwprintw(win, line, x, "%s", string);
74 
75     for (j = x + length; j < width; j++)
76         swin_waddch(win, ' ');
77 }
78 
filedlg_new(int pos_r,int pos_c,int height,int width)79 struct filedlg *filedlg_new(int pos_r, int pos_c, int height, int width)
80 {
81     struct filedlg *fd;
82 
83     /* Allocate a new structure */
84     fd = new filedlg();
85 
86     /* Initialize the structure */
87     fd->win = swin_newwin(height, width, pos_r, pos_c);
88 
89     /* Initialize the buffer */
90     fd->buf = (struct file_buffer *)cgdb_malloc(sizeof(struct file_buffer));
91 
92     fd->last_hlregex = NULL;
93     fd->hlregex = NULL;
94     fd->buf->files = NULL;
95     fd->buf->max_width = 0;
96     fd->buf->sel_line = 0;
97     fd->buf->sel_col = 0;
98     fd->buf->sel_rline = 0;
99 
100     return fd;
101 }
102 
filedlg_free(struct filedlg * fdlg)103 void filedlg_free(struct filedlg *fdlg)
104 {
105     filedlg_clear(fdlg);
106 
107     hl_regex_free(&fdlg->last_hlregex);
108     fdlg->last_hlregex = NULL;
109 
110     hl_regex_free(&fdlg->hlregex);
111     fdlg->hlregex = NULL;
112 
113     swin_delwin(fdlg->win);
114     fdlg->win = NULL;
115 
116     free(fdlg->buf);
117     fdlg->buf = NULL;
118 
119     delete fdlg;
120 }
121 
filedlg_add_file_choice(struct filedlg * fd,const char * file_choice)122 int filedlg_add_file_choice(struct filedlg *fd, const char *file_choice)
123 {
124     int length;
125     int index, i;
126     int equal = 1;              /* Not set to 0, because 0 *is* equal */
127 
128     if (file_choice == NULL || *file_choice == '\0')
129         return -1;
130 
131     /* Make sure file exists. If temp files are used to create an
132      * executable, and the temp files are deleted, they pollute the
133      * file open dialog with files you can't actually open.
134      *
135      * The downside to not showing them all is that a user might
136      * not understand why certain files aren't showing up. O well.
137      */
138     if (file_choice[0] != '*') {
139         if (fs_verify_file_exists(file_choice) == 0)
140             return -4;
141     }
142 
143     /* find index to insert by comparing:
144      * Absolute paths go to the end
145      * Relative paths go before the absolute paths
146      */
147     for (i = 0; i < sbcount(fd->buf->files); i++) {
148         /* Don't add duplicate entry's ... gdb outputs duplicates */
149         if ((equal = strcmp(fd->buf->files[i], file_choice)) == 0)
150             return -3;
151         else if (equal < 0) {
152             /* Inserting filename, stop before relative path */
153             if ((file_choice[0] != '.' && file_choice[0] != '/')
154                     && fd->buf->files[i][0] == '.')
155                 break;
156 
157             /* Inserting filename, stop before absolute path */
158             if (file_choice[0] != '/' && fd->buf->files[i][0] == '/')
159                 break;
160 
161         } else if (equal > 0) { /* Found ( file_choice is greater ) */
162             /* Inserting Absolute path, it goes to the end */
163             if (file_choice[0] == '/' && fd->buf->files[i][0] != '/')
164                 continue;
165 
166             /* Inserting relative path, continue until before absolute or relative path */
167             if (file_choice[0] == '.' && (fd->buf->files[i][0] != '.'
168                             && fd->buf->files[i][0] != '/'))
169                 continue;
170 
171             break;
172         }
173     }
174 
175     index = i;
176 
177     sbpush(fd->buf->files, NULL);
178 
179     /* shift everything down and then insert into index */
180     for (i = sbcount(fd->buf->files) - 1; i > index; i--)
181         fd->buf->files[i] = fd->buf->files[i - 1];
182 
183     fd->buf->files[index] = cgdb_strdup(file_choice);
184 
185     if ((length = strlen(file_choice)) > fd->buf->max_width)
186         fd->buf->max_width = length;
187 
188     return 0;
189 }
190 
filedlg_clear(struct filedlg * fd)191 void filedlg_clear(struct filedlg *fd)
192 {
193     int i;
194 
195     fd->G_line_number.clear();
196 
197     for (i = 0; i < sbcount(fd->buf->files); i++)
198         free(fd->buf->files[i]);
199 
200     sbfree(fd->buf->files);
201     fd->buf->files = NULL;
202 
203     fd->buf->max_width = 0;
204     fd->buf->sel_line = 0;
205     fd->buf->sel_col = 0;
206     fd->buf->sel_rline = 0;
207 }
208 
clamp_line(struct filedlg * fd,int line)209 static int clamp_line(struct filedlg *fd, int line)
210 {
211     if (line < 0)
212         line = 0;
213     if (line >= sbcount(fd->buf->files))
214         line = sbcount(fd->buf->files) - 1;
215 
216     return line;
217 }
218 
filedlg_vscroll(struct filedlg * fd,int offset)219 static void filedlg_vscroll(struct filedlg *fd, int offset)
220 {
221     if (fd->buf)
222         fd->buf->sel_line = clamp_line(fd, fd->buf->sel_line + offset);
223 }
224 
filedlg_hscroll(struct filedlg * fd,int offset)225 static void filedlg_hscroll(struct filedlg *fd, int offset)
226 {
227     int lwidth;
228     int max_width;
229     int width;
230 
231     if (fd->buf) {
232         width = swin_getmaxx(fd->win);
233 
234         lwidth = log10_uint(sbcount(fd->buf->files)) + 1;
235         max_width = fd->buf->max_width - width + lwidth + 6;
236 
237         fd->buf->sel_col += offset;
238         if (fd->buf->sel_col > max_width)
239             fd->buf->sel_col = max_width;
240         if (fd->buf->sel_col < 0)
241             fd->buf->sel_col = 0;
242     }
243 }
244 
filedlg_set_sel_line(struct filedlg * fd,int line)245 static void filedlg_set_sel_line(struct filedlg *fd, int line)
246 {
247     if (fd->buf)
248         fd->buf->sel_line = clamp_line(fd, line);
249 }
250 
filedlg_search_regex_init(struct filedlg * fd)251 static void filedlg_search_regex_init(struct filedlg *fd)
252 {
253     if (!fd || !fd->buf)
254         return;
255 
256     /* Start searching at the beginning of the selected line */
257     fd->buf->sel_rline = fd->buf->sel_line;
258 }
259 
wrap_line(struct file_buffer * buffer,int line)260 static int wrap_line(struct file_buffer *buffer, int line)
261 {
262     int count = sbcount(buffer->files);
263 
264     if (line < 0)
265         line = count - 1;
266     else if (line >= count)
267         line = 0;
268 
269     return line;
270 }
271 
filedlg_search_regex(struct filedlg * fd,const char * regex,int opt,int direction,int icase)272 static int filedlg_search_regex(struct filedlg *fd, const char *regex,
273         int opt, int direction, int icase)
274 {
275     if (!fd || !fd->buf)
276         return -1;
277 
278     if (regex && regex[0]) {
279         int line;
280         int line_end;
281         int line_inc = direction ? +1 : -1;
282         int line_start = fd->buf->sel_rline;
283 
284         line = wrap_line(fd->buf, line_start + line_inc);
285 
286         if (cgdbrc_get_int(CGDBRC_WRAPSCAN))
287         {
288             // Wrapping is on so stop at the line we started on.
289             line_end = line_start;
290         }
291         else
292         {
293             // No wrapping. Stop at line 0 if searching down and last line
294             // if searching up.
295             line_end = direction ? 0 : sbcount(fd->buf->files) - 1;
296         }
297 
298         for(;;) {
299             int ret;
300             int start, end;
301             char *file = fd->buf->files[line];
302 
303             ret = hl_regex_search(&fd->hlregex, file, regex, icase, &start, &end);
304             if (ret > 0) {
305                 /* Got a match */
306                 fd->buf->sel_line = line;
307 
308                 /* Finalized match - move to this location */
309                 if (opt == 2) {
310                     fd->buf->sel_rline = line;
311                     fd->last_hlregex = fd->hlregex;
312                     fd->hlregex = 0;
313                 }
314                 return 1;
315             }
316 
317             line = wrap_line(fd->buf, line + line_inc);
318             if (line == line_end)
319                 break;
320         }
321     }
322 
323     /* Nothing found - go back to original line */
324     fd->buf->sel_line = fd->buf->sel_rline;
325     return 0;
326 }
327 
filedlg_display(struct filedlg * fd)328 int filedlg_display(struct filedlg *fd)
329 {
330     char fmt[16];
331     int width, height;
332     int lwidth;
333     int file;
334     int i;
335     int statusbar;
336     int arrow_attr;
337     int count = sbcount(fd->buf->files);
338     int hlsearch = cgdbrc_get_int(CGDBRC_HLSEARCH);
339     static const char label[] = "Select a file or press q to cancel.";
340 
341     swin_curs_set(0);
342 
343     statusbar = hl_groups_get_attr(hl_groups_instance, HLG_STATUS_BAR);
344     arrow_attr = hl_groups_get_attr(hl_groups_instance, HLG_SELECTED_LINE_ARROW);
345 
346     /* Check that a file is loaded */
347     if (fd == NULL || fd->buf == NULL || fd->buf->files == NULL) {
348         swin_wrefresh(fd->win);
349         return 0;
350     }
351 
352     /* Initialize variables */
353     height = swin_getmaxy(fd->win);
354     width = swin_getmaxx(fd->win);
355 
356     /* The status bar and display line
357      * Fake the display function to think the height is 2 lines less */
358     height -= 2;
359 
360     /* Set starting line number (center source file if it's small enough) */
361     if (count < height)
362         file = (count - height) / 2;
363     else {
364         file = fd->buf->sel_line - height / 2;
365         if (file > count - height)
366             file = count - height;
367         else if (file < 0)
368             file = 0;
369     }
370 
371     /* Print 'height' lines of the file, starting at 'file' */
372     lwidth = log10_uint(count) + 1;
373     snprintf(fmt, sizeof(fmt), "%%%dd", lwidth);
374 
375     print_in_middle(fd->win, 0, width, label);
376 
377     swin_wmove(fd->win, 0, 0);
378 
379     for (i = 1; i < height + 1; i++, file++) {
380         swin_wmove(fd->win, i, 0);
381 
382         /* Outside of filename, just finish drawing the vertical file */
383         if (file < 0 || file >= count) {
384             int j;
385 
386             for (j = 1; j < lwidth; j++)
387                 swin_waddch(fd->win, ' ');
388             swin_waddch(fd->win, '~');
389 
390             swin_wattron(fd->win, SWIN_A_BOLD);
391             swin_waddch(fd->win, SWIN_SYM_VLINE);
392             swin_wattroff(fd->win, SWIN_A_BOLD);
393 
394             for (j = 2 + lwidth; j < width; j++)
395                 swin_waddch(fd->win, ' ');
396             continue;
397         }
398 
399         int x, y;
400         char *filename = fd->buf->files[file];
401 
402         /* Mark the current file with an arrow */
403         if (file == fd->buf->sel_line) {
404             swin_wattron(fd->win, SWIN_A_BOLD);
405             swin_wprintw(fd->win, fmt, file + 1);
406             swin_wattroff(fd->win, SWIN_A_BOLD);
407 
408             swin_wattron(fd->win, arrow_attr);
409             swin_waddch(fd->win, '-');
410             swin_waddch(fd->win, '>');
411             swin_wattroff(fd->win, arrow_attr);
412 
413         }
414         else {
415             /* Ordinary file */
416             swin_wprintw(fd->win, fmt, file + 1);
417 
418             swin_wattron(fd->win, SWIN_A_BOLD);
419             swin_waddch(fd->win, SWIN_SYM_VLINE);
420             swin_wattroff(fd->win, SWIN_A_BOLD);
421 
422             swin_waddch(fd->win, ' ');
423         }
424 
425         y = swin_getcury(fd->win);
426         x = swin_getcurx(fd->win);
427 
428         hl_printline(fd->win, filename, strlen(filename),
429                      NULL, -1, -1, fd->buf->sel_col, width - lwidth - 2);
430 
431         if (hlsearch && fd->last_hlregex) {
432             struct hl_line_attr *attrs = hl_regex_highlight(
433                     &fd->last_hlregex, filename, HLG_SEARCH);
434 
435             if (sbcount(attrs)) {
436                 hl_printline_highlight(fd->win, filename, strlen(filename),
437                              attrs, x, y, fd->buf->sel_col, width - lwidth - 2);
438                 sbfree(attrs);
439             }
440         }
441 
442         if (regex_search && file == fd->buf->sel_line) {
443             struct hl_line_attr *attrs = hl_regex_highlight(
444                     &fd->hlregex, filename, HLG_INCSEARCH);
445 
446             if (sbcount(attrs)) {
447                 hl_printline_highlight(fd->win, filename, strlen(filename),
448                              attrs, x, y, fd->buf->sel_col, width - lwidth - 2);
449                 sbfree(attrs);
450             }
451         }
452     }
453 
454     /* Add the 2 lines back in so the status bar can be drawn */
455     height += 2;
456 
457     /* Update status bar */
458     swin_wmove(fd->win, height, 0);
459 
460     /* Print white background */
461     swin_wattron(fd->win, statusbar);
462 
463     for (i = 0; i < width; i++)
464         swin_mvwprintw(fd->win, height - 1, i, " ");
465 
466     if (regex_search && regex_direction)
467         swin_mvwprintw(fd->win, height - 1, 0, "Search:%s", regex_line);
468     else if (regex_search)
469         swin_mvwprintw(fd->win, height - 1, 0, "RSearch:%s", regex_line);
470 
471     swin_wattroff(fd->win, statusbar);
472 
473     swin_wmove(fd->win, height - (file - fd->buf->sel_line) - 1, lwidth + 2);
474     swin_wrefresh(fd->win);
475 
476     return 0;
477 }
478 
filedlg_display_message(struct filedlg * fd,char * message)479 void filedlg_display_message(struct filedlg *fd, char *message)
480 {
481     int height, width, i;
482     int attr;
483 
484     attr = hl_groups_get_attr(hl_groups_instance, HLG_STATUS_BAR);
485 
486     height = swin_getmaxy(fd->win);
487     width = swin_getmaxx(fd->win);
488 
489     /* Print white background */
490     swin_wattron(fd->win, attr);
491 
492     for (i = 0; i < width; i++)
493         swin_mvwprintw(fd->win, height - 1, i, " ");
494 
495     swin_mvwprintw(fd->win, height - 1, 0, "%s", message);
496     swin_wattroff(fd->win, attr);
497     swin_wrefresh(fd->win);
498 }
499 
500 /* capture_regex: Captures a regular expression from the user.
501  * ---------------
502  *  Side Effect:
503  *
504  *  regex_line: The regex the user has entered.
505  *  regex_line_pos: The next available index into regex_line.
506  *
507  * Return Value: 0 if user gave a regex, otherwise 1.
508  */
capture_regex(struct filedlg * fd)509 static int capture_regex(struct filedlg *fd)
510 {
511     int c;
512     extern struct kui_manager *kui_ctx;
513 
514     /* Initialize the function for finding a regex and tell user */
515     regex_search = 1;
516     regex_line_pos = 0;
517     regex_line[regex_line_pos] = '\0';
518     filedlg_display(fd);
519 
520     do {
521         c = kui_manager_getkey_blocking(kui_ctx);
522 
523         if (regex_line_pos == (MAX_LINE - 1) && !(c == CGDB_KEY_ESC || c == 8 || c == 127))
524             continue;
525 
526         /* Quit the search if the user hit escape */
527         if (c == CGDB_KEY_ESC) {
528             regex_line_pos = 0;
529             regex_line[regex_line_pos] = '\0';
530             regex_search = 0;
531             filedlg_search_regex(fd, regex_line, 2, regex_direction, 1);
532             filedlg_display(fd);
533             return 1;
534         }
535 
536         /* If the user hit enter, then a successful regex has been received */
537         if (c == '\r' || c == '\n' || c == CGDB_KEY_CTRL_M) {
538             regex_line[regex_line_pos] = '\0';
539             break;
540         }
541 
542         /* If the user hit backspace or delete remove a char */
543         if (c == 8 || c == 127) {
544             if (regex_line_pos > 0)
545                 --regex_line_pos;
546 
547             regex_line[regex_line_pos] = '\0';
548             filedlg_search_regex(fd, regex_line, 1, regex_direction, 1);
549             filedlg_display(fd);
550             continue;
551         }
552 
553         /* Add a char, search and draw */
554         regex_line[regex_line_pos++] = c;
555         regex_line[regex_line_pos] = '\0';
556         filedlg_search_regex(fd, regex_line, 1, regex_direction, 1);
557         filedlg_display(fd);
558     } while (1);
559 
560     /* Finished */
561     regex_search = 0;
562     filedlg_search_regex(fd, regex_line, 2, regex_direction, 1);
563     filedlg_display(fd);
564     return 0;
565 }
566 
filedlg_recv_char(struct filedlg * fd,int key,char * file,int last_key_pressed)567 int filedlg_recv_char(struct filedlg *fd, int key, char *file, int last_key_pressed)
568 {
569     /* Initialize size variables */
570     int height = swin_getmaxy(fd->win);
571 
572     filedlg_display(fd);
573 
574     switch (key) {
575         case 'q':
576             return -1;
577             /* Vertical scrolling */
578         case CGDB_KEY_DOWN:
579         case 'j':
580             filedlg_vscroll(fd, 1);
581             break;
582         case CGDB_KEY_NPAGE:
583         case CGDB_KEY_CTRL_F:  /* VI-style page down */
584             filedlg_vscroll(fd, height - 1);
585             break;
586         case CGDB_KEY_CTRL_D:  /* VI-style 1/2 page down */
587             filedlg_vscroll(fd, height / 2);
588             break;
589         case CGDB_KEY_CTRL_U:  /* VI-style 1/2 page up */
590             filedlg_vscroll(fd, -height / 2);
591             break;
592         case CGDB_KEY_UP:
593         case 'k':
594             filedlg_vscroll(fd, -1);
595             break;
596         case CGDB_KEY_PPAGE:
597         case CGDB_KEY_CTRL_B:  /* VI-style page up */
598             filedlg_vscroll(fd, -(height - 1));
599             break;
600             /* Horizontal scrolling */
601         case CGDB_KEY_RIGHT:
602         case 'l':
603             filedlg_hscroll(fd, 1);
604             break;
605         case CGDB_KEY_LEFT:
606         case 'h':
607             filedlg_hscroll(fd, -1);
608             break;
609         case '/':
610         case '?':
611             regex_direction = ('/' == key);
612 
613             /* Capturing regular expressions */
614             filedlg_search_regex_init(fd);
615             capture_regex(fd);
616             break;
617         case 'n':
618             filedlg_search_regex(fd, regex_line, 2, regex_direction, 1);
619             break;
620         case 'N':
621             filedlg_search_regex(fd, regex_line, 2, !regex_direction, 1);
622             break;
623             /* User selected a file */
624         case '\n':
625         case '\r':
626         case CGDB_KEY_CTRL_M:
627             strcpy(file, fd->buf->files[fd->buf->sel_line]);
628             return 1;
629 
630         case 'g':              /* beginning of file */
631             if (last_key_pressed == 'g')
632                 filedlg_set_sel_line(fd, 0);
633             break;
634         case 'G': {             /* end of file, or a line number*/
635             int lineno = -1, result;
636             result = cgdb_string_to_int(fd->G_line_number.c_str(), &lineno);
637             if (result == 0) {
638                 filedlg_set_sel_line(fd, lineno -1);
639             }
640             break;
641         }
642         default:
643             break;
644     }
645 
646     /* Store digits into G_line_number for 'G' command. */
647     if (key >= '0' && key <= '9') {
648         fd->G_line_number.push_back(key);
649     } else {
650         fd->G_line_number.clear();
651     }
652 
653     filedlg_display(fd);
654 
655     return 0;
656 }
657