Skip to content

Commit 13be512

Browse files
radaretrufae
authored andcommitted
Support && and || command short-circuiting ##shell
1 parent 11f9a80 commit 13be512

4 files changed

Lines changed: 151 additions & 46 deletions

File tree

libr/core/cmd.c

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4373,6 +4373,29 @@ static char *find_last_unescaped_operator(char *cmd, char operator, const char *
43734373
return last;
43744374
}
43754375

4376+
static char *find_unescaped_double_operator(char *cmd, char operator, const char *quotes) {
4377+
const char *p = cmd;
4378+
while ((p = r_str_firstbut_escape (p, operator, quotes))) {
4379+
if (p[1] == operator) {
4380+
return (char *)p;
4381+
}
4382+
p++;
4383+
}
4384+
return NULL;
4385+
}
4386+
4387+
static char *find_unescaped_conditional(char *cmd, const char *quotes) {
4388+
if (r_str_startswith (cmd, "&&")) {
4389+
cmd += 2;
4390+
}
4391+
char *and = find_unescaped_double_operator (cmd, '&', quotes);
4392+
char *pipe = (char *)r_str_firstbut_escape (cmd, '|', quotes);
4393+
if (!pipe || (and && and < pipe)) {
4394+
return and;
4395+
}
4396+
return pipe[1] == '|'? pipe: NULL;
4397+
}
4398+
43764399
static char *getarg(char *ptr) {
43774400
if (*ptr == '{') {
43784401
char *mander = strdup (ptr + 1);
@@ -4472,7 +4495,7 @@ static int r_core_cmd_subst_i(RCore *core, char *cmd, char *colon, bool *tmpseek
44724495
RIODesc *tmpdesc = NULL;
44734496
bool old_iova = r_config_get_b (core->config, "io.va");
44744497
bool pamode = (core->io? !core->io->va: false);
4475-
int i, ret = 0, pipefd;
4498+
int i, ret = 0, pipefd, rc = 0;
44764499
bool usemyblock = false;
44774500
int scr_html = -1;
44784501
int scr_color = -1;
@@ -4714,10 +4737,36 @@ static int r_core_cmd_subst_i(RCore *core, char *cmd, char *colon, bool *tmpseek
47144737
if (backtick) {
47154738
goto escape_redir;
47164739
}
4740+
ptr = find_unescaped_conditional (cmd, quotestr);
4741+
if (ptr) {
4742+
bool run = true;
4743+
for (;;) {
4744+
const char condition = *ptr;
4745+
*ptr = 0;
4746+
if (run) {
4747+
ret = r_core_cmd_subst (core, cmd);
4748+
if (ret < 0) {
4749+
r_list_free (tmpenvs);
4750+
return ret;
4751+
}
4752+
}
4753+
run = condition == '&'? ret == 0: ret != 0;
4754+
cmd = (char *)r_str_trim_head_ro (ptr + 2);
4755+
ptr = find_unescaped_conditional (cmd, quotestr);
4756+
if (!ptr) {
4757+
break;
4758+
}
4759+
}
4760+
if (run) {
4761+
ret = r_core_cmd_subst (core, cmd);
4762+
}
4763+
r_list_free (tmpenvs);
4764+
return ret;
4765+
}
47174766

47184767
/* pipe console to shell process */
47194768
char *raw_operator = (char *)r_str_lastbut (cmd, '|', quotestr);
4720-
ptr = find_last_unescaped_operator (cmd, '|', quotestr);
4769+
ptr = (char *)r_str_firstbut_escape (cmd, '|', quotestr);
47214770
if (!ptr && raw_operator && is_escaped_operator (cmd, raw_operator)) {
47224771
memmove (raw_operator - 1, raw_operator, strlen (raw_operator) + 1);
47234772
goto escape_pipe;
@@ -4794,31 +4843,6 @@ static int r_core_cmd_subst_i(RCore *core, char *cmd, char *colon, bool *tmpseek
47944843
}
47954844
}
47964845
escape_pipe:
4797-
4798-
// TODO must honor " and `
4799-
/* bool conditions */
4800-
ptr = (char *)r_str_lastbut (cmd, '&', quotestr);
4801-
//ptr = strchr (cmd, '&');
4802-
while (ptr && *ptr && ptr[1] == '&') {
4803-
*ptr = '\0';
4804-
ret = r_cmd_call (core->rcmd, cmd);
4805-
if (ret == -1) {
4806-
R_LOG_ERROR ("command error(%s)", cmd);
4807-
if (scr_html != -1) {
4808-
r_config_set_b (core->config, "scr.html", scr_html);
4809-
}
4810-
if (scr_color != -1) {
4811-
r_config_set_i (core->config, "scr.color", scr_color);
4812-
}
4813-
r_list_free (tmpenvs);
4814-
return ret;
4815-
}
4816-
for (cmd = ptr + 2; cmd && *cmd == ' '; cmd++) {
4817-
;
4818-
}
4819-
ptr = strchr (cmd, '&');
4820-
}
4821-
48224846
ptr = strstr (cmd, "?*");
48234847
if (ptr && ((ptr - cmd) == 0 || ((ptr - cmd) > 0 && ptr[-1] != '~'))) {
48244848
char *pipechar = strchr (ptr, '>');
@@ -5134,7 +5158,6 @@ repeat:;
51345158
}
51355159

51365160
cmd_tmpseek = core->tmpseek = ptr;
5137-
int rc = 0;
51385161
if (ptr) {
51395162
char *f, *ptr2 = strchr (ptr + 1, '!');
51405163
ut64 addr = core->addr;

libr/core/cmd_print.inc.c

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -183,24 +183,6 @@ static RCoreHelpMessage help_msg_prg = {
183183
NULL
184184
};
185185

186-
static RCoreHelpMessage help_msg_amper = {
187-
"Usage:", "&[-|<cmd>]", "Manage tasks (WARNING: Experimental. Use with caution!)",
188-
"&", "", "list all tasks (alias for 'jobs' command)",
189-
"&", " <cmd>", "run <cmd> in a new background task (alias for 'bg')",
190-
"&:", "<cmd>", "queue <cmd> to be executed later when possible",
191-
"&t", " <cmd>", "run <cmd> in a new transient background task (auto-delete when it is finished)",
192-
"&j", "", "list all tasks (in JSON)",
193-
"&=", " 3", "show output of task 3",
194-
"&b", " 3", "break task 3",
195-
"&w", "", "wait for queued commands and execute them (^C to end)",
196-
"&-", " 1", "delete task #1 or schedule for deletion when it is finished",
197-
"&", "-*", "delete all done tasks",
198-
"&?", "", "show this help",
199-
"&&", " 3", "wait until task 3 is finished (alias for 'fg')",
200-
"&&", "", "wait until all tasks are finished (same as 'fg')",
201-
NULL
202-
};
203-
204186
static RCoreHelpMessage help_msg_p = {
205187
"Usage:", "p[=68abcdDfiImrstuxz] [arg|len] [@addr]", "",
206188
// "p", "[b|B|xb] [len] ([S])", "bindump N bits skipping S bytes",

libr/core/cmd_task.inc.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
// Task-related commands extracted from cmd.c
22
#include <r_core.h>
33

4+
static RCoreHelpMessage help_msg_amper = {
5+
"Usage:", "&[-|<cmd>]", "Manage tasks (WARNING: Experimental. Use with caution!)",
6+
"&", "", "list all tasks (alias for 'jobs' command)",
7+
"&", " <cmd>", "run <cmd> in a new background task (alias for 'bg')",
8+
"&:", "<cmd>", "queue <cmd> to be executed later when possible",
9+
"&t", " <cmd>", "run <cmd> in a new transient background task (auto-delete when it is finished)",
10+
"&j", "", "list all tasks (in JSON)",
11+
"&=", " 3", "show output of task 3",
12+
"&b", " 3", "break task 3",
13+
"&w", "", "wait for queued commands and execute them (^C to end)",
14+
"&-", " 1", "delete task #1 or schedule for deletion when it is finished",
15+
"&", "-*", "delete all done tasks",
16+
"&?", "", "show this help",
17+
"&&", " 3", "wait until task 3 is finished (alias for 'fg')",
18+
"&&", "", "wait until all tasks are finished (same as 'fg')",
19+
NULL
20+
};
21+
422
static int _cmd_tasks_impl(void *data, const char *input) {
523
RCore *core = (RCore*) data;
624
switch (input[0]) {
@@ -16,6 +34,9 @@ static int _cmd_tasks_impl(void *data, const char *input) {
1634
case 'w': // "&w"
1735
r_core_cmd_queue_wait (core);
1836
break;
37+
case '?': // "&?"
38+
r_core_cmd_help (core, help_msg_amper);
39+
break;
1940
case 'b': { // "&b"
2041
if (!r_sandbox_check (R_SANDBOX_GRAIN_EXEC)) {
2142
R_LOG_ERROR ("The &b command is disabled in sandbox mode");

test/db/cmd/shell

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,85 @@ EXPECT=<<EOF
77
EOF
88
RUN
99

10+
NAME=infix double ampersand short circuits
11+
FILE=malloc://32
12+
CMDS=<<EOF
13+
?e LEFT && ?e RIGHT
14+
?e A&&?e B&&?e C
15+
?e LEFT "&&" RIGHT
16+
?e LEFT '&&' RIGHT
17+
?e LEFT "&"'&' RIGHT
18+
?e LEFT \&\& RIGHT
19+
?e ODD\&& ?e LITERAL
20+
?e EVEN\\&& ?e ACTIVE
21+
EOF
22+
EXPECT=<<EOF
23+
LEFT
24+
RIGHT
25+
A
26+
B
27+
C
28+
LEFT && RIGHT
29+
LEFT && RIGHT
30+
LEFT && RIGHT
31+
LEFT && RIGHT
32+
ODD&& ?e LITERAL
33+
EVEN\
34+
ACTIVE
35+
EOF
36+
RUN
37+
38+
NAME=infix double ampersand stops on nonzero status
39+
FILE=malloc://32
40+
CMDS=<<EOF
41+
prj open /this/path/does/not/exist/r2-and.prj && ?e SHOULD_NOT_RUN
42+
EOF
43+
EXPECT=
44+
EXPECT_ERR=<<EOF
45+
ERROR: Cannot find project file: /this/path/does/not/exist/r2-and.prj
46+
EOF
47+
RUN
48+
49+
NAME=infix double pipe short circuits
50+
FILE=malloc://32
51+
CMDS=<<EOF
52+
?e LEFT || ?e SHOULD_NOT_RUN
53+
?e A||?e SKIPPED&&?e B
54+
?e PIPE | cat || printf SHOULD_NOT_RUN
55+
?e LEFT "||" RIGHT
56+
?e LEFT '||' RIGHT
57+
?e LEFT "|"'|' RIGHT
58+
?e LEFT \|\| RIGHT
59+
?e ODD\|| cat
60+
?e EVEN\\|| ?e ACTIVE
61+
EOF
62+
EXPECT=<<EOF
63+
LEFT
64+
A
65+
B
66+
PIPE
67+
LEFT || RIGHT
68+
LEFT || RIGHT
69+
LEFT || RIGHT
70+
LEFT || RIGHT
71+
ODD|
72+
EVEN\
73+
EOF
74+
RUN
75+
76+
NAME=mixed infix conditionals recover from nonzero status
77+
FILE=malloc://32
78+
CMDS=<<EOF
79+
prj open /this/path/does/not/exist/r2-or.prj && ?e SHOULD_NOT_RUN || ?e RECOVERED
80+
EOF
81+
EXPECT=<<EOF
82+
RECOVERED
83+
EOF
84+
EXPECT_ERR=<<EOF
85+
ERROR: Cannot find project file: /this/path/does/not/exist/r2-or.prj
86+
EOF
87+
RUN
88+
1089
NAME=touch
1190
FILE=malloc://32-
1291
CMDS=<<EOF

0 commit comments

Comments
 (0)