From be75b2d9ee0701582aae047c2a717c4a64d6503e Mon Sep 17 00:00:00 2001 From: pancake Date: Tue, 13 Feb 2024 20:39:31 +0100 Subject: [PATCH] Fix #22588 - Support multiple redirections in the same line ##shell * allowed: x > a 2> b * forbidden: x > a > b * broken: x>a * todo: add tests and support nesting and multiple redirects --- libr/cons/cons.c | 11 ++-- libr/cons/cpipe.c | 129 ++++++++++++++++++++++++++++++++++-------- libr/core/cmd.c | 73 ++++++++++++++++++------ libr/include/r_cons.h | 11 +++- 4 files changed, 174 insertions(+), 50 deletions(-) diff --git a/libr/cons/cons.c b/libr/cons/cons.c index 5c6044f2a6e81..dae06abf08400 100644 --- a/libr/cons/cons.c +++ b/libr/cons/cons.c @@ -1,4 +1,4 @@ -/* radare2 - LGPL - Copyright 2008-2023 - pancake, Jody Frankowski */ +/* radare2 - LGPL - Copyright 2008-2024 - pancake, Jody Frankowski */ #include #include @@ -15,7 +15,7 @@ static R_TH_LOCAL RCons *r_cons_instance = NULL; static R_TH_LOCAL ut64 prev = 0LL; //r_time_now_mono (); static R_TH_LOCAL RStrBuf *echodata = NULL; // TODO: move into RConsInstance? maybe nope #define I (r_cons_instance) -#define C (getctx()) +#define C (getctx ()) static inline void cons_input_state_init(InputState *state) { state->readbuffer = NULL; @@ -692,8 +692,7 @@ R_API RCons *r_cons_new(void) { I->teefile = NULL; I->fix_columns = 0; I->fix_rows = 0; - I->backup_fd = -1; - I->backup_fdn = -1; + RVecFdPairs_init (&I->fds); I->mouse_event = 0; I->force_rows = 0; I->force_columns = 0; @@ -771,12 +770,12 @@ R_API RCons *r_cons_free(void) { R_FREE (C->lastOutput); C->lastLength = 0; R_FREE (I->pager); + RVecFdPairs_fini (&I->fds); return NULL; } #define MOAR (4096 * 8) static bool palloc(int moar) { - void *temp; if (moar <= 0) { return false; } @@ -785,7 +784,7 @@ static bool palloc(int moar) { return false; } size_t new_sz = moar + MOAR; - temp = calloc (1, new_sz); + void *temp = calloc (1, new_sz); if (temp) { C->buffer_sz = new_sz; C->buffer = temp; diff --git a/libr/cons/cpipe.c b/libr/cons/cpipe.c index cce795026aa32..d805bc2773882 100644 --- a/libr/cons/cpipe.c +++ b/libr/cons/cpipe.c @@ -7,68 +7,147 @@ #define O_BINARY 0 #endif -#define I (r_cons_singleton ()) +#define HONOR_LAST_REDIRECT 0 +#define USE_HACK 0 -static bool __dupDescriptor(int fd, int fdn) { +#if 0 +static bool mydup(const int fd, const int fdn) { if (fd == fdn) { return false; } #if __wasi__ return false; -#elif R2__WINDOWS__ - I->backup_fd = 2002 - (fd - 2); // windows xp has 2048 as limit fd - return _dup2 (fdn, I->backup_fd) != -1; #else - I->backup_fd = sysconf (_SC_OPEN_MAX) - (fd - 2); // portable getdtablesize() - if (I->backup_fd < 2) { - I->backup_fd = 2002 - (fd - 2); // fallback +# if R2__WINDOWS__ + int newfd = -1; +# else + int newfd = sysconf (_SC_OPEN_MAX) - (fd - 2); // portable getdtablesize() +# endif + if (newfd < 2) { + newfd = 2002 - (fd - 2); // fallback } - return dup2 (fdn, I->backup_fd) != -1; + return dup2 (fd, newfd) != -1; #endif } +#endif -R_API int r_cons_pipe_open(const char *file, int fdn, int append) { +R_API int r_cons_pipe_open(const char *file, int fd_src, int append) { #if __wasi__ return -1; #else - if (fdn < 1) { + if (fd_src < 1) { return -1; } + RCons *ci = r_cons_singleton (); + RConsFdPair *pair; +#if !HONOR_LAST_REDIRECT + // prevent redirecting the same fd twice in the same line + R_VEC_FOREACH (&ci->fds, pair) { + if (fd_src == pair->fd_src) { + R_LOG_WARN ("cannot redirect the same fd twice"); + // do not permit redirecting output to more than one file + return -1; + } + } +#endif char *targetFile = (r_str_startswith (file, "~/") || r_str_startswith (file, "~\\")) ? r_file_home (file + 2): strdup (file); const int fd_flags = O_BINARY | O_RDWR | O_CREAT | (append? O_APPEND: O_TRUNC); - int fd = r_sandbox_open (targetFile, fd_flags, 0644); - if (fd == -1) { + int fd_new = r_sandbox_open (targetFile, fd_flags, 0644); + if (fd_new < 0) { R_LOG_ERROR ("ConsPipe cannot open file '%s'", file); free (targetFile); return -1; } - if (I->backup_fd != -1) { - close (I->backup_fd); - // already set in __dupDescriptor // backup_fd = -1; + R_LOG_DEBUG ("open (%s) = %d", targetFile, fd_new); + int fd_bak = fd_src + 32; // XXX wrong assumptions + bool is_dual = false; +#if HONOR_LAST_REDIRECT + R_VEC_FOREACH (&ci->fds, pair) { + if (fd_src == pair->fd_src) { + // do not permit redirecting output to more than one file +#if USE_HACK + int fd_new2 = pair->fd_new + 64; + dup2 (pair->fd_bak, fd_new2); +#else + int fd_new2 = dup (pair->fd_bak); +#endif + fd_bak = fd_new2; + break; + } } - I->backup_fdn = fdn; - if (!__dupDescriptor (fd, fdn)) { +#endif + // int res = dup2 (fdn, rfd); + int res; + if (!is_dual) { +#if USE_HACK + res = dup2 (fd_src, fd_bak); +#else + res = fd_bak = dup (fd_src); +#endif + R_LOG_DEBUG ("dup2 %d %d = %d", fd_src, fd_bak, res); + close (fd_src); + res = dup2 (fd_new, fd_src); + } + R_LOG_DEBUG ("dup2 %d %d = %d", fd_new, fd_src, res); + RConsFdPair newPair = { + .fd_src = fd_src, // original source file descriptor + .fd_new = fd_new, // new file descriptor created to write into the file + .fd_bak = fd_bak, // restored file descriptor to be used to recover the original fd into fdn + }; + // eprintf (" %d -> %d\n", fd_src, fd_new); + RVecFdPairs_push_back (&ci->fds, &newPair); +#if 0 + if (!mydup (fd, fdn)) { R_LOG_ERROR ("Cannot dup stdout to %d", fdn); free (targetFile); return -1; } - close (fdn); - dup2 (fd, fdn); +#endif + // close (fdn); + // res = dup2 (fd, fdn); + // eprintf ("dup2 %d %d = %d\n", fd, fdn, res); free (targetFile); - return fd; + return fd_new; #endif } R_API void r_cons_pipe_close(int fd) { #if !__wasi__ + r_cons_pipe_close_all (); +#if 0 if (fd != -1) { close (fd); - if (I->backup_fd != -1) { - dup2 (I->backup_fd, I->backup_fdn); - close (I->backup_fd); - I->backup_fd = -1; + RCons *ci = r_cons_singleton (); + if (ci->backup_fdn[fd] != -1) { + dup2 (ci->backup_fd, ci->backup_fdn[fd]); + close (ci->backup_fd); + ci->backup_fd = -1; + ci->backup_fdn[fd] = -1; } } #endif +#endif +} + +R_API void r_cons_pipe_close_all(void) { +#if !__wasi__ + RCons *ci = r_cons_singleton (); + RConsFdPair *pair; + int res; + R_VEC_FOREACH_PREV (&ci->fds, pair) { + res = dup2 (pair->fd_bak, pair->fd_src); + R_LOG_DEBUG ("dup2 %d -> %d = %d", pair->fd_bak, pair->fd_src, res); + res = close (pair->fd_bak); + R_LOG_DEBUG ("close (%d)=%d", pair->fd_bak, res); +#if 0 + res = dup2 (pair->fd_new, pair->fd_src); + eprintf ("dup %d -> %d\n", pair->fd_new, pair->fd_src); +#endif + res = close (pair->fd_new); + R_LOG_DEBUG ("close (%d)=%d", pair->fd_new, res); + } + RVecFdPairs_fini (&ci->fds); + RVecFdPairs_init (&ci->fds); +#endif } diff --git a/libr/core/cmd.c b/libr/core/cmd.c index 67ba36d389464..ea655989b085e 100644 --- a/libr/core/cmd.c +++ b/libr/core/cmd.c @@ -1,4 +1,4 @@ -/* radare - LGPL - Copyright 2009-2023 - nibble, pancake */ +/* radare - LGPL - Copyright 2009-2024 - nibble, pancake */ #define INTERACTIVE_MAX_REP 1024 @@ -875,7 +875,7 @@ static int cmd_alias(void *data, const char *input) { free (n); } } else if (*def == '$') { - char *s = strdup (def+1); + char *s = strdup (def + 1); int l = r_str_unescape (s); r_cmd_alias_set_raw (core->rcmd, buf, (ut8 *)s, l); free (s); @@ -4331,6 +4331,8 @@ static int r_core_cmd_subst_i(RCore *core, char *cmd, char *colon, bool *tmpseek ptr = NULL; } } + int fdn = 1; + char *next_redirect = NULL; if (ptr) { if (ptr > cmd) { char *ch = ptr - 1; @@ -4344,21 +4346,27 @@ static int r_core_cmd_subst_i(RCore *core, char *cmd, char *colon, bool *tmpseek r_list_free (tmpenvs); return true; } - int fdn = 1; bool pipecolor = r_config_get_b (core->config, "scr.color.pipe"); - int use_editor = false; + bool use_editor = false; int ocolor = r_config_get_i (core->config, "scr.color"); *ptr = '\0'; + r_cons_set_interactive (false); +repeat:; str = ptr + 1 + (ptr[1] == '>'); r_str_trim (str); if (!*str) { R_LOG_ERROR ("No output?"); goto next2; } + fdn = 1; /* r_cons_flush() handles interactive output (to the terminal) * differently (e.g. asking about too long output). This conflicts * with piping to a file. Disable it while piping. */ - if (ptr > (cmd + 1) && IS_WHITECHAR (ptr[-2])) { + // note that 'x>a' is not working .. but 'x > a' or 'x >a' is valid + bool redirect_check = (ptr > cmd && (!ptr[-1] || !ptr[-2] || IS_WHITECHAR (ptr[-2]))); + if (redirect_check) { // R2R db/cmd/cmd_macros + R_LOG_DEBUG ("FD FROM (%s)", ptr - 1 ); + // eprintf ("COMPUTE FD (%s) (ptr=%s)\n", ptr -1, ptr-1); char *fdnum = ptr - 1; if (*fdnum == 'H') { // "H>" scr_html = r_cons_context ()->is_html; @@ -4372,19 +4380,42 @@ static int r_core_cmd_subst_i(RCore *core, char *cmd, char *colon, bool *tmpseek *fdnum = 0; } } - r_cons_set_interactive (false); + R_LOG_DEBUG ("FD %d", fdn); if (!strcmp (str, "-")) { use_editor = true; str = r_file_temp ("dumpedit"); r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); } + + char *nextgt = strchr (r_str_trim_head_ro (ptr + 1), '>'); + if (nextgt) { + R_LOG_DEBUG ("CHUM!"); + char *back = ptr + 1; + while (nextgt > back) { + if (!isdigit (*nextgt) && *nextgt != 'H') { + break; + } + nextgt--; + } + next_redirect = nextgt; + while (nextgt > back) { + if (*nextgt == ' ') { + *nextgt = 0; + break; + } + nextgt--; + } + } else { + next_redirect = NULL; + } + // eprintf ("---> (%s)\n", ptr + 1); + // eprintf ("next (%s)\n", next_redirect); const bool appendResult = (ptr[1] == '>'); if (*str == '$' && !str[1]) { R_LOG_ERROR ("No alias name given"); } else if (*str == '$') { // pipe to alias variable // register output of command as an alias - r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); RBuffer *cmd_out = r_core_cmd_tobuf (core, cmd); int alias_len; @@ -4406,22 +4437,28 @@ static int r_core_cmd_subst_i(RCore *core, char *cmd, char *colon, bool *tmpseek } else if (fdn > 0) { // pipe to file (or append) pipefd = r_cons_pipe_open (str, fdn, appendResult); - if (pipefd != -1) { - if (!pipecolor) { - r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); - } - ret = r_core_cmd_subst (core, cmd); - r_cons_flush (); - r_cons_pipe_close (pipefd); + if (pipefd == -1) { + // R_LOG_ERROR ("Cannot open pipe with fd %d", fdn); + // goto errorout; } + if (next_redirect) { + ptr = next_redirect; + *next_redirect = ' '; + next_redirect = NULL; + goto repeat; + } + if (!pipecolor) { + r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); + } + ret = r_core_cmd_subst (core, cmd); + r_cons_flush (); } - r_cons_set_last_interactive (); if (!pipecolor) { r_config_set_i (core->config, "scr.color", ocolor); } if (use_editor) { const char *editor = r_config_get (core->config, "cfg.editor"); - if (editor && *editor) { + if (R_STR_ISNOTEMPTY (editor)) { r_sys_cmdf ("%s '%s'", editor, str); r_file_rm (str); } else { @@ -4438,6 +4475,8 @@ static int r_core_cmd_subst_i(RCore *core, char *cmd, char *colon, bool *tmpseek } core->cons->context->use_tts = false; r_list_free (tmpenvs); + r_cons_pipe_close_all (); + r_cons_set_last_interactive (); return ret; } escape_redir: @@ -6280,7 +6319,7 @@ R_API char *r_core_cmd_str_pipe(RCore *core, const char *cmd) { r_cons_reset (); r_sandbox_disable (true); if (r_file_mkstemp ("cmd", &tmp) != -1) { - int pipefd = r_cons_pipe_open (tmp, 1, 0); + int pipefd = r_cons_pipe_open (tmp, 1, false); if (pipefd == -1) { r_file_rm (tmp); r_sandbox_disable (false); diff --git a/libr/include/r_cons.h b/libr/include/r_cons.h index 85739c534584e..c5bcc48651ebd 100644 --- a/libr/include/r_cons.h +++ b/libr/include/r_cons.h @@ -93,6 +93,13 @@ typedef struct r_cons_mark_t { int pos; } RConsMark; +typedef struct r_cons_fd_pair { + st16 fd_src; // target fd + st16 fd_new; // output file + st16 fd_bak; // backup of target fd in a new dupped fd +} RConsFdPair; + +R_VEC_TYPE (RVecFdPairs, RConsFdPair); R_API void r_cons_mark_flush(void); R_API void r_cons_mark(ut64 addr, const char *name); R_API void r_cons_mark_free(RConsMark *m); @@ -545,8 +552,7 @@ typedef struct r_cons_t { // TODO: move into instance? + avoid unnecessary copies RThreadLock *lock; RConsCursorPos cpos; - int backup_fd; - int backup_fdn; + RVecFdPairs fds; } RCons; #define R_CONS_KEY_F1 0xf1 @@ -842,6 +848,7 @@ R_API void r_cons_break_timeout(int timeout); /* pipe */ R_API int r_cons_pipe_open(const char *file, int fdn, int append); R_API void r_cons_pipe_close(int fd); +R_API void r_cons_pipe_close_all(void); #if R2__WINDOWS__ R_API int r_cons_is_vtcompat(void);