aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-07-28 23:07:06 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-07-28 23:07:06 +0000
commit6a2d40f239566e886ef76542a75662cee9380a0e (patch)
treef2e26f50cae7ddc60112f51174c4d994a9954fea /shell
parentbcb25537d02b50ce26678defcf4f39d0c89f5b3b (diff)
downloadbusybox-w32-6a2d40f239566e886ef76542a75662cee9380a0e.tar.gz
busybox-w32-6a2d40f239566e886ef76542a75662cee9380a0e.tar.bz2
busybox-w32-6a2d40f239566e886ef76542a75662cee9380a0e.zip
hush: support "break N" and "continue N"
fix non-detection of builtins and applets in "v=break; ...; $v; ..." case add testsuite entries for the above function old new delta builtin_break 12 93 +81 run_list 1948 1971 +23 builtin_continue 12 21 +9 pseudo_exec_argv 132 138 +6 builtin_exec 23 25 +2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 5/0 up/down: 121/0) Total: 121 bytes
Diffstat (limited to 'shell')
-rw-r--r--shell/hush.c99
-rw-r--r--shell/hush_test/hush-misc/break1.right2
-rwxr-xr-xshell/hush_test/hush-misc/break1.tests3
-rw-r--r--shell/hush_test/hush-misc/break2.right3
-rwxr-xr-xshell/hush_test/hush-misc/break2.tests6
-rw-r--r--shell/hush_test/hush-misc/break3.right2
-rwxr-xr-xshell/hush_test/hush-misc/break3.tests2
7 files changed, 82 insertions, 35 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 21cb36564..a74fe4730 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -440,6 +440,7 @@ struct globals {
440 smalluint last_return_code; 440 smalluint last_return_code;
441 char **global_argv; 441 char **global_argv;
442 int global_argc; 442 int global_argc;
443 unsigned depth_break_continue;
443 pid_t last_bg_pid; 444 pid_t last_bg_pid;
444 const char *ifs; 445 const char *ifs;
445 const char *cwd; 446 const char *cwd;
@@ -487,7 +488,8 @@ enum { run_list_level = 0 };
487#define global_argc (G.global_argc ) 488#define global_argc (G.global_argc )
488#define last_return_code (G.last_return_code) 489#define last_return_code (G.last_return_code)
489#define ifs (G.ifs ) 490#define ifs (G.ifs )
490#define flag_break_continue (G.flag_break_continue) 491#define flag_break_continue (G.flag_break_continue )
492#define depth_break_continue (G.depth_break_continue)
491#define fake_mode (G.fake_mode ) 493#define fake_mode (G.fake_mode )
492#define cwd (G.cwd ) 494#define cwd (G.cwd )
493#define last_bg_pid (G.last_bg_pid ) 495#define last_bg_pid (G.last_bg_pid )
@@ -552,11 +554,11 @@ static int free_pipe(struct pipe *pi, int indent);
552static int setup_redirects(struct child_prog *prog, int squirrel[]); 554static int setup_redirects(struct child_prog *prog, int squirrel[]);
553static int run_list(struct pipe *pi); 555static int run_list(struct pipe *pi);
554#if BB_MMU 556#if BB_MMU
555#define pseudo_exec_argv(ptrs2free, argv) pseudo_exec_argv(argv) 557#define pseudo_exec_argv(ptrs2free, argv, argv_expanded) pseudo_exec_argv(argv, argv_expanded)
556#define pseudo_exec(ptrs2free, child) pseudo_exec(child) 558#define pseudo_exec(ptrs2free, child, argv_expanded) pseudo_exec(child, argv_expanded)
557#endif 559#endif
558static void pseudo_exec_argv(char **ptrs2free, char **argv) NORETURN; 560static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded) NORETURN;
559static void pseudo_exec(char **ptrs2free, struct child_prog *child) NORETURN; 561static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) NORETURN;
560static int run_pipe(struct pipe *pi); 562static int run_pipe(struct pipe *pi);
561/* data structure manipulation: */ 563/* data structure manipulation: */
562static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input); 564static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
@@ -1410,7 +1412,7 @@ static void restore_redirects(int squirrel[])
1410 * XXX no exit() here. If you don't exec, use _exit instead. 1412 * XXX no exit() here. If you don't exec, use _exit instead.
1411 * The at_exit handlers apparently confuse the calling process, 1413 * The at_exit handlers apparently confuse the calling process,
1412 * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ 1414 * in particular stdin handling. Not sure why? -- because of vfork! (vda) */
1413static void pseudo_exec_argv(char **ptrs2free, char **argv) 1415static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded)
1414{ 1416{
1415 int i, rcode; 1417 int i, rcode;
1416 char *p; 1418 char *p;
@@ -1432,10 +1434,14 @@ static void pseudo_exec_argv(char **ptrs2free, char **argv)
1432 if (!argv[0]) 1434 if (!argv[0])
1433 _exit(EXIT_SUCCESS); 1435 _exit(EXIT_SUCCESS);
1434 1436
1435 argv = expand_strvec_to_strvec(argv); 1437 if (argv_expanded) {
1438 argv = argv_expanded;
1439 } else {
1440 argv = expand_strvec_to_strvec(argv);
1436#if !BB_MMU 1441#if !BB_MMU
1437 *ptrs2free++ = (char*) argv; 1442 *ptrs2free++ = (char*) argv;
1438#endif 1443#endif
1444 }
1439 1445
1440 /* 1446 /*
1441 * Check if the command matches any of the builtins. 1447 * Check if the command matches any of the builtins.
@@ -1479,10 +1485,10 @@ static void pseudo_exec_argv(char **ptrs2free, char **argv)
1479 1485
1480/* Called after [v]fork() in run_pipe() 1486/* Called after [v]fork() in run_pipe()
1481 */ 1487 */
1482static void pseudo_exec(char **ptrs2free, struct child_prog *child) 1488static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded)
1483{ 1489{
1484 if (child->argv) 1490 if (child->argv)
1485 pseudo_exec_argv(ptrs2free, child->argv); 1491 pseudo_exec_argv(ptrs2free, child->argv, argv_expanded);
1486 1492
1487 if (child->group) { 1493 if (child->group) {
1488#if !BB_MMU 1494#if !BB_MMU
@@ -1760,14 +1766,16 @@ static int run_pipe(struct pipe *pi)
1760 int nextin; 1766 int nextin;
1761 int pipefds[2]; /* pipefds[0] is for reading */ 1767 int pipefds[2]; /* pipefds[0] is for reading */
1762 struct child_prog *child; 1768 struct child_prog *child;
1769 char **argv_expanded = NULL;
1770 char **argv;
1763 const struct built_in_command *x; 1771 const struct built_in_command *x;
1764 char *p; 1772 char *p;
1765 /* it is not always needed, but we aim to smaller code */ 1773 /* it is not always needed, but we aim to smaller code */
1766 int squirrel[] = { -1, -1, -1 }; 1774 int squirrel[] = { -1, -1, -1 };
1767 int rcode; 1775 int rcode;
1768 const int single_fg = (pi->num_progs == 1 && pi->followup != PIPE_BG); 1776 const int single_and_fg = (pi->num_progs == 1 && pi->followup != PIPE_BG);
1769 1777
1770 debug_printf_exec("run_pipe start: single_fg=%d\n", single_fg); 1778 debug_printf_exec("run_pipe start: single_and_fg=%d\n", single_and_fg);
1771 1779
1772#if ENABLE_HUSH_JOB 1780#if ENABLE_HUSH_JOB
1773 pi->pgrp = -1; 1781 pi->pgrp = -1;
@@ -1780,7 +1788,7 @@ static int run_pipe(struct pipe *pi)
1780 * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. 1788 * pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
1781 */ 1789 */
1782 child = &(pi->progs[0]); 1790 child = &(pi->progs[0]);
1783 if (single_fg && child->group && child->subshell == 0) { 1791 if (single_and_fg && child->group && child->subshell == 0) {
1784 debug_printf("non-subshell grouping\n"); 1792 debug_printf("non-subshell grouping\n");
1785 setup_redirects(child, squirrel); 1793 setup_redirects(child, squirrel);
1786 debug_printf_exec(": run_list\n"); 1794 debug_printf_exec(": run_list\n");
@@ -1791,41 +1799,45 @@ static int run_pipe(struct pipe *pi)
1791 return rcode; 1799 return rcode;
1792 } 1800 }
1793 1801
1794 if (single_fg && child->argv != NULL) { 1802 argv = child->argv;
1795 char **argv_expanded;
1796 char **argv = child->argv;
1797 1803
1804 if (single_and_fg && argv != NULL) {
1798 for (i = 0; is_assignment(argv[i]); i++) 1805 for (i = 0; is_assignment(argv[i]); i++)
1799 continue; 1806 continue;
1800 if (i != 0 && argv[i] == NULL) { 1807 if (i != 0 && argv[i] == NULL) {
1801 /* assignments, but no command: set the local environment */ 1808 /* assignments, but no command: set local environment */
1802 for (i = 0; argv[i] != NULL; i++) { 1809 for (i = 0; argv[i] != NULL; i++) {
1803 debug_printf("local environment set: %s\n", argv[i]); 1810 debug_printf("local environment set: %s\n", argv[i]);
1804 p = expand_string_to_string(argv[i]); 1811 p = expand_string_to_string(argv[i]);
1805 set_local_var(p, 0); 1812 set_local_var(p, 0);
1806 } 1813 }
1807 return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ 1814 return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
1808 } 1815 }
1816
1817 /* Expand assignments into one string each */
1809 for (i = 0; is_assignment(argv[i]); i++) { 1818 for (i = 0; is_assignment(argv[i]); i++) {
1810 p = expand_string_to_string(argv[i]); 1819 p = expand_string_to_string(argv[i]);
1811 putenv(p); 1820 putenv(p);
1812//FIXME: do we leak p?! 1821//FIXME: do we leak p?!
1813 } 1822 }
1823
1824 /* Expand the rest into (possibly) many strings each */
1825 argv_expanded = expand_strvec_to_strvec(argv + i);
1826
1814 for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { 1827 for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) {
1815 if (strcmp(argv[i], x->cmd) == 0) { 1828 if (strcmp(argv_expanded[0], x->cmd) == 0) {
1816 if (x->function == builtin_exec && argv[i+1] == NULL) { 1829 if (x->function == builtin_exec && argv_expanded[1] == NULL) {
1817 debug_printf("magic exec\n"); 1830 debug_printf("magic exec\n");
1818 setup_redirects(child, NULL); 1831 setup_redirects(child, NULL);
1819 return EXIT_SUCCESS; 1832 return EXIT_SUCCESS;
1820 } 1833 }
1821 debug_printf("builtin inline %s\n", argv[0]); 1834 debug_printf("builtin inline %s\n", argv_expanded[0]);
1822 /* XXX setup_redirects acts on file descriptors, not FILEs. 1835 /* XXX setup_redirects acts on file descriptors, not FILEs.
1823 * This is perfect for work that comes after exec(). 1836 * This is perfect for work that comes after exec().
1824 * Is it really safe for inline use? Experimentally, 1837 * Is it really safe for inline use? Experimentally,
1825 * things seem to work with glibc. */ 1838 * things seem to work with glibc. */
1826 setup_redirects(child, squirrel); 1839 setup_redirects(child, squirrel);
1827 debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv[i+1]); 1840 debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv_expanded[1]);
1828 argv_expanded = expand_strvec_to_strvec(argv + i);
1829 rcode = x->function(argv_expanded) & 0xff; 1841 rcode = x->function(argv_expanded) & 0xff;
1830 free(argv_expanded); 1842 free(argv_expanded);
1831 restore_redirects(squirrel); 1843 restore_redirects(squirrel);
@@ -1836,12 +1848,10 @@ static int run_pipe(struct pipe *pi)
1836 } 1848 }
1837#if ENABLE_FEATURE_SH_STANDALONE 1849#if ENABLE_FEATURE_SH_STANDALONE
1838 { 1850 {
1839 int a = find_applet_by_name(argv[i]); 1851 int a = find_applet_by_name(argv_expanded[0]);
1840 if (a >= 0 && APPLET_IS_NOFORK(a)) { 1852 if (a >= 0 && APPLET_IS_NOFORK(a)) {
1841 setup_redirects(child, squirrel); 1853 setup_redirects(child, squirrel);
1842 save_nofork_data(&nofork_save); 1854 save_nofork_data(&nofork_save);
1843 argv_expanded = argv + i;
1844 argv_expanded = expand_strvec_to_strvec(argv + i);
1845 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); 1855 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]);
1846 rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded); 1856 rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded);
1847 free(argv_expanded); 1857 free(argv_expanded);
@@ -1854,6 +1864,10 @@ static int run_pipe(struct pipe *pi)
1854#endif 1864#endif
1855 } 1865 }
1856 1866
1867 /* NB: argv_expanded may already be created, and that
1868 * might include `cmd` runs! Do not rerun it! We *must*
1869 * use argv_expanded if it's non-NULL */
1870
1857 /* Disable job control signals for shell (parent) and 1871 /* Disable job control signals for shell (parent) and
1858 * for initial child code after fork */ 1872 * for initial child code after fork */
1859 set_jobctrl_sighandler(SIG_IGN); 1873 set_jobctrl_sighandler(SIG_IGN);
@@ -1914,8 +1928,10 @@ static int run_pipe(struct pipe *pi)
1914 set_jobctrl_sighandler(SIG_DFL); 1928 set_jobctrl_sighandler(SIG_DFL);
1915 set_misc_sighandler(SIG_DFL); 1929 set_misc_sighandler(SIG_DFL);
1916 signal(SIGCHLD, SIG_DFL); 1930 signal(SIGCHLD, SIG_DFL);
1917 pseudo_exec(ptrs2free, child); /* does not return */ 1931 pseudo_exec(ptrs2free, child, argv_expanded); /* does not return */
1918 } 1932 }
1933 free(argv_expanded);
1934 argv_expanded = NULL;
1919#if !BB_MMU 1935#if !BB_MMU
1920 free_strings(ptrs2free); 1936 free_strings(ptrs2free);
1921#endif 1937#endif
@@ -2236,18 +2252,22 @@ static int run_list(struct pipe *pi)
2236 /* we only ran a builtin: rcode is already known 2252 /* we only ran a builtin: rcode is already known
2237 * and we don't need to wait for anything. */ 2253 * and we don't need to wait for anything. */
2238 /* was it "break" or "continue"? */ 2254 /* was it "break" or "continue"? */
2255
2239 if (flag_break_continue) { 2256 if (flag_break_continue) {
2240 smallint fbc = flag_break_continue; 2257 smallint fbc = flag_break_continue;
2241 /* we might fall into outer *loop*, 2258 /* we might fall into outer *loop*,
2242 * don't want to break it too */ 2259 * don't want to break it too */
2243 flag_break_continue = 0;
2244 if (loop_top) { 2260 if (loop_top) {
2245 if (fbc == BC_BREAK) 2261 depth_break_continue--;
2262 if (depth_break_continue == 0)
2263 flag_break_continue = 0;
2264 if (depth_break_continue != 0 || fbc == BC_BREAK)
2246 goto check_jobs_and_break; 2265 goto check_jobs_and_break;
2247 /* "continue": simulate end of loop */ 2266 /* "continue": simulate end of loop */
2248 rword = RES_DONE; 2267 rword = RES_DONE;
2249 continue; 2268 continue;
2250 } 2269 }
2270 flag_break_continue = 0;
2251 bb_error_msg("break/continue: only meaningful in a loop"); 2271 bb_error_msg("break/continue: only meaningful in a loop");
2252 /* bash compat: exit code is still 0 */ 2272 /* bash compat: exit code is still 0 */
2253 } 2273 }
@@ -4265,7 +4285,7 @@ static int builtin_exec(char **argv)
4265 char **ptrs2free = alloc_ptrs(argv); 4285 char **ptrs2free = alloc_ptrs(argv);
4266#endif 4286#endif
4267// FIXME: if exec fails, bash does NOT exit! We do... 4287// FIXME: if exec fails, bash does NOT exit! We do...
4268 pseudo_exec_argv(ptrs2free, argv + 1); 4288 pseudo_exec_argv(ptrs2free, argv + 1, NULL);
4269 /* never returns */ 4289 /* never returns */
4270 } 4290 }
4271} 4291}
@@ -4512,14 +4532,23 @@ static int builtin_unset(char **argv)
4512 return EXIT_SUCCESS; 4532 return EXIT_SUCCESS;
4513} 4533}
4514 4534
4515static int builtin_break(char **argv UNUSED_PARAM) 4535static int builtin_break(char **argv)
4516{ 4536{
4517 flag_break_continue = BC_BREAK; 4537 flag_break_continue++; /* BC_BREAK = 1 */
4538 depth_break_continue = 1;
4539 if (argv[1]) {
4540 depth_break_continue = bb_strtou(argv[1], NULL, 10);
4541 if (errno || !depth_break_continue || argv[2]) {
4542 bb_error_msg("bad arguments");
4543 flag_break_continue = BC_BREAK;
4544 depth_break_continue = UINT_MAX;
4545 }
4546 }
4518 return EXIT_SUCCESS; 4547 return EXIT_SUCCESS;
4519} 4548}
4520 4549
4521static int builtin_continue(char **argv UNUSED_PARAM) 4550static int builtin_continue(char **argv)
4522{ 4551{
4523 flag_break_continue = BC_CONTINUE; 4552 flag_break_continue++; /* BC_CONTINUE = 2 = 1+1 */
4524 return EXIT_SUCCESS; 4553 return builtin_break(argv);
4525} 4554}
diff --git a/shell/hush_test/hush-misc/break1.right b/shell/hush_test/hush-misc/break1.right
new file mode 100644
index 000000000..04a4b1757
--- /dev/null
+++ b/shell/hush_test/hush-misc/break1.right
@@ -0,0 +1,2 @@
1A
2OK:0
diff --git a/shell/hush_test/hush-misc/break1.tests b/shell/hush_test/hush-misc/break1.tests
new file mode 100755
index 000000000..912f149c1
--- /dev/null
+++ b/shell/hush_test/hush-misc/break1.tests
@@ -0,0 +1,3 @@
1while true; do echo A; break; echo B; done
2echo OK:$?
3
diff --git a/shell/hush_test/hush-misc/break2.right b/shell/hush_test/hush-misc/break2.right
new file mode 100644
index 000000000..8a15cb95f
--- /dev/null
+++ b/shell/hush_test/hush-misc/break2.right
@@ -0,0 +1,3 @@
1A
2AA
3OK:0
diff --git a/shell/hush_test/hush-misc/break2.tests b/shell/hush_test/hush-misc/break2.tests
new file mode 100755
index 000000000..7da9faf34
--- /dev/null
+++ b/shell/hush_test/hush-misc/break2.tests
@@ -0,0 +1,6 @@
1while true; do
2 echo A
3 while true; do echo AA; break 2; echo BB; done
4 echo B
5done
6echo OK:$?
diff --git a/shell/hush_test/hush-misc/break3.right b/shell/hush_test/hush-misc/break3.right
new file mode 100644
index 000000000..04a4b1757
--- /dev/null
+++ b/shell/hush_test/hush-misc/break3.right
@@ -0,0 +1,2 @@
1A
2OK:0
diff --git a/shell/hush_test/hush-misc/break3.tests b/shell/hush_test/hush-misc/break3.tests
new file mode 100755
index 000000000..d138dcac5
--- /dev/null
+++ b/shell/hush_test/hush-misc/break3.tests
@@ -0,0 +1,2 @@
1v=break; while true; do echo A; $v; echo B; break; echo C; done
2echo OK:$?