1 /* sources.c:
2 * ----------
3 *
4 * Source file management routines for the GUI. Provides the ability to
5 * add files to the list, load files, and display within a curses window.
6 * Files are buffered in memory when they are displayed, and held in
7 * memory for the duration of execution. If memory consumption becomes a
8 * problem, this can be optimized to unload files which have not been
9 * displayed recently, or only load portions of large files at once. (May
10 * affect syntax highlighting.)
11 *
12 */
13
14 #if HAVE_CONFIG_H
15 #include "config.h"
16 #endif /* HAVE_CONFIG_H */
17
18 /* System Includes */
19 #if HAVE_STDIO_H
20 #include <stdio.h>
21 #endif /* HAVE_STDIO_H */
22
23 #if HAVE_STDLIB_H
24 #include <stdlib.h>
25 #endif /* HAVE_STDLIB_H */
26
27 #if HAVE_STRING_H
28 #include <string.h>
29 #endif /* HAVE_STRING_H */
30
31 #ifdef HAVE_SYS_TIME_H
32 #include <sys/time.h>
33 #endif
34
35 #if HAVE_SYS_STAT_H
36 #include <sys/stat.h>
37 #endif
38
39 #if HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif /* HAVE_UNISTD_H */
42
43 #if HAVE_CTYPE_H
44 #include <ctype.h>
45 #endif
46
47 #ifdef HAVE_STDINT_H
48 #include <stdint.h>
49 #endif
50
51 /* Local Includes */
52 #include "sys_util.h"
53 #include "stretchy.h"
54 #include "sys_win.h"
55 #include "cgdb.h"
56 #include "highlight.h"
57 #include "tokenizer.h"
58 #include "sources.h"
59 #include "logo.h"
60 #include "fs_util.h"
61 #include "cgdbrc.h"
62 #include "highlight_groups.h"
63 #include "interface.h"
64 #include "tgdb.h"
65
66 int sources_syntax_on = 1;
67
68 // This speeds up loading sqlite.c from 2:48 down to ~2 seconds.
69 // sqlite3 is 6,596,401 bytes, 188,185 lines.
70
71 /* --------------- */
72 /* Local Functions */
73 /* --------------- */
74
75 /* source_get_node: Returns a pointer to the node that matches the given path.
76 * ---------
77 * path: Full path to source file
78 *
79 * Return Value: Pointer to the matching node, or NULL if not found.
80 */
source_get_node(struct sviewer * sview,const char * path)81 struct list_node *source_get_node(struct sviewer *sview, const char *path)
82 {
83 if (sview && path && path[0]) {
84 struct list_node *cur;
85
86 for (cur = sview->list_head; cur != NULL; cur = cur->next) {
87 if (cur->path && (strcmp(path, cur->path) == 0))
88 return cur;
89 }
90 }
91
92 return NULL;
93 }
94
95 /**
96 * Get's the timestamp of a particular file.
97 *
98 * \param path
99 * The path to the file to get the timestamp of
100 *
101 * \param timestamp
102 * The timestamp of the file, or 0 on error.
103 *
104 * \return
105 * 0 on success, -1 on error.
106 */
get_timestamp(const char * path,time_t * timestamp)107 static int get_timestamp(const char *path, time_t * timestamp)
108 {
109 int val;
110 struct stat s;
111
112 /* Special buffer not backed by file */
113 if (path[0] == '*') {
114 *timestamp = 0;
115 return 0;
116 }
117
118 val = path ? stat(path, &s) : -1;
119 *timestamp = val ? 0 : s.st_mtime;
120 return val;
121 }
122
init_file_buffer(struct buffer * buf)123 static void init_file_buffer(struct buffer *buf)
124 {
125 buf->lines = NULL;
126 buf->addrs = NULL;
127 buf->max_width = 0;
128 buf->file_data = NULL;
129 buf->tabstop = cgdbrc_get_int(CGDBRC_TABSTOP);
130 buf->language = TOKENIZER_LANGUAGE_UNKNOWN;
131 }
132
release_file_buffer(struct buffer * buf)133 static void release_file_buffer(struct buffer *buf)
134 {
135 if (buf) {
136 int i;
137
138 for (i = 0; i < sbcount(buf->lines); i++) {
139 sbfree(buf->lines[i].attrs);
140 buf->lines[i].attrs = NULL;
141
142 sbfree(buf->lines[i].line);
143 buf->lines[i].line = NULL;
144 }
145
146 /* Free entire file buffer */
147 sbfree(buf->file_data);
148 buf->file_data = NULL;
149
150 sbfree(buf->lines);
151 buf->lines = NULL;
152
153 sbfree(buf->addrs);
154 buf->addrs = NULL;
155
156 buf->max_width = 0;
157 buf->language = TOKENIZER_LANGUAGE_UNKNOWN;
158 }
159 }
160
161 /**
162 * Remove's the memory related to a file.
163 *
164 * \param node
165 * The node who's file buffer data needs to be freed.
166 *
167 * \return
168 * 0 on success, or -1 on error.
169 */
release_file_memory(struct list_node * node)170 static int release_file_memory(struct list_node *node)
171 {
172 if (!node)
173 return -1;
174
175 /* Release file buffers */
176 release_file_buffer(&node->file_buf);
177
178 return 0;
179 }
180
detab_buffer(char * buffer,int tabstop)181 static char *detab_buffer(char *buffer, int tabstop)
182 {
183 int i;
184 int dst = 0;
185 char *newbuf = NULL;
186 int size = sbcount(buffer);
187
188 char *tab = strchr(buffer, '\t');
189 if (!tab)
190 return buffer;
191
192 for (i = 0; i < size; i++) {
193 if (buffer[i] == '\t') {
194 int spaces = tabstop - dst % tabstop;
195
196 while(spaces--) {
197 sbpush(newbuf, ' ');
198 dst++;
199 }
200 } else {
201 sbpush(newbuf, buffer[i]);
202 dst++;
203 }
204
205 if (buffer[i] == '\n' || buffer[i] == '\r')
206 dst = 0;
207 }
208
209 sbfree(buffer);
210 return newbuf;
211 }
212
213 /**
214 * Load file and fill tlines line pointers.
215 *
216 * \param buf
217 * struct buffer pointer
218 *
219 * \param filename
220 * name of file to load
221 *
222 * \return
223 * 0 on sucess, -1 on error
224 */
load_file_buf(struct buffer * buf,const char * filename)225 static int load_file_buf(struct buffer *buf, const char *filename)
226 {
227 FILE *file;
228 long file_size;
229 int ret = -1;
230
231 /* Special buffer not backed by file */
232 if (filename[0] == '*')
233 return 0;
234
235 file = fopen(filename, "rb");
236 if (!file)
237 return -1;
238
239 file_size = get_file_size(file);
240 if (file_size > 0) {
241 size_t bytes_read;
242
243 /* Set the stretchy buffer size to our file size plus one for nil */
244 sbsetcount(buf->file_data, file_size + 1);
245
246 /* Read in the entire file */
247 bytes_read = fread(buf->file_data, 1, file_size, file);
248
249 /* If we had a partial read, bail */
250 if (bytes_read != file_size) {
251 sbfree(buf->file_data);
252 buf->file_data = NULL;
253
254 fclose(file);
255 return -1;
256 }
257
258 /* Zero terminate buffer */
259 buf->file_data[bytes_read] = 0;
260
261 /* Convert tabs to spaces */
262 buf->tabstop = cgdbrc_get_int(CGDBRC_TABSTOP);
263 buf->file_data = detab_buffer(buf->file_data, buf->tabstop);
264
265 {
266 char *line_start = buf->file_data;
267 char *line_feed = strchr(line_start, '\n');
268
269 while (line_feed)
270 {
271 size_t line_len;
272 char *line_end = line_feed;
273
274 /* Trim trailing cr-lfs */
275 while (line_end >= line_start && (*line_end == '\n' || *line_end == '\r'))
276 line_end--;
277
278 /* Update max length string found */
279 line_len = line_end - line_start + 1;
280 if (line_len > buf->max_width)
281 buf->max_width = line_len;
282
283 struct source_line sline;
284 sline.line = NULL;
285 sbsetcount(sline.line, line_len + 1);
286 strncpy(sline.line, line_start, line_len);
287 sline.len = line_len;
288 sline.attrs = NULL;
289
290 /* Add this line to lines array */
291 sbpush(buf->lines, sline);
292
293 line_start = line_feed + 1;
294 line_feed = strchr(line_start, '\n');
295 }
296
297 if (*line_start) {
298 struct source_line sline;
299 int len = strlen(line_start);
300 sline.line = NULL;
301 sbsetcount(sline.line, len + 1);
302 strncpy(sline.line, line_start, len);
303 sline.len = len;
304 sline.attrs = NULL;
305
306 sbpush(buf->lines, sline);
307 }
308
309 ret = 0;
310 }
311 }
312
313 fclose(file);
314 return ret;
315 }
316
317 /* load_file: Loads the file in the list_node into its memory buffer.
318 * ----------
319 *
320 * node: The list node to work on
321 *
322 * Return Value: Zero on success, non-zero on error.
323 */
load_file(struct list_node * node)324 static int load_file(struct list_node *node)
325 {
326 /* No node pointer? */
327 if (!node)
328 return -1;
329
330 /* File already loaded - success! */
331 if (node->file_buf.lines)
332 return 0;
333
334 /* Stat the file to get the timestamp */
335 if (get_timestamp(node->path, &(node->last_modification)) == -1)
336 return -1;
337
338 node->language = tokenizer_get_default_file_type(strrchr(node->path, '.'));
339
340 /* Add the highlighted lines */
341 return source_highlight(node);
342 }
343
344 /* --------- */
345 /* Functions */
346 /* --------- */
347
348 /* Descriptive comments found in header file: sources.h */
349
350 /* Returns HLG_LAST on error */
hlg_from_tokenizer_type(enum tokenizer_type type,const char * tok_data)351 static enum hl_group_kind hlg_from_tokenizer_type(enum tokenizer_type type, const char *tok_data)
352 {
353 switch(type) {
354 case TOKENIZER_KEYWORD: return HLG_KEYWORD;
355 case TOKENIZER_TYPE: return HLG_TYPE;
356 case TOKENIZER_LITERAL: return HLG_LITERAL;
357 case TOKENIZER_NUMBER: return HLG_TEXT;
358 case TOKENIZER_COMMENT: return HLG_COMMENT;
359 case TOKENIZER_DIRECTIVE: return HLG_DIRECTIVE;
360 case TOKENIZER_TEXT: return HLG_TEXT;
361 case TOKENIZER_NEWLINE: return HLG_LAST;
362 case TOKENIZER_ERROR: return HLG_TEXT;
363 case TOKENIZER_SEARCH: return HLG_SEARCH;
364 case TOKENIZER_STATUS_BAR: return HLG_STATUS_BAR;
365 case TOKENIZER_EXECUTING_LINE_ARROW: return HLG_EXECUTING_LINE_ARROW;
366 case TOKENIZER_SELECTED_LINE_ARROW: return HLG_SELECTED_LINE_ARROW;
367 case TOKENIZER_EXECUTING_LINE_HIGHLIGHT: return HLG_EXECUTING_LINE_HIGHLIGHT;
368 case TOKENIZER_SELECTED_LINE_HIGHLIGHT: return HLG_SELECTED_LINE_HIGHLIGHT;
369 case TOKENIZER_EXECUTING_LINE_BLOCK: return HLG_EXECUTING_LINE_BLOCK;
370 case TOKENIZER_SELECTED_LINE_BLOCK: return HLG_SELECTED_LINE_BLOCK;
371 case TOKENIZER_ENABLED_BREAKPOINT: return HLG_ENABLED_BREAKPOINT;
372 case TOKENIZER_DISABLED_BREAKPOINT: return HLG_DISABLED_BREAKPOINT;
373 case TOKENIZER_SELECTED_LINE_NUMBER: return HLG_SELECTED_LINE_NUMBER;
374 case TOKENIZER_SCROLL_MODE_STATUS: return HLG_SCROLL_MODE_STATUS;
375 case TOKENIZER_LOGO: return HLG_LOGO;
376 case TOKENIZER_COLOR: return hl_get_color_group(tok_data);
377 }
378
379 return HLG_TEXT;
380 }
381
highlight_node(struct list_node * node)382 static int highlight_node(struct list_node *node)
383 {
384 int i;
385 int ret;
386 int line = 0;
387 int length = 0;
388 int lasttype = -1;
389 struct token_data tok_data;
390 struct tokenizer *t = tokenizer_init();
391 struct buffer *buf = &node->file_buf;
392
393 for (i = 0; i < sbcount(buf->lines); i++) {
394 sbfree(buf->lines[i].attrs);
395 buf->lines[i].attrs = NULL;
396 }
397
398 if (!buf->file_data) {
399 for (line = 0; line < sbcount(buf->lines); line++) {
400 struct source_line *sline = &buf->lines[line];
401
402 tokenizer_set_buffer(t, sline->line, buf->language);
403
404 length = 0;
405 lasttype = -1;
406 while ((ret = tokenizer_get_token(t, &tok_data)) > 0) {
407 if (tok_data.e == TOKENIZER_NEWLINE)
408 break;
409
410 enum hl_group_kind hlg = hlg_from_tokenizer_type(tok_data.e, tok_data.data);
411
412 /* Add attribute if highlight group has changed */
413 if (lasttype != hlg) {
414 sbpush(buf->lines[line].attrs, hl_line_attr(length, hlg));
415
416 lasttype = hlg;
417 }
418
419 /* Add the text and bump our length */
420 length += strlen(tok_data.data);
421 }
422 }
423
424 } else {
425 if (tokenizer_set_buffer(t, buf->file_data, buf->language) == -1) {
426 if_print_message("%s:%d tokenizer_set_buffer error", __FILE__, __LINE__);
427 return -1;
428 }
429
430 while ((ret = tokenizer_get_token(t, &tok_data)) > 0) {
431 if (tok_data.e == TOKENIZER_NEWLINE) {
432 if (length > buf->max_width)
433 buf->max_width = length;
434
435 length = 0;
436 lasttype = -1;
437 line++;
438 } else {
439 enum hl_group_kind hlg = hlg_from_tokenizer_type(tok_data.e, tok_data.data);
440
441 if (hlg == HLG_LAST) {
442 clog_error(CLOG_CGDB, "Bad hlg_type for '%s', e==%d\n", tok_data.data, tok_data.e);
443 hlg = HLG_TEXT;
444 }
445
446 /* Add attribute if highlight group has changed */
447 if (lasttype != hlg) {
448 sbpush(buf->lines[line].attrs, hl_line_attr(length, hlg));
449
450 lasttype = hlg;
451 }
452
453 /* Add the text and bump our length */
454 length += strlen(tok_data.data);
455 }
456 }
457 }
458
459 tokenizer_destroy(t);
460 return 0;
461 }
462
source_highlight(struct list_node * node)463 int source_highlight(struct list_node *node)
464 {
465 int do_color = sources_syntax_on &&
466 (node->language != TOKENIZER_LANGUAGE_UNKNOWN) &&
467 swin_has_colors();
468
469 /* Load the entire file */
470 if (!sbcount(node->file_buf.lines))
471 load_file_buf(&node->file_buf, node->path);
472
473 /* If we're doing color and we haven't already loaded this file
474 * with this language, then load and highlight it.
475 */
476 if (do_color && (node->file_buf.language != node->language)) {
477 node->file_buf.language = node->language;
478 highlight_node(node);
479 }
480
481 /* Allocate the breakpoints array */
482 if (!node->lflags) {
483 int count = sbcount(node->file_buf.lines);
484 sbsetcount(node->lflags, count);
485
486 memset(node->lflags, 0, sbcount(node->lflags));
487 }
488
489 if (node->file_buf.lines)
490 return 0;
491
492 return -1;
493 }
494
source_new(SWINDOW * win)495 struct sviewer *source_new(SWINDOW *win)
496 {
497 struct sviewer *rv;
498
499 /* Allocate a new structure */
500 rv = (struct sviewer *)cgdb_malloc(sizeof (struct sviewer));
501
502 /* Initialize the structure */
503 rv->win = win;
504 rv->cur = NULL;
505 rv->list_head = NULL;
506
507 /* Initialize global marks */
508 memset(rv->global_marks, 0, sizeof(rv->global_marks));
509 rv->jump_back_mark.node = NULL;
510 rv->jump_back_mark.line = -1;
511
512 rv->addr_frame = 0;
513
514 rv->hlregex = NULL;
515 rv->last_hlregex = NULL;
516
517 return rv;
518 }
519
source_add(struct sviewer * sview,const char * path)520 struct list_node *source_add(struct sviewer *sview, const char *path)
521 {
522 struct list_node *new_node;
523
524 new_node = source_get_node(sview, path);
525 if (new_node)
526 return new_node;
527
528 new_node = (struct list_node *)cgdb_malloc(sizeof (struct list_node));
529 new_node->path = strdup(path);
530
531 init_file_buffer(&new_node->file_buf);
532
533 new_node->lflags = NULL;
534 new_node->sel_line = 0;
535 new_node->sel_col = 0;
536 new_node->sel_rline = 0;
537 new_node->exe_line = -1;
538 new_node->last_modification = 0; /* No timestamp yet */
539 new_node->language = TOKENIZER_LANGUAGE_UNKNOWN;
540 new_node->addr_start = 0;
541 new_node->addr_end = 0;
542
543 /* Initialize all local marks to -1 */
544 memset(new_node->local_marks, 0xff, sizeof(new_node->local_marks));
545
546 if (sview->list_head == NULL) {
547 /* List is empty, this is the first node */
548 new_node->next = NULL;
549 sview->list_head = new_node;
550 } else {
551 /* Insert at the front of the list (easy) */
552 new_node->next = sview->list_head;
553 sview->list_head = new_node;
554 }
555
556 return new_node;
557 }
558
source_add_disasm_line(struct list_node * node,const char * line)559 void source_add_disasm_line(struct list_node *node, const char *line)
560 {
561 uint64_t addr = 0;
562 struct source_line sline;
563 char *colon = 0, colon_char = 0;
564
565 sline.line = NULL;
566 sbsetcount(sline.line, strlen(line) + 1);
567 strcpy(sline.line, line);
568 sline.line = detab_buffer(sline.line, node->file_buf.tabstop);
569
570 sline.attrs = NULL;
571 sline.len = sbcount(sline.line);
572
573 colon = strchr((char*)line, ':');
574 if (colon) {
575 colon_char = *colon;
576 *colon = 0;
577 }
578
579 cgdb_hexstr_to_u64(line, &addr);
580
581 if (colon) {
582 *colon = colon_char;
583 }
584
585 sbpush(node->file_buf.addrs, addr);
586
587 struct line_flags lf = { 0, 0 };
588 sbpush(node->file_buf.lines, sline);
589 sbpush(node->lflags, lf);
590 }
591
source_del(struct sviewer * sview,const char * path)592 int source_del(struct sviewer *sview, const char *path)
593 {
594 int i;
595 struct list_node *cur;
596 struct list_node *prev = NULL;
597
598 /* Find the target node */
599 for (cur = sview->list_head; cur != NULL; cur = cur->next) {
600 if (strcmp(path, cur->path) == 0)
601 break;
602 prev = cur;
603 }
604
605 if (cur == NULL)
606 return 1; /* Node not found */
607
608 /* Release file buffers */
609 release_file_buffer(&cur->file_buf);
610
611 /* Release file name */
612 free(cur->path);
613 cur->path = NULL;
614
615 sbfree(cur->lflags);
616 cur->lflags = NULL;
617
618 /* Remove link from list */
619 if (cur == sview->list_head)
620 sview->list_head = sview->list_head->next;
621 else
622 prev->next = cur->next;
623
624 /* Free the node */
625 free(cur);
626
627 /* Free any global marks pointing to this bugger */
628 for (i = 0; i < sizeof(sview->global_marks) / sizeof(sview->global_marks[0]); i++) {
629 if (sview->global_marks[i].node == cur)
630 sview->global_marks[i].node = NULL;
631 }
632
633 return 0;
634 }
635
source_length(struct sviewer * sview,const char * path)636 int source_length(struct sviewer *sview, const char *path)
637 {
638 struct list_node *cur = source_get_node(sview, path);
639
640 /* Load the file if it's not already */
641 if (load_file(cur))
642 return -1;
643
644 return sbcount(cur->file_buf.lines);
645 }
646
source_current_file(struct sviewer * sview)647 char *source_current_file(struct sviewer *sview)
648 {
649 return (sview && sview->cur) ? sview->cur->path : NULL;
650 }
651
652 /* source_get_mark_char: Return mark char for line.
653 * --------------------
654 *
655 * sview: The source viewer object
656 * node:
657 * line: line to check for mark
658 * return: -1 on error, 0 if no char exists on line, otherwise char
659 */
source_get_mark_char(struct sviewer * sview,struct list_node * node,int line)660 static int source_get_mark_char(struct sviewer *sview,
661 struct list_node *node, int line)
662 {
663 if (!node || (line < 0) || (line >= sbcount(node->lflags)))
664 return -1;
665
666 if (node->lflags[line].has_mark) {
667 int i;
668
669 for (i = 0; i < MARK_COUNT; i++) {
670 if (sview->global_marks[i].line == line)
671 return 'A' + i;
672 }
673
674 for (i = 0; i < MARK_COUNT; i++) {
675 if (node->local_marks[i] == line)
676 return 'a' + i;
677 }
678 }
679
680 return 0;
681 }
682
source_set_mark(struct sviewer * sview,int key)683 int source_set_mark(struct sviewer *sview, int key)
684 {
685 int ret = 0;
686 int old_line;
687 struct list_node *old_node;
688 int sel_line = sview->cur->sel_line;
689
690 if (key >= 'a' && key <= 'z') {
691 /* Local buffer mark */
692 old_line = sview->cur->local_marks[key - 'a'];
693 old_node = sview->cur;
694 sview->cur->local_marks[key - 'a'] = sel_line;
695 ret = 1;
696 } else if (key >= 'A' && key <= 'Z') {
697 /* Global buffer mark */
698 old_line = sview->global_marks[key - 'A'].line;
699 old_node = sview->global_marks[key - 'A'].node;
700 sview->global_marks[key - 'A'].line = sel_line;
701 sview->global_marks[key - 'A'].node = sview->cur;
702 ret = 1;
703 }
704
705 if (ret) {
706 /* Just added a mark to the selected line, flag it */
707 sview->cur->lflags[sel_line].has_mark = 1;
708
709 /* Check if the old line still has a mark */
710 if (source_get_mark_char(sview, old_node, old_line) == 0)
711 old_node->lflags[old_line].has_mark = 0;
712 }
713
714 return ret;
715 }
716
source_goto_mark(struct sviewer * sview,int key)717 int source_goto_mark(struct sviewer *sview, int key)
718 {
719 int line;
720 struct list_node *node = NULL;
721
722 if (key >= 'a' && key <= 'z') {
723 /* Local buffer mark */
724 line = sview->cur->local_marks[key - 'a'];
725 node = (line >= 0) ? sview->cur : NULL;
726 } else if (key >= 'A' && key <= 'Z') {
727 /* Global buffer mark */
728 line = sview->global_marks[key - 'A'].line;
729 node = sview->global_marks[key - 'A'].node;
730 } else if (key == '\'' ) {
731 /* Jump back to where we jumped from */
732 line = sview->jump_back_mark.line;
733 node = sview->jump_back_mark.node;
734 } else if (key == '.') {
735 /* Jump to currently executing line if it's set */
736 line = sview->cur->exe_line;
737 node = (line >= 0) ? sview->cur : NULL;
738 }
739
740 if (node) {
741 sview->jump_back_mark.line = sview->cur->sel_line;
742 sview->jump_back_mark.node = sview->cur;
743
744 sview->cur = node;
745 source_set_sel_line(sview, line + 1);
746 return 1;
747 }
748
749 return 0;
750 }
751
get_line_leading_ws_count(const char * otext,int length)752 static int get_line_leading_ws_count(const char *otext, int length)
753 {
754 int i;
755 int column_offset = 0; /* Text to skip due to arrow */
756
757 for (i = 0; i < length - 1; i++) {
758 /* Bail if we hit a non whitespace character */
759 if (!isspace(otext[i]))
760 break;
761
762 column_offset++;
763 }
764
765 return column_offset;
766 }
767
768 /**
769 * Display the source.
770 *
771 * A line in the source viewer looks like,
772 * # │ marker text
773 * where,
774 * # is the line number to display or ~ if no line number
775 * │ is the divider between the line number or it is a mark
776 * marker is shortarrow, longarrow, highlight, block, etc
777 * text is the source code to display
778 *
779 * The syntax highlighting works as follows,
780 *
781 * The #
782 * - If breakpoint is set, use Breakpoint
783 * - If breakpoint is disabled, use DisabledBreakpoint
784 * - If selected line, use SelectedLineNr
785 * - If executing line, use ExecutingLineNr
786 * - Otherwise, no highlighting group
787 *
788 * The │
789 * - When source window is in focus, the character is bolded, otherwise normal
790 * - If the user has a mark set, the mark will be displayed instead of any
791 * other character.
792 * - Edge case: When the marker is long or short arrow, CGDB prints ├
793 * instead of │ the ├ is colored based on highlighting group for
794 * the selected or executing arrow.
795 *
796 * The marker
797 * - The marker is the shortarrow, longarrow, highlight or block
798 * - The color is based off the corresponding highlighting group
799 *
800 * The text
801 * - The syntax highlighting source code to display
802 * - Will be colored with SelectedLineHighlight or ExecutingLineHighlight
803 * if the line is the selected or executing line and the display is set
804 * to highlight.
805 */
source_display(struct sviewer * sview,int focus,enum win_refresh dorefresh)806 int source_display(struct sviewer *sview, int focus, enum win_refresh dorefresh)
807 {
808 int i;
809 int lwidth;
810 int line;
811 int count;
812
813 enum LineDisplayStyle exe_display_style, sel_display_style;
814 int sellineno, exelineno;
815 int enabled_bp, disabled_bp;
816 int exe_line_display_is_arrow, sel_line_display_is_arrow;
817 int exe_arrow_attr, sel_arrow_attr;
818 int exe_block_attr, sel_block_attr;
819 char fmt[16];
820 int width, height;
821 int focus_attr = focus ? SWIN_A_BOLD : 0;
822 int showmarks = cgdbrc_get_int(CGDBRC_SHOWMARKS);
823 int hlsearch = cgdbrc_get_int(CGDBRC_HLSEARCH);
824 int mark_attr;
825
826 struct hl_line_attr *sel_highlight_attrs = 0;
827 struct hl_line_attr *exe_highlight_attrs = 0;
828
829 /* Check that a file is loaded */
830 if (!sview->cur || !sview->cur->file_buf.lines) {
831 logo_display(sview->win);
832
833 if (dorefresh == WIN_REFRESH)
834 swin_wrefresh(sview->win);
835 else
836 swin_wnoutrefresh(sview->win);
837
838 return 0;
839 }
840
841 sellineno = hl_groups_get_attr(
842 hl_groups_instance, HLG_SELECTED_LINE_NUMBER);
843 exelineno = hl_groups_get_attr(
844 hl_groups_instance, HLG_EXECUTING_LINE_NUMBER);
845 enabled_bp = hl_groups_get_attr(
846 hl_groups_instance, HLG_ENABLED_BREAKPOINT);
847 disabled_bp = hl_groups_get_attr(
848 hl_groups_instance, HLG_DISABLED_BREAKPOINT);
849
850 exe_display_style = cgdbrc_get_displaystyle(CGDBRC_EXECUTING_LINE_DISPLAY);
851 exe_arrow_attr = hl_groups_get_attr(
852 hl_groups_instance, HLG_EXECUTING_LINE_ARROW);
853 exe_block_attr = hl_groups_get_attr(
854 hl_groups_instance, HLG_EXECUTING_LINE_BLOCK);
855
856 sel_display_style = cgdbrc_get_displaystyle(CGDBRC_SELECTED_LINE_DISPLAY);
857 sel_arrow_attr = hl_groups_get_attr(
858 hl_groups_instance, HLG_SELECTED_LINE_ARROW);
859 sel_block_attr = hl_groups_get_attr(
860 hl_groups_instance, HLG_SELECTED_LINE_BLOCK);
861
862 exe_line_display_is_arrow =
863 exe_display_style == LINE_DISPLAY_SHORT_ARROW ||
864 exe_display_style == LINE_DISPLAY_LONG_ARROW;
865 sel_line_display_is_arrow =
866 sel_display_style == LINE_DISPLAY_SHORT_ARROW ||
867 sel_display_style == LINE_DISPLAY_LONG_ARROW;
868
869 mark_attr = hl_groups_get_attr(hl_groups_instance, HLG_MARK);
870
871 sbpush(sel_highlight_attrs, hl_line_attr(0, HLG_SELECTED_LINE_HIGHLIGHT));
872 sbpush(exe_highlight_attrs, hl_line_attr(0, HLG_EXECUTING_LINE_HIGHLIGHT));
873
874 /* Make sure cursor is visible */
875 swin_curs_set(!!focus);
876
877 /* Initialize variables */
878 height = swin_getmaxy(sview->win);
879 width = swin_getmaxx(sview->win);
880
881 /* Set starting line number (center source file if it's small enough) */
882 count = sbcount(sview->cur->file_buf.lines);
883 if (count < height) {
884 line = (count - height) / 2;
885 } else {
886 line = sview->cur->sel_line - height / 2;
887 if (line > count - height)
888 line = count - height;
889 else if (line < 0)
890 line = 0;
891 }
892
893 /* Print 'height' lines of the file, starting at 'line' */
894 lwidth = log10_uint(count) + 1;
895 snprintf(fmt, sizeof(fmt), "%%%dd", lwidth);
896
897 for (i = 0; i < height; i++, line++) {
898
899 int column_offset = 0;
900 /* Is this the current selected line? */
901 int is_sel_line = (line >= 0 && sview->cur->sel_line == line);
902 /* Is this the current executing line */
903 int is_exe_line = (line >= 0 && sview->cur->exe_line == line);
904 struct source_line *sline = (line < 0 || line >= count)?
905 NULL:&sview->cur->file_buf.lines[line];
906 struct hl_line_attr *printline_attrs = (sline)?sline->attrs:0;
907
908 swin_wmove(sview->win, i, 0);
909
910 /* Print the line number */
911 if (line < 0 || line >= count) {
912 for (int j = 1; j < lwidth; j++)
913 swin_waddch(sview->win, ' ');
914 swin_waddch(sview->win, '~');
915 } else {
916 int line_attr = 0;
917 int bp_val = sview->cur->lflags[line].breakpt;
918 if (bp_val == 1) {
919 line_attr = enabled_bp;
920 } else if (bp_val == 2) {
921 line_attr = disabled_bp;
922 } else if (bp_val == 0 && is_exe_line) {
923 line_attr = exelineno;
924 } else if (bp_val == 0 && is_sel_line) {
925 line_attr = sellineno;
926 }
927
928 swin_wattron(sview->win, line_attr);
929 swin_wprintw(sview->win, fmt, line + 1);
930 swin_wattroff(sview->win, line_attr);
931 }
932
933 if (!swin_has_colors()) {
934 /* TODO:
935 swin_wprintw(sview->win, "%.*s\n",
936 sview->cur->file_buf.lines[line].line,
937 sview->cur->file_buf.lines[line].len);
938 */
939 continue;
940 }
941
942 /* Print the vertical bar or mark */
943 {
944 SWIN_CHTYPE vert_bar_char;
945 int vert_bar_attr;
946 int mc;
947
948 if (showmarks &&
949 ((mc = source_get_mark_char(sview, sview->cur, line)) > 0)) {
950 vert_bar_char = mc;
951 vert_bar_attr = mark_attr;
952 } else if (is_exe_line && exe_line_display_is_arrow) {
953 vert_bar_attr = exe_arrow_attr;
954 vert_bar_char = SWIN_SYM_LTEE;
955 } else if (is_sel_line && sel_line_display_is_arrow) {
956 vert_bar_attr = sel_arrow_attr;
957 vert_bar_char = SWIN_SYM_LTEE;
958 } else {
959 vert_bar_attr = focus_attr;
960 vert_bar_char = SWIN_SYM_VLINE;
961 }
962
963 swin_wattron(sview->win, vert_bar_attr);
964 swin_waddch(sview->win, vert_bar_char);
965 swin_wattroff(sview->win, vert_bar_attr);
966 }
967
968 /* Print the marker */
969 if (is_exe_line || is_sel_line) {
970 enum LineDisplayStyle display_style;
971 int arrow_attr, block_attr;
972 struct hl_line_attr *highlight_attr;
973
974 if (is_exe_line) {
975 display_style = exe_display_style;
976 arrow_attr = exe_arrow_attr;
977 block_attr = exe_block_attr;
978 highlight_attr = exe_highlight_attrs;
979 } else {
980 display_style = sel_display_style;
981 arrow_attr = sel_arrow_attr;
982 block_attr = sel_block_attr;
983 highlight_attr = sel_highlight_attrs;
984 }
985
986 switch (display_style) {
987 case LINE_DISPLAY_SHORT_ARROW:
988 swin_wattron(sview->win, arrow_attr);
989 swin_waddch(sview->win, '>');
990 swin_wattroff(sview->win, arrow_attr);
991 break;
992 case LINE_DISPLAY_LONG_ARROW:
993 swin_wattron(sview->win, arrow_attr);
994 column_offset = get_line_leading_ws_count(
995 sline->line, sline->len);
996 column_offset -= (sview->cur->sel_col + 1);
997 if (column_offset < 0)
998 column_offset = 0;
999
1000 /* Now actually draw the arrow */
1001 for (int j = 0; j < column_offset; j++)
1002 swin_waddch(sview->win, SWIN_SYM_HLINE);
1003
1004 swin_waddch(sview->win, '>');
1005 swin_wattroff(sview->win, arrow_attr);
1006
1007 break;
1008 case LINE_DISPLAY_HIGHLIGHT:
1009 swin_waddch(sview->win, ' ');
1010 printline_attrs = highlight_attr;
1011 break;
1012 case LINE_DISPLAY_BLOCK:
1013 column_offset = get_line_leading_ws_count(
1014 sline->line, sline->len);
1015 column_offset -= (sview->cur->sel_col + 1);
1016 if (column_offset < 0)
1017 column_offset = 0;
1018
1019 /* Now actually draw the space to the block */
1020 for (int j = 0; j < column_offset; j++)
1021 swin_waddch(sview->win, ' ');
1022
1023 /* Draw the block */
1024 swin_wattron(sview->win, block_attr);
1025 swin_waddch(sview->win, ' ');
1026 swin_wattroff(sview->win, block_attr);
1027 break;
1028 }
1029 } else {
1030 swin_waddch(sview->win, ' ');
1031 }
1032
1033
1034 /* Print the text */
1035 if (line < 0 || line >= count) {
1036 for (int j = 2 + lwidth; j < width; j++)
1037 swin_waddch(sview->win, ' ');
1038 } else {
1039 int x, y;
1040 y = swin_getcury(sview->win);
1041 x = swin_getcurx(sview->win);
1042
1043 hl_printline(sview->win, sline->line, sline->len,
1044 printline_attrs, -1, -1, sview->cur->sel_col + column_offset,
1045 width - lwidth - 2);
1046
1047 if (hlsearch && sview->last_hlregex) {
1048 struct hl_line_attr *attrs = hl_regex_highlight(
1049 &sview->last_hlregex, sline->line, HLG_SEARCH);
1050 if (sbcount(attrs)) {
1051 hl_printline_highlight(sview->win, sline->line, sline->len,
1052 attrs, x, y, sview->cur->sel_col + column_offset,
1053 width - lwidth - 2);
1054 sbfree(attrs);
1055 }
1056 }
1057
1058 if (is_sel_line && sview->hlregex) {
1059 struct hl_line_attr *attrs = hl_regex_highlight(
1060 &sview->hlregex, sline->line, HLG_INCSEARCH);
1061 if (sbcount(attrs)) {
1062 hl_printline_highlight(sview->win, sline->line, sline->len,
1063 attrs, x, y, sview->cur->sel_col + column_offset,
1064 width - lwidth - 2);
1065 sbfree(attrs);
1066 }
1067 }
1068 }
1069 }
1070
1071 switch(dorefresh) {
1072 case WIN_NO_REFRESH:
1073 swin_wnoutrefresh(sview->win);
1074 break;
1075 case WIN_REFRESH:
1076 swin_wrefresh(sview->win);
1077 break;
1078 }
1079
1080 sbfree(sel_highlight_attrs);
1081 sbfree(exe_highlight_attrs);
1082
1083 return 0;
1084 }
1085
source_move(struct sviewer * sview,SWINDOW * win)1086 void source_move(struct sviewer *sview, SWINDOW *win)
1087 {
1088 swin_delwin(sview->win);
1089 sview->win = win;
1090 }
1091
clamp_line(struct sviewer * sview,int line)1092 static int clamp_line(struct sviewer *sview, int line)
1093 {
1094 if (line < 0)
1095 line = 0;
1096 if (line >= sbcount(sview->cur->file_buf.lines))
1097 line = sbcount(sview->cur->file_buf.lines) - 1;
1098
1099 return line;
1100 }
1101
source_vscroll(struct sviewer * sview,int offset)1102 void source_vscroll(struct sviewer *sview, int offset)
1103 {
1104 if (sview->cur) {
1105 sview->cur->sel_line = clamp_line(sview, sview->cur->sel_line + offset);
1106 sview->cur->sel_rline = sview->cur->sel_line;
1107 }
1108 }
1109
source_hscroll(struct sviewer * sview,int offset)1110 void source_hscroll(struct sviewer *sview, int offset)
1111 {
1112 int lwidth;
1113 int max_width;
1114 int width, height;
1115
1116 if (sview->cur) {
1117 height = swin_getmaxy(sview->win);
1118 width = swin_getmaxx(sview->win);
1119
1120 lwidth = log10_uint(sbcount(sview->cur->file_buf.lines)) + 1;
1121 max_width = sview->cur->file_buf.max_width - width + lwidth + 6;
1122
1123 sview->cur->sel_col += offset;
1124 if (sview->cur->sel_col > max_width)
1125 sview->cur->sel_col = max_width;
1126 if (sview->cur->sel_col < 0)
1127 sview->cur->sel_col = 0;
1128 }
1129 }
1130
source_set_sel_line(struct sviewer * sview,int line)1131 void source_set_sel_line(struct sviewer *sview, int line)
1132 {
1133 if (sview->cur) {
1134 if (line == -1) {
1135 sview->cur->sel_line = sbcount(sview->cur->file_buf.lines) - 1;
1136 } else {
1137 /* Set line (note correction for 0-based line counting) */
1138 sview->cur->sel_line = clamp_line(sview, line - 1);
1139 }
1140
1141 sview->cur->sel_rline = sview->cur->sel_line;
1142 }
1143 }
1144
source_get_asmnode(struct sviewer * sview,uint64_t addr,int * line)1145 static struct list_node *source_get_asmnode(struct sviewer *sview,
1146 uint64_t addr, int *line)
1147 {
1148 struct list_node *node = NULL;
1149
1150 if (addr)
1151 {
1152 /* Search for a node which contains this address */
1153 for (node = sview->list_head; node; node = node->next)
1154 {
1155 if (addr >= node->addr_start && addr <= node->addr_end)
1156 break;
1157 }
1158 }
1159
1160 if (node && line)
1161 {
1162 int i;
1163
1164 for (i = 0; i < sbcount(node->file_buf.addrs); i++)
1165 {
1166 if (node->file_buf.addrs[i] == addr)
1167 {
1168 *line = i;
1169 break;
1170 }
1171 }
1172 }
1173
1174 return node;
1175 }
1176
source_set_exec_line(struct sviewer * sview,const char * path,int sel_line,int exe_line)1177 int source_set_exec_line(struct sviewer *sview, const char *path, int sel_line, int exe_line)
1178 {
1179 if (path) {
1180 /* If they passed us a path, try to locate that node */
1181 sview->cur = source_get_node(sview, path);
1182
1183 /* Not found.... */
1184 if (!sview->cur) {
1185 /* Check that the file exists */
1186 if (!fs_verify_file_exists(path))
1187 return 5;
1188
1189 /* Add a new node for this file */
1190 sview->cur = source_add(sview, path);
1191 }
1192 }
1193
1194 /* Buffer the file if it's not already */
1195 if (load_file(sview->cur))
1196 return 4;
1197
1198 /* Update line, if set */
1199 if (sel_line > 0)
1200 sview->cur->sel_line = clamp_line(sview, sel_line - 1);
1201
1202 /* Set executing line if passed a valid value */
1203 if (exe_line == -1) {
1204 sview->cur->exe_line = -1;
1205 } else if (exe_line > 0) {
1206 sview->cur->exe_line = clamp_line(sview, exe_line - 1);
1207 }
1208
1209 return 0;
1210 }
1211
source_set_exec_addr(struct sviewer * sview,uint64_t addr)1212 int source_set_exec_addr(struct sviewer *sview, uint64_t addr)
1213 {
1214 int line = -1;
1215
1216 if (!addr)
1217 addr = sview->addr_frame;
1218
1219 /* Search for a node which contains this address */
1220 sview->cur = source_get_asmnode(sview, addr, &line);
1221 if (!sview->cur)
1222 return -1;
1223
1224 sview->cur->sel_line = clamp_line(sview, line);
1225 sview->cur->exe_line = clamp_line(sview, line);
1226 return 0;
1227 }
1228
source_free(struct sviewer * sview)1229 void source_free(struct sviewer *sview)
1230 {
1231 /* Free all file buffers */
1232 while (sview->list_head)
1233 source_del(sview, sview->list_head->path);
1234
1235 hl_regex_free(&sview->hlregex);
1236 sview->hlregex = NULL;
1237 hl_regex_free(&sview->last_hlregex);
1238 sview->last_hlregex = NULL;
1239
1240 swin_delwin(sview->win);
1241 sview->win = NULL;
1242
1243 free(sview);
1244 }
1245
source_search_regex_init(struct sviewer * sview)1246 void source_search_regex_init(struct sviewer *sview)
1247 {
1248 if (!sview || !sview->cur)
1249 return;
1250
1251 /* Start searching at the beginning of the selected line */
1252 sview->cur->sel_rline = sview->cur->sel_line;
1253 }
1254
wrap_line(struct list_node * node,int line)1255 static int wrap_line(struct list_node *node, int line)
1256 {
1257 int count = sbcount(node->file_buf.lines);
1258
1259 if (line < 0)
1260 line = count - 1;
1261 else if (line >= count)
1262 line = 0;
1263
1264 return line;
1265 }
1266
source_search_regex(struct sviewer * sview,const char * regex,int opt,int direction,int icase)1267 int source_search_regex(struct sviewer *sview,
1268 const char *regex, int opt, int direction, int icase)
1269 {
1270 struct list_node *node = sview ? sview->cur : NULL;
1271
1272 if (!node)
1273 return -1;
1274
1275 if (regex && *regex) {
1276 int line;
1277 int line_end;
1278 int line_inc = direction ? +1 : -1;
1279 int line_start = node->sel_rline;
1280
1281 line = wrap_line(node, line_start + line_inc);
1282
1283 if (cgdbrc_get_int(CGDBRC_WRAPSCAN))
1284 {
1285 // Wrapping is on so stop at the line we started on.
1286 line_end = line_start;
1287 }
1288 else
1289 {
1290 // No wrapping. Stop at line 0 if searching down and last line
1291 // if searching up.
1292 line_end = direction ? 0 : sbcount(node->file_buf.lines) - 1;
1293 }
1294
1295 for(;;) {
1296 int ret;
1297 int start, end;
1298 char *line_str = node->file_buf.lines[line].line;
1299
1300 ret = hl_regex_search(&sview->hlregex, line_str, regex, icase, &start, &end);
1301 if (ret > 0) {
1302 /* Got a match */
1303 node->sel_line = line;
1304
1305 /* Finalized match - move to this location */
1306 if (opt == 2) {
1307 node->sel_rline = line;
1308
1309 hl_regex_free(&sview->last_hlregex);
1310 sview->last_hlregex = sview->hlregex;
1311 sview->hlregex = 0;
1312 }
1313 return 1;
1314 }
1315
1316 line = wrap_line(node, line + line_inc);
1317 if (line == line_end)
1318 break;
1319 }
1320 }
1321
1322 /* Nothing found - go back to original line */
1323 node->sel_line = node->sel_rline;
1324 return 0;
1325 }
1326
source_clear_breaks(struct sviewer * sview)1327 static void source_clear_breaks(struct sviewer *sview)
1328 {
1329 struct list_node *node;
1330
1331 for (node = sview->list_head; node != NULL; node = node->next)
1332 {
1333 int i;
1334 for (i = 0; i < sbcount(node->lflags); i++)
1335 node->lflags[i].breakpt = 0;
1336 }
1337 }
1338
source_set_breakpoints(struct sviewer * sview,struct tgdb_breakpoint * breakpoints)1339 void source_set_breakpoints(struct sviewer *sview,
1340 struct tgdb_breakpoint *breakpoints)
1341 {
1342 int i;
1343 struct list_node *node;
1344
1345 source_clear_breaks(sview);
1346
1347 // Loop over each breakpoint and let the source view and the
1348 // disassembly view know about them. This way if you set a breakpoint
1349 // in one mode, then switch modes, the other mode will know about
1350 // it as well.
1351 for (i = 0; i < sbcount(breakpoints); i++) {
1352 if (breakpoints[i].path) {
1353 node = source_get_node(sview, breakpoints[i].path);
1354 if (!load_file(node)) {
1355 int line = breakpoints[i].line;
1356 int enabled = breakpoints[i].enabled;
1357 if (line > 0 && line <= sbcount(node->lflags)) {
1358 node->lflags[line - 1].breakpt = enabled ? 1 : 2;
1359 }
1360 }
1361 }
1362 if (breakpoints[i].addr) {
1363 int line = 0;
1364 node = source_get_asmnode(sview, breakpoints[i].addr, &line);
1365 if (node) {
1366 node->lflags[line].breakpt = breakpoints[i].enabled ? 1 : 2;
1367 }
1368 }
1369 }
1370 }
1371
source_reload(struct sviewer * sview,const char * path,int force)1372 int source_reload(struct sviewer *sview, const char *path, int force)
1373 {
1374 time_t timestamp;
1375 struct list_node *cur;
1376 struct list_node *prev = NULL;
1377 int auto_source_reload = cgdbrc_get_int(CGDBRC_AUTOSOURCERELOAD);
1378
1379 if (!path)
1380 return -1;
1381
1382 if (get_timestamp(path, ×tamp) == -1)
1383 return -1;
1384
1385 /* Find the target node */
1386 for (cur = sview->list_head; cur != NULL; cur = cur->next) {
1387 if (strcmp(path, cur->path) == 0)
1388 break;
1389 prev = cur;
1390 }
1391
1392 if (cur == NULL)
1393 return 1; /* Node not found */
1394
1395 /* If the file timestamp or tab size changed, reload the file */
1396 int dirty = cur->last_modification < timestamp;
1397 dirty |= cgdbrc_get_int(CGDBRC_TABSTOP) != cur->file_buf.tabstop;
1398
1399 if ((auto_source_reload || force) && dirty) {
1400
1401 if (release_file_memory(cur) == -1)
1402 return -1;
1403
1404 if (load_file(cur))
1405 return -1;
1406 }
1407
1408 return 0;
1409 }
1410