diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 1139 |
1 files changed, 754 insertions, 385 deletions
diff --git a/shell/hush.c b/shell/hush.c index eabe83ac6..9b51f389e 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -103,6 +103,9 @@ | |||
103 | #else | 103 | #else |
104 | # define CLEAR_RANDOM_T(rnd) ((void)0) | 104 | # define CLEAR_RANDOM_T(rnd) ((void)0) |
105 | #endif | 105 | #endif |
106 | #ifndef F_DUPFD_CLOEXEC | ||
107 | # define F_DUPFD_CLOEXEC F_DUPFD | ||
108 | #endif | ||
106 | #ifndef PIPE_BUF | 109 | #ifndef PIPE_BUF |
107 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ | 110 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ |
108 | #endif | 111 | #endif |
@@ -459,19 +462,17 @@ static const char *const assignment_flag[] = { | |||
459 | 462 | ||
460 | typedef struct in_str { | 463 | typedef struct in_str { |
461 | const char *p; | 464 | const char *p; |
462 | /* eof_flag=1: last char in ->p is really an EOF */ | ||
463 | char eof_flag; /* meaningless if ->p == NULL */ | ||
464 | char peek_buf[2]; | ||
465 | #if ENABLE_HUSH_INTERACTIVE | 465 | #if ENABLE_HUSH_INTERACTIVE |
466 | smallint promptmode; /* 0: PS1, 1: PS2 */ | 466 | smallint promptmode; /* 0: PS1, 1: PS2 */ |
467 | #endif | 467 | #endif |
468 | int peek_buf[2]; | ||
468 | int last_char; | 469 | int last_char; |
469 | FILE *file; | 470 | FILE *file; |
470 | int (*get) (struct in_str *) FAST_FUNC; | 471 | int (*get) (struct in_str *) FAST_FUNC; |
471 | int (*peek) (struct in_str *) FAST_FUNC; | 472 | int (*peek) (struct in_str *) FAST_FUNC; |
472 | } in_str; | 473 | } in_str; |
473 | #define i_getch(input) ((input)->get(input)) | 474 | #define i_getch(input) ((input)->get(input)) |
474 | #define i_peek(input) ((input)->peek(input)) | 475 | #define i_peek(input) ((input)->peek(input)) |
475 | 476 | ||
476 | /* The descrip member of this structure is only used to make | 477 | /* The descrip member of this structure is only used to make |
477 | * debugging output pretty */ | 478 | * debugging output pretty */ |
@@ -711,6 +712,13 @@ enum { | |||
711 | }; | 712 | }; |
712 | 713 | ||
713 | 714 | ||
715 | struct FILE_list { | ||
716 | struct FILE_list *next; | ||
717 | FILE *fp; | ||
718 | int fd; | ||
719 | }; | ||
720 | |||
721 | |||
714 | /* "Globals" within this file */ | 722 | /* "Globals" within this file */ |
715 | /* Sorted roughly by size (smaller offsets == smaller code) */ | 723 | /* Sorted roughly by size (smaller offsets == smaller code) */ |
716 | struct globals { | 724 | struct globals { |
@@ -769,6 +777,9 @@ struct globals { | |||
769 | * 1: return is invoked, skip all till end of func | 777 | * 1: return is invoked, skip all till end of func |
770 | */ | 778 | */ |
771 | smallint flag_return_in_progress; | 779 | smallint flag_return_in_progress; |
780 | # define G_flag_return_in_progress (G.flag_return_in_progress) | ||
781 | #else | ||
782 | # define G_flag_return_in_progress 0 | ||
772 | #endif | 783 | #endif |
773 | smallint exiting; /* used to prevent EXIT trap recursion */ | 784 | smallint exiting; /* used to prevent EXIT trap recursion */ |
774 | /* These four support $?, $#, and $1 */ | 785 | /* These four support $?, $#, and $1 */ |
@@ -802,6 +813,7 @@ struct globals { | |||
802 | unsigned handled_SIGCHLD; | 813 | unsigned handled_SIGCHLD; |
803 | smallint we_have_children; | 814 | smallint we_have_children; |
804 | #endif | 815 | #endif |
816 | struct FILE_list *FILE_list; | ||
805 | /* Which signals have non-DFL handler (even with no traps set)? | 817 | /* Which signals have non-DFL handler (even with no traps set)? |
806 | * Set at the start to: | 818 | * Set at the start to: |
807 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) | 819 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) |
@@ -823,7 +835,9 @@ struct globals { | |||
823 | int debug_indent; | 835 | int debug_indent; |
824 | #endif | 836 | #endif |
825 | struct sigaction sa; | 837 | struct sigaction sa; |
826 | char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; | 838 | #if ENABLE_FEATURE_EDITING |
839 | char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN]; | ||
840 | #endif | ||
827 | }; | 841 | }; |
828 | #define G (*ptr_to_globals) | 842 | #define G (*ptr_to_globals) |
829 | /* Not #defining name to G.name - this quickly gets unwieldy | 843 | /* Not #defining name to G.name - this quickly gets unwieldy |
@@ -1252,6 +1266,91 @@ static void free_strings(char **strings) | |||
1252 | } | 1266 | } |
1253 | 1267 | ||
1254 | 1268 | ||
1269 | static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC) | ||
1270 | { | ||
1271 | /* We avoid taking stdio fds. Mimicking ash: use fds above 9 */ | ||
1272 | int newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, 10); | ||
1273 | if (newfd < 0) { | ||
1274 | /* fd was not open? */ | ||
1275 | if (errno == EBADF) | ||
1276 | return fd; | ||
1277 | xfunc_die(); | ||
1278 | } | ||
1279 | close(fd); | ||
1280 | return newfd; | ||
1281 | } | ||
1282 | |||
1283 | |||
1284 | /* Manipulating the list of open FILEs */ | ||
1285 | static FILE *remember_FILE(FILE *fp) | ||
1286 | { | ||
1287 | if (fp) { | ||
1288 | struct FILE_list *n = xmalloc(sizeof(*n)); | ||
1289 | n->next = G.FILE_list; | ||
1290 | G.FILE_list = n; | ||
1291 | n->fp = fp; | ||
1292 | n->fd = fileno(fp); | ||
1293 | close_on_exec_on(n->fd); | ||
1294 | } | ||
1295 | return fp; | ||
1296 | } | ||
1297 | static void fclose_and_forget(FILE *fp) | ||
1298 | { | ||
1299 | struct FILE_list **pp = &G.FILE_list; | ||
1300 | while (*pp) { | ||
1301 | struct FILE_list *cur = *pp; | ||
1302 | if (cur->fp == fp) { | ||
1303 | *pp = cur->next; | ||
1304 | free(cur); | ||
1305 | break; | ||
1306 | } | ||
1307 | pp = &cur->next; | ||
1308 | } | ||
1309 | fclose(fp); | ||
1310 | } | ||
1311 | static int save_FILEs_on_redirect(int fd) | ||
1312 | { | ||
1313 | struct FILE_list *fl = G.FILE_list; | ||
1314 | while (fl) { | ||
1315 | if (fd == fl->fd) { | ||
1316 | /* We use it only on script files, they are all CLOEXEC */ | ||
1317 | fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC); | ||
1318 | return 1; | ||
1319 | } | ||
1320 | fl = fl->next; | ||
1321 | } | ||
1322 | return 0; | ||
1323 | } | ||
1324 | static void restore_redirected_FILEs(void) | ||
1325 | { | ||
1326 | struct FILE_list *fl = G.FILE_list; | ||
1327 | while (fl) { | ||
1328 | int should_be = fileno(fl->fp); | ||
1329 | if (fl->fd != should_be) { | ||
1330 | xmove_fd(fl->fd, should_be); | ||
1331 | fl->fd = should_be; | ||
1332 | } | ||
1333 | fl = fl->next; | ||
1334 | } | ||
1335 | } | ||
1336 | #if ENABLE_FEATURE_SH_STANDALONE | ||
1337 | static void close_all_FILE_list(void) | ||
1338 | { | ||
1339 | struct FILE_list *fl = G.FILE_list; | ||
1340 | while (fl) { | ||
1341 | /* fclose would also free FILE object. | ||
1342 | * It is disastrous if we share memory with a vforked parent. | ||
1343 | * I'm not sure we never come here after vfork. | ||
1344 | * Therefore just close fd, nothing more. | ||
1345 | */ | ||
1346 | /*fclose(fl->fp); - unsafe */ | ||
1347 | close(fl->fd); | ||
1348 | fl = fl->next; | ||
1349 | } | ||
1350 | } | ||
1351 | #endif | ||
1352 | |||
1353 | |||
1255 | /* Helpers for setting new $n and restoring them back | 1354 | /* Helpers for setting new $n and restoring them back |
1256 | */ | 1355 | */ |
1257 | typedef struct save_arg_t { | 1356 | typedef struct save_arg_t { |
@@ -1477,19 +1576,50 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler) | |||
1477 | return old_sa.sa_handler; | 1576 | return old_sa.sa_handler; |
1478 | } | 1577 | } |
1479 | 1578 | ||
1579 | static void hush_exit(int exitcode) NORETURN; | ||
1580 | static void fflush_and__exit(void) NORETURN; | ||
1581 | static void restore_ttypgrp_and__exit(void) NORETURN; | ||
1582 | |||
1583 | static void restore_ttypgrp_and__exit(void) | ||
1584 | { | ||
1585 | /* xfunc has failed! die die die */ | ||
1586 | /* no EXIT traps, this is an escape hatch! */ | ||
1587 | G.exiting = 1; | ||
1588 | hush_exit(xfunc_error_retval); | ||
1589 | } | ||
1590 | |||
1591 | /* Needed only on some libc: | ||
1592 | * It was observed that on exit(), fgetc'ed buffered data | ||
1593 | * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). | ||
1594 | * With the net effect that even after fork(), not vfork(), | ||
1595 | * exit() in NOEXECed applet in "sh SCRIPT": | ||
1596 | * noexec_applet_here | ||
1597 | * echo END_OF_SCRIPT | ||
1598 | * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". | ||
1599 | * This makes "echo END_OF_SCRIPT" executed twice. | ||
1600 | * Similar problems can be seen with die_if_script() -> xfunc_die() | ||
1601 | * and in `cmd` handling. | ||
1602 | * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): | ||
1603 | */ | ||
1604 | static void fflush_and__exit(void) | ||
1605 | { | ||
1606 | fflush_all(); | ||
1607 | _exit(xfunc_error_retval); | ||
1608 | } | ||
1609 | |||
1480 | #if ENABLE_HUSH_JOB | 1610 | #if ENABLE_HUSH_JOB |
1481 | 1611 | ||
1482 | static void xfunc_has_died(void); | ||
1483 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ | 1612 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ |
1484 | # define disable_restore_tty_pgrp_on_exit() (die_func = NULL) | 1613 | # define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit) |
1485 | /* After [v]fork, in parent: restore tty pgrp on xfunc death */ | 1614 | /* After [v]fork, in parent: restore tty pgrp on xfunc death */ |
1486 | # define enable_restore_tty_pgrp_on_exit() (die_func = xfunc_has_died) | 1615 | # define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit) |
1487 | 1616 | ||
1488 | /* Restores tty foreground process group, and exits. | 1617 | /* Restores tty foreground process group, and exits. |
1489 | * May be called as signal handler for fatal signal | 1618 | * May be called as signal handler for fatal signal |
1490 | * (will resend signal to itself, producing correct exit state) | 1619 | * (will resend signal to itself, producing correct exit state) |
1491 | * or called directly with -EXITCODE. | 1620 | * or called directly with -EXITCODE. |
1492 | * We also call it if xfunc is exiting. */ | 1621 | * We also call it if xfunc is exiting. |
1622 | */ | ||
1493 | static void sigexit(int sig) NORETURN; | 1623 | static void sigexit(int sig) NORETURN; |
1494 | static void sigexit(int sig) | 1624 | static void sigexit(int sig) |
1495 | { | 1625 | { |
@@ -1544,7 +1674,6 @@ static sighandler_t pick_sighandler(unsigned sig) | |||
1544 | } | 1674 | } |
1545 | 1675 | ||
1546 | /* Restores tty foreground process group, and exits. */ | 1676 | /* Restores tty foreground process group, and exits. */ |
1547 | static void hush_exit(int exitcode) NORETURN; | ||
1548 | static void hush_exit(int exitcode) | 1677 | static void hush_exit(int exitcode) |
1549 | { | 1678 | { |
1550 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | 1679 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT |
@@ -1580,23 +1709,14 @@ static void hush_exit(int exitcode) | |||
1580 | } | 1709 | } |
1581 | #endif | 1710 | #endif |
1582 | 1711 | ||
1583 | #if ENABLE_HUSH_JOB | ||
1584 | fflush_all(); | 1712 | fflush_all(); |
1713 | #if ENABLE_HUSH_JOB | ||
1585 | sigexit(- (exitcode & 0xff)); | 1714 | sigexit(- (exitcode & 0xff)); |
1586 | #else | 1715 | #else |
1587 | exit(exitcode); | 1716 | _exit(exitcode); |
1588 | #endif | 1717 | #endif |
1589 | } | 1718 | } |
1590 | 1719 | ||
1591 | static void xfunc_has_died(void) NORETURN; | ||
1592 | static void xfunc_has_died(void) | ||
1593 | { | ||
1594 | /* xfunc has failed! die die die */ | ||
1595 | /* no EXIT traps, this is an escape hatch! */ | ||
1596 | G.exiting = 1; | ||
1597 | hush_exit(xfunc_error_retval); | ||
1598 | } | ||
1599 | |||
1600 | 1720 | ||
1601 | //TODO: return a mask of ALL handled sigs? | 1721 | //TODO: return a mask of ALL handled sigs? |
1602 | static int check_and_run_traps(void) | 1722 | static int check_and_run_traps(void) |
@@ -1619,6 +1739,7 @@ static int check_and_run_traps(void) | |||
1619 | break; | 1739 | break; |
1620 | got_sig: | 1740 | got_sig: |
1621 | if (G.traps && G.traps[sig]) { | 1741 | if (G.traps && G.traps[sig]) { |
1742 | debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]); | ||
1622 | if (G.traps[sig][0]) { | 1743 | if (G.traps[sig][0]) { |
1623 | /* We have user-defined handler */ | 1744 | /* We have user-defined handler */ |
1624 | smalluint save_rcode; | 1745 | smalluint save_rcode; |
@@ -1636,6 +1757,7 @@ static int check_and_run_traps(void) | |||
1636 | /* not a trap: special action */ | 1757 | /* not a trap: special action */ |
1637 | switch (sig) { | 1758 | switch (sig) { |
1638 | case SIGINT: | 1759 | case SIGINT: |
1760 | debug_printf_exec("%s: sig:%d default SIGINT handler\n", __func__, sig); | ||
1639 | /* Builtin was ^C'ed, make it look prettier: */ | 1761 | /* Builtin was ^C'ed, make it look prettier: */ |
1640 | bb_putchar('\n'); | 1762 | bb_putchar('\n'); |
1641 | G.flag_SIGINT = 1; | 1763 | G.flag_SIGINT = 1; |
@@ -1644,6 +1766,7 @@ static int check_and_run_traps(void) | |||
1644 | #if ENABLE_HUSH_JOB | 1766 | #if ENABLE_HUSH_JOB |
1645 | case SIGHUP: { | 1767 | case SIGHUP: { |
1646 | struct pipe *job; | 1768 | struct pipe *job; |
1769 | debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig); | ||
1647 | /* bash is observed to signal whole process groups, | 1770 | /* bash is observed to signal whole process groups, |
1648 | * not individual processes */ | 1771 | * not individual processes */ |
1649 | for (job = G.job_list; job; job = job->next) { | 1772 | for (job = G.job_list; job; job = job->next) { |
@@ -1658,6 +1781,7 @@ static int check_and_run_traps(void) | |||
1658 | #endif | 1781 | #endif |
1659 | #if ENABLE_HUSH_FAST | 1782 | #if ENABLE_HUSH_FAST |
1660 | case SIGCHLD: | 1783 | case SIGCHLD: |
1784 | debug_printf_exec("%s: sig:%d default SIGCHLD handler\n", __func__, sig); | ||
1661 | G.count_SIGCHLD++; | 1785 | G.count_SIGCHLD++; |
1662 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | 1786 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); |
1663 | /* Note: | 1787 | /* Note: |
@@ -1667,6 +1791,7 @@ static int check_and_run_traps(void) | |||
1667 | break; | 1791 | break; |
1668 | #endif | 1792 | #endif |
1669 | default: /* ignored: */ | 1793 | default: /* ignored: */ |
1794 | debug_printf_exec("%s: sig:%d default handling is to ignore\n", __func__, sig); | ||
1670 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ | 1795 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ |
1671 | /* Note: | 1796 | /* Note: |
1672 | * We dont do 'last_sig = sig' here -> NOT returning this sig. | 1797 | * We dont do 'last_sig = sig' here -> NOT returning this sig. |
@@ -1766,6 +1891,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
1766 | { | 1891 | { |
1767 | struct variable **var_pp; | 1892 | struct variable **var_pp; |
1768 | struct variable *cur; | 1893 | struct variable *cur; |
1894 | char *free_me = NULL; | ||
1769 | char *eq_sign; | 1895 | char *eq_sign; |
1770 | int name_len; | 1896 | int name_len; |
1771 | 1897 | ||
@@ -1782,6 +1908,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
1782 | var_pp = &cur->next; | 1908 | var_pp = &cur->next; |
1783 | continue; | 1909 | continue; |
1784 | } | 1910 | } |
1911 | |||
1785 | /* We found an existing var with this name */ | 1912 | /* We found an existing var with this name */ |
1786 | if (cur->flg_read_only) { | 1913 | if (cur->flg_read_only) { |
1787 | #if !BB_MMU | 1914 | #if !BB_MMU |
@@ -1830,12 +1957,17 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
1830 | strcpy(cur->varstr, str); | 1957 | strcpy(cur->varstr, str); |
1831 | goto free_and_exp; | 1958 | goto free_and_exp; |
1832 | } | 1959 | } |
1833 | } else { | 1960 | /* Can't reuse */ |
1834 | /* max_len == 0 signifies "malloced" var, which we can | 1961 | cur->max_len = 0; |
1835 | * (and has to) free */ | 1962 | goto set_str_and_exp; |
1836 | free(cur->varstr); | 1963 | } |
1837 | } | 1964 | /* max_len == 0 signifies "malloced" var, which we can |
1838 | cur->max_len = 0; | 1965 | * (and have to) free. But we can't free(cur->varstr) here: |
1966 | * if cur->flg_export is 1, it is in the environment. | ||
1967 | * We should either unsetenv+free, or wait until putenv, | ||
1968 | * then putenv(new)+free(old). | ||
1969 | */ | ||
1970 | free_me = cur->varstr; | ||
1839 | goto set_str_and_exp; | 1971 | goto set_str_and_exp; |
1840 | } | 1972 | } |
1841 | 1973 | ||
@@ -1862,10 +1994,15 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ | |||
1862 | cur->flg_export = 0; | 1994 | cur->flg_export = 0; |
1863 | /* unsetenv was already done */ | 1995 | /* unsetenv was already done */ |
1864 | } else { | 1996 | } else { |
1997 | int i; | ||
1865 | debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); | 1998 | debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); |
1866 | return putenv(cur->varstr); | 1999 | i = putenv(cur->varstr); |
2000 | /* only now we can free old exported malloced string */ | ||
2001 | free(free_me); | ||
2002 | return i; | ||
1867 | } | 2003 | } |
1868 | } | 2004 | } |
2005 | free(free_me); | ||
1869 | return 0; | 2006 | return 0; |
1870 | } | 2007 | } |
1871 | 2008 | ||
@@ -2005,28 +2142,19 @@ static void reinit_unicode_for_hush(void) | |||
2005 | } | 2142 | } |
2006 | } | 2143 | } |
2007 | 2144 | ||
2008 | |||
2009 | /* | 2145 | /* |
2010 | * in_str support | 2146 | * in_str support (strings, and "strings" read from files). |
2011 | */ | 2147 | */ |
2012 | static int FAST_FUNC static_get(struct in_str *i) | ||
2013 | { | ||
2014 | int ch = *i->p; | ||
2015 | if (ch != '\0') { | ||
2016 | i->p++; | ||
2017 | i->last_char = ch; | ||
2018 | return ch; | ||
2019 | } | ||
2020 | return EOF; | ||
2021 | } | ||
2022 | |||
2023 | static int FAST_FUNC static_peek(struct in_str *i) | ||
2024 | { | ||
2025 | return *i->p; | ||
2026 | } | ||
2027 | 2148 | ||
2028 | #if ENABLE_HUSH_INTERACTIVE | 2149 | #if ENABLE_HUSH_INTERACTIVE |
2029 | 2150 | /* To test correct lineedit/interactive behavior, type from command line: | |
2151 | * echo $P\ | ||
2152 | * \ | ||
2153 | * AT\ | ||
2154 | * H\ | ||
2155 | * \ | ||
2156 | * It excercises a lot of corner cases. | ||
2157 | */ | ||
2030 | static void cmdedit_update_prompt(void) | 2158 | static void cmdedit_update_prompt(void) |
2031 | { | 2159 | { |
2032 | if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { | 2160 | if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { |
@@ -2040,7 +2168,6 @@ static void cmdedit_update_prompt(void) | |||
2040 | if (G.PS2 == NULL) | 2168 | if (G.PS2 == NULL) |
2041 | G.PS2 = "> "; | 2169 | G.PS2 = "> "; |
2042 | } | 2170 | } |
2043 | |||
2044 | static const char *setup_prompt_string(int promptmode) | 2171 | static const char *setup_prompt_string(int promptmode) |
2045 | { | 2172 | { |
2046 | const char *prompt_str; | 2173 | const char *prompt_str; |
@@ -2058,33 +2185,36 @@ static const char *setup_prompt_string(int promptmode) | |||
2058 | prompt_str = G.PS2; | 2185 | prompt_str = G.PS2; |
2059 | } else | 2186 | } else |
2060 | prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; | 2187 | prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; |
2061 | debug_printf("result '%s'\n", prompt_str); | 2188 | debug_printf("prompt_str '%s'\n", prompt_str); |
2062 | return prompt_str; | 2189 | return prompt_str; |
2063 | } | 2190 | } |
2064 | 2191 | static int get_user_input(struct in_str *i) | |
2065 | static void get_user_input(struct in_str *i) | ||
2066 | { | 2192 | { |
2067 | int r; | 2193 | int r; |
2068 | const char *prompt_str; | 2194 | const char *prompt_str; |
2069 | 2195 | ||
2070 | prompt_str = setup_prompt_string(i->promptmode); | 2196 | prompt_str = setup_prompt_string(i->promptmode); |
2071 | # if ENABLE_FEATURE_EDITING | 2197 | # if ENABLE_FEATURE_EDITING |
2072 | /* Enable command line editing only while a command line | ||
2073 | * is actually being read */ | ||
2074 | do { | 2198 | do { |
2075 | reinit_unicode_for_hush(); | 2199 | reinit_unicode_for_hush(); |
2076 | G.flag_SIGINT = 0; | 2200 | G.flag_SIGINT = 0; |
2077 | /* buglet: SIGINT will not make new prompt to appear _at once_, | 2201 | /* buglet: SIGINT will not make new prompt to appear _at once_, |
2078 | * only after <Enter>. (^C will work) */ | 2202 | * only after <Enter>. (^C will work) */ |
2079 | r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); | 2203 | r = read_line_input(G.line_input_state, prompt_str, |
2204 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, | ||
2205 | /*timeout*/ -1 | ||
2206 | ); | ||
2080 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ | 2207 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ |
2081 | check_and_run_traps(); | 2208 | check_and_run_traps(); |
2082 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ | 2209 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ |
2083 | i->eof_flag = (r < 0); | 2210 | if (r < 0) { |
2084 | if (i->eof_flag) { /* EOF/error detected */ | 2211 | /* EOF/error detected */ |
2085 | G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ | 2212 | i->p = NULL; |
2086 | G.user_input_buf[1] = '\0'; | 2213 | i->peek_buf[0] = r = EOF; |
2214 | return r; | ||
2087 | } | 2215 | } |
2216 | i->p = G.user_input_buf; | ||
2217 | return (unsigned char)*i->p++; | ||
2088 | # else | 2218 | # else |
2089 | do { | 2219 | do { |
2090 | G.flag_SIGINT = 0; | 2220 | G.flag_SIGINT = 0; |
@@ -2098,76 +2228,149 @@ static void get_user_input(struct in_str *i) | |||
2098 | fputs(prompt_str, stdout); | 2228 | fputs(prompt_str, stdout); |
2099 | } | 2229 | } |
2100 | fflush_all(); | 2230 | fflush_all(); |
2101 | G.user_input_buf[0] = r = fgetc(i->file); | 2231 | r = fgetc(i->file); |
2102 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ | 2232 | } while (G.flag_SIGINT || r == '\0'); |
2103 | } while (G.flag_SIGINT); | 2233 | return r; |
2104 | i->eof_flag = (r == EOF); | ||
2105 | # endif | 2234 | # endif |
2106 | i->p = G.user_input_buf; | ||
2107 | } | 2235 | } |
2108 | |||
2109 | #endif /* INTERACTIVE */ | ||
2110 | |||
2111 | /* This is the magic location that prints prompts | 2236 | /* This is the magic location that prints prompts |
2112 | * and gets data back from the user */ | 2237 | * and gets data back from the user */ |
2238 | static int fgetc_interactive(struct in_str *i) | ||
2239 | { | ||
2240 | int ch; | ||
2241 | /* If it's interactive stdin, get new line. */ | ||
2242 | if (G_interactive_fd && i->file == stdin) { | ||
2243 | /* Returns first char (or EOF), the rest is in i->p[] */ | ||
2244 | ch = get_user_input(i); | ||
2245 | i->promptmode = 1; /* PS2 */ | ||
2246 | } else { | ||
2247 | /* Not stdin: script file, sourced file, etc */ | ||
2248 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2249 | } | ||
2250 | return ch; | ||
2251 | } | ||
2252 | #else | ||
2253 | static inline int fgetc_interactive(struct in_str *i) | ||
2254 | { | ||
2255 | int ch; | ||
2256 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2257 | return ch; | ||
2258 | } | ||
2259 | #endif /* INTERACTIVE */ | ||
2260 | |||
2113 | static int FAST_FUNC file_get(struct in_str *i) | 2261 | static int FAST_FUNC file_get(struct in_str *i) |
2114 | { | 2262 | { |
2115 | int ch; | 2263 | int ch; |
2116 | 2264 | ||
2117 | /* If there is data waiting, eat it up */ | 2265 | #if ENABLE_FEATURE_EDITING |
2118 | if (i->p && *i->p) { | 2266 | /* This can be stdin, check line editing char[] buffer */ |
2119 | #if ENABLE_HUSH_INTERACTIVE | 2267 | if (i->p && *i->p != '\0') { |
2120 | take_cached: | 2268 | ch = (unsigned char)*i->p++; |
2121 | #endif | 2269 | goto out; |
2122 | ch = *i->p++; | 2270 | } |
2123 | if (i->eof_flag && !*i->p) | ||
2124 | ch = EOF; | ||
2125 | /* note: ch is never NUL */ | ||
2126 | } else { | ||
2127 | /* need to double check i->file because we might be doing something | ||
2128 | * more complicated by now, like sourcing or substituting. */ | ||
2129 | #if ENABLE_HUSH_INTERACTIVE | ||
2130 | if (G_interactive_fd && i->file == stdin) { | ||
2131 | do { | ||
2132 | get_user_input(i); | ||
2133 | } while (!*i->p); /* need non-empty line */ | ||
2134 | i->promptmode = 1; /* PS2 */ | ||
2135 | goto take_cached; | ||
2136 | } | ||
2137 | #endif | 2271 | #endif |
2138 | do ch = fgetc(i->file); while (ch == '\0'); | 2272 | /* peek_buf[] is an int array, not char. Can contain EOF. */ |
2273 | ch = i->peek_buf[0]; | ||
2274 | if (ch != 0) { | ||
2275 | int ch2 = i->peek_buf[1]; | ||
2276 | i->peek_buf[0] = ch2; | ||
2277 | if (ch2 == 0) /* very likely, avoid redundant write */ | ||
2278 | goto out; | ||
2279 | i->peek_buf[1] = 0; | ||
2280 | goto out; | ||
2139 | } | 2281 | } |
2282 | |||
2283 | ch = fgetc_interactive(i); | ||
2284 | out: | ||
2140 | debug_printf("file_get: got '%c' %d\n", ch, ch); | 2285 | debug_printf("file_get: got '%c' %d\n", ch, ch); |
2141 | i->last_char = ch; | 2286 | i->last_char = ch; |
2142 | return ch; | 2287 | return ch; |
2143 | } | 2288 | } |
2144 | 2289 | ||
2145 | /* All callers guarantee this routine will never | ||
2146 | * be used right after a newline, so prompting is not needed. | ||
2147 | */ | ||
2148 | static int FAST_FUNC file_peek(struct in_str *i) | 2290 | static int FAST_FUNC file_peek(struct in_str *i) |
2149 | { | 2291 | { |
2150 | int ch; | 2292 | int ch; |
2151 | if (i->p && *i->p) { | 2293 | |
2152 | if (i->eof_flag && !i->p[1]) | 2294 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE |
2153 | return EOF; | 2295 | /* This can be stdin, check line editing char[] buffer */ |
2154 | return *i->p; | 2296 | if (i->p && *i->p != '\0') |
2155 | /* note: ch is never NUL */ | 2297 | return (unsigned char)*i->p; |
2298 | #endif | ||
2299 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | ||
2300 | ch = i->peek_buf[0]; | ||
2301 | if (ch != 0) | ||
2302 | return ch; | ||
2303 | |||
2304 | /* Need to get a new char */ | ||
2305 | ch = fgetc_interactive(i); | ||
2306 | debug_printf("file_peek: got '%c' %d\n", ch, ch); | ||
2307 | |||
2308 | /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */ | ||
2309 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE | ||
2310 | if (i->p) { | ||
2311 | i->p -= 1; | ||
2312 | return ch; | ||
2156 | } | 2313 | } |
2157 | do ch = fgetc(i->file); while (ch == '\0'); | 2314 | #endif |
2158 | i->eof_flag = (ch == EOF); | ||
2159 | i->peek_buf[0] = ch; | 2315 | i->peek_buf[0] = ch; |
2160 | i->peek_buf[1] = '\0'; | 2316 | /*i->peek_buf[1] = 0; - already is */ |
2161 | i->p = i->peek_buf; | 2317 | return ch; |
2162 | debug_printf("file_peek: got '%c' %d\n", ch, ch); | 2318 | } |
2319 | |||
2320 | static int FAST_FUNC static_get(struct in_str *i) | ||
2321 | { | ||
2322 | int ch = (unsigned char)*i->p; | ||
2323 | if (ch != '\0') { | ||
2324 | i->p++; | ||
2325 | i->last_char = ch; | ||
2326 | return ch; | ||
2327 | } | ||
2328 | return EOF; | ||
2329 | } | ||
2330 | |||
2331 | static int FAST_FUNC static_peek(struct in_str *i) | ||
2332 | { | ||
2333 | /* Doesn't report EOF on NUL. None of the callers care. */ | ||
2334 | return (unsigned char)*i->p; | ||
2335 | } | ||
2336 | |||
2337 | /* Only ever called if i_peek() was called, and did not return EOF. | ||
2338 | * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL, | ||
2339 | * not end-of-line. Therefore we never need to read a new editing line here. | ||
2340 | */ | ||
2341 | static int i_peek2(struct in_str *i) | ||
2342 | { | ||
2343 | int ch; | ||
2344 | |||
2345 | /* There are two cases when i->p[] buffer exists. | ||
2346 | * (1) it's a string in_str. | ||
2347 | * (2) It's a file, and we have a saved line editing buffer. | ||
2348 | * In both cases, we know that i->p[0] exists and not NUL, and | ||
2349 | * the peek2 result is in i->p[1]. | ||
2350 | */ | ||
2351 | if (i->p) | ||
2352 | return (unsigned char)i->p[1]; | ||
2353 | |||
2354 | /* Now we know it is a file-based in_str. */ | ||
2355 | |||
2356 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | ||
2357 | /* Is there 2nd char? */ | ||
2358 | ch = i->peek_buf[1]; | ||
2359 | if (ch == 0) { | ||
2360 | /* We did not read it yet, get it now */ | ||
2361 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2362 | i->peek_buf[1] = ch; | ||
2363 | } | ||
2364 | |||
2365 | debug_printf("file_peek2: got '%c' %d\n", ch, ch); | ||
2163 | return ch; | 2366 | return ch; |
2164 | } | 2367 | } |
2165 | 2368 | ||
2166 | static void setup_file_in_str(struct in_str *i, FILE *f) | 2369 | static void setup_file_in_str(struct in_str *i, FILE *f) |
2167 | { | 2370 | { |
2168 | memset(i, 0, sizeof(*i)); | 2371 | memset(i, 0, sizeof(*i)); |
2169 | i->peek = file_peek; | ||
2170 | i->get = file_get; | 2372 | i->get = file_get; |
2373 | i->peek = file_peek; | ||
2171 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2374 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2172 | i->file = f; | 2375 | i->file = f; |
2173 | /* i->p = NULL; */ | 2376 | /* i->p = NULL; */ |
@@ -2176,11 +2379,10 @@ static void setup_file_in_str(struct in_str *i, FILE *f) | |||
2176 | static void setup_string_in_str(struct in_str *i, const char *s) | 2379 | static void setup_string_in_str(struct in_str *i, const char *s) |
2177 | { | 2380 | { |
2178 | memset(i, 0, sizeof(*i)); | 2381 | memset(i, 0, sizeof(*i)); |
2179 | i->peek = static_peek; | ||
2180 | i->get = static_get; | 2382 | i->get = static_get; |
2383 | i->peek = static_peek; | ||
2181 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2384 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2182 | i->p = s; | 2385 | i->p = s; |
2183 | /* i->eof_flag = 0; */ | ||
2184 | } | 2386 | } |
2185 | 2387 | ||
2186 | 2388 | ||
@@ -2211,7 +2413,7 @@ static ALWAYS_INLINE void o_free_unsafe(o_string *o) | |||
2211 | static void o_grow_by(o_string *o, int len) | 2413 | static void o_grow_by(o_string *o, int len) |
2212 | { | 2414 | { |
2213 | if (o->length + len > o->maxlen) { | 2415 | if (o->length + len > o->maxlen) { |
2214 | o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK); | 2416 | o->maxlen += (2 * len) | (B_CHUNK-1); |
2215 | o->data = xrealloc(o->data, 1 + o->maxlen); | 2417 | o->data = xrealloc(o->data, 1 + o->maxlen); |
2216 | } | 2418 | } |
2217 | } | 2419 | } |
@@ -2219,11 +2421,26 @@ static void o_grow_by(o_string *o, int len) | |||
2219 | static void o_addchr(o_string *o, int ch) | 2421 | static void o_addchr(o_string *o, int ch) |
2220 | { | 2422 | { |
2221 | debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); | 2423 | debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); |
2424 | if (o->length < o->maxlen) { | ||
2425 | /* likely. avoid o_grow_by() call */ | ||
2426 | add: | ||
2427 | o->data[o->length] = ch; | ||
2428 | o->length++; | ||
2429 | o->data[o->length] = '\0'; | ||
2430 | return; | ||
2431 | } | ||
2222 | o_grow_by(o, 1); | 2432 | o_grow_by(o, 1); |
2223 | o->data[o->length] = ch; | 2433 | goto add; |
2224 | o->length++; | 2434 | } |
2435 | |||
2436 | #if 0 | ||
2437 | /* Valid only if we know o_string is not empty */ | ||
2438 | static void o_delchr(o_string *o) | ||
2439 | { | ||
2440 | o->length--; | ||
2225 | o->data[o->length] = '\0'; | 2441 | o->data[o->length] = '\0'; |
2226 | } | 2442 | } |
2443 | #endif | ||
2227 | 2444 | ||
2228 | static void o_addblock(o_string *o, const char *str, int len) | 2445 | static void o_addblock(o_string *o, const char *str, int len) |
2229 | { | 2446 | { |
@@ -2937,6 +3154,14 @@ static int done_command(struct parse_context *ctx) | |||
2937 | struct pipe *pi = ctx->pipe; | 3154 | struct pipe *pi = ctx->pipe; |
2938 | struct command *command = ctx->command; | 3155 | struct command *command = ctx->command; |
2939 | 3156 | ||
3157 | #if 0 /* Instead we emit error message at run time */ | ||
3158 | if (ctx->pending_redirect) { | ||
3159 | /* For example, "cmd >" (no filename to redirect to) */ | ||
3160 | die_if_script("syntax error: %s", "invalid redirect"); | ||
3161 | ctx->pending_redirect = NULL; | ||
3162 | } | ||
3163 | #endif | ||
3164 | |||
2940 | if (command) { | 3165 | if (command) { |
2941 | if (IS_NULL_CMD(command)) { | 3166 | if (IS_NULL_CMD(command)) { |
2942 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); | 3167 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); |
@@ -3459,6 +3684,12 @@ static int parse_redirect(struct parse_context *ctx, | |||
3459 | debug_printf_parse("duplicating redirect '%d>&%d'\n", | 3684 | debug_printf_parse("duplicating redirect '%d>&%d'\n", |
3460 | redir->rd_fd, redir->rd_dup); | 3685 | redir->rd_fd, redir->rd_dup); |
3461 | } else { | 3686 | } else { |
3687 | #if 0 /* Instead we emit error message at run time */ | ||
3688 | if (ctx->pending_redirect) { | ||
3689 | /* For example, "cmd > <file" */ | ||
3690 | die_if_script("syntax error: %s", "invalid redirect"); | ||
3691 | } | ||
3692 | #endif | ||
3462 | /* Set ctx->pending_redirect, so we know what to do at the | 3693 | /* Set ctx->pending_redirect, so we know what to do at the |
3463 | * end of the next parsed word. */ | 3694 | * end of the next parsed word. */ |
3464 | ctx->pending_redirect = redir; | 3695 | ctx->pending_redirect = redir; |
@@ -3724,6 +3955,39 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
3724 | /* command remains "open", available for possible redirects */ | 3955 | /* command remains "open", available for possible redirects */ |
3725 | } | 3956 | } |
3726 | 3957 | ||
3958 | static int i_getch_and_eat_bkslash_nl(struct in_str *input) | ||
3959 | { | ||
3960 | for (;;) { | ||
3961 | int ch, ch2; | ||
3962 | |||
3963 | ch = i_getch(input); | ||
3964 | if (ch != '\\') | ||
3965 | return ch; | ||
3966 | ch2 = i_peek(input); | ||
3967 | if (ch2 != '\n') | ||
3968 | return ch; | ||
3969 | /* backslash+newline, skip it */ | ||
3970 | i_getch(input); | ||
3971 | } | ||
3972 | } | ||
3973 | |||
3974 | static int i_peek_and_eat_bkslash_nl(struct in_str *input) | ||
3975 | { | ||
3976 | for (;;) { | ||
3977 | int ch, ch2; | ||
3978 | |||
3979 | ch = i_peek(input); | ||
3980 | if (ch != '\\') | ||
3981 | return ch; | ||
3982 | ch2 = i_peek2(input); | ||
3983 | if (ch2 != '\n') | ||
3984 | return ch; | ||
3985 | /* backslash+newline, skip it */ | ||
3986 | i_getch(input); | ||
3987 | i_getch(input); | ||
3988 | } | ||
3989 | } | ||
3990 | |||
3727 | #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS | 3991 | #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS |
3728 | /* Subroutines for copying $(...) and `...` things */ | 3992 | /* Subroutines for copying $(...) and `...` things */ |
3729 | static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); | 3993 | static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); |
@@ -3841,7 +4105,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign | |||
3841 | if (!dbl) | 4105 | if (!dbl) |
3842 | break; | 4106 | break; |
3843 | /* we look for closing )) of $((EXPR)) */ | 4107 | /* we look for closing )) of $((EXPR)) */ |
3844 | if (i_peek(input) == end_ch) { | 4108 | if (i_peek_and_eat_bkslash_nl(input) == end_ch) { |
3845 | i_getch(input); /* eat second ')' */ | 4109 | i_getch(input); /* eat second ')' */ |
3846 | break; | 4110 | break; |
3847 | } | 4111 | } |
@@ -3879,6 +4143,13 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign | |||
3879 | syntax_error_unterm_ch(')'); | 4143 | syntax_error_unterm_ch(')'); |
3880 | return 0; | 4144 | return 0; |
3881 | } | 4145 | } |
4146 | #if 0 | ||
4147 | if (ch == '\n') { | ||
4148 | /* "backslash+newline", ignore both */ | ||
4149 | o_delchr(dest); /* undo insertion of '\' */ | ||
4150 | continue; | ||
4151 | } | ||
4152 | #endif | ||
3882 | o_addchr(dest, ch); | 4153 | o_addchr(dest, ch); |
3883 | continue; | 4154 | continue; |
3884 | } | 4155 | } |
@@ -3897,7 +4168,7 @@ static int parse_dollar(o_string *as_string, | |||
3897 | o_string *dest, | 4168 | o_string *dest, |
3898 | struct in_str *input, unsigned char quote_mask) | 4169 | struct in_str *input, unsigned char quote_mask) |
3899 | { | 4170 | { |
3900 | int ch = i_peek(input); /* first character after the $ */ | 4171 | int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */ |
3901 | 4172 | ||
3902 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); | 4173 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); |
3903 | if (isalpha(ch)) { | 4174 | if (isalpha(ch)) { |
@@ -3909,9 +4180,11 @@ static int parse_dollar(o_string *as_string, | |||
3909 | debug_printf_parse(": '%c'\n", ch); | 4180 | debug_printf_parse(": '%c'\n", ch); |
3910 | o_addchr(dest, ch | quote_mask); | 4181 | o_addchr(dest, ch | quote_mask); |
3911 | quote_mask = 0; | 4182 | quote_mask = 0; |
3912 | ch = i_peek(input); | 4183 | ch = i_peek_and_eat_bkslash_nl(input); |
3913 | if (!isalnum(ch) && ch != '_') | 4184 | if (!isalnum(ch) && ch != '_') { |
4185 | /* End of variable name reached */ | ||
3914 | break; | 4186 | break; |
4187 | } | ||
3915 | ch = i_getch(input); | 4188 | ch = i_getch(input); |
3916 | nommu_addchr(as_string, ch); | 4189 | nommu_addchr(as_string, ch); |
3917 | } | 4190 | } |
@@ -3938,7 +4211,7 @@ static int parse_dollar(o_string *as_string, | |||
3938 | ch = i_getch(input); /* eat '{' */ | 4211 | ch = i_getch(input); /* eat '{' */ |
3939 | nommu_addchr(as_string, ch); | 4212 | nommu_addchr(as_string, ch); |
3940 | 4213 | ||
3941 | ch = i_getch(input); /* first char after '{' */ | 4214 | ch = i_getch_and_eat_bkslash_nl(input); /* first char after '{' */ |
3942 | /* It should be ${?}, or ${#var}, | 4215 | /* It should be ${?}, or ${#var}, |
3943 | * or even ${?+subst} - operator acting on a special variable, | 4216 | * or even ${?+subst} - operator acting on a special variable, |
3944 | * or the beginning of variable name. | 4217 | * or the beginning of variable name. |
@@ -4044,7 +4317,7 @@ static int parse_dollar(o_string *as_string, | |||
4044 | ch = i_getch(input); | 4317 | ch = i_getch(input); |
4045 | nommu_addchr(as_string, ch); | 4318 | nommu_addchr(as_string, ch); |
4046 | # if ENABLE_SH_MATH_SUPPORT | 4319 | # if ENABLE_SH_MATH_SUPPORT |
4047 | if (i_peek(input) == '(') { | 4320 | if (i_peek_and_eat_bkslash_nl(input) == '(') { |
4048 | ch = i_getch(input); | 4321 | ch = i_getch(input); |
4049 | nommu_addchr(as_string, ch); | 4322 | nommu_addchr(as_string, ch); |
4050 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4323 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
@@ -4081,7 +4354,7 @@ static int parse_dollar(o_string *as_string, | |||
4081 | case '_': | 4354 | case '_': |
4082 | ch = i_getch(input); | 4355 | ch = i_getch(input); |
4083 | nommu_addchr(as_string, ch); | 4356 | nommu_addchr(as_string, ch); |
4084 | ch = i_peek(input); | 4357 | ch = i_peek_and_eat_bkslash_nl(input); |
4085 | if (isalnum(ch)) { /* it's $_name or $_123 */ | 4358 | if (isalnum(ch)) { /* it's $_name or $_123 */ |
4086 | ch = '_'; | 4359 | ch = '_'; |
4087 | goto make_var; | 4360 | goto make_var; |
@@ -5548,7 +5821,7 @@ static char* expand_strvec_to_string(char **argv) | |||
5548 | n++; | 5821 | n++; |
5549 | } | 5822 | } |
5550 | } | 5823 | } |
5551 | overlapping_strcpy((char*)list, list[0]); | 5824 | overlapping_strcpy((char*)list, list[0] ? list[0] : ""); |
5552 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); | 5825 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); |
5553 | return (char*)list; | 5826 | return (char*)list; |
5554 | } | 5827 | } |
@@ -5825,10 +6098,8 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger) | |||
5825 | debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); | 6098 | debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); |
5826 | run_and_free_list(pipe_list); | 6099 | run_and_free_list(pipe_list); |
5827 | empty = 0; | 6100 | empty = 0; |
5828 | #if ENABLE_HUSH_FUNCTIONS | 6101 | if (G_flag_return_in_progress == 1) |
5829 | if (G.flag_return_in_progress == 1) | ||
5830 | break; | 6102 | break; |
5831 | #endif | ||
5832 | } | 6103 | } |
5833 | } | 6104 | } |
5834 | 6105 | ||
@@ -5913,7 +6184,8 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | |||
5913 | ) { | 6184 | ) { |
5914 | static const char *const argv[] = { NULL, NULL }; | 6185 | static const char *const argv[] = { NULL, NULL }; |
5915 | builtin_trap((char**)argv); | 6186 | builtin_trap((char**)argv); |
5916 | exit(0); /* not _exit() - we need to fflush */ | 6187 | fflush_all(); /* important */ |
6188 | _exit(0); | ||
5917 | } | 6189 | } |
5918 | # if BB_MMU | 6190 | # if BB_MMU |
5919 | reset_traps_to_defaults(); | 6191 | reset_traps_to_defaults(); |
@@ -5946,8 +6218,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | |||
5946 | free(to_free); | 6218 | free(to_free); |
5947 | # endif | 6219 | # endif |
5948 | close(channel[1]); | 6220 | close(channel[1]); |
5949 | close_on_exec_on(channel[0]); | 6221 | return remember_FILE(xfdopen_for_read(channel[0])); |
5950 | return xfdopen_for_read(channel[0]); | ||
5951 | } | 6222 | } |
5952 | 6223 | ||
5953 | /* Return code is exit status of the process that is run. */ | 6224 | /* Return code is exit status of the process that is run. */ |
@@ -5976,7 +6247,7 @@ static int process_command_subs(o_string *dest, const char *s) | |||
5976 | } | 6247 | } |
5977 | 6248 | ||
5978 | debug_printf("done reading from `cmd` pipe, closing it\n"); | 6249 | debug_printf("done reading from `cmd` pipe, closing it\n"); |
5979 | fclose(fp); | 6250 | fclose_and_forget(fp); |
5980 | /* We need to extract exitcode. Test case | 6251 | /* We need to extract exitcode. Test case |
5981 | * "true; echo `sleep 1; false` $?" | 6252 | * "true; echo `sleep 1; false` $?" |
5982 | * should print 1 */ | 6253 | * should print 1 */ |
@@ -6068,6 +6339,74 @@ static void setup_heredoc(struct redir_struct *redir) | |||
6068 | wait(NULL); /* wait till child has died */ | 6339 | wait(NULL); /* wait till child has died */ |
6069 | } | 6340 | } |
6070 | 6341 | ||
6342 | /* fd: redirect wants this fd to be used (e.g. 3>file). | ||
6343 | * Move all conflicting internally used fds, | ||
6344 | * and remember them so that we can restore them later. | ||
6345 | */ | ||
6346 | static int save_fds_on_redirect(int fd, int squirrel[3]) | ||
6347 | { | ||
6348 | if (squirrel) { | ||
6349 | /* Handle redirects of fds 0,1,2 */ | ||
6350 | |||
6351 | /* If we collide with an already moved stdio fd... */ | ||
6352 | if (fd == squirrel[0]) { | ||
6353 | squirrel[0] = xdup_and_close(squirrel[0], F_DUPFD); | ||
6354 | return 1; | ||
6355 | } | ||
6356 | if (fd == squirrel[1]) { | ||
6357 | squirrel[1] = xdup_and_close(squirrel[1], F_DUPFD); | ||
6358 | return 1; | ||
6359 | } | ||
6360 | if (fd == squirrel[2]) { | ||
6361 | squirrel[2] = xdup_and_close(squirrel[2], F_DUPFD); | ||
6362 | return 1; | ||
6363 | } | ||
6364 | /* If we are about to redirect stdio fd, and did not yet move it... */ | ||
6365 | if (fd <= 2 && squirrel[fd] < 0) { | ||
6366 | /* We avoid taking stdio fds */ | ||
6367 | squirrel[fd] = fcntl(fd, F_DUPFD, 10); | ||
6368 | if (squirrel[fd] < 0 && errno != EBADF) | ||
6369 | xfunc_die(); | ||
6370 | return 0; /* "we did not close fd" */ | ||
6371 | } | ||
6372 | } | ||
6373 | |||
6374 | #if ENABLE_HUSH_INTERACTIVE | ||
6375 | if (fd != 0 && fd == G.interactive_fd) { | ||
6376 | G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC); | ||
6377 | return 1; | ||
6378 | } | ||
6379 | #endif | ||
6380 | |||
6381 | /* Are we called from setup_redirects(squirrel==NULL)? Two cases: | ||
6382 | * (1) Redirect in a forked child. No need to save FILEs' fds, | ||
6383 | * we aren't going to use them anymore, ok to trash. | ||
6384 | * (2) "exec 3>FILE". Bummer. We can save FILEs' fds, | ||
6385 | * but how are we doing to use them? | ||
6386 | * "fileno(fd) = new_fd" can't be done. | ||
6387 | */ | ||
6388 | if (!squirrel) | ||
6389 | return 0; | ||
6390 | |||
6391 | return save_FILEs_on_redirect(fd); | ||
6392 | } | ||
6393 | |||
6394 | static void restore_redirects(int squirrel[3]) | ||
6395 | { | ||
6396 | int i, fd; | ||
6397 | for (i = 0; i <= 2; i++) { | ||
6398 | fd = squirrel[i]; | ||
6399 | if (fd != -1) { | ||
6400 | /* We simply die on error */ | ||
6401 | xmove_fd(fd, i); | ||
6402 | } | ||
6403 | } | ||
6404 | |||
6405 | /* Moved G.interactive_fd stays on new fd, not doing anything for it */ | ||
6406 | |||
6407 | restore_redirected_FILEs(); | ||
6408 | } | ||
6409 | |||
6071 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, | 6410 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, |
6072 | * and stderr if they are redirected. */ | 6411 | * and stderr if they are redirected. */ |
6073 | static int setup_redirects(struct command *prog, int squirrel[]) | 6412 | static int setup_redirects(struct command *prog, int squirrel[]) |
@@ -6077,12 +6416,8 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
6077 | 6416 | ||
6078 | for (redir = prog->redirects; redir; redir = redir->next) { | 6417 | for (redir = prog->redirects; redir; redir = redir->next) { |
6079 | if (redir->rd_type == REDIRECT_HEREDOC2) { | 6418 | if (redir->rd_type == REDIRECT_HEREDOC2) { |
6080 | /* rd_fd<<HERE case */ | 6419 | /* "rd_fd<<HERE" case */ |
6081 | if (squirrel && redir->rd_fd < 3 | 6420 | save_fds_on_redirect(redir->rd_fd, squirrel); |
6082 | && squirrel[redir->rd_fd] < 0 | ||
6083 | ) { | ||
6084 | squirrel[redir->rd_fd] = dup(redir->rd_fd); | ||
6085 | } | ||
6086 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 6421 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ |
6087 | * of the heredoc */ | 6422 | * of the heredoc */ |
6088 | debug_printf_parse("set heredoc '%s'\n", | 6423 | debug_printf_parse("set heredoc '%s'\n", |
@@ -6092,12 +6427,15 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
6092 | } | 6427 | } |
6093 | 6428 | ||
6094 | if (redir->rd_dup == REDIRFD_TO_FILE) { | 6429 | if (redir->rd_dup == REDIRFD_TO_FILE) { |
6095 | /* rd_fd<*>file case (<*> is <,>,>>,<>) */ | 6430 | /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */ |
6096 | char *p; | 6431 | char *p; |
6097 | if (redir->rd_filename == NULL) { | 6432 | if (redir->rd_filename == NULL) { |
6098 | /* Something went wrong in the parse. | 6433 | /* |
6099 | * Pretend it didn't happen */ | 6434 | * Examples: |
6100 | bb_error_msg("bug in redirect parse"); | 6435 | * "cmd >" (no filename) |
6436 | * "cmd > <file" (2nd redirect starts too early) | ||
6437 | */ | ||
6438 | die_if_script("syntax error: %s", "invalid redirect"); | ||
6101 | continue; | 6439 | continue; |
6102 | } | 6440 | } |
6103 | mode = redir_table[redir->rd_type].mode; | 6441 | mode = redir_table[redir->rd_type].mode; |
@@ -6105,47 +6443,39 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
6105 | openfd = open_or_warn(p, mode); | 6443 | openfd = open_or_warn(p, mode); |
6106 | free(p); | 6444 | free(p); |
6107 | if (openfd < 0) { | 6445 | if (openfd < 0) { |
6108 | /* this could get lost if stderr has been redirected, but | 6446 | /* Error message from open_or_warn can be lost |
6109 | * bash and ash both lose it as well (though zsh doesn't!) */ | 6447 | * if stderr has been redirected, but bash |
6110 | //what the above comment tries to say? | 6448 | * and ash both lose it as well |
6449 | * (though zsh doesn't!) | ||
6450 | */ | ||
6111 | return 1; | 6451 | return 1; |
6112 | } | 6452 | } |
6113 | } else { | 6453 | } else { |
6114 | /* rd_fd<*>rd_dup or rd_fd<*>- cases */ | 6454 | /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ |
6115 | openfd = redir->rd_dup; | 6455 | openfd = redir->rd_dup; |
6116 | } | 6456 | } |
6117 | 6457 | ||
6118 | if (openfd != redir->rd_fd) { | 6458 | if (openfd != redir->rd_fd) { |
6119 | if (squirrel && redir->rd_fd < 3 | 6459 | int closed = save_fds_on_redirect(redir->rd_fd, squirrel); |
6120 | && squirrel[redir->rd_fd] < 0 | ||
6121 | ) { | ||
6122 | squirrel[redir->rd_fd] = dup(redir->rd_fd); | ||
6123 | } | ||
6124 | if (openfd == REDIRFD_CLOSE) { | 6460 | if (openfd == REDIRFD_CLOSE) { |
6125 | /* "n>-" means "close me" */ | 6461 | /* "rd_fd >&-" means "close me" */ |
6126 | close(redir->rd_fd); | 6462 | if (!closed) { |
6463 | /* ^^^ optimization: saving may already | ||
6464 | * have closed it. If not... */ | ||
6465 | close(redir->rd_fd); | ||
6466 | } | ||
6127 | } else { | 6467 | } else { |
6128 | xdup2(openfd, redir->rd_fd); | 6468 | xdup2(openfd, redir->rd_fd); |
6129 | if (redir->rd_dup == REDIRFD_TO_FILE) | 6469 | if (redir->rd_dup == REDIRFD_TO_FILE) |
6470 | /* "rd_fd > FILE" */ | ||
6130 | close(openfd); | 6471 | close(openfd); |
6472 | /* else: "rd_fd > rd_dup" */ | ||
6131 | } | 6473 | } |
6132 | } | 6474 | } |
6133 | } | 6475 | } |
6134 | return 0; | 6476 | return 0; |
6135 | } | 6477 | } |
6136 | 6478 | ||
6137 | static void restore_redirects(int squirrel[]) | ||
6138 | { | ||
6139 | int i, fd; | ||
6140 | for (i = 0; i < 3; i++) { | ||
6141 | fd = squirrel[i]; | ||
6142 | if (fd != -1) { | ||
6143 | /* We simply die on error */ | ||
6144 | xmove_fd(fd, i); | ||
6145 | } | ||
6146 | } | ||
6147 | } | ||
6148 | |||
6149 | static char *find_in_path(const char *arg) | 6479 | static char *find_in_path(const char *arg) |
6150 | { | 6480 | { |
6151 | char *ret = NULL; | 6481 | char *ret = NULL; |
@@ -6329,8 +6659,8 @@ static int run_function(const struct function *funcp, char **argv) | |||
6329 | save_and_replace_G_args(&sv, argv); | 6659 | save_and_replace_G_args(&sv, argv); |
6330 | 6660 | ||
6331 | /* "we are in function, ok to use return" */ | 6661 | /* "we are in function, ok to use return" */ |
6332 | sv_flg = G.flag_return_in_progress; | 6662 | sv_flg = G_flag_return_in_progress; |
6333 | G.flag_return_in_progress = -1; | 6663 | G_flag_return_in_progress = -1; |
6334 | # if ENABLE_HUSH_LOCAL | 6664 | # if ENABLE_HUSH_LOCAL |
6335 | G.func_nest_level++; | 6665 | G.func_nest_level++; |
6336 | # endif | 6666 | # endif |
@@ -6371,7 +6701,7 @@ static int run_function(const struct function *funcp, char **argv) | |||
6371 | G.func_nest_level--; | 6701 | G.func_nest_level--; |
6372 | } | 6702 | } |
6373 | # endif | 6703 | # endif |
6374 | G.flag_return_in_progress = sv_flg; | 6704 | G_flag_return_in_progress = sv_flg; |
6375 | 6705 | ||
6376 | restore_G_args(&sv, argv); | 6706 | restore_G_args(&sv, argv); |
6377 | 6707 | ||
@@ -6417,13 +6747,17 @@ static void exec_builtin(char ***to_free, | |||
6417 | static void execvp_or_die(char **argv) NORETURN; | 6747 | static void execvp_or_die(char **argv) NORETURN; |
6418 | static void execvp_or_die(char **argv) | 6748 | static void execvp_or_die(char **argv) |
6419 | { | 6749 | { |
6750 | int e; | ||
6420 | debug_printf_exec("execing '%s'\n", argv[0]); | 6751 | debug_printf_exec("execing '%s'\n", argv[0]); |
6421 | /* Don't propagate SIG_IGN to the child */ | 6752 | /* Don't propagate SIG_IGN to the child */ |
6422 | if (SPECIAL_JOBSTOP_SIGS != 0) | 6753 | if (SPECIAL_JOBSTOP_SIGS != 0) |
6423 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); | 6754 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); |
6424 | execvp(argv[0], argv); | 6755 | execvp(argv[0], argv); |
6756 | e = 2; | ||
6757 | if (errno == EACCES) e = 126; | ||
6758 | if (errno == ENOENT) e = 127; | ||
6425 | bb_perror_msg("can't execute '%s'", argv[0]); | 6759 | bb_perror_msg("can't execute '%s'", argv[0]); |
6426 | _exit(127); /* bash compat */ | 6760 | _exit(e); |
6427 | } | 6761 | } |
6428 | 6762 | ||
6429 | #if ENABLE_HUSH_MODE_X | 6763 | #if ENABLE_HUSH_MODE_X |
@@ -6466,7 +6800,8 @@ static void dump_cmd_in_x_mode(char **argv) | |||
6466 | * Never returns. | 6800 | * Never returns. |
6467 | * Don't exit() here. If you don't exec, use _exit instead. | 6801 | * Don't exit() here. If you don't exec, use _exit instead. |
6468 | * The at_exit handlers apparently confuse the calling process, | 6802 | * The at_exit handlers apparently confuse the calling process, |
6469 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ | 6803 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) |
6804 | */ | ||
6470 | static void pseudo_exec_argv(nommu_save_t *nommu_save, | 6805 | static void pseudo_exec_argv(nommu_save_t *nommu_save, |
6471 | char **argv, int assignment_cnt, | 6806 | char **argv, int assignment_cnt, |
6472 | char **argv_expanded) NORETURN; | 6807 | char **argv_expanded) NORETURN; |
@@ -6546,6 +6881,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
6546 | if (a >= 0) { | 6881 | if (a >= 0) { |
6547 | # if BB_MMU /* see above why on NOMMU it is not allowed */ | 6882 | # if BB_MMU /* see above why on NOMMU it is not allowed */ |
6548 | if (APPLET_IS_NOEXEC(a)) { | 6883 | if (APPLET_IS_NOEXEC(a)) { |
6884 | /* Do not leak open fds from opened script files etc */ | ||
6885 | close_all_FILE_list(); | ||
6549 | debug_printf_exec("running applet '%s'\n", argv[0]); | 6886 | debug_printf_exec("running applet '%s'\n", argv[0]); |
6550 | run_applet_no_and_exit(a, argv); | 6887 | run_applet_no_and_exit(a, argv); |
6551 | } | 6888 | } |
@@ -7121,6 +7458,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7121 | if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { | 7458 | if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { |
7122 | debug_printf("exec with redirects only\n"); | 7459 | debug_printf("exec with redirects only\n"); |
7123 | rcode = setup_redirects(command, NULL); | 7460 | rcode = setup_redirects(command, NULL); |
7461 | /* rcode=1 can be if redir file can't be opened */ | ||
7124 | goto clean_up_and_ret1; | 7462 | goto clean_up_and_ret1; |
7125 | } | 7463 | } |
7126 | } | 7464 | } |
@@ -7247,9 +7585,20 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7247 | if (pipefds.rd > 1) | 7585 | if (pipefds.rd > 1) |
7248 | close(pipefds.rd); | 7586 | close(pipefds.rd); |
7249 | /* Like bash, explicit redirects override pipes, | 7587 | /* Like bash, explicit redirects override pipes, |
7250 | * and the pipe fd is available for dup'ing. */ | 7588 | * and the pipe fd (fd#1) is available for dup'ing: |
7251 | if (setup_redirects(command, NULL)) | 7589 | * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr |
7590 | * of cmd1 goes into pipe. | ||
7591 | */ | ||
7592 | if (setup_redirects(command, NULL)) { | ||
7593 | /* Happens when redir file can't be opened: | ||
7594 | * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ' | ||
7595 | * FOO | ||
7596 | * hush: can't open '/qwe/rty': No such file or directory | ||
7597 | * BAZ | ||
7598 | * (echo BAR is not executed, it hits _exit(1) below) | ||
7599 | */ | ||
7252 | _exit(1); | 7600 | _exit(1); |
7601 | } | ||
7253 | 7602 | ||
7254 | /* Stores to nommu_save list of env vars putenv'ed | 7603 | /* Stores to nommu_save list of env vars putenv'ed |
7255 | * (NOMMU, on MMU we don't need that) */ | 7604 | * (NOMMU, on MMU we don't need that) */ |
@@ -7380,6 +7729,8 @@ static int run_list(struct pipe *pi) | |||
7380 | for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { | 7729 | for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { |
7381 | if (G.flag_SIGINT) | 7730 | if (G.flag_SIGINT) |
7382 | break; | 7731 | break; |
7732 | if (G_flag_return_in_progress == 1) | ||
7733 | break; | ||
7383 | 7734 | ||
7384 | IF_HAS_KEYWORDS(rword = pi->res_word;) | 7735 | IF_HAS_KEYWORDS(rword = pi->res_word;) |
7385 | debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", | 7736 | debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", |
@@ -7550,12 +7901,10 @@ static int run_list(struct pipe *pi) | |||
7550 | continue; | 7901 | continue; |
7551 | } | 7902 | } |
7552 | #endif | 7903 | #endif |
7553 | #if ENABLE_HUSH_FUNCTIONS | 7904 | if (G_flag_return_in_progress == 1) { |
7554 | if (G.flag_return_in_progress == 1) { | ||
7555 | checkjobs(NULL); | 7905 | checkjobs(NULL); |
7556 | break; | 7906 | break; |
7557 | } | 7907 | } |
7558 | #endif | ||
7559 | } else if (pi->followup == PIPE_BG) { | 7908 | } else if (pi->followup == PIPE_BG) { |
7560 | /* What does bash do with attempts to background builtins? */ | 7909 | /* What does bash do with attempts to background builtins? */ |
7561 | /* even bash 3.2 doesn't do that well with nested bg: | 7910 | /* even bash 3.2 doesn't do that well with nested bg: |
@@ -7787,6 +8136,7 @@ int hush_main(int argc, char **argv) | |||
7787 | INIT_G(); | 8136 | INIT_G(); |
7788 | if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ | 8137 | if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ |
7789 | G.last_exitcode = EXIT_SUCCESS; | 8138 | G.last_exitcode = EXIT_SUCCESS; |
8139 | |||
7790 | #if ENABLE_HUSH_FAST | 8140 | #if ENABLE_HUSH_FAST |
7791 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | 8141 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ |
7792 | #endif | 8142 | #endif |
@@ -7876,7 +8226,7 @@ int hush_main(int argc, char **argv) | |||
7876 | /* Initialize some more globals to non-zero values */ | 8226 | /* Initialize some more globals to non-zero values */ |
7877 | cmdedit_update_prompt(); | 8227 | cmdedit_update_prompt(); |
7878 | 8228 | ||
7879 | die_func = xfunc_has_died; | 8229 | die_func = restore_ttypgrp_and__exit; |
7880 | 8230 | ||
7881 | /* Shell is non-interactive at first. We need to call | 8231 | /* Shell is non-interactive at first. We need to call |
7882 | * install_special_sighandlers() if we are going to execute "sh <script>", | 8232 | * install_special_sighandlers() if we are going to execute "sh <script>", |
@@ -8035,10 +8385,10 @@ int hush_main(int argc, char **argv) | |||
8035 | debug_printf("sourcing /etc/profile\n"); | 8385 | debug_printf("sourcing /etc/profile\n"); |
8036 | input = fopen_for_read("/etc/profile"); | 8386 | input = fopen_for_read("/etc/profile"); |
8037 | if (input != NULL) { | 8387 | if (input != NULL) { |
8038 | close_on_exec_on(fileno(input)); | 8388 | remember_FILE(input); |
8039 | install_special_sighandlers(); | 8389 | install_special_sighandlers(); |
8040 | parse_and_run_file(input); | 8390 | parse_and_run_file(input); |
8041 | fclose(input); | 8391 | fclose_and_forget(input); |
8042 | } | 8392 | } |
8043 | /* bash: after sourcing /etc/profile, | 8393 | /* bash: after sourcing /etc/profile, |
8044 | * tries to source (in the given order): | 8394 | * tries to source (in the given order): |
@@ -8060,11 +8410,11 @@ int hush_main(int argc, char **argv) | |||
8060 | G.global_argv++; | 8410 | G.global_argv++; |
8061 | debug_printf("running script '%s'\n", G.global_argv[0]); | 8411 | debug_printf("running script '%s'\n", G.global_argv[0]); |
8062 | input = xfopen_for_read(G.global_argv[0]); | 8412 | input = xfopen_for_read(G.global_argv[0]); |
8063 | close_on_exec_on(fileno(input)); | 8413 | remember_FILE(input); |
8064 | install_special_sighandlers(); | 8414 | install_special_sighandlers(); |
8065 | parse_and_run_file(input); | 8415 | parse_and_run_file(input); |
8066 | #if ENABLE_FEATURE_CLEAN_UP | 8416 | #if ENABLE_FEATURE_CLEAN_UP |
8067 | fclose(input); | 8417 | fclose_and_forget(input); |
8068 | #endif | 8418 | #endif |
8069 | goto final_return; | 8419 | goto final_return; |
8070 | } | 8420 | } |
@@ -8205,7 +8555,7 @@ int hush_main(int argc, char **argv) | |||
8205 | int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 8555 | int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
8206 | int msh_main(int argc, char **argv) | 8556 | int msh_main(int argc, char **argv) |
8207 | { | 8557 | { |
8208 | //bb_error_msg("msh is deprecated, please use hush instead"); | 8558 | bb_error_msg("msh is deprecated, please use hush instead"); |
8209 | return hush_main(argc, argv); | 8559 | return hush_main(argc, argv); |
8210 | } | 8560 | } |
8211 | #endif | 8561 | #endif |
@@ -8399,6 +8749,14 @@ static void helper_export_local(char **argv, int exp, int lvl) | |||
8399 | continue; | 8749 | continue; |
8400 | } | 8750 | } |
8401 | } | 8751 | } |
8752 | #if ENABLE_HUSH_LOCAL | ||
8753 | if (exp == 0 /* local? */ | ||
8754 | && var && var->func_nest_level == lvl | ||
8755 | ) { | ||
8756 | /* "local x=abc; ...; local x" - ignore second local decl */ | ||
8757 | continue; | ||
8758 | } | ||
8759 | #endif | ||
8402 | /* Exporting non-existing variable. | 8760 | /* Exporting non-existing variable. |
8403 | * bash does not put it in environment, | 8761 | * bash does not put it in environment, |
8404 | * but remembers that it is exported, | 8762 | * but remembers that it is exported, |
@@ -8473,6 +8831,212 @@ static int FAST_FUNC builtin_local(char **argv) | |||
8473 | } | 8831 | } |
8474 | #endif | 8832 | #endif |
8475 | 8833 | ||
8834 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ | ||
8835 | static int FAST_FUNC builtin_unset(char **argv) | ||
8836 | { | ||
8837 | int ret; | ||
8838 | unsigned opts; | ||
8839 | |||
8840 | /* "!": do not abort on errors */ | ||
8841 | /* "+": stop at 1st non-option */ | ||
8842 | opts = getopt32(argv, "!+vf"); | ||
8843 | if (opts == (unsigned)-1) | ||
8844 | return EXIT_FAILURE; | ||
8845 | if (opts == 3) { | ||
8846 | bb_error_msg("unset: -v and -f are exclusive"); | ||
8847 | return EXIT_FAILURE; | ||
8848 | } | ||
8849 | argv += optind; | ||
8850 | |||
8851 | ret = EXIT_SUCCESS; | ||
8852 | while (*argv) { | ||
8853 | if (!(opts & 2)) { /* not -f */ | ||
8854 | if (unset_local_var(*argv)) { | ||
8855 | /* unset <nonexistent_var> doesn't fail. | ||
8856 | * Error is when one tries to unset RO var. | ||
8857 | * Message was printed by unset_local_var. */ | ||
8858 | ret = EXIT_FAILURE; | ||
8859 | } | ||
8860 | } | ||
8861 | #if ENABLE_HUSH_FUNCTIONS | ||
8862 | else { | ||
8863 | unset_func(*argv); | ||
8864 | } | ||
8865 | #endif | ||
8866 | argv++; | ||
8867 | } | ||
8868 | return ret; | ||
8869 | } | ||
8870 | |||
8871 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set | ||
8872 | * built-in 'set' handler | ||
8873 | * SUSv3 says: | ||
8874 | * set [-abCefhmnuvx] [-o option] [argument...] | ||
8875 | * set [+abCefhmnuvx] [+o option] [argument...] | ||
8876 | * set -- [argument...] | ||
8877 | * set -o | ||
8878 | * set +o | ||
8879 | * Implementations shall support the options in both their hyphen and | ||
8880 | * plus-sign forms. These options can also be specified as options to sh. | ||
8881 | * Examples: | ||
8882 | * Write out all variables and their values: set | ||
8883 | * Set $1, $2, and $3 and set "$#" to 3: set c a b | ||
8884 | * Turn on the -x and -v options: set -xv | ||
8885 | * Unset all positional parameters: set -- | ||
8886 | * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" | ||
8887 | * Set the positional parameters to the expansion of x, even if x expands | ||
8888 | * with a leading '-' or '+': set -- $x | ||
8889 | * | ||
8890 | * So far, we only support "set -- [argument...]" and some of the short names. | ||
8891 | */ | ||
8892 | static int FAST_FUNC builtin_set(char **argv) | ||
8893 | { | ||
8894 | int n; | ||
8895 | char **pp, **g_argv; | ||
8896 | char *arg = *++argv; | ||
8897 | |||
8898 | if (arg == NULL) { | ||
8899 | struct variable *e; | ||
8900 | for (e = G.top_var; e; e = e->next) | ||
8901 | puts(e->varstr); | ||
8902 | return EXIT_SUCCESS; | ||
8903 | } | ||
8904 | |||
8905 | do { | ||
8906 | if (strcmp(arg, "--") == 0) { | ||
8907 | ++argv; | ||
8908 | goto set_argv; | ||
8909 | } | ||
8910 | if (arg[0] != '+' && arg[0] != '-') | ||
8911 | break; | ||
8912 | for (n = 1; arg[n]; ++n) { | ||
8913 | if (set_mode((arg[0] == '-'), arg[n], argv[1])) | ||
8914 | goto error; | ||
8915 | if (arg[n] == 'o' && argv[1]) | ||
8916 | argv++; | ||
8917 | } | ||
8918 | } while ((arg = *++argv) != NULL); | ||
8919 | /* Now argv[0] is 1st argument */ | ||
8920 | |||
8921 | if (arg == NULL) | ||
8922 | return EXIT_SUCCESS; | ||
8923 | set_argv: | ||
8924 | |||
8925 | /* NB: G.global_argv[0] ($0) is never freed/changed */ | ||
8926 | g_argv = G.global_argv; | ||
8927 | if (G.global_args_malloced) { | ||
8928 | pp = g_argv; | ||
8929 | while (*++pp) | ||
8930 | free(*pp); | ||
8931 | g_argv[1] = NULL; | ||
8932 | } else { | ||
8933 | G.global_args_malloced = 1; | ||
8934 | pp = xzalloc(sizeof(pp[0]) * 2); | ||
8935 | pp[0] = g_argv[0]; /* retain $0 */ | ||
8936 | g_argv = pp; | ||
8937 | } | ||
8938 | /* This realloc's G.global_argv */ | ||
8939 | G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); | ||
8940 | |||
8941 | n = 1; | ||
8942 | while (*++pp) | ||
8943 | n++; | ||
8944 | G.global_argc = n; | ||
8945 | |||
8946 | return EXIT_SUCCESS; | ||
8947 | |||
8948 | /* Nothing known, so abort */ | ||
8949 | error: | ||
8950 | bb_error_msg("set: %s: invalid option", arg); | ||
8951 | return EXIT_FAILURE; | ||
8952 | } | ||
8953 | |||
8954 | static int FAST_FUNC builtin_shift(char **argv) | ||
8955 | { | ||
8956 | int n = 1; | ||
8957 | argv = skip_dash_dash(argv); | ||
8958 | if (argv[0]) { | ||
8959 | n = atoi(argv[0]); | ||
8960 | } | ||
8961 | if (n >= 0 && n < G.global_argc) { | ||
8962 | if (G.global_args_malloced) { | ||
8963 | int m = 1; | ||
8964 | while (m <= n) | ||
8965 | free(G.global_argv[m++]); | ||
8966 | } | ||
8967 | G.global_argc -= n; | ||
8968 | memmove(&G.global_argv[1], &G.global_argv[n+1], | ||
8969 | G.global_argc * sizeof(G.global_argv[0])); | ||
8970 | return EXIT_SUCCESS; | ||
8971 | } | ||
8972 | return EXIT_FAILURE; | ||
8973 | } | ||
8974 | |||
8975 | /* Interruptibility of read builtin in bash | ||
8976 | * (tested on bash-4.2.8 by sending signals (not by ^C)): | ||
8977 | * | ||
8978 | * Empty trap makes read ignore corresponding signal, for any signal. | ||
8979 | * | ||
8980 | * SIGINT: | ||
8981 | * - terminates non-interactive shell; | ||
8982 | * - interrupts read in interactive shell; | ||
8983 | * if it has non-empty trap: | ||
8984 | * - executes trap and returns to command prompt in interactive shell; | ||
8985 | * - executes trap and returns to read in non-interactive shell; | ||
8986 | * SIGTERM: | ||
8987 | * - is ignored (does not interrupt) read in interactive shell; | ||
8988 | * - terminates non-interactive shell; | ||
8989 | * if it has non-empty trap: | ||
8990 | * - executes trap and returns to read; | ||
8991 | * SIGHUP: | ||
8992 | * - terminates shell (regardless of interactivity); | ||
8993 | * if it has non-empty trap: | ||
8994 | * - executes trap and returns to read; | ||
8995 | */ | ||
8996 | static int FAST_FUNC builtin_read(char **argv) | ||
8997 | { | ||
8998 | const char *r; | ||
8999 | char *opt_n = NULL; | ||
9000 | char *opt_p = NULL; | ||
9001 | char *opt_t = NULL; | ||
9002 | char *opt_u = NULL; | ||
9003 | const char *ifs; | ||
9004 | int read_flags; | ||
9005 | |||
9006 | /* "!": do not abort on errors. | ||
9007 | * Option string must start with "sr" to match BUILTIN_READ_xxx | ||
9008 | */ | ||
9009 | read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); | ||
9010 | if (read_flags == (uint32_t)-1) | ||
9011 | return EXIT_FAILURE; | ||
9012 | argv += optind; | ||
9013 | ifs = get_local_var_value("IFS"); /* can be NULL */ | ||
9014 | |||
9015 | again: | ||
9016 | r = shell_builtin_read(set_local_var_from_halves, | ||
9017 | argv, | ||
9018 | ifs, | ||
9019 | read_flags, | ||
9020 | opt_n, | ||
9021 | opt_p, | ||
9022 | opt_t, | ||
9023 | opt_u | ||
9024 | ); | ||
9025 | |||
9026 | if ((uintptr_t)r == 1 && errno == EINTR) { | ||
9027 | unsigned sig = check_and_run_traps(); | ||
9028 | if (sig && sig != SIGINT) | ||
9029 | goto again; | ||
9030 | } | ||
9031 | |||
9032 | if ((uintptr_t)r > 1) { | ||
9033 | bb_error_msg("%s", r); | ||
9034 | r = (char*)(uintptr_t)1; | ||
9035 | } | ||
9036 | |||
9037 | return (uintptr_t)r; | ||
9038 | } | ||
9039 | |||
8476 | static int FAST_FUNC builtin_trap(char **argv) | 9040 | static int FAST_FUNC builtin_trap(char **argv) |
8477 | { | 9041 | { |
8478 | int sig; | 9042 | int sig; |
@@ -8671,7 +9235,6 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) | |||
8671 | if (x->b_descr) | 9235 | if (x->b_descr) |
8672 | printf("%-10s%s\n", x->b_cmd, x->b_descr); | 9236 | printf("%-10s%s\n", x->b_cmd, x->b_descr); |
8673 | } | 9237 | } |
8674 | bb_putchar('\n'); | ||
8675 | return EXIT_SUCCESS; | 9238 | return EXIT_SUCCESS; |
8676 | } | 9239 | } |
8677 | #endif | 9240 | #endif |
@@ -8721,6 +9284,15 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) | |||
8721 | if (l < (unsigned long)p) l = (unsigned long)p; | 9284 | if (l < (unsigned long)p) l = (unsigned long)p; |
8722 | free(p); | 9285 | free(p); |
8723 | 9286 | ||
9287 | |||
9288 | # if 0 /* debug */ | ||
9289 | { | ||
9290 | struct mallinfo mi = mallinfo(); | ||
9291 | printf("top alloc:0x%lx malloced:%d+%d=%d\n", l, | ||
9292 | mi.arena, mi.hblkhd, mi.arena + mi.hblkhd); | ||
9293 | } | ||
9294 | # endif | ||
9295 | |||
8724 | if (!G.memleak_value) | 9296 | if (!G.memleak_value) |
8725 | G.memleak_value = l; | 9297 | G.memleak_value = l; |
8726 | 9298 | ||
@@ -8742,175 +9314,6 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) | |||
8742 | return EXIT_SUCCESS; | 9314 | return EXIT_SUCCESS; |
8743 | } | 9315 | } |
8744 | 9316 | ||
8745 | /* Interruptibility of read builtin in bash | ||
8746 | * (tested on bash-4.2.8 by sending signals (not by ^C)): | ||
8747 | * | ||
8748 | * Empty trap makes read ignore corresponding signal, for any signal. | ||
8749 | * | ||
8750 | * SIGINT: | ||
8751 | * - terminates non-interactive shell; | ||
8752 | * - interrupts read in interactive shell; | ||
8753 | * if it has non-empty trap: | ||
8754 | * - executes trap and returns to command prompt in interactive shell; | ||
8755 | * - executes trap and returns to read in non-interactive shell; | ||
8756 | * SIGTERM: | ||
8757 | * - is ignored (does not interrupt) read in interactive shell; | ||
8758 | * - terminates non-interactive shell; | ||
8759 | * if it has non-empty trap: | ||
8760 | * - executes trap and returns to read; | ||
8761 | * SIGHUP: | ||
8762 | * - terminates shell (regardless of interactivity); | ||
8763 | * if it has non-empty trap: | ||
8764 | * - executes trap and returns to read; | ||
8765 | */ | ||
8766 | static int FAST_FUNC builtin_read(char **argv) | ||
8767 | { | ||
8768 | const char *r; | ||
8769 | char *opt_n = NULL; | ||
8770 | char *opt_p = NULL; | ||
8771 | char *opt_t = NULL; | ||
8772 | char *opt_u = NULL; | ||
8773 | const char *ifs; | ||
8774 | int read_flags; | ||
8775 | |||
8776 | /* "!": do not abort on errors. | ||
8777 | * Option string must start with "sr" to match BUILTIN_READ_xxx | ||
8778 | */ | ||
8779 | read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); | ||
8780 | if (read_flags == (uint32_t)-1) | ||
8781 | return EXIT_FAILURE; | ||
8782 | argv += optind; | ||
8783 | ifs = get_local_var_value("IFS"); /* can be NULL */ | ||
8784 | |||
8785 | again: | ||
8786 | r = shell_builtin_read(set_local_var_from_halves, | ||
8787 | argv, | ||
8788 | ifs, | ||
8789 | read_flags, | ||
8790 | opt_n, | ||
8791 | opt_p, | ||
8792 | opt_t, | ||
8793 | opt_u | ||
8794 | ); | ||
8795 | |||
8796 | if ((uintptr_t)r == 1 && errno == EINTR) { | ||
8797 | unsigned sig = check_and_run_traps(); | ||
8798 | if (sig && sig != SIGINT) | ||
8799 | goto again; | ||
8800 | } | ||
8801 | |||
8802 | if ((uintptr_t)r > 1) { | ||
8803 | bb_error_msg("%s", r); | ||
8804 | r = (char*)(uintptr_t)1; | ||
8805 | } | ||
8806 | |||
8807 | return (uintptr_t)r; | ||
8808 | } | ||
8809 | |||
8810 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set | ||
8811 | * built-in 'set' handler | ||
8812 | * SUSv3 says: | ||
8813 | * set [-abCefhmnuvx] [-o option] [argument...] | ||
8814 | * set [+abCefhmnuvx] [+o option] [argument...] | ||
8815 | * set -- [argument...] | ||
8816 | * set -o | ||
8817 | * set +o | ||
8818 | * Implementations shall support the options in both their hyphen and | ||
8819 | * plus-sign forms. These options can also be specified as options to sh. | ||
8820 | * Examples: | ||
8821 | * Write out all variables and their values: set | ||
8822 | * Set $1, $2, and $3 and set "$#" to 3: set c a b | ||
8823 | * Turn on the -x and -v options: set -xv | ||
8824 | * Unset all positional parameters: set -- | ||
8825 | * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" | ||
8826 | * Set the positional parameters to the expansion of x, even if x expands | ||
8827 | * with a leading '-' or '+': set -- $x | ||
8828 | * | ||
8829 | * So far, we only support "set -- [argument...]" and some of the short names. | ||
8830 | */ | ||
8831 | static int FAST_FUNC builtin_set(char **argv) | ||
8832 | { | ||
8833 | int n; | ||
8834 | char **pp, **g_argv; | ||
8835 | char *arg = *++argv; | ||
8836 | |||
8837 | if (arg == NULL) { | ||
8838 | struct variable *e; | ||
8839 | for (e = G.top_var; e; e = e->next) | ||
8840 | puts(e->varstr); | ||
8841 | return EXIT_SUCCESS; | ||
8842 | } | ||
8843 | |||
8844 | do { | ||
8845 | if (strcmp(arg, "--") == 0) { | ||
8846 | ++argv; | ||
8847 | goto set_argv; | ||
8848 | } | ||
8849 | if (arg[0] != '+' && arg[0] != '-') | ||
8850 | break; | ||
8851 | for (n = 1; arg[n]; ++n) { | ||
8852 | if (set_mode((arg[0] == '-'), arg[n], argv[1])) | ||
8853 | goto error; | ||
8854 | if (arg[n] == 'o' && argv[1]) | ||
8855 | argv++; | ||
8856 | } | ||
8857 | } while ((arg = *++argv) != NULL); | ||
8858 | /* Now argv[0] is 1st argument */ | ||
8859 | |||
8860 | if (arg == NULL) | ||
8861 | return EXIT_SUCCESS; | ||
8862 | set_argv: | ||
8863 | |||
8864 | /* NB: G.global_argv[0] ($0) is never freed/changed */ | ||
8865 | g_argv = G.global_argv; | ||
8866 | if (G.global_args_malloced) { | ||
8867 | pp = g_argv; | ||
8868 | while (*++pp) | ||
8869 | free(*pp); | ||
8870 | g_argv[1] = NULL; | ||
8871 | } else { | ||
8872 | G.global_args_malloced = 1; | ||
8873 | pp = xzalloc(sizeof(pp[0]) * 2); | ||
8874 | pp[0] = g_argv[0]; /* retain $0 */ | ||
8875 | g_argv = pp; | ||
8876 | } | ||
8877 | /* This realloc's G.global_argv */ | ||
8878 | G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); | ||
8879 | |||
8880 | n = 1; | ||
8881 | while (*++pp) | ||
8882 | n++; | ||
8883 | G.global_argc = n; | ||
8884 | |||
8885 | return EXIT_SUCCESS; | ||
8886 | |||
8887 | /* Nothing known, so abort */ | ||
8888 | error: | ||
8889 | bb_error_msg("set: %s: invalid option", arg); | ||
8890 | return EXIT_FAILURE; | ||
8891 | } | ||
8892 | |||
8893 | static int FAST_FUNC builtin_shift(char **argv) | ||
8894 | { | ||
8895 | int n = 1; | ||
8896 | argv = skip_dash_dash(argv); | ||
8897 | if (argv[0]) { | ||
8898 | n = atoi(argv[0]); | ||
8899 | } | ||
8900 | if (n >= 0 && n < G.global_argc) { | ||
8901 | if (G.global_args_malloced) { | ||
8902 | int m = 1; | ||
8903 | while (m <= n) | ||
8904 | free(G.global_argv[m++]); | ||
8905 | } | ||
8906 | G.global_argc -= n; | ||
8907 | memmove(&G.global_argv[1], &G.global_argv[n+1], | ||
8908 | G.global_argc * sizeof(G.global_argv[0])); | ||
8909 | return EXIT_SUCCESS; | ||
8910 | } | ||
8911 | return EXIT_FAILURE; | ||
8912 | } | ||
8913 | |||
8914 | static int FAST_FUNC builtin_source(char **argv) | 9317 | static int FAST_FUNC builtin_source(char **argv) |
8915 | { | 9318 | { |
8916 | char *arg_path, *filename; | 9319 | char *arg_path, *filename; |
@@ -8932,7 +9335,7 @@ static int FAST_FUNC builtin_source(char **argv) | |||
8932 | if (arg_path) | 9335 | if (arg_path) |
8933 | filename = arg_path; | 9336 | filename = arg_path; |
8934 | } | 9337 | } |
8935 | input = fopen_or_warn(filename, "r"); | 9338 | input = remember_FILE(fopen_or_warn(filename, "r")); |
8936 | free(arg_path); | 9339 | free(arg_path); |
8937 | if (!input) { | 9340 | if (!input) { |
8938 | /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ | 9341 | /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ |
@@ -8941,23 +9344,24 @@ static int FAST_FUNC builtin_source(char **argv) | |||
8941 | */ | 9344 | */ |
8942 | return EXIT_FAILURE; | 9345 | return EXIT_FAILURE; |
8943 | } | 9346 | } |
8944 | close_on_exec_on(fileno(input)); | ||
8945 | 9347 | ||
8946 | #if ENABLE_HUSH_FUNCTIONS | 9348 | #if ENABLE_HUSH_FUNCTIONS |
8947 | sv_flg = G.flag_return_in_progress; | 9349 | sv_flg = G_flag_return_in_progress; |
8948 | /* "we are inside sourced file, ok to use return" */ | 9350 | /* "we are inside sourced file, ok to use return" */ |
8949 | G.flag_return_in_progress = -1; | 9351 | G_flag_return_in_progress = -1; |
8950 | #endif | 9352 | #endif |
8951 | if (argv[1]) | 9353 | if (argv[1]) |
8952 | save_and_replace_G_args(&sv, argv); | 9354 | save_and_replace_G_args(&sv, argv); |
8953 | 9355 | ||
9356 | /* "false; . ./empty_line; echo Zero:$?" should print 0 */ | ||
9357 | G.last_exitcode = 0; | ||
8954 | parse_and_run_file(input); | 9358 | parse_and_run_file(input); |
8955 | fclose(input); | 9359 | fclose_and_forget(input); |
8956 | 9360 | ||
8957 | if (argv[1]) | 9361 | if (argv[1]) |
8958 | restore_G_args(&sv, argv); | 9362 | restore_G_args(&sv, argv); |
8959 | #if ENABLE_HUSH_FUNCTIONS | 9363 | #if ENABLE_HUSH_FUNCTIONS |
8960 | G.flag_return_in_progress = sv_flg; | 9364 | G_flag_return_in_progress = sv_flg; |
8961 | #endif | 9365 | #endif |
8962 | 9366 | ||
8963 | return G.last_exitcode; | 9367 | return G.last_exitcode; |
@@ -9000,43 +9404,6 @@ static int FAST_FUNC builtin_umask(char **argv) | |||
9000 | return !rc; /* rc != 0 - success */ | 9404 | return !rc; /* rc != 0 - success */ |
9001 | } | 9405 | } |
9002 | 9406 | ||
9003 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ | ||
9004 | static int FAST_FUNC builtin_unset(char **argv) | ||
9005 | { | ||
9006 | int ret; | ||
9007 | unsigned opts; | ||
9008 | |||
9009 | /* "!": do not abort on errors */ | ||
9010 | /* "+": stop at 1st non-option */ | ||
9011 | opts = getopt32(argv, "!+vf"); | ||
9012 | if (opts == (unsigned)-1) | ||
9013 | return EXIT_FAILURE; | ||
9014 | if (opts == 3) { | ||
9015 | bb_error_msg("unset: -v and -f are exclusive"); | ||
9016 | return EXIT_FAILURE; | ||
9017 | } | ||
9018 | argv += optind; | ||
9019 | |||
9020 | ret = EXIT_SUCCESS; | ||
9021 | while (*argv) { | ||
9022 | if (!(opts & 2)) { /* not -f */ | ||
9023 | if (unset_local_var(*argv)) { | ||
9024 | /* unset <nonexistent_var> doesn't fail. | ||
9025 | * Error is when one tries to unset RO var. | ||
9026 | * Message was printed by unset_local_var. */ | ||
9027 | ret = EXIT_FAILURE; | ||
9028 | } | ||
9029 | } | ||
9030 | #if ENABLE_HUSH_FUNCTIONS | ||
9031 | else { | ||
9032 | unset_func(*argv); | ||
9033 | } | ||
9034 | #endif | ||
9035 | argv++; | ||
9036 | } | ||
9037 | return ret; | ||
9038 | } | ||
9039 | |||
9040 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ | 9407 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ |
9041 | static int FAST_FUNC builtin_wait(char **argv) | 9408 | static int FAST_FUNC builtin_wait(char **argv) |
9042 | { | 9409 | { |
@@ -9153,9 +9520,11 @@ static int FAST_FUNC builtin_break(char **argv) | |||
9153 | unsigned depth; | 9520 | unsigned depth; |
9154 | if (G.depth_of_loop == 0) { | 9521 | if (G.depth_of_loop == 0) { |
9155 | bb_error_msg("%s: only meaningful in a loop", argv[0]); | 9522 | bb_error_msg("%s: only meaningful in a loop", argv[0]); |
9523 | /* if we came from builtin_continue(), need to undo "= 1" */ | ||
9524 | G.flag_break_continue = 0; | ||
9156 | return EXIT_SUCCESS; /* bash compat */ | 9525 | return EXIT_SUCCESS; /* bash compat */ |
9157 | } | 9526 | } |
9158 | G.flag_break_continue++; /* BC_BREAK = 1 */ | 9527 | G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */ |
9159 | 9528 | ||
9160 | G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1); | 9529 | G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1); |
9161 | if (depth == UINT_MAX) | 9530 | if (depth == UINT_MAX) |
@@ -9178,12 +9547,12 @@ static int FAST_FUNC builtin_return(char **argv) | |||
9178 | { | 9547 | { |
9179 | int rc; | 9548 | int rc; |
9180 | 9549 | ||
9181 | if (G.flag_return_in_progress != -1) { | 9550 | if (G_flag_return_in_progress != -1) { |
9182 | bb_error_msg("%s: not in a function or sourced script", argv[0]); | 9551 | bb_error_msg("%s: not in a function or sourced script", argv[0]); |
9183 | return EXIT_FAILURE; /* bash compat */ | 9552 | return EXIT_FAILURE; /* bash compat */ |
9184 | } | 9553 | } |
9185 | 9554 | ||
9186 | G.flag_return_in_progress = 1; | 9555 | G_flag_return_in_progress = 1; |
9187 | 9556 | ||
9188 | /* bash: | 9557 | /* bash: |
9189 | * out of range: wraps around at 256, does not error out | 9558 | * out of range: wraps around at 256, does not error out |