#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "my_pty.h" #include "utils.h" #include "term.h" #include "help.h" #include "mem.h" #include "ui.h" #include "misc.h" #include "globals.h" int scrollback_search_to_new_window(buffer *pbuf, char *org_title, char *find_str, mybool_t case_insensitive); int find_string(buffer *pbuf, char *find, int offset, char direction, mybool_t case_insensitive) { int loop, index = -1, rc; regex_t regex; /* compile the searchstring (which can be a regular expression) */ if ((rc = regcomp(®ex, find, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, ®ex); return -1; /* failed -> not found */ } if (direction == 1) { for(loop=offset; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline == NULL) continue; if (regexec(®ex, (pbuf -> be)[loop].Bline, 0, NULL, 0) == 0) { index = loop; break; } } } else if (direction == (char)-1) { for(loop=offset; loop>=0; loop--) { if ((pbuf -> be)[loop].Bline == NULL) continue; if (regexec(®ex, (pbuf -> be)[loop].Bline, 0, NULL, 0) == 0) { index = loop; break; } } } regfree(®ex); return index; } void scrollback_find_popup(char **find_str, mybool_t *pcase_insensitive) { char *dummy; NEWWIN *mywin = create_popup(5, 40); win_header(mywin, "Find"); dummy = edit_string(mywin, 3, 2, 40, 80, 0, reuse_searchstring?*find_str:NULL, HELP_SCROLLBACK_EDIT_SEARCH_STRING, -1, &search_h, pcase_insensitive); myfree(*find_str); *find_str = dummy; delete_popup(mywin); } void scrollback_savefile(buffer *pbuf) { char *file = NULL; NEWWIN *mywin = create_popup(8, 40); win_header(mywin, "Save buffer to file"); mvwprintw(mywin -> win, 4, 2, "Select file"); file = edit_string(mywin, 5, 2, 40, find_path_max(), 0, NULL, HELP_SCROLLBACK_SAVEFILE_ENTER_FILENAME, -1, &cmdfile_h, NULL); if (file) { FILE *fh = fopen(file, "w"); if (fh) { int loop; for(loop=0; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline) { char display; char *error; int dummy = -1; regmatch_t *pmatch = NULL; /* check filter */ (void)check_filter((pbuf -> be)[loop].pi, (pbuf -> be)[loop].Bline, &pmatch, &error, &dummy, 0, &display); if (error) { fprintf(fh, "%s\n", error); myfree(error); } if (display) { fprintf(fh, "%s\n", USE_IF_SET((pbuf -> be)[loop].Bline, "")); } if (pmatch) myfree(pmatch); } } fclose(fh); } else { error_popup("Save scrollback buffer", -1, "Cannot write to file, reason: %s", strerror(errno)); } } delete_popup(mywin); } int get_lines_needed(char *string, int terminal_width) { if (string) return (strlen(string) + terminal_width - 1) / terminal_width; else return 1; } void scrollback_displayline(int window_nr, NEWWIN *win, buffer *pbuf, int buffer_offset, int terminal_offset, int offset_in_line, mybool_t force_to_winwidth, char show_winnr) { char *cur_line = (pbuf -> be)[buffer_offset].Bline; wmove(win -> win, terminal_offset, 0); if (cur_line) { proginfo *cur_line_meta = (pbuf -> be)[buffer_offset].pi; double ts = (pbuf -> be)[buffer_offset].ts; char old_color_settings = 0; if (scrollback_no_colors && cur_line_meta != NULL) { old_color_settings = cur_line_meta -> cdef.colorize; cur_line_meta -> cdef.colorize = 0; } if (IS_MARKERLINE(cur_line_meta)) { color_print(window_nr, win, cur_line_meta, cur_line, NULL, -1, MY_FALSE, 0, 0, ts, show_winnr); } else /* just a buffered line */ { char *error = NULL; regmatch_t *pmatch = NULL; int matching_regex = -1; char display; (void)check_filter(cur_line_meta, cur_line, &pmatch, &error, &matching_regex, 0, &display); if (error) { color_print(window_nr, win, cur_line_meta, error, NULL, -1, MY_FALSE, 0, 0, ts, show_winnr); myfree(error); } if (display) { if (offset_in_line) { int line_len = strlen(cur_line); int new_size = 0; if (offset_in_line < line_len) new_size = min(win -> width, line_len - offset_in_line); color_print(window_nr, win, cur_line_meta, cur_line, pmatch, matching_regex, force_to_winwidth, offset_in_line, offset_in_line + new_size, ts, show_winnr); } else { color_print(window_nr, win, cur_line_meta, cur_line, pmatch, matching_regex, force_to_winwidth, 0, 0, ts, show_winnr); } } myfree(pmatch); } if (scrollback_no_colors && cur_line_meta != NULL) { cur_line_meta -> cdef.colorize = old_color_settings; } } else /* an empty line */ { /* do nothing */ } } int scrollback_do(int window_nr, buffer *pbuf, int *winnrs, char *header) { int rc = 0; char *find = NULL; NEWWIN *mywin1, *mywin2; int nlines = max_y - 6, ncols = max_x - 6; int offset = max(0, pbuf -> curpos - nlines); // FIXME: aantal regels laten afhangen van lengte char redraw = 2; int line_offset = 0; char show_winnr = default_sb_showwinnr; mybool_t case_insensitive = re_case_insensitive; if (global_highlight_str) { find = mystrdup(global_highlight_str, __FILE__, __PRETTY_FUNCTION__, __LINE__); } mywin1 = create_popup(max_y - 4, max_x - 4); mywin2 = create_popup(nlines, ncols); scrollok(mywin2 -> win, FALSE); /* supposed to always return OK, according to the manpage */ for(;;) { int c, uc; if (redraw == 2) { int index = 0; int lines_used = 0; ui_inverse_on(mywin1); mvwprintw(mywin1 -> win, nlines + 1, 1, "%s - %d buffered lines", shorten_filename(header, max(24, ncols - 24)), pbuf -> curpos); ui_inverse_off(mywin1); if (!no_linewrap) ui_inverse_on(mywin1); mvwprintw(mywin1 -> win, nlines + 1, ncols - 8, "LINEWRAP"); if (!no_linewrap) ui_inverse_off(mywin1); werase(mywin2 -> win); if (!no_linewrap && line_offset > 0) { int temp_line_offset = line_offset; int n_chars_to_display_left = strlen((pbuf -> be)[offset].Bline) - temp_line_offset; while(lines_used < nlines && n_chars_to_display_left > 0) { scrollback_displayline(winnrs?winnrs[offset]:window_nr, mywin2, pbuf, offset, lines_used, temp_line_offset, 1, show_winnr); temp_line_offset += ncols; n_chars_to_display_left -= ncols; lines_used++; } index++; } for(;(offset + index) < pbuf -> curpos && lines_used < nlines;) { int lines_needed = get_lines_needed((pbuf -> be)[offset + index].Bline, ncols); if (no_linewrap || lines_needed == 1) { scrollback_displayline(winnrs?winnrs[offset + index]:window_nr, mywin2, pbuf, offset + index, lines_used, no_linewrap?line_offset:0, no_linewrap, show_winnr); lines_used++; } else { int cur_line_offset = 0; while(lines_used < nlines && lines_needed > 0) { scrollback_displayline(winnrs?winnrs[offset + index]:window_nr, mywin2, pbuf, offset + index, lines_used, cur_line_offset, 1, show_winnr); cur_line_offset += ncols; lines_used++; lines_needed--; } } index++; } redraw = 1; } if (redraw == 1) { mydoupdate(); redraw = 0; } c = wait_for_keypress(HELP_SCROLLBACK_HELP, 0, NULL, 1); uc = toupper(c); if (c == 'q' || uc == 'X' || c == abort_key || c == KEY_CLOSE || c == KEY_EXIT) { break; } else if (c == 'Q' || c == -1) /* Q: close whole stack of scrollbackwindows, -1: something got closed */ { rc = -1; break; } else if (c == 20 && winnrs != NULL) /* ^t */ { show_winnr = 1 - show_winnr; redraw = 2; } else if (c == 'Y') { no_linewrap = !no_linewrap; redraw = 2; line_offset = 0; } else if (c == 't') { statistics_menu(); } else if ((c == KEY_LEFT || c == KEY_BACKSPACE) && no_linewrap) { if (line_offset > 0) line_offset--; redraw = 2; } else if (c == KEY_SLEFT && no_linewrap) { if (line_offset >= (ncols / 2)) line_offset -= (ncols / 2); else line_offset = 0; redraw = 2; } else if (c == KEY_SRIGHT && no_linewrap) { line_offset += (ncols / 2); redraw = 2; } else if (c == KEY_BEG && no_linewrap) { if (line_offset) { line_offset = 0; redraw = 2; } } else if (c == KEY_BTAB) { if (line_offset >= 4) line_offset -= 4; else line_offset = 0; redraw = 2; } else if (c == KEY_RIGHT && no_linewrap) { line_offset++; redraw = 2; } else if ((c == KEY_UP || c == 'y' || c == 25 || /* ^y */ c == 'k' || /* c == 11 || *//* ^k */ c == 16) /* ^p */ && (offset > 0 || (!no_linewrap && line_offset > 0))) { if (no_linewrap) { offset--; } else if (line_offset > 0) { line_offset = max(0, line_offset - ncols); } else { offset--; line_offset = (get_lines_needed((pbuf -> be)[offset].Bline, ncols) - 1) * ncols; } wmove(mywin2 -> win, 0, 0); winsdelln(mywin2 -> win, 1); scrollback_displayline(winnrs?winnrs[offset]:window_nr, mywin2, pbuf, offset, 0, line_offset, no_linewrap, show_winnr); redraw = 1; } else if ((c == KEY_DOWN || c == 'e' || c == 5 || /* ^e */ c == 'j' || c == 14 || /* ^n */ c == 13 || c == KEY_ENTER) && offset < (pbuf -> curpos - 1)) { if (no_linewrap) { offset++; } else if (strlen((pbuf -> be)[offset].Bline) > (line_offset + ncols)) { line_offset += ncols; } else if (offset < (pbuf -> curpos - 1)) { if (strlen((pbuf -> be)[offset].Bline) > (line_offset + ncols)) line_offset += ncols; else { line_offset = 0; offset++; } } redraw = 2; } else if ((c == KEY_NPAGE || c == 'f' || c == 6 || /* ^f */ c == ('V' - 65 + 1) || /* ^v */ c == ' ' || c == 'z' || c == 'u' || c == ('U' - 65 + 1)) /* ^u */ && offset < (pbuf -> curpos - 1)) { if (no_linewrap) { offset += nlines; if (offset >= pbuf -> curpos) offset = pbuf -> curpos - 1; } else { int n_lines_to_move = nlines; while(n_lines_to_move > 0 && offset < (pbuf -> curpos)) { if (line_offset > 0) { if (line_offset + ncols >= strlen((pbuf -> be)[offset].Bline)) { line_offset = 0; offset++; n_lines_to_move--; } else { line_offset += ncols; n_lines_to_move--; } } else { n_lines_to_move -= get_lines_needed((pbuf -> be)[offset].Bline, ncols); offset++; } } if (n_lines_to_move < 0) line_offset = (-n_lines_to_move) * ncols; } redraw = 2; } else if ((c == KEY_PPAGE || c == 'b' || c == 2 || /* ^b */ c == 'w' || c == 'd' || c == 4) /* ^d */ && offset > 0) { if (no_linewrap) { offset -= nlines; if (offset < 0) offset = 0; } else { int n_lines_to_move = nlines; if (line_offset) n_lines_to_move -= line_offset / ncols; while(n_lines_to_move > 0 && offset > 0) { offset--; n_lines_to_move -= get_lines_needed((pbuf -> be)[offset].Bline, ncols); if (n_lines_to_move < 0) { line_offset = (get_lines_needed((pbuf -> be)[offset].Bline, ncols) + n_lines_to_move) * ncols; } } } redraw = 2; } else if ((c == KEY_HOME || c == 'g' || c == '<' || c == KEY_SBEG) && offset > 0) { line_offset = offset = 0; redraw = 2; } else if ((c == KEY_END || c == 'G' || c == '>' || c == KEY_SEND) && offset < (pbuf -> curpos - 1)) { offset = pbuf -> curpos - 1; redraw = 2; } else if (uc == 'R' || c == ('R' - 65 + 1) || c == ('L' - 65 + 1) || c == KEY_REFRESH) { redraw = 2; } else if (c == ('K' - 65 + 1) || c == KEY_MARK) { scrollback_find_popup(&find, &case_insensitive); if (find) { int rc; regfree(&global_highlight_re); myfree(global_highlight_str); global_highlight_str = NULL; if ((rc = regcomp(&global_highlight_re, find, (case_insensitive == MY_TRUE?REG_ICASE:0) | REG_EXTENDED))) { regexp_error_popup(rc, &global_highlight_re); myfree(find); } else { global_highlight_str = find; } redraw = 2; /* force redraw */ } } else if (c == 'f' || c == '/' || c == '?' || c == KEY_FIND || c == KEY_SFIND) { char direction = (c == '?' || c == KEY_SFIND) ? -1 : 1; scrollback_find_popup(&find, &case_insensitive); if (find) { if (scrollback_search_new_window) { if (scrollback_search_to_new_window(pbuf, header, find, case_insensitive) == -1) { /* cascaded close */ rc = -1; break; } } else { int new_f_index; redraw = 2; /* force redraw */ regfree(&global_highlight_re); myfree(global_highlight_str); global_highlight_str = NULL; new_f_index = find_string(pbuf, find, 0, direction, case_insensitive); if (new_f_index == -1) { wrong_key(); } else { offset = new_f_index; line_offset = 0; } } } } else if (uc == 'N' || c == KEY_NEXT || c == KEY_PREVIOUS || c == KEY_SNEXT) { if (find != NULL) { char direction = (c == 'n' || c == KEY_NEXT) ? 1 : -1; int start_offset = offset + direction; int new_f_index = find_string(pbuf, find, start_offset, direction, case_insensitive); if (new_f_index == -1) { wrong_key(); } else { redraw = 2; /* force redraw */ offset = new_f_index; line_offset = 0; } } else { wrong_key(); } } else if (c == 's' || c == KEY_SAVE) { scrollback_savefile(pbuf); redraw = 2; /* force redraw */ } else if (c == 'h') { show_help(HELP_SCROLLBACK_HELP); } else if (c == 'c') { toggle_colors(); redraw = 2; /* force redraw */ } else if (c == 'i') { info(); } else if (c == 'T') { statistics_menu(); } else if (c == 20) { toggle_subwindow_nr(); redraw = 2; /* force redraw */ } else { wrong_key(); } } delete_popup(mywin2); delete_popup(mywin1); myfree(find); return rc; } void scrollback(void) { int window = 0; if (nfd > 1) { window = select_window(HELP_SCROLLBACK_SELECT_WINDOW, NULL); } if (window != -1) { if (lb[window].bufferwhat == 0) error_popup("Scrollback", HELP_SCROLLBACK_NO_MARK, "Cannot scrollback: buffering is disabled."); } if (window != -1 && lb[window].bufferwhat != 0) { int header_size = strlen(pi[window].filename) + 4; char *header = (char *)mymalloc(header_size + 1, __FILE__, __PRETTY_FUNCTION__, __LINE__); snprintf(header, header_size, "%02d] %s", window, pi[window].filename); scrollback_do(window, &lb[window], NULL, header); myfree(header); } } void merged_scrollback_with_search(char *search_for, mybool_t case_insensitive) { int lc_size = nfd * sizeof(int); int *last_check = (int *)mymalloc(lc_size, __FILE__, __PRETTY_FUNCTION__, __LINE__); int *winnr = NULL; buffer buf; regex_t reg; int rc; memset(last_check, 0x00, lc_size); memset(&buf, 0x00, sizeof(buf)); /* compile the search string which is supposed to be a valid regular * expression */ if (search_for) { if ((rc=regcomp(®, search_for, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, ®); return; } } /* merge all windows into one */ for(;;) { int loop; double chosen_ts = (double)(((long long int)1) << 62); int chosen_win = -1; int curline; char *string; int checked_all = 0; for(loop=0; loop last_check[loop]) last_check[loop]++; } continue; } if (!IS_MARKERLINE(lb[chosen_win].be[last_check[chosen_win]].pi)) { /*** ADD LINE TO BUFFER ***/ buf.be = (buffered_entry *)myrealloc(buf.be, sizeof(buffered_entry) * (buf.curpos + 1), __FILE__, __PRETTY_FUNCTION__, __LINE__); winnr = (int *)myrealloc(winnr, sizeof(int) * (buf.curpos + 1), __FILE__, __PRETTY_FUNCTION__, __LINE__); curline = buf.curpos++; /* add the logline itself */ string = lb[chosen_win].be[last_check[chosen_win]].Bline; if (string) buf.be[curline].Bline = mystrdup(string, __FILE__, __PRETTY_FUNCTION__, __LINE__); else buf.be[curline].Bline = NULL; /* remember pointer to subwindow (required for setting colors etc.) */ buf.be[curline].pi = lb[chosen_win].be[last_check[chosen_win]].pi; buf.be[curline].ts = lb[chosen_win].be[last_check[chosen_win]].ts; /* remember window nr. */ winnr[curline] = chosen_win; } last_check[chosen_win]++; } if (buf.curpos == 0) error_popup("Search in all windows", -1, "Nothing found."); else { char *header; if (search_for) { char *help = "Searched for: "; int len = strlen(help) + strlen(search_for) + 1; header = mymalloc(len, __FILE__, __PRETTY_FUNCTION__, __LINE__); snprintf(header, len, "%s%s", help, search_for); } else { char *help = "Merge view"; header = mymalloc(strlen(help) + 1, __FILE__, __PRETTY_FUNCTION__, __LINE__); sprintf(header, "%s", help); } scrollback_do(-1, &buf, winnr, header); myfree(header); } delete_be_in_buffer(&buf); myfree(winnr); myfree(last_check); } int scrollback_search_to_new_window(buffer *pbuf, char *org_title, char *find_str, mybool_t case_insensitive) { int loop, rc; regex_t regex; buffer cur_lb; char *new_header; /* compile the searchstring (which can be a regular expression) */ if ((rc = regcomp(®ex, find_str, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, ®ex); return 0; } memset(&cur_lb, 0x00, sizeof(buffer)); for(loop=0; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline == NULL) continue; if (regexec(®ex, (pbuf -> be)[loop].Bline, 0, NULL, 0) == 0) { cur_lb.be = myrealloc(cur_lb.be, (cur_lb.curpos + 1) * sizeof(buffered_entry), __FILE__, __PRETTY_FUNCTION__, __LINE__); cur_lb.be[cur_lb.curpos].Bline = (pbuf -> be)[loop].Bline; cur_lb.be[cur_lb.curpos].pi = (pbuf -> be)[loop].pi; cur_lb.curpos++; } } new_header = (char *)mymalloc(strlen(org_title) + 1 + strlen(find_str) + 1, __FILE__, __PRETTY_FUNCTION__, __LINE__); sprintf(new_header, "%s %s", org_title, find_str); rc = scrollback_do(-1, &cur_lb, NULL, new_header); myfree(new_header); myfree(cur_lb.be); regfree(®ex); return rc; }