summaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-05-04 21:37:27 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-05-04 21:37:27 +0000
commitac0e5ab96ac35d46a6e0755f6f24f016ce788d90 (patch)
treeaccdf0c9bacae6e89a658d0382cbe343c062a84f /shell/hush.c
parent3349fc4da948eb1393cd9201cd9c2615c0056c97 (diff)
downloadbusybox-w32-ac0e5ab96ac35d46a6e0755f6f24f016ce788d90.tar.gz
busybox-w32-ac0e5ab96ac35d46a6e0755f6f24f016ce788d90.tar.bz2
busybox-w32-ac0e5ab96ac35d46a6e0755f6f24f016ce788d90.zip
hush: fix "while true; do true; done" + ctrl-z
Diffstat (limited to '')
-rw-r--r--shell/hush.c212
1 files changed, 120 insertions, 92 deletions
diff --git a/shell/hush.c b/shell/hush.c
index c51ed1a51..78531e864 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -468,7 +468,6 @@ static const struct built_in_command bltins[] = {
468 468
469#if ENABLE_HUSH_JOB 469#if ENABLE_HUSH_JOB
470 470
471#if ENABLE_FEATURE_SH_STANDALONE
472/* move to libbb? */ 471/* move to libbb? */
473static void signal_SA_RESTART(int sig, void (*handler)(int)) 472static void signal_SA_RESTART(int sig, void (*handler)(int))
474{ 473{
@@ -478,7 +477,6 @@ static void signal_SA_RESTART(int sig, void (*handler)(int))
478 sigemptyset(&sa.sa_mask); 477 sigemptyset(&sa.sa_mask);
479 sigaction(sig, &sa, NULL); 478 sigaction(sig, &sa, NULL);
480} 479}
481#endif
482 480
483/* Signals are grouped, we handle them in batches */ 481/* Signals are grouped, we handle them in batches */
484static void set_fatal_sighandler(void (*handler)(int)) 482static void set_fatal_sighandler(void (*handler)(int))
@@ -508,7 +506,6 @@ static void set_misc_sighandler(void (*handler)(int))
508} 506}
509/* SIGCHLD is special and handled separately */ 507/* SIGCHLD is special and handled separately */
510 508
511#if ENABLE_FEATURE_SH_STANDALONE
512static void set_every_sighandler(void (*handler)(int)) 509static void set_every_sighandler(void (*handler)(int))
513{ 510{
514 set_fatal_sighandler(handler); 511 set_fatal_sighandler(handler);
@@ -517,53 +514,57 @@ static void set_every_sighandler(void (*handler)(int))
517 signal(SIGCHLD, handler); 514 signal(SIGCHLD, handler);
518} 515}
519 516
520static struct pipe *nofork_pipe; 517static struct pipe *toplevel_list;
518static sigjmp_buf toplevel_jb;
519smallint ctrl_z_flag;
520#if ENABLE_FEATURE_SH_STANDALONE
521struct nofork_save_area nofork_save; 521struct nofork_save_area nofork_save;
522static sigjmp_buf nofork_jb; 522#endif
523 523
524static void handler_ctrl_c(int sig) 524static void handler_ctrl_c(int sig)
525{ 525{
526 debug_printf_jobs("got sig %d\n", sig); 526 debug_printf_jobs("got sig %d\n", sig);
527// as usual we can have all kinds of nasty problems with leaked malloc data here 527// as usual we can have all kinds of nasty problems with leaked malloc data here
528 siglongjmp(nofork_jb, 1); 528 siglongjmp(toplevel_jb, 1);
529} 529}
530 530
531static void handler_ctrl_z(int sig) 531static void handler_ctrl_z(int sig)
532{ 532{
533 pid_t pid; 533 pid_t pid;
534 534
535 debug_printf_jobs("got tty sig %d\n", sig); 535 debug_printf_jobs("got tty sig %d in pid %d\n", sig, getpid());
536 pid = fork(); 536 pid = fork();
537 if (pid < 0) /* can't fork. Pretend there were no Ctrl-Z */ 537 if (pid < 0) /* can't fork. Pretend there were no ctrl-Z */
538 return; 538 return;
539 debug_printf_jobs("bg'ing nofork\n"); 539 ctrl_z_flag = 1;
540 nofork_save.saved = 0; /* flag the fact that Ctrl-Z was handled */ 540//vda: wrong!!
541 nofork_pipe->running_progs = 1; 541// toplevel_list->running_progs = 1;
542 nofork_pipe->stopped_progs = 0; 542// toplevel_list->stopped_progs = 0;
543//
543 if (!pid) { /* child */ 544 if (!pid) { /* child */
544 debug_printf_jobs("setting pgrp for child\n");
545 setpgrp(); 545 setpgrp();
546 debug_printf_jobs("set pgrp for child %d ok\n", getpid());
546 set_every_sighandler(SIG_DFL); 547 set_every_sighandler(SIG_DFL);
547 raise(SIGTSTP); /* resend TSTP so that child will be stopped */ 548 raise(SIGTSTP); /* resend TSTP so that child will be stopped */
548 debug_printf_jobs("returning to child\n"); 549 debug_printf_jobs("returning in child\n");
549 /* return to nofork, it will eventually exit now, 550 /* return to nofork, it will eventually exit now,
550 * not return back to shell */ 551 * not return back to shell */
551 return; 552 return;
552 } 553 }
553 /* parent */ 554 /* parent */
554 /* finish filling up pipe info */ 555 /* finish filling up pipe info */
555 nofork_pipe->pgrp = pid; /* child is in its own pgrp */ 556 toplevel_list->pgrp = pid; /* child is in its own pgrp */
556 nofork_pipe->progs[0].pid = pid; 557 toplevel_list->progs[0].pid = pid;
557 nofork_pipe->running_progs = 1; 558//vda: wrong!!
558 nofork_pipe->stopped_progs = 0; 559// toplevel_list->running_progs = 1;
560// toplevel_list->stopped_progs = 0;
559 /* parent needs to longjmp out of running nofork. 561 /* parent needs to longjmp out of running nofork.
560 * we will "return" exitcode 0, with child put in background */ 562 * we will "return" exitcode 0, with child put in background */
561// as usual we can have all kinds of nasty problems with leaked malloc data here 563// as usual we can have all kinds of nasty problems with leaked malloc data here
562 siglongjmp(nofork_jb, 1); 564 debug_printf_jobs("siglongjmp in parent\n");
565 siglongjmp(toplevel_jb, 1);
563} 566}
564 567
565#endif
566
567/* Restores tty foreground process group, and exits. 568/* Restores tty foreground process group, and exits.
568 * May be called as signal handler for fatal signal 569 * May be called as signal handler for fatal signal
569 * (will faithfully resend signal to itself, producing correct exit state) 570 * (will faithfully resend signal to itself, producing correct exit state)
@@ -1039,6 +1040,7 @@ static int static_peek(struct in_str *i)
1039} 1040}
1040 1041
1041#if ENABLE_HUSH_INTERACTIVE 1042#if ENABLE_HUSH_INTERACTIVE
1043#if ENABLE_FEATURE_EDITING
1042static void cmdedit_set_initial_prompt(void) 1044static void cmdedit_set_initial_prompt(void)
1043{ 1045{
1044#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT 1046#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
@@ -1049,6 +1051,7 @@ static void cmdedit_set_initial_prompt(void)
1049 PS1 = "\\w \\$ "; 1051 PS1 = "\\w \\$ ";
1050#endif 1052#endif
1051} 1053}
1054#endif
1052 1055
1053static const char* setup_prompt_string(int promptmode) 1056static const char* setup_prompt_string(int promptmode)
1054{ 1057{
@@ -1072,7 +1075,7 @@ static const char* setup_prompt_string(int promptmode)
1072 debug_printf("result %s\n", prompt_str); 1075 debug_printf("result %s\n", prompt_str);
1073 return prompt_str; 1076 return prompt_str;
1074} 1077}
1075#endif 1078#endif /* ENABLE_HUSH_INTERACTIVE */
1076 1079
1077#if ENABLE_FEATURE_EDITING 1080#if ENABLE_FEATURE_EDITING
1078static line_input_t *line_input_state; 1081static line_input_t *line_input_state;
@@ -1470,7 +1473,7 @@ static int checkjobs(struct pipe* fg_pipe)
1470 1473
1471/* Do we do this right? 1474/* Do we do this right?
1472 * bash-3.00# sleep 20 | false 1475 * bash-3.00# sleep 20 | false
1473 * <Ctrl-Z pressed> 1476 * <ctrl-Z pressed>
1474 * [3]+ Stopped sleep 20 | false 1477 * [3]+ Stopped sleep 20 | false
1475 * bash-3.00# echo $? 1478 * bash-3.00# echo $?
1476 * 1 <========== bg pipe is not fully done, but exitcode is already known! 1479 * 1 <========== bg pipe is not fully done, but exitcode is already known!
@@ -1590,43 +1593,6 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
1590} 1593}
1591#endif 1594#endif
1592 1595
1593#if ENABLE_FEATURE_SH_STANDALONE
1594/* run_pipe_real's helper */
1595static int run_single_fg_nofork(struct pipe *pi, const struct bb_applet *a,
1596 char **argv)
1597{
1598#if ENABLE_HUSH_JOB
1599 int rcode;
1600 /* TSTP handler will store pid etc in pi */
1601 nofork_pipe = pi;
1602 save_nofork_data(&nofork_save);
1603 if (sigsetjmp(nofork_jb, 1) == 0) {
1604 signal_SA_RESTART(SIGTSTP, handler_ctrl_z);
1605 signal(SIGINT, handler_ctrl_c);
1606 rcode = run_nofork_applet_prime(&nofork_save, a, argv);
1607 if (--nofork_save.saved != 0) {
1608 /* Ctrl-Z forked, we are child */
1609 exit(rcode);
1610 }
1611 return rcode;
1612 }
1613 /* Ctrl-Z forked, we are parent; or Ctrl-C.
1614 * Sighandler has longjmped us here */
1615 signal(SIGINT, SIG_IGN);
1616 signal(SIGTSTP, SIG_IGN);
1617 debug_printf_jobs("Exiting nofork early\n");
1618 restore_nofork_data(&nofork_save);
1619 if (nofork_save.saved == 0) /* Ctrl-Z, not Ctrl-C */
1620 insert_bg_job(pi);
1621 else
1622 putchar('\n'); /* bash does this on Ctrl-C */
1623 return 0;
1624#else
1625 return run_nofork_applet(a, argv);
1626#endif
1627}
1628#endif
1629
1630/* run_pipe_real() starts all the jobs, but doesn't wait for anything 1596/* run_pipe_real() starts all the jobs, but doesn't wait for anything
1631 * to finish. See checkjobs(). 1597 * to finish. See checkjobs().
1632 * 1598 *
@@ -1662,7 +1628,7 @@ static int run_pipe_real(struct pipe *pi)
1662#if ENABLE_HUSH_JOB 1628#if ENABLE_HUSH_JOB
1663 pi->pgrp = -1; 1629 pi->pgrp = -1;
1664#endif 1630#endif
1665 pi->running_progs = 0; 1631 pi->running_progs = 1;
1666 pi->stopped_progs = 0; 1632 pi->stopped_progs = 0;
1667 1633
1668 /* Check if this is a simple builtin (not part of a pipe). 1634 /* Check if this is a simple builtin (not part of a pipe).
@@ -1673,8 +1639,6 @@ static int run_pipe_real(struct pipe *pi)
1673 if (single_fg && child->group && child->subshell == 0) { 1639 if (single_fg && child->group && child->subshell == 0) {
1674 debug_printf("non-subshell grouping\n"); 1640 debug_printf("non-subshell grouping\n");
1675 setup_redirects(child, squirrel); 1641 setup_redirects(child, squirrel);
1676 /* XXX could we merge code with following builtin case,
1677 * by creating a pseudo builtin that calls run_list_real? */
1678 debug_printf_exec(": run_list_real\n"); 1642 debug_printf_exec(": run_list_real\n");
1679 rcode = run_list_real(child->group); 1643 rcode = run_list_real(child->group);
1680 restore_redirects(squirrel); 1644 restore_redirects(squirrel);
@@ -1758,8 +1722,9 @@ static int run_pipe_real(struct pipe *pi)
1758 const struct bb_applet *a = find_applet_by_name(argv[i]); 1722 const struct bb_applet *a = find_applet_by_name(argv[i]);
1759 if (a && a->nofork) { 1723 if (a && a->nofork) {
1760 setup_redirects(child, squirrel); 1724 setup_redirects(child, squirrel);
1761 debug_printf_exec(": run_single_fg_nofork '%s' '%s'...\n", argv[i], argv[i+1]); 1725 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv[i], argv[i+1]);
1762 rcode = run_single_fg_nofork(pi, a, argv + i); 1726 save_nofork_data(&nofork_save);
1727 rcode = run_nofork_applet_prime(&nofork_save, a, argv);
1763 restore_redirects(squirrel); 1728 restore_redirects(squirrel);
1764 debug_printf_exec("run_pipe_real return %d\n", rcode); 1729 debug_printf_exec("run_pipe_real return %d\n", rcode);
1765 return rcode; 1730 return rcode;
@@ -1769,6 +1734,7 @@ static int run_pipe_real(struct pipe *pi)
1769 } 1734 }
1770 1735
1771 /* Going to fork a child per each pipe member */ 1736 /* Going to fork a child per each pipe member */
1737 pi->running_progs = 0;
1772 1738
1773 /* Disable job control signals for shell (parent) and 1739 /* Disable job control signals for shell (parent) and
1774 * for initial child code after fork */ 1740 * for initial child code after fork */
@@ -1865,26 +1831,26 @@ static int run_pipe_real(struct pipe *pi)
1865static void debug_print_tree(struct pipe *pi, int lvl) 1831static void debug_print_tree(struct pipe *pi, int lvl)
1866{ 1832{
1867 static const char *PIPE[] = { 1833 static const char *PIPE[] = {
1868 [PIPE_SEQ] = "SEQ", 1834 [PIPE_SEQ] = "SEQ",
1869 [PIPE_AND] = "AND", 1835 [PIPE_AND] = "AND",
1870 [PIPE_OR ] = "OR", 1836 [PIPE_OR ] = "OR",
1871 [PIPE_BG ] = "BG", 1837 [PIPE_BG ] = "BG",
1872 }; 1838 };
1873 static const char *RES[] = { 1839 static const char *RES[] = {
1874 [RES_NONE ] = "NONE" , 1840 [RES_NONE ] = "NONE" ,
1875 [RES_IF ] = "IF" , 1841 [RES_IF ] = "IF" ,
1876 [RES_THEN ] = "THEN" , 1842 [RES_THEN ] = "THEN" ,
1877 [RES_ELIF ] = "ELIF" , 1843 [RES_ELIF ] = "ELIF" ,
1878 [RES_ELSE ] = "ELSE" , 1844 [RES_ELSE ] = "ELSE" ,
1879 [RES_FI ] = "FI" , 1845 [RES_FI ] = "FI" ,
1880 [RES_FOR ] = "FOR" , 1846 [RES_FOR ] = "FOR" ,
1881 [RES_WHILE] = "WHILE", 1847 [RES_WHILE] = "WHILE",
1882 [RES_UNTIL] = "UNTIL", 1848 [RES_UNTIL] = "UNTIL",
1883 [RES_DO ] = "DO" , 1849 [RES_DO ] = "DO" ,
1884 [RES_DONE ] = "DONE" , 1850 [RES_DONE ] = "DONE" ,
1885 [RES_XXXX ] = "XXXX" , 1851 [RES_XXXX ] = "XXXX" ,
1886 [RES_IN ] = "IN" , 1852 [RES_IN ] = "IN" ,
1887 [RES_SNTX ] = "SNTX" , 1853 [RES_SNTX ] = "SNTX" ,
1888 }; 1854 };
1889 1855
1890 int pin, prn; 1856 int pin, prn;
@@ -1897,7 +1863,9 @@ static void debug_print_tree(struct pipe *pi, int lvl)
1897 while (prn < pi->num_progs) { 1863 while (prn < pi->num_progs) {
1898 fprintf(stderr, "%*s prog %d", lvl*2, "", prn); 1864 fprintf(stderr, "%*s prog %d", lvl*2, "", prn);
1899 if (pi->progs[prn].group) { 1865 if (pi->progs[prn].group) {
1900 fprintf(stderr, " group: (argv=%p)\n", pi->progs[prn].argv); 1866 fprintf(stderr, " group %s: (argv=%p)\n",
1867 (pi->subshell ? "()" : "{}"),
1868 pi->progs[prn].argv);
1901 debug_print_tree(pi->progs[prn].group, lvl+1); 1869 debug_print_tree(pi->progs[prn].group, lvl+1);
1902 prn++; 1870 prn++;
1903 continue; 1871 continue;
@@ -1920,18 +1888,25 @@ static void debug_print_tree(struct pipe *pi, int lvl)
1920// global data until exec/_exit (we can be a child after vfork!) 1888// global data until exec/_exit (we can be a child after vfork!)
1921static int run_list_real(struct pipe *pi) 1889static int run_list_real(struct pipe *pi)
1922{ 1890{
1891#if ENABLE_HUSH_JOB
1892 static int level;
1893#else
1894 enum { level = 0 };
1895#endif
1896
1923 char *save_name = NULL; 1897 char *save_name = NULL;
1924 char **list = NULL; 1898 char **list = NULL;
1925 char **save_list = NULL; 1899 char **save_list = NULL;
1926 struct pipe *rpipe; 1900 struct pipe *rpipe;
1927 int flag_rep = 0; 1901 int flag_rep = 0;
1928 int save_num_progs; 1902 int save_num_progs;
1929 int rcode = 0, flag_skip = 1; 1903 int flag_skip = 1;
1904 int rcode = 0; /* probaly for gcc only */
1930 int flag_restore = 0; 1905 int flag_restore = 0;
1931 int if_code = 0, next_if_code = 0; /* need double-buffer to handle elif */ 1906 int if_code = 0, next_if_code = 0; /* need double-buffer to handle elif */
1932 reserved_style rmode, skip_more_in_this_rmode = RES_XXXX; 1907 reserved_style rmode, skip_more_in_this_rmode = RES_XXXX;
1933 1908
1934 debug_printf_exec("run_list_real start:\n"); 1909 debug_printf_exec("run_list_real start lvl %d\n", level + 1);
1935 1910
1936 /* check syntax for "for" */ 1911 /* check syntax for "for" */
1937 for (rpipe = pi; rpipe; rpipe = rpipe->next) { 1912 for (rpipe = pi; rpipe; rpipe = rpipe->next) {
@@ -1939,17 +1914,60 @@ static int run_list_real(struct pipe *pi)
1939 && (rpipe->next == NULL) 1914 && (rpipe->next == NULL)
1940 ) { 1915 ) {
1941 syntax(); 1916 syntax();
1942 debug_printf_exec("run_list_real return 1\n"); 1917 debug_printf_exec("run_list_real lvl %d return 1\n", level);
1943 return 1; 1918 return 1;
1944 } 1919 }
1945 if ((rpipe->r_mode == RES_IN && rpipe->next->r_mode == RES_IN && rpipe->next->progs->argv != NULL) 1920 if ((rpipe->r_mode == RES_IN && rpipe->next->r_mode == RES_IN && rpipe->next->progs->argv != NULL)
1946 || (rpipe->r_mode == RES_FOR && rpipe->next->r_mode != RES_IN) 1921 || (rpipe->r_mode == RES_FOR && rpipe->next->r_mode != RES_IN)
1947 ) { 1922 ) {
1948 syntax(); 1923 syntax();
1949 debug_printf_exec("run_list_real return 1\n"); 1924 debug_printf_exec("run_list_real lvl %d return 1\n", level);
1950 return 1; 1925 return 1;
1951 } 1926 }
1952 } 1927 }
1928
1929#if ENABLE_HUSH_JOB
1930 /* Example of nested list: "while true; do { sleep 1 | exit 2; } done".
1931 * We are saving state before entering outermost list ("while...done")
1932 * so that ctrl-Z will correctly background _entire_ outermost list,
1933 * not just a part of it (like "sleep 1 | exit 2") */
1934 if (++level == 1 && interactive_fd) {
1935 if (sigsetjmp(toplevel_jb, 1)) {
1936 /* ctrl-Z forked and we are parent; or ctrl-C.
1937 * Sighandler has longjmped us here */
1938 signal(SIGINT, SIG_IGN);
1939 signal(SIGTSTP, SIG_IGN);
1940 /* Restore level (we can be coming from deep inside
1941 * nested levels) */
1942 level = 1;
1943#if ENABLE_FEATURE_SH_STANDALONE
1944 if (nofork_save.saved) { /* if save area is valid */
1945 debug_printf_jobs("exiting nofork early\n");
1946 restore_nofork_data(&nofork_save);
1947 }
1948#endif
1949 if (ctrl_z_flag) {
1950 /* ctrl-Z has forked and stored pid of the child in pi->pid.
1951 * Remember this child as background job */
1952 insert_bg_job(pi);
1953 } else {
1954 /* ctrl-C. We just stop doing whatever we was doing */
1955 putchar('\n');
1956 }
1957 rcode = 0;
1958 goto ret;
1959 }
1960 /* ctrl-Z handler will store pid etc in pi */
1961 toplevel_list = pi;
1962 ctrl_z_flag = 0;
1963#if ENABLE_FEATURE_SH_STANDALONE
1964 nofork_save.saved = 0; /* in case we will run a nofork later */
1965#endif
1966 signal_SA_RESTART(SIGTSTP, handler_ctrl_z);
1967 signal(SIGINT, handler_ctrl_c);
1968 }
1969#endif
1970
1953 for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) { 1971 for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) {
1954 if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL 1972 if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL
1955 || pi->r_mode == RES_FOR 1973 || pi->r_mode == RES_FOR
@@ -1961,7 +1979,7 @@ static int run_list_real(struct pipe *pi)
1961 } 1979 }
1962 } 1980 }
1963 rmode = pi->r_mode; 1981 rmode = pi->r_mode;
1964 debug_printf("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n", 1982 debug_printf_exec(": rmode=%d if_code=%d next_if_code=%d skip_more=%d\n",
1965 rmode, if_code, next_if_code, skip_more_in_this_rmode); 1983 rmode, if_code, next_if_code, skip_more_in_this_rmode);
1966 if (rmode == skip_more_in_this_rmode && flag_skip) { 1984 if (rmode == skip_more_in_this_rmode && flag_skip) {
1967 if (pi->followup == PIPE_SEQ) 1985 if (pi->followup == PIPE_SEQ)
@@ -2044,9 +2062,9 @@ static int run_list_real(struct pipe *pi)
2044 { 2062 {
2045 rcode = checkjobs(pi); 2063 rcode = checkjobs(pi);
2046 } 2064 }
2047 debug_printf_exec("checkjobs returned %d\n", rcode); 2065 debug_printf_exec(": checkjobs returned %d\n", rcode);
2048 } 2066 }
2049 debug_printf_exec("setting last_return_code=%d\n", rcode); 2067 debug_printf_exec(": setting last_return_code=%d\n", rcode);
2050 last_return_code = rcode; 2068 last_return_code = rcode;
2051 pi->num_progs = save_num_progs; /* restore number of programs */ 2069 pi->num_progs = save_num_progs; /* restore number of programs */
2052 if (rmode == RES_IF || rmode == RES_ELIF) 2070 if (rmode == RES_IF || rmode == RES_ELIF)
@@ -2062,7 +2080,17 @@ static int run_list_real(struct pipe *pi)
2062 } 2080 }
2063 checkjobs(NULL); 2081 checkjobs(NULL);
2064 } 2082 }
2065 debug_printf_exec("run_list_real return %d\n", rcode); 2083
2084#if ENABLE_HUSH_JOB
2085 if (ctrl_z_flag) {
2086 /* ctrl-Z forked somewhere in the past, we are the child,
2087 * and now we completed running the list. Exit. */
2088 exit(rcode);
2089 }
2090 ret:
2091 level--;
2092#endif
2093 debug_printf_exec("run_list_real lvl %d return %d\n", level + 1, rcode);
2066 return rcode; 2094 return rcode;
2067} 2095}
2068 2096