/* radare2 - LGPL - Copyright 2008-2015 - pancake */ #include #include #include #include #include #include #if __UNIX__ || __CYGWIN__ #include #endif R_LIB_VERSION (r_cons); static RCons r_cons_instance; #define I r_cons_instance static void break_signal(int sig) { I.breaked = R_TRUE; r_print_set_interrupted (I.breaked); if (I.event_interrupt) I.event_interrupt (I.data); } static inline void r_cons_write (const char *buf, int len) { #if __WINDOWS__ && !__CYGWIN__ if (I.fdout == 1) { r_cons_w32_print ((const ut8*)buf, len, 0); } else { (void)write (I.fdout, buf, len); } #else if (write (I.fdout, buf, len) == -1) { //eprintf ("r_cons_write: write error\n"); //exit (1); } #endif } R_API char *r_cons_color_random(int bg) { int r, g, b; if (I.truecolor>0) { char out[32]; r = r_num_rand (0xff); g = r_num_rand (0xff); b = r_num_rand (0xff); r_cons_rgb_str (out, r, g, b, bg); return strdup (out); } // random ansi r = r_num_rand (16); switch (r) { case 0: return strdup (Color_RED); case 1: return strdup (Color_BRED); case 2: return strdup (Color_WHITE); case 3: return strdup (Color_BWHITE); case 4: return strdup (Color_GREEN); case 5: return strdup (Color_BGREEN); case 6: return strdup (Color_MAGENTA); case 7: return strdup (Color_BMAGENTA); case 8: return strdup (Color_YELLOW); case 9: return strdup (Color_BYELLOW); case 10: return strdup (Color_CYAN); case 11: return strdup (Color_BCYAN); case 12: return strdup (Color_BLUE); case 13: return strdup (Color_BBLUE); case 14: return strdup (Color_GRAY); case 15: return strdup (Color_BGRAY); } return strdup (Color_RESET); } R_API void r_cons_color (int fg, int r, int g, int b) { int k; r = R_DIM (r, 0, 255); g = R_DIM (g, 0, 255); b = R_DIM (b, 0, 255); if (r == g && g == b) { // b&w k = 232 + (int)(((r+g+b)/3)/10.3); } else { r = (int)(r/42.6); g = (int)(g/42.6); b = (int)(b/42.6); k = 16 + (r*36) + (g*6) + b; } r_cons_printf ("\x1b[%d;5;%dm", fg? 48: 38, k); } R_API void r_cons_strcat_justify (const char *str, int j, char c) { int i, o, len; for (o=i=len=0; str[i]; i++, len++) { if (str[i]=='\n') { r_cons_memset (' ', j); if (c) { r_cons_memset (c, 1); r_cons_memset (' ', 1); } r_cons_memcat (str+o, len); if (str[o+len] == '\n') r_cons_newline (); o = i+1; len = 0; } } if (len>1) r_cons_memcat (str+o, len); } R_API RCons *r_cons_singleton () { return &I; } R_API void r_cons_break(void (*cb)(void *u), void *user) { I.breaked = R_FALSE; I.event_interrupt = cb; I.data = user; #if __UNIX__ || __CYGWIN__ signal (SIGINT, break_signal); #endif // TODO: add support for w32 ^C } R_API int r_cons_is_breaked() { return I.breaked; } R_API void r_cons_break_end() { I.breaked = R_FALSE; r_print_set_interrupted (I.breaked); #if __UNIX__ || __CYGWIN__ signal (SIGINT, SIG_IGN); #endif } #if __WINDOWS__ && !__CYGWIN__ static HANDLE h; static BOOL __w32_control(DWORD type) { if (type == CTRL_C_EVENT) { break_signal (2); // SIGINT eprintf("{ctrl+c} pressed.\n"); return R_TRUE; } return R_FALSE; } #elif __UNIX__ || __CYGWIN__ static void resize (int sig) { if (I.event_resize) I.event_resize (I.event_data); } #endif // http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt R_API int r_cons_enable_mouse (const int enable) { #if __UNIX__ || __CYGWIN__ // TODO: this sequence must be flushed, so maybe we should not use the rcons buffer api int enabled = I.mouse; if ((I.mouse = enable)) { // r_cons_memcat ("\x1b[?1001s", 8); const char *code = "\x1b[?1001s" "\x1b[?1000h"; write (2, code, strlen (code)); } else { //r_cons_memcat ("\x1b[?1001r", 8); const char *code = "\x1b[?1001r" "\x1b[?1000l"; write (2, code, strlen (code)); } return enabled; #else return R_FALSE; #endif } static void r_cons_pal_null (){ int i; RCons *cons = r_cons_singleton (); for (i = 0; i< R_CONS_PALETTE_LIST_SIZE; i++){ cons->pal.list[i] = NULL; } } R_API RCons *r_cons_new () { I.refcnt++; if (I.refcnt != 1) return &I; I.line = r_line_new (); I.highlight = NULL; I.event_interrupt = NULL; I.is_wine = -1; I.fps = 0; I.blankline = R_TRUE; I.teefile = NULL; I.fix_columns = 0; I.fix_rows = 0; I.mouse_event = 0; I.force_rows = 0; I.force_columns = 0; I.event_resize = NULL; I.data = NULL; I.event_data = NULL; I.is_interactive = R_TRUE; I.noflush = R_FALSE; I.fdin = stdin; I.fdout = 1; I.breaked = R_FALSE; //I.lines = 0; I.buffer = NULL; I.buffer_sz = 0; I.buffer_len = 0; r_cons_get_size (NULL); I.num = NULL; I.null = 0; #if EMSCRIPTEN /* do nothing here :? */ #elif __UNIX__ || __CYGWIN__ tcgetattr (0, &I.term_buf); memcpy (&I.term_raw, &I.term_buf, sizeof (I.term_raw)); I.term_raw.c_iflag &= ~(BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); I.term_raw.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); I.term_raw.c_cflag &= ~(CSIZE|PARENB); I.term_raw.c_cflag |= CS8; I.term_raw.c_cc[VMIN] = 1; // Solaris stuff hehe signal (SIGWINCH, resize); #elif __WINDOWS__ h = GetStdHandle (STD_INPUT_HANDLE); GetConsoleMode (h, (PDWORD) &I.term_buf); I.term_raw = 0; if (!SetConsoleCtrlHandler ((PHANDLER_ROUTINE)__w32_control, TRUE)) eprintf ("r_cons: Cannot set control console handler\n"); #endif I.pager = NULL; /* no pager by default */ I.truecolor = 0; I.mouse = 0; r_cons_pal_null (); r_cons_pal_init (NULL); r_cons_rgb_init (); r_cons_reset (); return &I; } R_API RCons *r_cons_free () { I.refcnt--; if (I.refcnt != 0) return NULL; r_cons_pal_free (); if (I.line) { r_line_free (); I.line = NULL; } if (I.buffer) { free (I.buffer); I.buffer = NULL; } return NULL; } #define MOAR 4096*8 static void palloc(int moar) { if (I.buffer == NULL) { I.buffer_sz = moar+MOAR; I.buffer = (char *)malloc (I.buffer_sz); I.buffer[0] = '\0'; } else if (moar + I.buffer_len >= I.buffer_sz) { char *new_buffer; int old_buffer_sz = I.buffer_sz; I.buffer_sz += moar+MOAR; new_buffer = (char *)realloc (I.buffer, I.buffer_sz); if (new_buffer) I.buffer = new_buffer; else I.buffer_sz = old_buffer_sz; } } R_API int r_cons_eof() { return feof (I.fdin); } R_API void r_cons_gotoxy(int x, int y) { #if 0 #if __WINDOWS__ static HANDLE hStdout = NULL; COORD coord; coord.X = x; coord.Y = y; if (!hStdout) hStdout = GetStdHandle (STD_OUTPUT_HANDLE); SetConsoleCursorPosition (hStdout, coord); #else r_cons_printf ("\x1b[%d;%dH", y, x); #endif #endif r_cons_printf ("\x1b[%d;%dH", y, x); } R_API void r_cons_print_clear() { // xlr8! r_cons_write ("\x1b[0;0H", 6); r_cons_write ("\x1b[0m", 4); //r_cons_memcat ("\x1b[2J", 4); } R_API void r_cons_fill_line() { char *p, white[1024]; int cols = I.columns-1; if (cols<1) return; p = (cols>=sizeof (white))? malloc (cols+1): white; memset (p, ' ', cols); p[cols] = 0; r_cons_strcat (p); if (white != p) free (p); } R_API void r_cons_clear_line(int std_err) { #if __WINDOWS__ char white[1024]; memset (&white, ' ', sizeof (white)); if (I.columns0||I.grep.tokenfrom!=0||I.grep.tokento!=ST32_MAX||I.grep.line!=-1 || I.grep.less || I.grep.json) r_cons_grepbuf (I.buffer, I.buffer_len); /* html */ /* TODO */ } static char *backup = NULL; static int backup_len = 0; static int backup_size = 0; R_API void r_cons_push() { if (!backup) { if (I.buffer_len<1) I.buffer_len = 1; backup = I.buffer; //malloc (I.buffer_len); backup_len = I.buffer_len; backup_size = I.buffer_sz; I.buffer = malloc (I.buffer_sz); memcpy (I.buffer, backup, I.buffer_len); I.buffer_len = 0; } } R_API void r_cons_pop() { if (backup) { free (I.buffer); I.buffer = backup; I.buffer_len = backup_len; I.buffer_sz = backup_size; backup = NULL; } } R_API void r_cons_flush() { const char *tee = I.teefile; if (I.noflush) return; if (I.null) { r_cons_reset (); return; } r_cons_filter (); if (I.is_interactive) { /* Use a pager if the output doesn't fit on the terminal window. */ if (I.pager && *I.pager && I.buffer_len > 0 && r_str_char_count (I.buffer, '\n') >= I.rows) { I.buffer[I.buffer_len-1] = 0; r_sys_cmd_str_full (I.pager, I.buffer, NULL, NULL, NULL); r_cons_reset (); } else if (I.buffer_len > CONS_MAX_USER) { #define COUNT_LINES 1 #if COUNT_LINES int i, lines = 0; for (i=0; I.buffer[i]; i++) { if (I.buffer[i]=='\n') lines ++; } if (lines>0 && !r_cons_yesno ('n',"Do you want to print %d lines? (y/N)", lines)) { r_cons_reset (); return; } #else char buf[64]; char *buflen = r_num_units (buf, I.buffer_len); if (buflen && !r_cons_yesno ('n',"Do you want to print %s chars? (y/N)", buflen)) { r_cons_reset (); return; } #endif // fix | more | less problem r_cons_set_raw (1); } } if (tee && *tee) { FILE *d = r_sandbox_fopen (tee, "a+"); if (d != NULL) { if (I.buffer_len != fwrite (I.buffer, 1, I.buffer_len, d)) eprintf ("r_cons_flush: fwrite: error (%s)\n", tee); fclose (d); } else eprintf ("Cannot write on '%s'\n", tee); } r_cons_highlight (I.highlight); // is_html must be a filter, not a write endpoint if (I.is_html) r_cons_html_print (I.buffer); else r_cons_write (I.buffer, I.buffer_len); r_cons_reset (); } R_API void r_cons_visual_flush() { if (I.noflush) return; r_cons_highlight (I.highlight); if (!I.null) { /* TODO: this ifdef must go in the function body */ #if __WINDOWS__ && !__CYGWIN__ r_cons_w32_print ((const ut8*)I.buffer, I.buffer_len, 1); #else r_cons_visual_write (I.buffer); #endif } r_cons_reset (); if (I.fps) { int w = r_cons_get_size (NULL); int fps = 0; static ut64 prev = 0LL; //r_sys_now (); fps = 0; if (prev) { ut64 now = r_sys_now (); ut64 diff = now-prev; fps = (diff<1000000)? (1000000/diff): 0; prev = now; } else prev = r_sys_now (); eprintf ("\x1b[0;%dH[%d FPS] \n", w-10, fps); } } R_API void r_cons_visual_write (char *buffer) { char white[1024]; int cols = I.columns; int alen, plen, lines = I.rows; const char *endptr; char *nl, *ptr = buffer, *pptr; if (I.null) return; memset (&white, ' ', sizeof (white)); while ((nl = strchr (ptr, '\n'))) { int len = ((int)(size_t)(nl-ptr))+1; *nl = 0; { int utf8len = r_str_len_utf8 (ptr); int ansilen = r_str_ansi_len (ptr); int diff = len-utf8len; if (diff) diff--; alen = ansilen - diff; } *nl = '\n'; pptr = ptr > buffer ? ptr - 1 : ptr; plen = ptr > buffer ? len : len - 1; if (alen > cols) { endptr = r_str_ansi_chrn (ptr, cols); endptr++; len = (endptr-ptr); plen = ptr > buffer ? len : len - 1; if (lines > 0) r_cons_write (pptr, plen); } else { if (lines > 0) { int w = cols - alen; r_cons_write (pptr, plen); if (I.blankline && w>0) { if (w>sizeof (white)-1) w = sizeof (white)-1; r_cons_write (white, w); } } // TRICK to empty columns.. maybe buggy in w32 if (r_mem_mem ((const ut8*)ptr, len, (const ut8*)"\x1b[0;0H", 6)) { lines = I.rows; r_cons_write (pptr, plen); } } lines--; // do not use last line ptr = nl+1; } /* fill the rest of screen */ if (lines>0) { if (cols>sizeof (white)) cols = sizeof (white); while (--lines > 0) r_cons_write (white, cols); } } R_API void r_cons_printf(const char *format, ...) { size_t size, written; va_list ap; if (I.null) return; if (strchr (format, '%')) { palloc (MOAR + strlen (format)*20); size = I.buffer_sz-I.buffer_len-1; /* remaining space in I.buffer */ va_start (ap, format); written = vsnprintf (I.buffer+I.buffer_len, size, format, ap); va_end (ap); if (written>=size) { /* not all bytes were written */ palloc (written); va_start (ap, format); written = vsnprintf (I.buffer+I.buffer_len, written, format, ap); va_end (ap); } I.buffer_len += written; } else r_cons_strcat (format); } R_API int r_cons_get_column() { char *line = strrchr (I.buffer, '\n'); if (!line) line = I.buffer; I.buffer[I.buffer_len] = 0; return r_str_ansi_len (line); } /* final entrypoint for adding stuff in the buffer screen */ R_API void r_cons_memcat(const char *str, int len) { if (len<0 || (I.buffer_len + len)<0) { return; } if (I.echo) { write (2, str, len); } if (str && len>0 && !I.null) { palloc (len+1); memcpy (I.buffer+I.buffer_len, str, len); I.buffer_len += len; I.buffer[I.buffer_len] = 0; } } R_API void r_cons_memset(char ch, int len) { if (I.null) return; if (len>0) { palloc (len+1); memset (I.buffer+I.buffer_len, ch, len+1); I.buffer_len += len; } } R_API void r_cons_strcat(const char *str) { int len; if (!str||I.null) return; len = strlen (str); if (len>0) r_cons_memcat (str, len); } R_API void r_cons_newline() { if (!I.null) r_cons_strcat ("\n"); //if (I.is_html) r_cons_strcat ("
\n"); //else r_cons_strcat ("\n"); } /* return the aproximated x,y of cursor before flushing */ R_API int r_cons_get_cursor(int *rows) { int i, col = 0; int row = 0; // TODO: we need to handle GOTOXY and CLRSCR ansi escape code too for (i=0; iis_interactive; r_cons_singleton ()->is_interactive = x; } R_API void r_cons_set_last_interactive() { r_cons_singleton ()->is_interactive = lasti; } R_API void r_cons_set_title(const char *str) { r_cons_printf ("\x1b]0;%s\007", str); } R_API void r_cons_zero() { write (1, "", 1); } R_API void r_cons_highlight (const char *word) { char *rword, *res, *clean; char *inv[2] = {R_CONS_INVERT(R_TRUE, R_TRUE), R_CONS_INVERT(R_FALSE, R_TRUE)}; int linv[2] = {strlen(inv[0]), strlen(inv[1])}; int l, *cpos; if (word && *word && I.buffer) { int word_len = strlen (word); char *orig; clean = I.buffer; l = r_str_ansi_filter (clean, &orig, &cpos, 0); I.buffer = orig; if (I.highlight) { if (strcmp (word, I.highlight)) { free (I.highlight); I.highlight = strdup (word); } } else { I.highlight = strdup (word); } rword = malloc (word_len + linv[0] + linv[1] + 1); strcpy (rword, inv[0]); strcpy (rword + linv[0], word); strcpy (rword + linv[0] + word_len, inv[1]); res = r_str_replace_thunked (I.buffer, clean, cpos, l, word, rword, 1); if (res) { I.buffer = res; I.buffer_len = I.buffer_sz = strlen (res); } free (rword); free (clean); free (cpos); /* don't free orig - it's assigned * to I.buffer and possibly realloc'd */ } else { free (I.highlight); I.highlight = NULL; } } R_API char *r_cons_lastline () { char *b = I.buffer+I.buffer_len; while (b >I.buffer) { if (*b=='\n') { b++; break; } b--; } return b; } /* swap color from foreground to background, returned value must be freed */ R_API char *r_cons_swap_ground(const char *col) { if (!strncmp (col, "\x1b[48;5;", 7)) { /* rgb background */ return r_str_newf ("\x1b[38;5;%s", col+7); } else if (!strncmp (col, "\x1b[38;5;", 7)) { /* rgb foreground */ return r_str_newf ("\x1b[48;5;%s", col+7); } else if (!strncmp (col, "\x1b[4", 3)) { /* is background */ return r_str_newf ("\x1b[3%s", col+3); } else if (!strncmp (col, "\x1b[3", 3)) { /* is foreground */ return r_str_newf ("\x1b[4%s", col+3); } return strdup (col); }