aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 12:17:00 +1000
committerNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 12:17:00 +1000
commit38a1e6da34ffe9f0c6cdb8661b263eb80d72981f (patch)
tree434501acda4fa6c635e058eca4eea449dcfbbd08
parentadcaaebedad72991d917fc57348a1b7a87067c2d (diff)
parent4f63c7931c42351e38619842681026ff2c20c7ee (diff)
downloadbusybox-w32-38a1e6da34ffe9f0c6cdb8661b263eb80d72981f.tar.gz
busybox-w32-38a1e6da34ffe9f0c6cdb8661b263eb80d72981f.tar.bz2
busybox-w32-38a1e6da34ffe9f0c6cdb8661b263eb80d72981f.zip
Merge branch 'origin/master' (early part)
-rw-r--r--Makefile4
-rw-r--r--console-tools/setkeycodes.c16
-rw-r--r--editors/vi.c86
-rw-r--r--include/applets.h2
-rw-r--r--include/usage.h16
-rw-r--r--libbb/loop.c2
-rw-r--r--loginutils/getty.c1
-rw-r--r--procps/fuser.c258
-rw-r--r--procps/ps.c22
-rw-r--r--scripts/kconfig/lxdialog/check-lxdialog.sh6
-rwxr-xr-xscripts/trylink17
-rw-r--r--shell/Config.in43
-rw-r--r--shell/README6
-rw-r--r--shell/ash.c48
-rw-r--r--shell/hush.c649
-rw-r--r--shell/hush_test/hush-arith/arith.right9
-rwxr-xr-xshell/hush_test/hush-arith/arith.tests19
-rw-r--r--shell/hush_test/hush-psubst/falsetick.right27
-rwxr-xr-xshell/hush_test/hush-psubst/falsetick.tests22
-rw-r--r--shell/hush_test/hush-vars/param_expand_alt.right2
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_alt.tests4
-rw-r--r--shell/hush_test/hush-vars/param_expand_bash_substring.right64
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_bash_substring.tests83
-rw-r--r--shell/hush_test/hush-vars/param_expand_indicate_error.right15
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_indicate_error.tests20
-rw-r--r--shell/hush_test/hush-vars/param_expand_len.right5
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_len.tests7
-rw-r--r--shell/hush_test/hush-vars/var3.right2
-rw-r--r--shell/hush_test/hush-vars/var_posix1.right7
-rwxr-xr-xshell/hush_test/hush-vars/var_posix1.tests10
-rw-r--r--util-linux/losetup.c59
-rw-r--r--util-linux/mount.c10
32 files changed, 996 insertions, 545 deletions
diff --git a/Makefile b/Makefile
index 547c7166e..6f332825f 100644
--- a/Makefile
+++ b/Makefile
@@ -1003,8 +1003,8 @@ $(mrproper-dirs):
1003mrproper: clean archmrproper $(mrproper-dirs) 1003mrproper: clean archmrproper $(mrproper-dirs)
1004 $(call cmd,rmdirs) 1004 $(call cmd,rmdirs)
1005 $(call cmd,rmfiles) 1005 $(call cmd,rmfiles)
1006 @find -name Config.src | sed 's/.src$/.in/' | xargs -r rm -f 1006 @find -name Config.src | sed 's/.src$$/.in/' | xargs -r rm -f
1007 @find -name Kbuild.src | sed 's/.src$//' | xargs -r rm -f 1007 @find -name Kbuild.src | sed 's/.src$$//' | xargs -r rm -f
1008 1008
1009# distclean 1009# distclean
1010# 1010#
diff --git a/console-tools/setkeycodes.c b/console-tools/setkeycodes.c
index b7851016a..b6a9a32af 100644
--- a/console-tools/setkeycodes.c
+++ b/console-tools/setkeycodes.c
@@ -21,7 +21,7 @@ enum {
21int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 21int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
22int setkeycodes_main(int argc, char **argv) 22int setkeycodes_main(int argc, char **argv)
23{ 23{
24 int fd, sc; 24 int fd;
25 struct kbkeycode a; 25 struct kbkeycode a;
26 26
27 if (!(argc & 1) /* if even */ || argc < 2) { 27 if (!(argc & 1) /* if even */ || argc < 2) {
@@ -30,17 +30,17 @@ int setkeycodes_main(int argc, char **argv)
30 30
31 fd = get_console_fd_or_die(); 31 fd = get_console_fd_or_die();
32 32
33 while (argc > 2) { 33 while (argv[1]) {
34 a.keycode = xatou_range(argv[2], 0, 127); 34 int sc = xstrtoul_range(argv[1], 16, 0, 0xe07f);
35 a.scancode = sc = xstrtoul_range(argv[1], 16, 0, 255); 35 if (sc >= 0xe000) {
36 if (a.scancode > 127) { 36 sc -= 0xe000;
37 a.scancode -= 0xe000; 37 sc += 0x0080;
38 a.scancode += 128;
39 } 38 }
39 a.scancode = sc;
40 a.keycode = xatou_range(argv[2], 0, 255);
40 ioctl_or_perror_and_die(fd, KDSETKEYCODE, &a, 41 ioctl_or_perror_and_die(fd, KDSETKEYCODE, &a,
41 "can't set SCANCODE %x to KEYCODE %d", 42 "can't set SCANCODE %x to KEYCODE %d",
42 sc, a.keycode); 43 sc, a.keycode);
43 argc -= 2;
44 argv += 2; 44 argv += 2;
45 } 45 }
46 return EXIT_SUCCESS; 46 return EXIT_SUCCESS;
diff --git a/editors/vi.c b/editors/vi.c
index d9124fd76..9b050e115 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -793,7 +793,7 @@ static void colon(char *buf)
793 // 793 //
794 794
795 if (!buf[0]) 795 if (!buf[0])
796 goto vc1; 796 goto ret;
797 if (*buf == ':') 797 if (*buf == ':')
798 buf++; // move past the ':' 798 buf++; // move past the ':'
799 799
@@ -881,7 +881,7 @@ static void colon(char *buf)
881 // don't edit, if the current file has been modified 881 // don't edit, if the current file has been modified
882 if (file_modified && !useforce) { 882 if (file_modified && !useforce) {
883 status_line_bold("No write since last change (:edit! overrides)"); 883 status_line_bold("No write since last change (:edit! overrides)");
884 goto vc1; 884 goto ret;
885 } 885 }
886 if (args[0]) { 886 if (args[0]) {
887 // the user supplied a file name 887 // the user supplied a file name
@@ -892,11 +892,11 @@ static void colon(char *buf)
892 } else { 892 } else {
893 // no user file name, no current name- punt 893 // no user file name, no current name- punt
894 status_line_bold("No current filename"); 894 status_line_bold("No current filename");
895 goto vc1; 895 goto ret;
896 } 896 }
897 897
898 if (init_text_buffer(fn) < 0) 898 if (init_text_buffer(fn) < 0)
899 goto vc1; 899 goto ret;
900 900
901#if ENABLE_FEATURE_VI_YANKMARK 901#if ENABLE_FEATURE_VI_YANKMARK
902 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) { 902 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
@@ -921,7 +921,7 @@ static void colon(char *buf)
921 } else if (strncmp(cmd, "file", i) == 0) { // what File is this 921 } else if (strncmp(cmd, "file", i) == 0) { // what File is this
922 if (b != -1 || e != -1) { 922 if (b != -1 || e != -1) {
923 status_line_bold("No address allowed on this command"); 923 status_line_bold("No address allowed on this command");
924 goto vc1; 924 goto ret;
925 } 925 }
926 if (args[0]) { 926 if (args[0]) {
927 // user wants a new filename 927 // user wants a new filename
@@ -967,11 +967,8 @@ static void colon(char *buf)
967 if (c_is_no_print) 967 if (c_is_no_print)
968 standout_end(); 968 standout_end();
969 } 969 }
970#if ENABLE_FEATURE_VI_SET
971 vc2:
972#endif
973 Hit_Return(); 970 Hit_Return();
974 } else if (strncmp(cmd, "quit", i) == 0 // Quit 971 } else if (strncmp(cmd, "quit", i) == 0 // quit
975 || strncmp(cmd, "next", i) == 0 // edit next file 972 || strncmp(cmd, "next", i) == 0 // edit next file
976 ) { 973 ) {
977 int n; 974 int n;
@@ -981,30 +978,30 @@ static void colon(char *buf)
981 optind = save_argc; 978 optind = save_argc;
982 } 979 }
983 editing = 0; 980 editing = 0;
984 goto vc1; 981 goto ret;
985 } 982 }
986 // don't exit if the file been modified 983 // don't exit if the file been modified
987 if (file_modified) { 984 if (file_modified) {
988 status_line_bold("No write since last change (:%s! overrides)", 985 status_line_bold("No write since last change (:%s! overrides)",
989 (*cmd == 'q' ? "quit" : "next")); 986 (*cmd == 'q' ? "quit" : "next"));
990 goto vc1; 987 goto ret;
991 } 988 }
992 // are there other file to edit 989 // are there other file to edit
993 n = save_argc - optind - 1; 990 n = save_argc - optind - 1;
994 if (*cmd == 'q' && n > 0) { 991 if (*cmd == 'q' && n > 0) {
995 status_line_bold("%d more file(s) to edit", n); 992 status_line_bold("%d more file(s) to edit", n);
996 goto vc1; 993 goto ret;
997 } 994 }
998 if (*cmd == 'n' && n <= 0) { 995 if (*cmd == 'n' && n <= 0) {
999 status_line_bold("No more files to edit"); 996 status_line_bold("No more files to edit");
1000 goto vc1; 997 goto ret;
1001 } 998 }
1002 editing = 0; 999 editing = 0;
1003 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] 1000 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
1004 fn = args; 1001 fn = args;
1005 if (!fn[0]) { 1002 if (!fn[0]) {
1006 status_line_bold("No filename given"); 1003 status_line_bold("No filename given");
1007 goto vc1; 1004 goto ret;
1008 } 1005 }
1009 if (b < 0) { // no addr given- use defaults 1006 if (b < 0) { // no addr given- use defaults
1010 q = begin_line(dot); // assume "dot" 1007 q = begin_line(dot); // assume "dot"
@@ -1018,7 +1015,7 @@ static void colon(char *buf)
1018 q = text + ofs; 1015 q = text + ofs;
1019 } 1016 }
1020 if (ch < 0) 1017 if (ch < 0)
1021 goto vc1; // nothing was inserted 1018 goto ret; // nothing was inserted
1022 // how many lines in text[]? 1019 // how many lines in text[]?
1023 li = count_lines(q, q + ch - 1); 1020 li = count_lines(q, q + ch - 1);
1024 status_line("\"%s\"" 1021 status_line("\"%s\""
@@ -1049,25 +1046,21 @@ static void colon(char *buf)
1049 // only blank is regarded as args delmiter. What about tab '\t' ? 1046 // only blank is regarded as args delmiter. What about tab '\t' ?
1050 if (!args[0] || strcasecmp(args, "all") == 0) { 1047 if (!args[0] || strcasecmp(args, "all") == 0) {
1051 // print out values of all options 1048 // print out values of all options
1052 go_bottom_and_clear_to_eol();
1053 printf("----------------------------------------\r\n");
1054#if ENABLE_FEATURE_VI_SETOPTS 1049#if ENABLE_FEATURE_VI_SETOPTS
1055 if (!autoindent) 1050 status_line_bold(
1056 printf("no"); 1051 "%sautoindent "
1057 printf("autoindent "); 1052 "%sflash "
1058 if (!err_method) 1053 "%signorecase "
1059 printf("no"); 1054 "%sshowmatch "
1060 printf("flash "); 1055 "tabstop=%u",
1061 if (!ignorecase) 1056 autoindent ? "" : "no",
1062 printf("no"); 1057 err_method ? "" : "no",
1063 printf("ignorecase "); 1058 ignorecase ? "" : "no",
1064 if (!showmatch) 1059 showmatch ? "" : "no",
1065 printf("no"); 1060 tabstop
1066 printf("showmatch "); 1061 );
1067 printf("tabstop=%d ", tabstop); 1062#endif
1068#endif 1063 goto ret;
1069 printf("\r\n");
1070 goto vc2;
1071 } 1064 }
1072#if ENABLE_FEATURE_VI_SETOPTS 1065#if ENABLE_FEATURE_VI_SETOPTS
1073 argp = args; 1066 argp = args;
@@ -1075,19 +1068,17 @@ static void colon(char *buf)
1075 if (strncmp(argp, "no", 2) == 0) 1068 if (strncmp(argp, "no", 2) == 0)
1076 i = 2; // ":set noautoindent" 1069 i = 2; // ":set noautoindent"
1077 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT); 1070 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
1078 setops(argp, "flash ", i, "fl", VI_ERR_METHOD); 1071 setops(argp, "flash " , i, "fl", VI_ERR_METHOD);
1079 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE); 1072 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
1080 setops(argp, "showmatch ", i, "ic", VI_SHOWMATCH); 1073 setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
1081 /* tabstopXXXX */ 1074 if (strncmp(argp + i, "tabstop=", 8) == 0) {
1082 if (strncmp(argp + i, "tabstop=%d ", 7) == 0) { 1075 int t = 0;
1083 sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch); 1076 sscanf(argp + i+8, "%u", &t);
1084 if (ch > 0 && ch <= MAX_TABSTOP) 1077 if (t > 0 && t <= MAX_TABSTOP)
1085 tabstop = ch; 1078 tabstop = t;
1086 } 1079 }
1087 while (*argp && *argp != ' ') 1080 argp = skip_non_whitespace(argp);
1088 argp++; // skip to arg delimiter (i.e. blank) 1081 argp = skip_whitespace(argp);
1089 while (*argp && *argp == ' ')
1090 argp++; // skip all delimiting blanks
1091 } 1082 }
1092#endif /* FEATURE_VI_SETOPTS */ 1083#endif /* FEATURE_VI_SETOPTS */
1093#endif /* FEATURE_VI_SET */ 1084#endif /* FEATURE_VI_SET */
@@ -1159,7 +1150,7 @@ static void colon(char *buf)
1159#if ENABLE_FEATURE_VI_READONLY 1150#if ENABLE_FEATURE_VI_READONLY
1160 if (readonly_mode && !useforce) { 1151 if (readonly_mode && !useforce) {
1161 status_line_bold("\"%s\" File is read only", fn); 1152 status_line_bold("\"%s\" File is read only", fn);
1162 goto vc3; 1153 goto ret;
1163 } 1154 }
1164#endif 1155#endif
1165 // how many lines in text[]? 1156 // how many lines in text[]?
@@ -1196,9 +1187,6 @@ static void colon(char *buf)
1196 editing = 0; 1187 editing = 0;
1197 } 1188 }
1198 } 1189 }
1199#if ENABLE_FEATURE_VI_READONLY
1200 vc3:;
1201#endif
1202#if ENABLE_FEATURE_VI_YANKMARK 1190#if ENABLE_FEATURE_VI_YANKMARK
1203 } else if (strncmp(cmd, "yank", i) == 0) { // yank lines 1191 } else if (strncmp(cmd, "yank", i) == 0) { // yank lines
1204 if (b < 0) { // no addr given- use defaults 1192 if (b < 0) { // no addr given- use defaults
@@ -1214,7 +1202,7 @@ static void colon(char *buf)
1214 // cmd unknown 1202 // cmd unknown
1215 not_implemented(cmd); 1203 not_implemented(cmd);
1216 } 1204 }
1217 vc1: 1205 ret:
1218 dot = bound_dot(dot); // make sure "dot" is valid 1206 dot = bound_dot(dot); // make sure "dot" is valid
1219 return; 1207 return;
1220#if ENABLE_FEATURE_VI_SEARCH 1208#if ENABLE_FEATURE_VI_SEARCH
diff --git a/include/applets.h b/include/applets.h
index 36b24856a..ff8799c63 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -79,6 +79,8 @@ IF_ARPING(APPLET(arping, _BB_DIR_USR_BIN, _BB_SUID_DROP))
79IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP)) 79IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP))
80IF_AWK(APPLET_NOEXEC(awk, awk, _BB_DIR_USR_BIN, _BB_SUID_DROP, awk)) 80IF_AWK(APPLET_NOEXEC(awk, awk, _BB_DIR_USR_BIN, _BB_SUID_DROP, awk))
81IF_BASENAME(APPLET_NOFORK(basename, basename, _BB_DIR_USR_BIN, _BB_SUID_DROP, basename)) 81IF_BASENAME(APPLET_NOFORK(basename, basename, _BB_DIR_USR_BIN, _BB_SUID_DROP, basename))
82IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash))
83IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash))
82IF_BBCONFIG(APPLET(bbconfig, _BB_DIR_BIN, _BB_SUID_DROP)) 84IF_BBCONFIG(APPLET(bbconfig, _BB_DIR_BIN, _BB_SUID_DROP))
83//IF_BBSH(APPLET(bbsh, _BB_DIR_BIN, _BB_SUID_DROP)) 85//IF_BBSH(APPLET(bbsh, _BB_DIR_BIN, _BB_SUID_DROP))
84IF_BEEP(APPLET(beep, _BB_DIR_USR_BIN, _BB_SUID_DROP)) 86IF_BEEP(APPLET(beep, _BB_DIR_USR_BIN, _BB_SUID_DROP))
diff --git a/include/usage.h b/include/usage.h
index 3fce939bb..d53b86731 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -123,6 +123,8 @@
123#define lash_full_usage "" 123#define lash_full_usage ""
124#define msh_trivial_usage NOUSAGE_STR 124#define msh_trivial_usage NOUSAGE_STR
125#define msh_full_usage "" 125#define msh_full_usage ""
126#define bash_trivial_usage NOUSAGE_STR
127#define bash_full_usage ""
126 128
127#define awk_trivial_usage \ 129#define awk_trivial_usage \
128 "[OPTIONS] [AWK_PROGRAM] [FILE]..." 130 "[OPTIONS] [AWK_PROGRAM] [FILE]..."
@@ -1472,9 +1474,9 @@
1472 "\n -m Find processes which use same fs as FILEs" \ 1474 "\n -m Find processes which use same fs as FILEs" \
1473 "\n -4 Search only IPv4 space" \ 1475 "\n -4 Search only IPv4 space" \
1474 "\n -6 Search only IPv6 space" \ 1476 "\n -6 Search only IPv6 space" \
1475 "\n -s Silent: just exit with 0 if any processes are found" \ 1477 "\n -s Don't display PIDs" \
1476 "\n -k Kill found processes (otherwise display PIDs)" \ 1478 "\n -k Kill found processes" \
1477 "\n -SIGNAL Signal to send (default: TERM)" \ 1479 "\n -SIGNAL Signal to send (default: KILL)" \
1478 1480
1479#define getenforce_trivial_usage NOUSAGE_STR 1481#define getenforce_trivial_usage NOUSAGE_STR
1480#define getenforce_full_usage "" 1482#define getenforce_full_usage ""
@@ -3572,11 +3574,11 @@
3572#if ENABLE_DESKTOP 3574#if ENABLE_DESKTOP
3573 3575
3574#define ps_trivial_usage \ 3576#define ps_trivial_usage \
3575 "" 3577 "[-o COL1,COL2=HEADER]" IF_FEATURE_SHOW_THREADS(" [-T]")
3576#define ps_full_usage "\n\n" \ 3578#define ps_full_usage "\n\n" \
3577 "Report process status\n" \ 3579 "Show list of processes\n" \
3578 "\nOptions:" \ 3580 "\nOptions:" \
3579 "\n -o col1,col2=header Select columns for display" \ 3581 "\n -o COL1,COL2=HEADER Select columns for display" \
3580 IF_FEATURE_SHOW_THREADS( \ 3582 IF_FEATURE_SHOW_THREADS( \
3581 "\n -T Show threads" \ 3583 "\n -T Show threads" \
3582 ) 3584 )
@@ -3592,7 +3594,7 @@
3592#define ps_trivial_usage \ 3594#define ps_trivial_usage \
3593 "" 3595 ""
3594#define ps_full_usage "\n\n" \ 3596#define ps_full_usage "\n\n" \
3595 "Report process status\n" \ 3597 "Show list of processes\n" \
3596 USAGE_PS \ 3598 USAGE_PS \
3597 IF_SELINUX( \ 3599 IF_SELINUX( \
3598 "\n -Z Show selinux context" \ 3600 "\n -Z Show selinux context" \
diff --git a/libbb/loop.c b/libbb/loop.c
index b69d9d961..eb7016d56 100644
--- a/libbb/loop.c
+++ b/libbb/loop.c
@@ -56,7 +56,7 @@ char* FAST_FUNC query_loop(const char *device)
56 fd = open(device, O_RDONLY); 56 fd = open(device, O_RDONLY);
57 if (fd >= 0) { 57 if (fd >= 0) {
58 if (ioctl(fd, BB_LOOP_GET_STATUS, &loopinfo) == 0) { 58 if (ioctl(fd, BB_LOOP_GET_STATUS, &loopinfo) == 0) {
59 dev = xasprintf("%lu %s", (long) loopinfo.lo_offset, 59 dev = xasprintf("%"OFF_FMT"u %s", (off_t) loopinfo.lo_offset,
60 (char *)loopinfo.lo_file_name); 60 (char *)loopinfo.lo_file_name);
61 } 61 }
62 close(fd); 62 close(fd);
diff --git a/loginutils/getty.c b/loginutils/getty.c
index d032357e2..7fb861f9d 100644
--- a/loginutils/getty.c
+++ b/loginutils/getty.c
@@ -431,6 +431,7 @@ static char *get_logname(char *logname, unsigned size_logname,
431 while (cp->eol == '\0') { 431 while (cp->eol == '\0') {
432 432
433 /* Do not report trivial EINTR/EIO errors. */ 433 /* Do not report trivial EINTR/EIO errors. */
434 errno = EINTR; /* make read of 0 bytes be silent too */
434 if (read(STDIN_FILENO, &c, 1) < 1) { 435 if (read(STDIN_FILENO, &c, 1) < 1) {
435 if (errno == EINTR || errno == EIO) 436 if (errno == EINTR || errno == EIO)
436 exit(EXIT_SUCCESS); 437 exit(EXIT_SUCCESS);
diff --git a/procps/fuser.c b/procps/fuser.c
index dc3d01bda..7465d4554 100644
--- a/procps/fuser.c
+++ b/procps/fuser.c
@@ -31,6 +31,15 @@ typedef struct pid_list {
31 pid_t pid; 31 pid_t pid;
32} pid_list; 32} pid_list;
33 33
34
35struct globals {
36 pid_list *pid_list_head;
37 inode_list *inode_list_head;
38};
39#define G (*(struct globals*)&bb_common_bufsiz1)
40#define INIT_G() do { } while (0)
41
42
34static dev_t find_socket_dev(void) 43static dev_t find_socket_dev(void)
35{ 44{
36 int fd = socket(AF_INET, SOCK_DGRAM, 0); 45 int fd = socket(AF_INET, SOCK_DGRAM, 0);
@@ -44,16 +53,6 @@ static dev_t find_socket_dev(void)
44 return 0; 53 return 0;
45} 54}
46 55
47static int file_to_dev_inode(const char *filename, dev_t *dev, ino_t *inode)
48{
49 struct stat f_stat;
50 if (stat(filename, &f_stat))
51 return 0;
52 *inode = f_stat.st_ino;
53 *dev = f_stat.st_dev;
54 return 1;
55}
56
57static char *parse_net_arg(const char *arg, unsigned *port) 56static char *parse_net_arg(const char *arg, unsigned *port)
58{ 57{
59 char path[20], tproto[5]; 58 char path[20], tproto[5];
@@ -63,54 +62,54 @@ static char *parse_net_arg(const char *arg, unsigned *port)
63 sprintf(path, "/proc/net/%s", tproto); 62 sprintf(path, "/proc/net/%s", tproto);
64 if (access(path, R_OK) != 0) 63 if (access(path, R_OK) != 0)
65 return NULL; 64 return NULL;
66 return xstrdup(tproto); 65 return xstrdup(path);
67} 66}
68 67
69static pid_list *add_pid(pid_list *plist, pid_t pid) 68static void add_pid(const pid_t pid)
70{ 69{
71 pid_list *curr = plist; 70 pid_list **curr = &G.pid_list_head;
72 while (curr != NULL) { 71
73 if (curr->pid == pid) 72 while (*curr) {
74 return plist; 73 if ((*curr)->pid == pid)
75 curr = curr->next; 74 return;
75 curr = &(*curr)->next;
76 } 76 }
77 curr = xmalloc(sizeof(pid_list)); 77
78 curr->pid = pid; 78 *curr = xzalloc(sizeof(pid_list));
79 curr->next = plist; 79 (*curr)->pid = pid;
80 return curr;
81} 80}
82 81
83static inode_list *add_inode(inode_list *ilist, dev_t dev, ino_t inode) 82static void add_inode(const struct stat *st)
84{ 83{
85 inode_list *curr = ilist; 84 inode_list **curr = &G.inode_list_head;
86 while (curr != NULL) { 85
87 if (curr->inode == inode && curr->dev == dev) 86 while (*curr) {
88 return ilist; 87 if ((*curr)->dev == st->st_dev
89 curr = curr->next; 88 && (*curr)->inode == st->st_ino
89 ) {
90 return;
91 }
92 curr = &(*curr)->next;
90 } 93 }
91 curr = xmalloc(sizeof(inode_list)); 94
92 curr->dev = dev; 95 *curr = xzalloc(sizeof(inode_list));
93 curr->inode = inode; 96 (*curr)->dev = st->st_dev;
94 curr->next = ilist; 97 (*curr)->inode = st->st_ino;
95 return curr;
96} 98}
97 99
98static inode_list *scan_proc_net(const char *proto, 100static void scan_proc_net(const char *path, unsigned port)
99 unsigned port, inode_list *ilist)
100{ 101{
101 char path[20], line[MAX_LINE + 1]; 102 char line[MAX_LINE + 1];
102 ino_t tmp_inode;
103 dev_t tmp_dev;
104 long long uint64_inode; 103 long long uint64_inode;
105 unsigned tmp_port; 104 unsigned tmp_port;
106 FILE *f; 105 FILE *f;
106 struct stat st;
107 107
108 tmp_dev = find_socket_dev(); 108 st.st_dev = find_socket_dev();
109 109
110 sprintf(path, "/proc/net/%s", proto);
111 f = fopen_for_read(path); 110 f = fopen_for_read(path);
112 if (!f) 111 if (!f)
113 return ilist; 112 return;
114 113
115 while (fgets(line, MAX_LINE, f)) { 114 while (fgets(line, MAX_LINE, f)) {
116 char addr[68]; 115 char addr[68];
@@ -124,22 +123,23 @@ static inode_list *scan_proc_net(const char *proto,
124 if (len > 8 && (option_mask32 & OPT_IP4)) 123 if (len > 8 && (option_mask32 & OPT_IP4))
125 continue; 124 continue;
126 if (tmp_port == port) { 125 if (tmp_port == port) {
127 tmp_inode = uint64_inode; 126 st.st_ino = uint64_inode;
128 ilist = add_inode(ilist, tmp_dev, tmp_inode); 127 add_inode(&st);
129 } 128 }
130 } 129 }
131 } 130 }
132 fclose(f); 131 fclose(f);
133 return ilist;
134} 132}
135 133
136static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode) 134static int search_dev_inode(const struct stat *st)
137{ 135{
136 inode_list *ilist = G.inode_list_head;
137
138 while (ilist) { 138 while (ilist) {
139 if (ilist->dev == dev) { 139 if (ilist->dev == st->st_dev) {
140 if (option_mask32 & OPT_MOUNT) 140 if (option_mask32 & OPT_MOUNT)
141 return 1; 141 return 1;
142 if (ilist->inode == inode) 142 if (ilist->inode == st->st_ino)
143 return 1; 143 return 1;
144 } 144 }
145 ilist = ilist->next; 145 ilist = ilist->next;
@@ -147,48 +147,42 @@ static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode)
147 return 0; 147 return 0;
148} 148}
149 149
150static pid_list *scan_pid_maps(const char *fname, pid_t pid, 150static void scan_pid_maps(const char *fname, pid_t pid)
151 inode_list *ilist, pid_list *plist)
152{ 151{
153 FILE *file; 152 FILE *file;
154 char line[MAX_LINE + 1]; 153 char line[MAX_LINE + 1];
155 int major, minor; 154 int major, minor;
156 ino_t inode;
157 long long uint64_inode; 155 long long uint64_inode;
158 dev_t dev; 156 struct stat st;
159 157
160 file = fopen_for_read(fname); 158 file = fopen_for_read(fname);
161 if (!file) 159 if (!file)
162 return plist; 160 return;
161
163 while (fgets(line, MAX_LINE, file)) { 162 while (fgets(line, MAX_LINE, file)) {
164 if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) 163 if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3)
165 continue; 164 continue;
166 inode = uint64_inode; 165 st.st_ino = uint64_inode;
167 if (major == 0 && minor == 0 && inode == 0) 166 if (major == 0 && minor == 0 && st.st_ino == 0)
168 continue; 167 continue;
169 dev = makedev(major, minor); 168 st.st_dev = makedev(major, minor);
170 if (search_dev_inode(ilist, dev, inode)) 169 if (search_dev_inode(&st))
171 plist = add_pid(plist, pid); 170 add_pid(pid);
172 } 171 }
173 fclose(file); 172 fclose(file);
174 return plist;
175} 173}
176 174
177static pid_list *scan_link(const char *lname, pid_t pid, 175static void scan_link(const char *lname, pid_t pid)
178 inode_list *ilist, pid_list *plist)
179{ 176{
180 ino_t inode; 177 struct stat st;
181 dev_t dev;
182 178
183 if (!file_to_dev_inode(lname, &dev, &inode)) 179 if (stat(lname, &st) >= 0) {
184 return plist; 180 if (search_dev_inode(&st))
185 if (search_dev_inode(ilist, dev, inode)) 181 add_pid(pid);
186 plist = add_pid(plist, pid); 182 }
187 return plist;
188} 183}
189 184
190static pid_list *scan_dir_links(const char *dname, pid_t pid, 185static void scan_dir_links(const char *dname, pid_t pid)
191 inode_list *ilist, pid_list *plist)
192{ 186{
193 DIR *d; 187 DIR *d;
194 struct dirent *de; 188 struct dirent *de;
@@ -196,102 +190,73 @@ static pid_list *scan_dir_links(const char *dname, pid_t pid,
196 190
197 d = opendir(dname); 191 d = opendir(dname);
198 if (!d) 192 if (!d)
199 return plist; 193 return;
194
200 while ((de = readdir(d)) != NULL) { 195 while ((de = readdir(d)) != NULL) {
201 lname = concat_subpath_file(dname, de->d_name); 196 lname = concat_subpath_file(dname, de->d_name);
202 if (lname == NULL) 197 if (lname == NULL)
203 continue; 198 continue;
204 plist = scan_link(lname, pid, ilist, plist); 199 scan_link(lname, pid);
205 free(lname); 200 free(lname);
206 } 201 }
207 closedir(d); 202 closedir(d);
208 return plist;
209} 203}
210 204
211/* NB: does chdir internally */ 205/* NB: does chdir internally */
212static pid_list *scan_proc_pids(inode_list *ilist) 206static void scan_proc_pids(void)
213{ 207{
214 DIR *d; 208 DIR *d;
215 struct dirent *de; 209 struct dirent *de;
216 pid_t pid; 210 pid_t pid;
217 pid_list *plist;
218 211
219 xchdir("/proc"); 212 xchdir("/proc");
220 d = opendir("/proc"); 213 d = opendir("/proc");
221 if (!d) 214 if (!d)
222 return NULL; 215 return;
223 216
224 plist = NULL;
225 while ((de = readdir(d)) != NULL) { 217 while ((de = readdir(d)) != NULL) {
226 pid = (pid_t)bb_strtou(de->d_name, NULL, 10); 218 pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
227 if (errno) 219 if (errno)
228 continue; 220 continue;
229 if (chdir(de->d_name) < 0) 221 if (chdir(de->d_name) < 0)
230 continue; 222 continue;
231 plist = scan_link("cwd", pid, ilist, plist); 223 scan_link("cwd", pid);
232 plist = scan_link("exe", pid, ilist, plist); 224 scan_link("exe", pid);
233 plist = scan_link("root", pid, ilist, plist); 225 scan_link("root", pid);
234 plist = scan_dir_links("fd", pid, ilist, plist);
235 plist = scan_dir_links("lib", pid, ilist, plist);
236 plist = scan_dir_links("mmap", pid, ilist, plist);
237 plist = scan_pid_maps("maps", pid, ilist, plist);
238 xchdir("/proc");
239 }
240 closedir(d);
241 return plist;
242}
243 226
244static int print_pid_list(pid_list *plist) 227 scan_dir_links("fd", pid);
245{ 228 scan_dir_links("lib", pid);
246 while (plist != NULL) { 229 scan_dir_links("mmap", pid);
247 printf("%u ", (unsigned)plist->pid);
248 plist = plist->next;
249 }
250 bb_putchar('\n');
251 return 1;
252}
253
254static int kill_pid_list(pid_list *plist, int sig)
255{
256 pid_t mypid = getpid();
257 int success = 1;
258 230
259 while (plist != NULL) { 231 scan_pid_maps("maps", pid);
260 if (plist->pid != mypid) { 232 xchdir("/proc");
261 if (kill(plist->pid, sig) != 0) {
262 bb_perror_msg("kill pid %u", (unsigned)plist->pid);
263 success = 0;
264 }
265 }
266 plist = plist->next;
267 } 233 }
268 return success; 234 closedir(d);
269} 235}
270 236
271int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 237int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
272int fuser_main(int argc UNUSED_PARAM, char **argv) 238int fuser_main(int argc UNUSED_PARAM, char **argv)
273{ 239{
274 pid_list *plist; 240 pid_list *plist;
275 inode_list *ilist; 241 pid_t mypid;
276 char **pp; 242 char **pp;
277 dev_t dev; 243 struct stat st;
278 ino_t inode;
279 unsigned port; 244 unsigned port;
280 int opt; 245 int opt;
281 int success; 246 int exitcode;
282 int killsig; 247 int killsig;
283/* 248/*
284fuser [options] FILEs or PORT/PROTOs 249fuser [OPTIONS] FILE or PORT/PROTO
285Find processes which use FILEs or PORTs 250Find processes which use FILEs or PORTs
286 -m Find processes which use same fs as FILEs 251 -m Find processes which use same fs as FILEs
287 -4 Search only IPv4 space 252 -4 Search only IPv4 space
288 -6 Search only IPv6 space 253 -6 Search only IPv6 space
289 -s Silent: just exit with 0 if any processes are found 254 -s Don't display PIDs
290 -k Kill found processes (otherwise display PIDs) 255 -k Kill found processes
291 -SIGNAL Signal to send (default: TERM) 256 -SIGNAL Signal to send (default: KILL)
292*/ 257*/
293 /* Handle -SIGNAL. Oh my... */ 258 /* Handle -SIGNAL. Oh my... */
294 killsig = SIGTERM; 259 killsig = SIGKILL; /* yes, the default is not SIGTERM */
295 pp = argv; 260 pp = argv;
296 while (*++pp) { 261 while (*++pp) {
297 char *arg = *pp; 262 char *arg = *pp;
@@ -313,33 +278,54 @@ Find processes which use FILEs or PORTs
313 break; 278 break;
314 } 279 }
315 280
281 opt_complementary = "-1"; /* at least one param */
316 opt = getopt32(argv, OPTION_STRING); 282 opt = getopt32(argv, OPTION_STRING);
317 argv += optind; 283 argv += optind;
318 284
319 ilist = NULL;
320 pp = argv; 285 pp = argv;
321 while (*pp) { 286 while (*pp) {
322 char *proto = parse_net_arg(*pp, &port); 287 char *path = parse_net_arg(*pp, &port);
323 if (proto) { /* PORT/PROTO */ 288 if (path) { /* PORT/PROTO */
324 ilist = scan_proc_net(proto, port, ilist); 289 scan_proc_net(path, port);
325 free(proto); 290 free(path);
326 } else { /* FILE */ 291 } else { /* FILE */
327 if (!file_to_dev_inode(*pp, &dev, &inode)) 292 xstat(*pp, &st);
328 bb_perror_msg_and_die("can't open '%s'", *pp); 293 add_inode(&st);
329 ilist = add_inode(ilist, dev, inode);
330 } 294 }
331 pp++; 295 pp++;
332 } 296 }
333 297
334 plist = scan_proc_pids(ilist); /* changes dir to "/proc" */ 298 scan_proc_pids(); /* changes dir to "/proc" */
335 299
336 if (!plist) 300 mypid = getpid();
337 return EXIT_FAILURE; 301 plist = G.pid_list_head;
338 success = 1; 302 while (1) {
339 if (opt & OPT_KILL) { 303 if (!plist)
340 success = kill_pid_list(plist, killsig); 304 return EXIT_FAILURE;
341 } else if (!(opt & OPT_SILENT)) { 305 if (plist->pid != mypid)
342 success = print_pid_list(plist); 306 break;
307 plist = plist->next;
343 } 308 }
344 return (success != 1); /* 0 == success */ 309
310 exitcode = EXIT_SUCCESS;
311 do {
312 if (plist->pid != mypid) {
313 if (opt & OPT_KILL) {
314 if (kill(plist->pid, killsig) != 0) {
315 bb_perror_msg("kill pid %u", (unsigned)plist->pid);
316 exitcode = EXIT_FAILURE;
317 }
318 }
319 if (!(opt & OPT_SILENT)) {
320 printf("%u ", (unsigned)plist->pid);
321 }
322 }
323 plist = plist->next;
324 } while (plist);
325
326 if (!(opt & (OPT_SILENT))) {
327 bb_putchar('\n');
328 }
329
330 return exitcode;
345} 331}
diff --git a/procps/ps.c b/procps/ps.c
index c3b200866..a3220a926 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -232,7 +232,6 @@ static void func_tty(char *buf, int size, const procps_status_t *ps)
232 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); 232 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor);
233} 233}
234 234
235
236#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 235#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
237 236
238static void func_rgroup(char *buf, int size, const procps_status_t *ps) 237static void func_rgroup(char *buf, int size, const procps_status_t *ps)
@@ -250,9 +249,10 @@ static void func_nice(char *buf, int size, const procps_status_t *ps)
250 sprintf(buf, "%*d", size, ps->niceness); 249 sprintf(buf, "%*d", size, ps->niceness);
251} 250}
252 251
253#endif /* FEATURE_PS_ADDITIONAL_COLUMNS */ 252#endif
254 253
255#if ENABLE_FEATURE_PS_TIME 254#if ENABLE_FEATURE_PS_TIME
255
256static void func_etime(char *buf, int size, const procps_status_t *ps) 256static void func_etime(char *buf, int size, const procps_status_t *ps)
257{ 257{
258 /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */ 258 /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */
@@ -278,6 +278,7 @@ static void func_time(char *buf, int size, const procps_status_t *ps)
278 mm /= 60; 278 mm /= 60;
279 snprintf(buf, size+1, "%3lu:%02u", mm, ss); 279 snprintf(buf, size+1, "%3lu:%02u", mm, ss);
280} 280}
281
281#endif 282#endif
282 283
283#if ENABLE_SELINUX 284#if ENABLE_SELINUX
@@ -337,11 +338,24 @@ static ps_out_t* new_out_t(void)
337static const ps_out_t* find_out_spec(const char *name) 338static const ps_out_t* find_out_spec(const char *name)
338{ 339{
339 unsigned i; 340 unsigned i;
341#if ENABLE_DESKTOP
342 char buf[ARRAY_SIZE(out_spec)*7 + 1];
343 char *p = buf;
344#endif
345
340 for (i = 0; i < ARRAY_SIZE(out_spec); i++) { 346 for (i = 0; i < ARRAY_SIZE(out_spec); i++) {
341 if (!strncmp(name, out_spec[i].name6, 6)) 347 if (strncmp(name, out_spec[i].name6, 6) == 0)
342 return &out_spec[i]; 348 return &out_spec[i];
349#if ENABLE_DESKTOP
350 p += sprintf(p, "%.6s,", out_spec[i].name6);
351#endif
343 } 352 }
344 bb_error_msg_and_die("bad -o argument '%s'", name); 353#if ENABLE_DESKTOP
354 p[-1] = '\0';
355 bb_error_msg_and_die("bad -o argument '%s', supported arguments: %s", name, buf);
356#else
357 bb_error_msg_and_die("bad -o argument '%s'");
358#endif
345} 359}
346 360
347static void parse_o(char* opt) 361static void parse_o(char* opt)
diff --git a/scripts/kconfig/lxdialog/check-lxdialog.sh b/scripts/kconfig/lxdialog/check-lxdialog.sh
index 5552154cb..acabe385c 100644
--- a/scripts/kconfig/lxdialog/check-lxdialog.sh
+++ b/scripts/kconfig/lxdialog/check-lxdialog.sh
@@ -19,7 +19,11 @@ ldflags()
19# Where is ncurses.h? 19# Where is ncurses.h?
20ccflags() 20ccflags()
21{ 21{
22 if [ -f /usr/include/ncurses/ncurses.h ]; then 22 if [ -f /usr/include/ncursesw/ncurses.h ]; then
23 echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncurses.h>"'
24 elif [ -f /usr/include/ncursesw/curses.h ]; then
25 echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncursesw/curses.h>"'
26 elif [ -f /usr/include/ncurses/ncurses.h ]; then
23 echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"' 27 echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
24 elif [ -f /usr/include/ncurses/curses.h ]; then 28 elif [ -f /usr/include/ncurses/curses.h ]; then
25 echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"' 29 echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"'
diff --git a/scripts/trylink b/scripts/trylink
index 8c8854679..164f5274c 100755
--- a/scripts/trylink
+++ b/scripts/trylink
@@ -85,6 +85,9 @@ LDLIBS="$7"
85# The --sort-section option is not supported by older versions of ld 85# The --sort-section option is not supported by older versions of ld
86SORT_SECTION=`check_cc "-Wl,--sort-section,alignment" ""` 86SORT_SECTION=`check_cc "-Wl,--sort-section,alignment" ""`
87 87
88# gold may not support --sort-common (yet)
89SORT_COMMON=`check_cc "-Wl,--sort-common" ""`
90
88# Static linking against glibc produces buggy executables 91# Static linking against glibc produces buggy executables
89# (glibc does not cope well with ld --gc-sections). 92# (glibc does not cope well with ld --gc-sections).
90# See sources.redhat.com/bugzilla/show_bug.cgi?id=3400 93# See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
@@ -114,7 +117,7 @@ l_list=`echo "$LDLIBS" | sed -e 's/ / -l/g' -e 's/^/-l/' -e 's/^-l$//'`
114test "x$l_list" != "x" && l_list="-Wl,--start-group $l_list -Wl,--end-group" 117test "x$l_list" != "x" && l_list="-Wl,--start-group $l_list -Wl,--end-group"
115try $CC $CFLAGS $LDFLAGS \ 118try $CC $CFLAGS $LDFLAGS \
116 -o $EXE \ 119 -o $EXE \
117 -Wl,--sort-common \ 120 $SORT_COMMON \
118 $SORT_SECTION \ 121 $SORT_SECTION \
119 $GC_SECTIONS \ 122 $GC_SECTIONS \
120 -Wl,--start-group $O_FILES $A_FILES -Wl,--end-group \ 123 -Wl,--start-group $O_FILES $A_FILES -Wl,--end-group \
@@ -139,7 +142,7 @@ while test "$LDLIBS"; do
139 $debug && echo "Trying -l options: '$l_list'" 142 $debug && echo "Trying -l options: '$l_list'"
140 try $CC $CFLAGS $LDFLAGS \ 143 try $CC $CFLAGS $LDFLAGS \
141 -o $EXE \ 144 -o $EXE \
142 -Wl,--sort-common \ 145 $SORT_COMMON \
143 $SORT_SECTION \ 146 $SORT_SECTION \
144 $GC_SECTIONS \ 147 $GC_SECTIONS \
145 -Wl,--start-group $O_FILES $A_FILES -Wl,--end-group \ 148 -Wl,--start-group $O_FILES $A_FILES -Wl,--end-group \
@@ -171,7 +174,7 @@ test "x$l_list" != "x" && l_list="-Wl,--start-group $l_list -Wl,--end-group"
171if ! test -f busybox_ldscript; then 174if ! test -f busybox_ldscript; then
172 try $CC $CFLAGS $LDFLAGS \ 175 try $CC $CFLAGS $LDFLAGS \
173 -o $EXE \ 176 -o $EXE \
174 -Wl,--sort-common \ 177 $SORT_COMMON \
175 $SORT_SECTION \ 178 $SORT_SECTION \
176 $GC_SECTIONS \ 179 $GC_SECTIONS \
177 -Wl,--start-group $O_FILES $A_FILES -Wl,--end-group \ 180 -Wl,--start-group $O_FILES $A_FILES -Wl,--end-group \
@@ -193,7 +196,7 @@ else
193 # Hmm, "ld --sort-section alignment" should do it too. 196 # Hmm, "ld --sort-section alignment" should do it too.
194 try $CC $CFLAGS $LDFLAGS \ 197 try $CC $CFLAGS $LDFLAGS \
195 -o $EXE \ 198 -o $EXE \
196 -Wl,--sort-common \ 199 $SORT_COMMON \
197 $SORT_SECTION \ 200 $SORT_SECTION \
198 $GC_SECTIONS \ 201 $GC_SECTIONS \
199 -Wl,-T,busybox_ldscript \ 202 -Wl,-T,busybox_ldscript \
@@ -228,7 +231,7 @@ if test "$CONFIG_BUILD_LIBBUSYBOX" = y; then
228 -Wl,-z,combreloc \ 231 -Wl,-z,combreloc \
229 -Wl,-soname="libbusybox.so.$BB_VER" \ 232 -Wl,-soname="libbusybox.so.$BB_VER" \
230 -Wl,--undefined=lbb_main \ 233 -Wl,--undefined=lbb_main \
231 -Wl,--sort-common \ 234 $SORT_COMMON \
232 $SORT_SECTION \ 235 $SORT_SECTION \
233 -Wl,--start-group $A_FILES -Wl,--end-group \ 236 -Wl,--start-group $A_FILES -Wl,--end-group \
234 $l_list \ 237 $l_list \
@@ -249,7 +252,7 @@ if test "$CONFIG_FEATURE_SHARED_BUSYBOX" = y; then
249 EXE="$sharedlib_dir/busybox_unstripped" 252 EXE="$sharedlib_dir/busybox_unstripped"
250 try $CC $CFLAGS $LDFLAGS \ 253 try $CC $CFLAGS $LDFLAGS \
251 -o $EXE \ 254 -o $EXE \
252 -Wl,--sort-common \ 255 $SORT_COMMON \
253 $SORT_SECTION \ 256 $SORT_SECTION \
254 $GC_SECTIONS \ 257 $GC_SECTIONS \
255 -Wl,--start-group $O_FILES -Wl,--end-group \ 258 -Wl,--start-group $O_FILES -Wl,--end-group \
@@ -288,7 +291,7 @@ int main(int argc, char **argv)
288 EXE="$sharedlib_dir/$name" 291 EXE="$sharedlib_dir/$name"
289 try $CC $CFLAGS $LDFLAGS "$sharedlib_dir/applet.c" \ 292 try $CC $CFLAGS $LDFLAGS "$sharedlib_dir/applet.c" \
290 -o $EXE \ 293 -o $EXE \
291 -Wl,--sort-common \ 294 $SORT_COMMON \
292 $SORT_SECTION \ 295 $SORT_SECTION \
293 $GC_SECTIONS \ 296 $GC_SECTIONS \
294 -L"$sharedlib_dir" -lbusybox \ 297 -L"$sharedlib_dir" -lbusybox \
diff --git a/shell/Config.in b/shell/Config.in
index cf599dff4..286a3415e 100644
--- a/shell/Config.in
+++ b/shell/Config.in
@@ -6,11 +6,11 @@
6menu "Shells" 6menu "Shells"
7 7
8choice 8choice
9 prompt "Choose your default shell" 9 prompt "Choose which shell is aliased to 'sh' name"
10 default FEATURE_SH_IS_NONE 10 default FEATURE_SH_IS_NONE
11 help 11 help
12 Choose a shell. The ash shell is the most bash compatible 12 Choose which shell you want to be executed by 'sh' alias.
13 and full featured one. 13 The ash shell is the most bash compatible and full featured one.
14 14
15config FEATURE_SH_IS_ASH 15config FEATURE_SH_IS_ASH
16 select ASH 16 select ASH
@@ -21,15 +21,38 @@ config FEATURE_SH_IS_HUSH
21 select HUSH 21 select HUSH
22 bool "hush" 22 bool "hush"
23 23
24####config FEATURE_SH_IS_LASH 24config FEATURE_SH_IS_NONE
25#### select LASH 25 bool "none"
26#### bool "lash"
27 26
28####config FEATURE_SH_IS_MSH 27endchoice
29#### select MSH
30#### bool "msh"
31 28
32config FEATURE_SH_IS_NONE 29choice
30 prompt "Choose which shell is aliased to 'bash' name"
31 default FEATURE_BASH_IS_NONE
32 help
33 Choose which shell you want to be executed by 'bash' alias.
34 The ash shell is the most bash compatible and full featured one.
35
36 Note that selecting this option does not switch on any bash
37 compatibility code. It merely makes it possible to install
38 /bin/bash (sym)link and run scripts which start with
39 #!/bin/bash line.
40
41 Many systems use it in scripts which use bash-specific features,
42 even simple ones like $RANDOM. Without this option, busybox
43 can't be used for running them because it won't recongnize
44 "bash" as a supported applet name.
45
46config FEATURE_BASH_IS_ASH
47 select ASH
48 bool "ash"
49 depends on !NOMMU
50
51config FEATURE_BASH_IS_HUSH
52 select HUSH
53 bool "hush"
54
55config FEATURE_BASH_IS_NONE
33 bool "none" 56 bool "none"
34 57
35endchoice 58endchoice
diff --git a/shell/README b/shell/README
index 550c712d3..6a9f5b6ae 100644
--- a/shell/README
+++ b/shell/README
@@ -34,7 +34,7 @@ Shell Command Language
34 34
35It says that shell must implement special built-ins. Special built-ins 35It says that shell must implement special built-ins. Special built-ins
36differ from regular ones by the fact that variable assignments 36differ from regular ones by the fact that variable assignments
37done on special builtin is *PRESERVED*. That is, 37done on special builtin are *PRESERVED*. That is,
38 38
39VAR=VAL special_builtin; echo $VAR 39VAR=VAL special_builtin; echo $VAR
40 40
@@ -43,7 +43,7 @@ should print VAL.
43(Another distinction is that an error in special built-in should 43(Another distinction is that an error in special built-in should
44abort the shell, but this is not such a critical difference, 44abort the shell, but this is not such a critical difference,
45and moreover, at least bash's "set" does not follow this rule, 45and moreover, at least bash's "set" does not follow this rule,
46which is even codified in autoconf now...). 46which is even codified in autoconf configure logic now...)
47 47
48List of special builtins: 48List of special builtins:
49 49
@@ -73,7 +73,7 @@ unset [-fv] name...
73In practice, no one uses this obscure feature - none of these builtins 73In practice, no one uses this obscure feature - none of these builtins
74gives any special reasons to play such dirty tricks. 74gives any special reasons to play such dirty tricks.
75 75
76However. This section says that *function invocation* should act 76However. This section also says that *function invocation* should act
77similar to special built-in. That is, variable assignments 77similar to special built-in. That is, variable assignments
78done on function invocation should be preserved after function invocation. 78done on function invocation should be preserved after function invocation.
79 79
diff --git a/shell/ash.c b/shell/ash.c
index 4e80b097c..ef2121885 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -7868,8 +7868,10 @@ changepath(const char *new)
7868 if (*old != *new) { 7868 if (*old != *new) {
7869 firstchange = idx; 7869 firstchange = idx;
7870 if ((*old == '\0' && *new == ':') 7870 if ((*old == '\0' && *new == ':')
7871 || (*old == ':' && *new == '\0')) 7871 || (*old == ':' && *new == '\0')
7872 ) {
7872 firstchange++; 7873 firstchange++;
7874 }
7873 old = new; /* ignore subsequent differences */ 7875 old = new; /* ignore subsequent differences */
7874 } 7876 }
7875 if (*new == '\0') 7877 if (*new == '\0')
@@ -7878,7 +7880,8 @@ changepath(const char *new)
7878 idx_bltin = idx; 7880 idx_bltin = idx;
7879 if (*new == ':') 7881 if (*new == ':')
7880 idx++; 7882 idx++;
7881 new++, old++; 7883 new++;
7884 old++;
7882 } 7885 }
7883 if (builtinloc < 0 && idx_bltin >= 0) 7886 if (builtinloc < 0 && idx_bltin >= 0)
7884 builtinloc = idx_bltin; /* zap builtins */ 7887 builtinloc = idx_bltin; /* zap builtins */
@@ -7954,23 +7957,6 @@ static const char *const tokname_array[] = {
7954 "\1}", 7957 "\1}",
7955}; 7958};
7956 7959
7957static const char *
7958tokname(int tok)
7959{
7960 static char buf[16];
7961
7962//try this:
7963//if (tok < TSEMI) return tokname_array[tok] + 1;
7964//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7965//return buf;
7966
7967 if (tok >= TSEMI)
7968 buf[0] = '"';
7969 sprintf(buf + (tok >= TSEMI), "%s%c",
7970 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7971 return buf;
7972}
7973
7974/* Wrapper around strcmp for qsort/bsearch/... */ 7960/* Wrapper around strcmp for qsort/bsearch/... */
7975static int 7961static int
7976pstrcmp(const void *a, const void *b) 7962pstrcmp(const void *a, const void *b)
@@ -10745,7 +10731,16 @@ static struct nodelist *backquotelist;
10745static union node *redirnode; 10731static union node *redirnode;
10746static struct heredoc *heredoc; 10732static struct heredoc *heredoc;
10747 10733
10748/* 10734static const char *
10735tokname(char *buf, int tok)
10736{
10737 if (tok < TSEMI)
10738 return tokname_array[tok] + 1;
10739 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10740 return buf;
10741}
10742
10743/* raise_error_unexpected_syntax:
10749 * Called when an unexpected token is read during the parse. The argument 10744 * Called when an unexpected token is read during the parse. The argument
10750 * is the token that is expected, or -1 if more than one type of token can 10745 * is the token that is expected, or -1 if more than one type of token can
10751 * occur at this point. 10746 * occur at this point.
@@ -10755,11 +10750,12 @@ static void
10755raise_error_unexpected_syntax(int token) 10750raise_error_unexpected_syntax(int token)
10756{ 10751{
10757 char msg[64]; 10752 char msg[64];
10753 char buf[16];
10758 int l; 10754 int l;
10759 10755
10760 l = sprintf(msg, "unexpected %s", tokname(lasttoken)); 10756 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
10761 if (token >= 0) 10757 if (token >= 0)
10762 sprintf(msg + l, " (expecting %s)", tokname(token)); 10758 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
10763 raise_error_syntax(msg); 10759 raise_error_syntax(msg);
10764 /* NOTREACHED */ 10760 /* NOTREACHED */
10765} 10761}
@@ -11147,7 +11143,7 @@ parse_command(void)
11147 n1->nbinary.ch1 = list(0); 11143 n1->nbinary.ch1 = list(0);
11148 got = readtoken(); 11144 got = readtoken();
11149 if (got != TDO) { 11145 if (got != TDO) {
11150 TRACE(("expecting DO got %s %s\n", tokname(got), 11146 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
11151 got == TWORD ? wordtext : "")); 11147 got == TWORD ? wordtext : ""));
11152 raise_error_unexpected_syntax(TDO); 11148 raise_error_unexpected_syntax(TDO);
11153 } 11149 }
@@ -12231,7 +12227,7 @@ readtoken(void)
12231 pp = findkwd(wordtext); 12227 pp = findkwd(wordtext);
12232 if (pp) { 12228 if (pp) {
12233 lasttoken = t = pp - tokname_array; 12229 lasttoken = t = pp - tokname_array;
12234 TRACE(("keyword %s recognized\n", tokname(t))); 12230 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
12235 goto out; 12231 goto out;
12236 } 12232 }
12237 } 12233 }
@@ -12252,9 +12248,9 @@ readtoken(void)
12252 checkkwd = 0; 12248 checkkwd = 0;
12253#if DEBUG 12249#if DEBUG
12254 if (!alreadyseen) 12250 if (!alreadyseen)
12255 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); 12251 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
12256 else 12252 else
12257 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); 12253 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
12258#endif 12254#endif
12259 return t; 12255 return t;
12260} 12256}
diff --git a/shell/hush.c b/shell/hush.c
index 8baccf246..72cfb232a 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -37,31 +37,33 @@
37 * handle the recursion implicit in the various substitutions, especially 37 * handle the recursion implicit in the various substitutions, especially
38 * across continuation lines. 38 * across continuation lines.
39 * 39 *
40 * POSIX syntax not implemented: 40 * TODOs:
41 * grep for "TODO" and fix (some of them are easy)
42 * special variables (done: PWD, PPID, RANDOM)
43 * tilde expansion
41 * aliases 44 * aliases
42 * Tilde Expansion 45 * follow IFS rules more precisely, including update semantics
46 * builtins mandated by standards we don't support:
47 * [un]alias, command, fc, getopts, newgrp, readonly, times
48 * make complex ${var%...} constructs support optional
49 * make here documents optional
43 * 50 *
44 * Bash compat TODO: 51 * Bash compat TODO:
45 * redirection of stdout+stderr: &> and >& 52 * redirection of stdout+stderr: &> and >&
53 * subst operator: ${var/[/]expr/expr}
46 * brace expansion: one/{two,three,four} 54 * brace expansion: one/{two,three,four}
47 * reserved words: function select 55 * reserved words: function select
48 * advanced test: [[ ]] 56 * advanced test: [[ ]]
49 * substrings: ${var:1:5}
50 * process substitution: <(list) and >(list) 57 * process substitution: <(list) and >(list)
51 * =~: regex operator 58 * =~: regex operator
52 * let EXPR [EXPR...] 59 * let EXPR [EXPR...]
53 * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) 60 * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION)
54 * If the last arg evaluates to 0, let returns 1; 0 otherwise. 61 * If the last arg evaluates to 0, let returns 1; 0 otherwise.
55 * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used) 62 * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used)
56 * ((EXPR)) 63 * ((EXPR))
57 * The EXPR is evaluated according to ARITHMETIC EVALUATION. 64 * The EXPR is evaluated according to ARITHMETIC EVALUATION.
58 * This is exactly equivalent to let "EXPR". 65 * This is exactly equivalent to let "EXPR".
59 * $[EXPR]: synonym for $((EXPR)) 66 * $[EXPR]: synonym for $((EXPR))
60 *
61 * TODOs:
62 * grep for "TODO" and fix (some of them are easy)
63 * special variables (done: PWD, PPID, RANDOM)
64 * follow IFS rules more precisely, including update semantics
65 * export builtin should be special, its arguments are assignments 67 * export builtin should be special, its arguments are assignments
66 * and therefore expansion of them should be "one-word" expansion: 68 * and therefore expansion of them should be "one-word" expansion:
67 * $ export i=`echo 'a b'` # export has one arg: "i=a b" 69 * $ export i=`echo 'a b'` # export has one arg: "i=a b"
@@ -170,6 +172,7 @@
170#define debug_printf_env(...) do {} while (0) 172#define debug_printf_env(...) do {} while (0)
171#define debug_printf_jobs(...) do {} while (0) 173#define debug_printf_jobs(...) do {} while (0)
172#define debug_printf_expand(...) do {} while (0) 174#define debug_printf_expand(...) do {} while (0)
175#define debug_printf_varexp(...) do {} while (0)
173#define debug_printf_glob(...) do {} while (0) 176#define debug_printf_glob(...) do {} while (0)
174#define debug_printf_list(...) do {} while (0) 177#define debug_printf_list(...) do {} while (0)
175#define debug_printf_subst(...) do {} while (0) 178#define debug_printf_subst(...) do {} while (0)
@@ -177,9 +180,13 @@
177 180
178#define ERR_PTR ((void*)(long)1) 181#define ERR_PTR ((void*)(long)1)
179 182
180#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" 183#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
181 184
182#define SPECIAL_VAR_SYMBOL 3 185#define _SPECIAL_VARS_STR "_*@$!?#"
186#define SPECIAL_VARS_STR ("_*@$!?#" + 1)
187#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3)
188
189#define SPECIAL_VAR_SYMBOL 3
183 190
184struct variable; 191struct variable;
185 192
@@ -742,6 +749,10 @@ static const struct built_in_command bltins2[] = {
742# define DEBUG_EXPAND 0 749# define DEBUG_EXPAND 0
743#endif 750#endif
744 751
752#ifndef debug_printf_varexp
753# define debug_printf_varexp(...) (indent(), fprintf(stderr, __VA_ARGS__))
754#endif
755
745#ifndef debug_printf_glob 756#ifndef debug_printf_glob
746# define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__)) 757# define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__))
747# define DEBUG_GLOB 1 758# define DEBUG_GLOB 1
@@ -803,10 +814,10 @@ static void xxfree(void *ptr)
803 fdprintf(2, "free %p\n", ptr); 814 fdprintf(2, "free %p\n", ptr);
804 free(ptr); 815 free(ptr);
805} 816}
806#define xmalloc(s) xxmalloc(__LINE__, s) 817# define xmalloc(s) xxmalloc(__LINE__, s)
807#define xrealloc(p, s) xxrealloc(__LINE__, p, s) 818# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
808#define xstrdup(s) xxstrdup(__LINE__, s) 819# define xstrdup(s) xxstrdup(__LINE__, s)
809#define free(p) xxfree(p) 820# define free(p) xxfree(p)
810#endif 821#endif
811 822
812 823
@@ -1150,9 +1161,9 @@ static void SIGCHLD_handler(int sig UNUSED_PARAM)
1150#if ENABLE_HUSH_JOB 1161#if ENABLE_HUSH_JOB
1151 1162
1152/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 1163/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
1153#define disable_restore_tty_pgrp_on_exit() (die_sleep = 0) 1164# define disable_restore_tty_pgrp_on_exit() (die_sleep = 0)
1154/* After [v]fork, in parent: restore tty pgrp on xfunc death */ 1165/* After [v]fork, in parent: restore tty pgrp on xfunc death */
1155#define enable_restore_tty_pgrp_on_exit() (die_sleep = -1) 1166# define enable_restore_tty_pgrp_on_exit() (die_sleep = -1)
1156 1167
1157/* Restores tty foreground process group, and exits. 1168/* Restores tty foreground process group, and exits.
1158 * May be called as signal handler for fatal signal 1169 * May be called as signal handler for fatal signal
@@ -1178,8 +1189,8 @@ static void sigexit(int sig)
1178} 1189}
1179#else 1190#else
1180 1191
1181#define disable_restore_tty_pgrp_on_exit() ((void)0) 1192# define disable_restore_tty_pgrp_on_exit() ((void)0)
1182#define enable_restore_tty_pgrp_on_exit() ((void)0) 1193# define enable_restore_tty_pgrp_on_exit() ((void)0)
1183 1194
1184#endif 1195#endif
1185 1196
@@ -1511,8 +1522,8 @@ static void unset_vars(char **strings)
1511} 1522}
1512 1523
1513#if ENABLE_SH_MATH_SUPPORT 1524#if ENABLE_SH_MATH_SUPPORT
1514#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) 1525# define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1515#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) 1526# define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1516static char* FAST_FUNC endofname(const char *name) 1527static char* FAST_FUNC endofname(const char *name)
1517{ 1528{
1518 char *p; 1529 char *p;
@@ -1596,10 +1607,11 @@ static struct variable *set_vars_and_save_old(char **strings)
1596 */ 1607 */
1597static int FAST_FUNC static_get(struct in_str *i) 1608static int FAST_FUNC static_get(struct in_str *i)
1598{ 1609{
1599 int ch = *i->p++; 1610 int ch = *i->p;
1600 if (ch != '\0') 1611 if (ch != '\0') {
1612 i->p++;
1601 return ch; 1613 return ch;
1602 i->p--; 1614 }
1603 return EOF; 1615 return EOF;
1604} 1616}
1605 1617
@@ -1651,7 +1663,7 @@ static void get_user_input(struct in_str *i)
1651 const char *prompt_str; 1663 const char *prompt_str;
1652 1664
1653 prompt_str = setup_prompt_string(i->promptmode); 1665 prompt_str = setup_prompt_string(i->promptmode);
1654#if ENABLE_FEATURE_EDITING 1666# if ENABLE_FEATURE_EDITING
1655 /* Enable command line editing only while a command line 1667 /* Enable command line editing only while a command line
1656 * is actually being read */ 1668 * is actually being read */
1657 do { 1669 do {
@@ -1667,7 +1679,7 @@ static void get_user_input(struct in_str *i)
1667 G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ 1679 G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */
1668 G.user_input_buf[1] = '\0'; 1680 G.user_input_buf[1] = '\0';
1669 } 1681 }
1670#else 1682# else
1671 do { 1683 do {
1672 G.flag_SIGINT = 0; 1684 G.flag_SIGINT = 0;
1673 fputs(prompt_str, stdout); 1685 fputs(prompt_str, stdout);
@@ -1677,7 +1689,7 @@ static void get_user_input(struct in_str *i)
1677//do we need check_and_run_traps(0)? (maybe only if stdin) 1689//do we need check_and_run_traps(0)? (maybe only if stdin)
1678 } while (G.flag_SIGINT); 1690 } while (G.flag_SIGINT);
1679 i->eof_flag = (r == EOF); 1691 i->eof_flag = (r == EOF);
1680#endif 1692# endif
1681 i->p = G.user_input_buf; 1693 i->p = G.user_input_buf;
1682} 1694}
1683 1695
@@ -1816,11 +1828,12 @@ static void o_addblock(o_string *o, const char *str, int len)
1816 o->data[o->length] = '\0'; 1828 o->data[o->length] = '\0';
1817} 1829}
1818 1830
1819#if !BB_MMU
1820static void o_addstr(o_string *o, const char *str) 1831static void o_addstr(o_string *o, const char *str)
1821{ 1832{
1822 o_addblock(o, str, strlen(str)); 1833 o_addblock(o, str, strlen(str));
1823} 1834}
1835
1836#if !BB_MMU
1824static void nommu_addchr(o_string *o, int ch) 1837static void nommu_addchr(o_string *o, int ch)
1825{ 1838{
1826 if (o) 1839 if (o)
@@ -2209,7 +2222,7 @@ static int o_glob(o_string *o, int n)
2209 return n; 2222 return n;
2210} 2223}
2211 2224
2212#else 2225#else /* !HUSH_BRACE_EXP */
2213 2226
2214/* Helper */ 2227/* Helper */
2215static int glob_needed(const char *s) 2228static int glob_needed(const char *s)
@@ -2286,7 +2299,7 @@ static int o_glob(o_string *o, int n)
2286 return n; 2299 return n;
2287} 2300}
2288 2301
2289#endif 2302#endif /* !HUSH_BRACE_EXP */
2290 2303
2291/* If o->o_glob == 1, glob the string so far remembered. 2304/* If o->o_glob == 1, glob the string so far remembered.
2292 * Otherwise, just finish current list[] and start new */ 2305 * Otherwise, just finish current list[] and start new */
@@ -2404,6 +2417,23 @@ static char *expand_pseudo_dquoted(const char *str)
2404 return exp_str; 2417 return exp_str;
2405} 2418}
2406 2419
2420#if ENABLE_SH_MATH_SUPPORT
2421static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p)
2422{
2423 arith_eval_hooks_t hooks;
2424 arith_t res;
2425 char *exp_str;
2426
2427 hooks.lookupvar = get_local_var_value;
2428 hooks.setvar = set_local_var_from_halves;
2429 hooks.endofname = endofname;
2430 exp_str = expand_pseudo_dquoted(arg);
2431 res = arith(exp_str ? exp_str : arg, errcode_p, &hooks);
2432 free(exp_str);
2433 return res;
2434}
2435#endif
2436
2407/* Expand all variable references in given string, adding words to list[] 2437/* Expand all variable references in given string, adding words to list[]
2408 * at n, n+1,... positions. Return updated n (so that list[n] is next one 2438 * at n, n+1,... positions. Return updated n (so that list[n] is next one
2409 * to be filled). This routine is extremely tricky: has to deal with 2439 * to be filled). This routine is extremely tricky: has to deal with
@@ -2428,7 +2458,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2428 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { 2458 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
2429 char first_ch; 2459 char first_ch;
2430 int i; 2460 int i;
2431 char *dyn_val = NULL; 2461 char *to_be_freed = NULL;
2432 const char *val = NULL; 2462 const char *val = NULL;
2433#if ENABLE_HUSH_TICK 2463#if ENABLE_HUSH_TICK
2434 o_string subst_result = NULL_O_STRING; 2464 o_string subst_result = NULL_O_STRING;
@@ -2449,21 +2479,6 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2449 2479
2450 switch (first_ch & 0x7f) { 2480 switch (first_ch & 0x7f) {
2451 /* Highest bit in first_ch indicates that var is double-quoted */ 2481 /* Highest bit in first_ch indicates that var is double-quoted */
2452 case '$': /* pid */
2453 val = utoa(G.root_pid);
2454 break;
2455 case '!': /* bg pid */
2456 val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
2457 break;
2458 case '?': /* exitcode */
2459 val = utoa(G.last_exitcode);
2460 break;
2461 case '#': /* argc */
2462 if (arg[1] != SPECIAL_VAR_SYMBOL)
2463 /* actually, it's a ${#var} */
2464 goto case_default;
2465 val = utoa(G.global_argc ? G.global_argc-1 : 0);
2466 break;
2467 case '*': 2482 case '*':
2468 case '@': 2483 case '@':
2469 i = 1; 2484 i = 1;
@@ -2523,27 +2538,19 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2523 * and $IFS-splitted */ 2538 * and $IFS-splitted */
2524 debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); 2539 debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
2525 G.last_exitcode = process_command_subs(&subst_result, arg); 2540 G.last_exitcode = process_command_subs(&subst_result, arg);
2526 debug_printf_subst("SUBST RES '%s'\n", subst_result.data); 2541 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
2527 val = subst_result.data; 2542 val = subst_result.data;
2528 goto store_val; 2543 goto store_val;
2529#endif 2544#endif
2530#if ENABLE_SH_MATH_SUPPORT 2545#if ENABLE_SH_MATH_SUPPORT
2531 case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ 2546 case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
2532 arith_eval_hooks_t hooks;
2533 arith_t res; 2547 arith_t res;
2534 int errcode; 2548 int errcode;
2535 char *exp_str;
2536 2549
2537 arg++; /* skip '+' */ 2550 arg++; /* skip '+' */
2538 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ 2551 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
2539 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); 2552 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
2540 2553 res = expand_and_evaluate_arith(arg, &errcode);
2541 exp_str = expand_pseudo_dquoted(arg);
2542 hooks.lookupvar = get_local_var_value;
2543 hooks.setvar = set_local_var_from_halves;
2544 hooks.endofname = endofname;
2545 res = arith(exp_str ? exp_str : arg, &errcode, &hooks);
2546 free(exp_str);
2547 2554
2548 if (errcode < 0) { 2555 if (errcode < 0) {
2549 const char *msg = "error in arithmetic"; 2556 const char *msg = "error in arithmetic";
@@ -2566,88 +2573,194 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2566 break; 2573 break;
2567 } 2574 }
2568#endif 2575#endif
2569 default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ 2576 default: { /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */
2570 case_default: { 2577 char *var;
2571 bool exp_len = false; 2578 char first_char;
2572 bool exp_null = false; 2579 char exp_op;
2573 char *var = arg;
2574 char exp_save = exp_save; /* for compiler */ 2580 char exp_save = exp_save; /* for compiler */
2575 char exp_op = exp_op; /* for compiler */ 2581 char *exp_saveptr; /* points to expansion operator */
2576 char *exp_word = exp_word; /* for compiler */ 2582 char *exp_word = exp_word; /* for compiler */
2577 size_t exp_off = 0;
2578 2583
2584 var = arg;
2579 *p = '\0'; 2585 *p = '\0';
2580 arg[0] = first_ch & 0x7f; 2586 exp_saveptr = arg[1] ? strchr("%#:-=+?", arg[1]) : NULL;
2587 first_char = arg[0] = first_ch & 0x7f;
2588 exp_op = 0;
2581 2589
2582 /* prepare for expansions */ 2590 if (first_char == '#' && arg[1] && !exp_saveptr) {
2583 if (var[0] == '#') {
2584 /* handle length expansion ${#var} */ 2591 /* handle length expansion ${#var} */
2585 exp_len = true; 2592 var++;
2586 ++var; 2593 exp_op = 'L';
2587 } else { 2594 } else {
2588 /* maybe handle parameter expansion */ 2595 /* maybe handle parameter expansion */
2589 exp_off = strcspn(var, ":-=+?%#"); 2596 if (exp_saveptr /* if 2nd char is one of expansion operators */
2590 if (!var[exp_off]) 2597 && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
2591 exp_off = 0; 2598 ) {
2592 if (exp_off) { 2599 /* ${?:0}, ${#[:]%0} etc */
2593 exp_save = var[exp_off]; 2600 exp_saveptr = var + 1;
2594 exp_null = exp_save == ':'; 2601 } else {
2595 exp_word = var + exp_off; 2602 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
2596 if (exp_null) 2603 exp_saveptr = var+1 + strcspn(var+1, "%#:-=+?");
2597 ++exp_word;
2598 exp_op = *exp_word++;
2599 var[exp_off] = '\0';
2600 } 2604 }
2605 exp_op = exp_save = *exp_saveptr;
2606 if (exp_op) {
2607 exp_word = exp_saveptr + 1;
2608 if (exp_op == ':') {
2609 exp_op = *exp_word++;
2610 if (ENABLE_HUSH_BASH_COMPAT
2611 && (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op))
2612 ) {
2613 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
2614 exp_op = ':';
2615 exp_word--;
2616 }
2617 }
2618 *exp_saveptr = '\0';
2619 } /* else: it's not an expansion op, but bare ${var} */
2601 } 2620 }
2602 2621
2603 /* lookup the variable in question */ 2622 /* lookup the variable in question */
2604 if (isdigit(var[0])) { 2623 if (isdigit(var[0])) {
2605 /* handle_dollar() should have vetted var for us */ 2624 /* parse_dollar() should have vetted var for us */
2606 i = xatoi_u(var); 2625 i = xatoi_u(var);
2607 if (i < G.global_argc) 2626 if (i < G.global_argc)
2608 val = G.global_argv[i]; 2627 val = G.global_argv[i];
2609 /* else val remains NULL: $N with too big N */ 2628 /* else val remains NULL: $N with too big N */
2610 } else 2629 } else {
2611 val = get_local_var_value(var); 2630 switch (var[0]) {
2631 case '$': /* pid */
2632 val = utoa(G.root_pid);
2633 break;
2634 case '!': /* bg pid */
2635 val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
2636 break;
2637 case '?': /* exitcode */
2638 val = utoa(G.last_exitcode);
2639 break;
2640 case '#': /* argc */
2641 val = utoa(G.global_argc ? G.global_argc-1 : 0);
2642 break;
2643 default:
2644 val = get_local_var_value(var);
2645 }
2646 }
2612 2647
2613 /* handle any expansions */ 2648 /* handle any expansions */
2614 if (exp_len) { 2649 if (exp_op == 'L') {
2615 debug_printf_expand("expand: length of '%s' = ", val); 2650 debug_printf_expand("expand: length(%s)=", val);
2616 val = utoa(val ? strlen(val) : 0); 2651 val = utoa(val ? strlen(val) : 0);
2617 debug_printf_expand("%s\n", val); 2652 debug_printf_expand("%s\n", val);
2618 } else if (exp_off) { 2653 } else if (exp_op) {
2619 if (exp_op == '%' || exp_op == '#') { 2654 if (exp_op == '%' || exp_op == '#') {
2655 /* Standard-mandated substring removal ops:
2656 * ${parameter%word} - remove smallest suffix pattern
2657 * ${parameter%%word} - remove largest suffix pattern
2658 * ${parameter#word} - remove smallest prefix pattern
2659 * ${parameter##word} - remove largest prefix pattern
2660 *
2661 * Word is expanded to produce a glob pattern.
2662 * Then var's value is matched to it and matching part removed.
2663 */
2620 if (val) { 2664 if (val) {
2621 /* we need to do a pattern match */
2622 bool match_at_left; 2665 bool match_at_left;
2623 char *loc; 2666 char *loc;
2624 scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left); 2667 scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left);
2625 if (exp_op == *exp_word) /* ## or %% */ 2668 if (exp_op == *exp_word) /* ## or %% */
2626 ++exp_word; 2669 exp_word++;
2627 val = dyn_val = xstrdup(val); 2670 val = to_be_freed = xstrdup(val);
2628 loc = scan(dyn_val, exp_word, match_at_left); 2671 {
2629 if (match_at_left) /* # or ## */ 2672 char *exp_exp_word = expand_pseudo_dquoted(exp_word);
2630 val = loc; 2673 if (exp_exp_word)
2631 else if (loc) /* % or %% and match was found */ 2674 exp_word = exp_exp_word;
2632 *loc = '\0'; 2675 loc = scan(to_be_freed, exp_word, match_at_left);
2676 //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
2677 // exp_op, to_be_freed, exp_word, loc);
2678 free(exp_exp_word);
2679 }
2680 if (loc) { /* match was found */
2681 if (match_at_left) /* # or ## */
2682 val = loc;
2683 else /* % or %% */
2684 *loc = '\0';
2685 }
2633 } 2686 }
2634 } else { 2687 } else if (exp_op == ':') {
2635 /* we need to do an expansion */ 2688#if ENABLE_HUSH_BASH_COMPAT
2636 int exp_test = (!val || (exp_null && !val[0])); 2689 /* It's ${var:N[:M]} bashism.
2690 * Note that in encoded form it has TWO parts:
2691 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
2692 */
2693 arith_t beg, len;
2694 int errcode = 0;
2695
2696 beg = expand_and_evaluate_arith(exp_word, &errcode);
2697 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
2698 *p++ = SPECIAL_VAR_SYMBOL;
2699 exp_word = p;
2700 p = strchr(p, SPECIAL_VAR_SYMBOL);
2701 *p = '\0';
2702 len = expand_and_evaluate_arith(exp_word, &errcode);
2703 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
2704
2705 if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */
2706 if (beg < 0) /* bash compat */
2707 beg = 0;
2708 debug_printf_varexp("from val:'%s'\n", val);
2709 if (len == 0 || !val || beg >= strlen(val))
2710 val = "";
2711 else {
2712 /* Paranoia. What if user entered 9999999999999
2713 * which fits in arith_t but not int? */
2714 if (len >= INT_MAX)
2715 len = INT_MAX;
2716 val = to_be_freed = xstrndup(val + beg, len);
2717 }
2718 debug_printf_varexp("val:'%s'\n", val);
2719 } else
2720#endif
2721 {
2722 die_if_script("malformed ${%s:...}", var);
2723 val = "";
2724 }
2725 } else { /* one of "-=+?" */
2726 /* Standard-mandated substitution ops:
2727 * ${var?word} - indicate error if unset
2728 * If var is unset, word (or a message indicating it is unset
2729 * if word is null) is written to standard error
2730 * and the shell exits with a non-zero exit status.
2731 * Otherwise, the value of var is substituted.
2732 * ${var-word} - use default value
2733 * If var is unset, word is substituted.
2734 * ${var=word} - assign and use default value
2735 * If var is unset, word is assigned to var.
2736 * In all cases, final value of var is substituted.
2737 * ${var+word} - use alternative value
2738 * If var is unset, null is substituted.
2739 * Otherwise, word is substituted.
2740 *
2741 * Word is subjected to tilde expansion, parameter expansion,
2742 * command substitution, and arithmetic expansion.
2743 * If word is not needed, it is not expanded.
2744 *
2745 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
2746 * but also treat null var as if it is unset.
2747 */
2748 int use_word = (!val || ((exp_save == ':') && !val[0]));
2637 if (exp_op == '+') 2749 if (exp_op == '+')
2638 exp_test = !exp_test; 2750 use_word = !use_word;
2639 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, 2751 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
2640 exp_null ? "true" : "false", exp_test); 2752 (exp_save == ':') ? "true" : "false", use_word);
2641 if (exp_test) { 2753 if (use_word) {
2754 to_be_freed = expand_pseudo_dquoted(exp_word);
2755 if (to_be_freed)
2756 exp_word = to_be_freed;
2642 if (exp_op == '?') { 2757 if (exp_op == '?') {
2643//TODO: how interactive bash aborts expansion mid-command?
2644 /* ${var?[error_msg_if_unset]} */
2645 /* ${var:?[error_msg_if_unset_or_null]} */
2646 /* mimic bash message */ 2758 /* mimic bash message */
2647 die_if_script("%s: %s", 2759 die_if_script("%s: %s",
2648 var, 2760 var,
2649 exp_word[0] ? exp_word : "parameter null or not set" 2761 exp_word[0] ? exp_word : "parameter null or not set"
2650 ); 2762 );
2763//TODO: how interactive bash aborts expansion mid-command?
2651 } else { 2764 } else {
2652 val = exp_word; 2765 val = exp_word;
2653 } 2766 }
@@ -2664,10 +2777,10 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2664 } 2777 }
2665 } 2778 }
2666 } 2779 }
2667 } 2780 } /* one of "-=+?" */
2668 2781
2669 var[exp_off] = exp_save; 2782 *exp_saveptr = exp_save;
2670 } 2783 } /* if (exp_op) */
2671 2784
2672 arg[0] = first_ch; 2785 arg[0] = first_ch;
2673#if ENABLE_HUSH_TICK 2786#if ENABLE_HUSH_TICK
@@ -2692,7 +2805,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2692 if (val) { 2805 if (val) {
2693 o_addQstr(output, val, strlen(val)); 2806 o_addQstr(output, val, strlen(val));
2694 } 2807 }
2695 free(dyn_val); 2808 free(to_be_freed);
2696 /* Do the check to avoid writing to a const string */ 2809 /* Do the check to avoid writing to a const string */
2697 if (*p != SPECIAL_VAR_SYMBOL) 2810 if (*p != SPECIAL_VAR_SYMBOL)
2698 *p = SPECIAL_VAR_SYMBOL; 2811 *p = SPECIAL_VAR_SYMBOL;
@@ -2921,7 +3034,7 @@ static void re_execute_shell(char ***to_free, const char *s,
2921 char *g_argv0, char **g_argv, 3034 char *g_argv0, char **g_argv,
2922 char **builtin_argv) 3035 char **builtin_argv)
2923{ 3036{
2924#define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x")) 3037# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x"))
2925 /* delims + 2 * (number of bytes in printed hex numbers) */ 3038 /* delims + 2 * (number of bytes in printed hex numbers) */
2926 char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)]; 3039 char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)];
2927 char *heredoc_argv[4]; 3040 char *heredoc_argv[4];
@@ -2966,7 +3079,7 @@ static void re_execute_shell(char ***to_free, const char *s,
2966 , empty_trap_mask 3079 , empty_trap_mask
2967 IF_HUSH_LOOPS(, G.depth_of_loop) 3080 IF_HUSH_LOOPS(, G.depth_of_loop)
2968 ); 3081 );
2969#undef NOMMU_HACK_FMT 3082# undef NOMMU_HACK_FMT
2970 /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...> 3083 /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...>
2971 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL 3084 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
2972 */ 3085 */
@@ -4117,9 +4230,9 @@ static NOINLINE int run_pipe(struct pipe *pi)
4117 4230
4118 if (argv[command->assignment_cnt] == NULL) { 4231 if (argv[command->assignment_cnt] == NULL) {
4119 /* Assignments, but no command */ 4232 /* Assignments, but no command */
4120 /* Ensure redirects take effect. Try "a=t >file" */ 4233 /* Ensure redirects take effect (that is, create files).
4234 * Try "a=t >file": */
4121 rcode = setup_redirects(command, squirrel); 4235 rcode = setup_redirects(command, squirrel);
4122//FIXME: "false; q=`false`; echo $?" should print 1
4123 restore_redirects(squirrel); 4236 restore_redirects(squirrel);
4124 /* Set shell variables */ 4237 /* Set shell variables */
4125 while (*argv) { 4238 while (*argv) {
@@ -4129,6 +4242,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
4129 set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 4242 set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
4130 argv++; 4243 argv++;
4131 } 4244 }
4245 /* Redirect error sets $? to 1. Othervise,
4246 * if evaluating assignment value set $?, retain it.
4247 * Try "false; q=`exit 2`; echo $?" - should print 2: */
4248 if (rcode == 0)
4249 rcode = G.last_exitcode;
4132 /* Do we need to flag set_local_var() errors? 4250 /* Do we need to flag set_local_var() errors?
4133 * "assignment to readonly var" and "putenv error" 4251 * "assignment to readonly var" and "putenv error"
4134 */ 4252 */
@@ -4186,7 +4304,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4186 old_vars = set_vars_and_save_old(new_env); 4304 old_vars = set_vars_and_save_old(new_env);
4187 if (!funcp) { 4305 if (!funcp) {
4188 debug_printf_exec(": builtin '%s' '%s'...\n", 4306 debug_printf_exec(": builtin '%s' '%s'...\n",
4189 x->cmd, argv_expanded[1]); 4307 x->b_cmd, argv_expanded[1]);
4190 rcode = x->b_function(argv_expanded) & 0xff; 4308 rcode = x->b_function(argv_expanded) & 0xff;
4191 fflush_all(); 4309 fflush_all();
4192 } 4310 }
@@ -4430,11 +4548,11 @@ static void debug_print_tree(struct pipe *pi, int lvl)
4430 fprintf(stderr, " group %s: (argv=%p)%s%s\n", 4548 fprintf(stderr, " group %s: (argv=%p)%s%s\n",
4431 CMDTYPE[command->cmd_type], 4549 CMDTYPE[command->cmd_type],
4432 argv 4550 argv
4433#if !BB_MMU 4551# if !BB_MMU
4434 , " group_as_string:", command->group_as_string 4552 , " group_as_string:", command->group_as_string
4435#else 4553# else
4436 , "", "" 4554 , "", ""
4437#endif 4555# endif
4438 ); 4556 );
4439 debug_print_tree(command->group, lvl+1); 4557 debug_print_tree(command->group, lvl+1);
4440 prn++; 4558 prn++;
@@ -5680,7 +5798,9 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
5680 } 5798 }
5681 5799
5682 { 5800 {
5683#if !BB_MMU 5801#if BB_MMU
5802# define as_string NULL
5803#else
5684 char *as_string = NULL; 5804 char *as_string = NULL;
5685#endif 5805#endif
5686 pipe_list = parse_stream(&as_string, input, endch); 5806 pipe_list = parse_stream(&as_string, input, endch);
@@ -5691,9 +5811,8 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
5691 /* empty ()/{} or parse error? */ 5811 /* empty ()/{} or parse error? */
5692 if (!pipe_list || pipe_list == ERR_PTR) { 5812 if (!pipe_list || pipe_list == ERR_PTR) {
5693 /* parse_stream already emitted error msg */ 5813 /* parse_stream already emitted error msg */
5694#if !BB_MMU 5814 if (!BB_MMU)
5695 free(as_string); 5815 free(as_string);
5696#endif
5697 debug_printf_parse("parse_group return 1: " 5816 debug_printf_parse("parse_group return 1: "
5698 "parse_stream returned %p\n", pipe_list); 5817 "parse_stream returned %p\n", pipe_list);
5699 return 1; 5818 return 1;
@@ -5705,6 +5824,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
5705 debug_printf_parse("end of group, remembering as:'%s'\n", 5824 debug_printf_parse("end of group, remembering as:'%s'\n",
5706 command->group_as_string); 5825 command->group_as_string);
5707#endif 5826#endif
5827#undef as_string
5708 } 5828 }
5709 debug_printf_parse("parse_group return 0\n"); 5829 debug_printf_parse("parse_group return 0\n");
5710 return 0; 5830 return 0;
@@ -5801,29 +5921,44 @@ static void add_till_backquote(o_string *dest, struct in_str *input)
5801 * echo $(echo '(TEST)' BEST) (TEST) BEST 5921 * echo $(echo '(TEST)' BEST) (TEST) BEST
5802 * echo $(echo 'TEST)' BEST) TEST) BEST 5922 * echo $(echo 'TEST)' BEST) TEST) BEST
5803 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST 5923 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
5924 *
5925 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
5926 * can contain arbitrary constructs, just like $(cmd).
5927 * In bash compat mode, it needs to also be able to stop on '}' or ':'
5928 * for ${var:N[:M]} parsing.
5804 */ 5929 */
5805static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl) 5930#define DOUBLE_CLOSE_CHAR_FLAG 0x80
5931static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
5806{ 5932{
5807 int count = 0; 5933 int ch;
5934 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
5935#if ENABLE_HUSH_BASH_COMPAT
5936 char end_char2 = end_ch >> 8;
5937#endif
5938 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
5939
5808 while (1) { 5940 while (1) {
5809 int ch = i_getch(input); 5941 ch = i_getch(input);
5810 if (ch == EOF) { 5942 if (ch == EOF) {
5811 syntax_error_unterm_ch(')'); 5943 syntax_error_unterm_ch(end_ch);
5812 /*xfunc_die(); - redundant */ 5944 /*xfunc_die(); - redundant */
5813 } 5945 }
5814 if (ch == '(') 5946 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
5815 count++; 5947 if (!dbl)
5816 if (ch == ')') { 5948 break;
5817 if (--count < 0) { 5949 /* we look for closing )) of $((EXPR)) */
5818 if (!dbl) 5950 if (i_peek(input) == end_ch) {
5819 break; 5951 i_getch(input); /* eat second ')' */
5820 if (i_peek(input) == ')') { 5952 break;
5821 i_getch(input);
5822 break;
5823 }
5824 } 5953 }
5825 } 5954 }
5826 o_addchr(dest, ch); 5955 o_addchr(dest, ch);
5956 if (ch == '(' || ch == '{') {
5957 ch = (ch == '(' ? ')' : '}');
5958 add_till_closing_bracket(dest, input, ch);
5959 o_addchr(dest, ch);
5960 continue;
5961 }
5827 if (ch == '\'') { 5962 if (ch == '\'') {
5828 add_till_single_quote(dest, input); 5963 add_till_single_quote(dest, input);
5829 o_addchr(dest, ch); 5964 o_addchr(dest, ch);
@@ -5834,6 +5969,11 @@ static void add_till_closing_paren(o_string *dest, struct in_str *input, bool db
5834 o_addchr(dest, ch); 5969 o_addchr(dest, ch);
5835 continue; 5970 continue;
5836 } 5971 }
5972 if (ch == '`') {
5973 add_till_backquote(dest, input);
5974 o_addchr(dest, ch);
5975 continue;
5976 }
5837 if (ch == '\\') { 5977 if (ch == '\\') {
5838 /* \x. Copy verbatim. Important for \(, \) */ 5978 /* \x. Copy verbatim. Important for \(, \) */
5839 ch = i_getch(input); 5979 ch = i_getch(input);
@@ -5845,22 +5985,24 @@ static void add_till_closing_paren(o_string *dest, struct in_str *input, bool db
5845 continue; 5985 continue;
5846 } 5986 }
5847 } 5987 }
5988 return ch;
5848} 5989}
5849#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ 5990#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */
5850 5991
5851/* Return code: 0 for OK, 1 for syntax error */ 5992/* Return code: 0 for OK, 1 for syntax error */
5852#if BB_MMU 5993#if BB_MMU
5853#define handle_dollar(as_string, dest, input) \ 5994#define parse_dollar(as_string, dest, input) \
5854 handle_dollar(dest, input) 5995 parse_dollar(dest, input)
5996#define as_string NULL
5855#endif 5997#endif
5856static int handle_dollar(o_string *as_string, 5998static int parse_dollar(o_string *as_string,
5857 o_string *dest, 5999 o_string *dest,
5858 struct in_str *input) 6000 struct in_str *input)
5859{ 6001{
5860 int ch = i_peek(input); /* first character after the $ */ 6002 int ch = i_peek(input); /* first character after the $ */
5861 unsigned char quote_mask = dest->o_escape ? 0x80 : 0; 6003 unsigned char quote_mask = dest->o_escape ? 0x80 : 0;
5862 6004
5863 debug_printf_parse("handle_dollar entered: ch='%c'\n", ch); 6005 debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
5864 if (isalpha(ch)) { 6006 if (isalpha(ch)) {
5865 ch = i_getch(input); 6007 ch = i_getch(input);
5866 nommu_addchr(as_string, ch); 6008 nommu_addchr(as_string, ch);
@@ -5894,92 +6036,91 @@ static int handle_dollar(o_string *as_string,
5894 case '@': /* args */ 6036 case '@': /* args */
5895 goto make_one_char_var; 6037 goto make_one_char_var;
5896 case '{': { 6038 case '{': {
5897 bool first_char, all_digits; 6039 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5898 int expansion;
5899 6040
5900 ch = i_getch(input); 6041 ch = i_getch(input); /* eat '{' */
5901 nommu_addchr(as_string, ch); 6042 nommu_addchr(as_string, ch);
5902 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5903 6043
5904 /* TODO: maybe someone will try to escape the '}' */ 6044 ch = i_getch(input); /* first char after '{' */
5905 expansion = 0; 6045 nommu_addchr(as_string, ch);
5906 first_char = true; 6046 /* It should be ${?}, or ${#var},
5907 all_digits = false; 6047 * or even ${?+subst} - operator acting on a special variable,
6048 * or the beginning of variable name.
6049 */
6050 if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */
6051 bad_dollar_syntax:
6052 syntax_error_unterm_str("${name}");
6053 debug_printf_parse("parse_dollar return 1: unterminated ${name}\n");
6054 return 1;
6055 }
6056 ch |= quote_mask;
6057
6058 /* It's possible to just call add_till_closing_bracket() at this point.
6059 * However, this regresses some of our testsuite cases
6060 * which check invalid constructs like ${%}.
6061 * Oh well... let's check that the var name part is fine... */
6062
5908 while (1) { 6063 while (1) {
6064 unsigned pos;
6065
6066 o_addchr(dest, ch);
6067 debug_printf_parse(": '%c'\n", ch);
6068
5909 ch = i_getch(input); 6069 ch = i_getch(input);
5910 nommu_addchr(as_string, ch); 6070 nommu_addchr(as_string, ch);
5911 if (ch == '}') { 6071 if (ch == '}')
5912 break; 6072 break;
5913 }
5914
5915 if (first_char) {
5916 if (ch == '#') {
5917 /* ${#var}: length of var contents */
5918 goto char_ok;
5919 }
5920 if (isdigit(ch)) {
5921 all_digits = true;
5922 goto char_ok;
5923 }
5924 /* They're being verbose and doing ${?} */
5925 if (i_peek(input) == '}' && strchr("$!?#*@_", ch))
5926 goto char_ok;
5927 }
5928 6073
5929 if (expansion < 2 6074 if (!isalnum(ch) && ch != '_') {
5930 && ( (all_digits && !isdigit(ch)) 6075 unsigned end_ch;
5931 || (!all_digits && !isalnum(ch) && ch != '_') 6076 unsigned char last_ch;
5932 )
5933 ) {
5934 /* handle parameter expansions 6077 /* handle parameter expansions
5935 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 6078 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
5936 */ 6079 */
5937 if (first_char) 6080 if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */
5938 goto case_default; 6081 goto bad_dollar_syntax;
5939 switch (ch) { 6082 o_addchr(dest, ch);
5940 case ':': /* null modifier */ 6083
5941 if (expansion == 0) { 6084 /* Eat everything until closing '}' (or ':') */
5942 debug_printf_parse(": null modifier\n"); 6085 end_ch = '}';
5943 ++expansion; 6086 if (ENABLE_HUSH_BASH_COMPAT
5944 break; 6087 && ch == ':'
5945 } 6088 && !strchr("%#:-=+?"+3, i_peek(input))
5946 goto case_default; 6089 ) {
5947 case '#': /* remove prefix */ 6090 /* It's ${var:N[:M]} thing */
5948 case '%': /* remove suffix */ 6091 end_ch = '}' * 0x100 + ':';
5949 if (expansion == 0) { 6092 }
5950 debug_printf_parse(": remove suffix/prefix\n"); 6093 again:
5951 expansion = 2; 6094 if (!BB_MMU)
5952 break; 6095 pos = dest->length;
6096 last_ch = add_till_closing_bracket(dest, input, end_ch);
6097 if (as_string) {
6098 o_addstr(as_string, dest->data + pos);
6099 o_addchr(as_string, last_ch);
6100 }
6101
6102 if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
6103 /* close the first block: */
6104 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6105 /* while parsing N from ${var:N[:M]}... */
6106 if ((end_ch & 0xff) == last_ch) {
6107 /* ...got ':' - parse the rest */
6108 end_ch = '}';
6109 goto again;
5953 } 6110 }
5954 goto case_default; 6111 /* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */
5955 case '-': /* default value */ 6112 o_addstr(dest, "999999999");
5956 case '=': /* assign default */
5957 case '+': /* alternative */
5958 case '?': /* error indicate */
5959 debug_printf_parse(": parameter expansion\n");
5960 expansion = 2;
5961 break;
5962 default:
5963 case_default:
5964 syntax_error_unterm_str("${name}");
5965 debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
5966 return 1;
5967 } 6113 }
6114 break;
5968 } 6115 }
5969 char_ok: 6116 }
5970 debug_printf_parse(": '%c'\n", ch);
5971 o_addchr(dest, ch | quote_mask);
5972 quote_mask = 0;
5973 first_char = false;
5974 } /* while (1) */
5975 o_addchr(dest, SPECIAL_VAR_SYMBOL); 6117 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5976 break; 6118 break;
5977 } 6119 }
5978#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK 6120#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
5979 case '(': { 6121 case '(': {
5980# if !BB_MMU 6122 unsigned pos;
5981 int pos; 6123
5982# endif
5983 ch = i_getch(input); 6124 ch = i_getch(input);
5984 nommu_addchr(as_string, ch); 6125 nommu_addchr(as_string, ch);
5985# if ENABLE_SH_MATH_SUPPORT 6126# if ENABLE_SH_MATH_SUPPORT
@@ -5988,17 +6129,14 @@ static int handle_dollar(o_string *as_string,
5988 nommu_addchr(as_string, ch); 6129 nommu_addchr(as_string, ch);
5989 o_addchr(dest, SPECIAL_VAR_SYMBOL); 6130 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5990 o_addchr(dest, /*quote_mask |*/ '+'); 6131 o_addchr(dest, /*quote_mask |*/ '+');
5991# if !BB_MMU 6132 if (!BB_MMU)
5992 pos = dest->length; 6133 pos = dest->length;
5993# endif 6134 add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
5994 add_till_closing_paren(dest, input, true);
5995# if !BB_MMU
5996 if (as_string) { 6135 if (as_string) {
5997 o_addstr(as_string, dest->data + pos); 6136 o_addstr(as_string, dest->data + pos);
5998 o_addchr(as_string, ')'); 6137 o_addchr(as_string, ')');
5999 o_addchr(as_string, ')'); 6138 o_addchr(as_string, ')');
6000 } 6139 }
6001# endif
6002 o_addchr(dest, SPECIAL_VAR_SYMBOL); 6140 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6003 break; 6141 break;
6004 } 6142 }
@@ -6006,16 +6144,13 @@ static int handle_dollar(o_string *as_string,
6006# if ENABLE_HUSH_TICK 6144# if ENABLE_HUSH_TICK
6007 o_addchr(dest, SPECIAL_VAR_SYMBOL); 6145 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6008 o_addchr(dest, quote_mask | '`'); 6146 o_addchr(dest, quote_mask | '`');
6009# if !BB_MMU 6147 if (!BB_MMU)
6010 pos = dest->length; 6148 pos = dest->length;
6011# endif 6149 add_till_closing_bracket(dest, input, ')');
6012 add_till_closing_paren(dest, input, false);
6013# if !BB_MMU
6014 if (as_string) { 6150 if (as_string) {
6015 o_addstr(as_string, dest->data + pos); 6151 o_addstr(as_string, dest->data + pos);
6016 o_addchr(as_string, ')'); 6152 o_addchr(as_string, ')');
6017 } 6153 }
6018# endif
6019 o_addchr(dest, SPECIAL_VAR_SYMBOL); 6154 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6020# endif 6155# endif
6021 break; 6156 break;
@@ -6038,13 +6173,15 @@ static int handle_dollar(o_string *as_string,
6038 default: 6173 default:
6039 o_addQchr(dest, '$'); 6174 o_addQchr(dest, '$');
6040 } 6175 }
6041 debug_printf_parse("handle_dollar return 0\n"); 6176 debug_printf_parse("parse_dollar return 0\n");
6042 return 0; 6177 return 0;
6178#undef as_string
6043} 6179}
6044 6180
6045#if BB_MMU 6181#if BB_MMU
6046#define parse_stream_dquoted(as_string, dest, input, dquote_end) \ 6182#define parse_stream_dquoted(as_string, dest, input, dquote_end) \
6047 parse_stream_dquoted(dest, input, dquote_end) 6183 parse_stream_dquoted(dest, input, dquote_end)
6184#define as_string NULL
6048#endif 6185#endif
6049static int parse_stream_dquoted(o_string *as_string, 6186static int parse_stream_dquoted(o_string *as_string,
6050 o_string *dest, 6187 o_string *dest,
@@ -6099,16 +6236,16 @@ static int parse_stream_dquoted(o_string *as_string,
6099 goto again; 6236 goto again;
6100 } 6237 }
6101 if (ch == '$') { 6238 if (ch == '$') {
6102 if (handle_dollar(as_string, dest, input) != 0) { 6239 if (parse_dollar(as_string, dest, input) != 0) {
6103 debug_printf_parse("parse_stream_dquoted return 1: " 6240 debug_printf_parse("parse_stream_dquoted return 1: "
6104 "handle_dollar returned non-0\n"); 6241 "parse_dollar returned non-0\n");
6105 return 1; 6242 return 1;
6106 } 6243 }
6107 goto again; 6244 goto again;
6108 } 6245 }
6109#if ENABLE_HUSH_TICK 6246#if ENABLE_HUSH_TICK
6110 if (ch == '`') { 6247 if (ch == '`') {
6111 //int pos = dest->length; 6248 //unsigned pos = dest->length;
6112 o_addchr(dest, SPECIAL_VAR_SYMBOL); 6249 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6113 o_addchr(dest, 0x80 | '`'); 6250 o_addchr(dest, 0x80 | '`');
6114 add_till_backquote(dest, input); 6251 add_till_backquote(dest, input);
@@ -6126,6 +6263,7 @@ static int parse_stream_dquoted(o_string *as_string,
6126 dest->o_assignment = DEFINITELY_ASSIGNMENT; 6263 dest->o_assignment = DEFINITELY_ASSIGNMENT;
6127 } 6264 }
6128 goto again; 6265 goto again;
6266#undef as_string
6129} 6267}
6130 6268
6131/* 6269/*
@@ -6463,9 +6601,9 @@ static struct pipe *parse_stream(char **pstring,
6463#endif 6601#endif
6464 break; 6602 break;
6465 case '$': 6603 case '$':
6466 if (handle_dollar(&ctx.as_string, &dest, input) != 0) { 6604 if (parse_dollar(&ctx.as_string, &dest, input) != 0) {
6467 debug_printf_parse("parse_stream parse error: " 6605 debug_printf_parse("parse_stream parse error: "
6468 "handle_dollar returned non-0\n"); 6606 "parse_dollar returned non-0\n");
6469 goto parse_error; 6607 goto parse_error;
6470 } 6608 }
6471 break; 6609 break;
@@ -6491,19 +6629,16 @@ static struct pipe *parse_stream(char **pstring,
6491 break; 6629 break;
6492#if ENABLE_HUSH_TICK 6630#if ENABLE_HUSH_TICK
6493 case '`': { 6631 case '`': {
6494#if !BB_MMU 6632 unsigned pos;
6495 int pos; 6633
6496#endif
6497 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 6634 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6498 o_addchr(&dest, '`'); 6635 o_addchr(&dest, '`');
6499#if !BB_MMU
6500 pos = dest.length; 6636 pos = dest.length;
6501#endif
6502 add_till_backquote(&dest, input); 6637 add_till_backquote(&dest, input);
6503#if !BB_MMU 6638# if !BB_MMU
6504 o_addstr(&ctx.as_string, dest.data + pos); 6639 o_addstr(&ctx.as_string, dest.data + pos);
6505 o_addchr(&ctx.as_string, '`'); 6640 o_addchr(&ctx.as_string, '`');
6506#endif 6641# endif
6507 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 6642 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6508 //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos); 6643 //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
6509 break; 6644 break;
@@ -6814,7 +6949,7 @@ int hush_main(int argc, char **argv)
6814 struct variable *cur_var; 6949 struct variable *cur_var;
6815 6950
6816 INIT_G(); 6951 INIT_G();
6817 if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, is already done */ 6952 if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */
6818 G.last_exitcode = EXIT_SUCCESS; 6953 G.last_exitcode = EXIT_SUCCESS;
6819#if !BB_MMU 6954#if !BB_MMU
6820 G.argv0_for_re_execing = argv[0]; 6955 G.argv0_for_re_execing = argv[0];
@@ -6954,7 +7089,7 @@ int hush_main(int argc, char **argv)
6954 /* -c 'script' (no params): prevent empty $0 */ 7089 /* -c 'script' (no params): prevent empty $0 */
6955 G.global_argv--; /* points to argv[i] of 'script' */ 7090 G.global_argv--; /* points to argv[i] of 'script' */
6956 G.global_argv[0] = argv[0]; 7091 G.global_argv[0] = argv[0];
6957 G.global_argc--; 7092 G.global_argc++;
6958 } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ 7093 } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
6959 init_sigmasks(); 7094 init_sigmasks();
6960 parse_and_run_string(optarg); 7095 parse_and_run_string(optarg);
@@ -7258,11 +7393,20 @@ static int FAST_FUNC builtin_printf(char **argv)
7258} 7393}
7259#endif 7394#endif
7260 7395
7396static char **skip_dash_dash(char **argv)
7397{
7398 argv++;
7399 if (argv[0] && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0')
7400 argv++;
7401 return argv;
7402}
7403
7261static int FAST_FUNC builtin_eval(char **argv) 7404static int FAST_FUNC builtin_eval(char **argv)
7262{ 7405{
7263 int rcode = EXIT_SUCCESS; 7406 int rcode = EXIT_SUCCESS;
7264 7407
7265 if (*++argv) { 7408 argv = skip_dash_dash(argv);
7409 if (*argv) {
7266 char *str = expand_strvec_to_string(argv); 7410 char *str = expand_strvec_to_string(argv);
7267 /* bash: 7411 /* bash:
7268 * eval "echo Hi; done" ("done" is syntax error): 7412 * eval "echo Hi; done" ("done" is syntax error):
@@ -7277,7 +7421,10 @@ static int FAST_FUNC builtin_eval(char **argv)
7277 7421
7278static int FAST_FUNC builtin_cd(char **argv) 7422static int FAST_FUNC builtin_cd(char **argv)
7279{ 7423{
7280 const char *newdir = argv[1]; 7424 const char *newdir;
7425
7426 argv = skip_dash_dash(argv);
7427 newdir = argv[0];
7281 if (newdir == NULL) { 7428 if (newdir == NULL) {
7282 /* bash does nothing (exitcode 0) if HOME is ""; if it's unset, 7429 /* bash does nothing (exitcode 0) if HOME is ""; if it's unset,
7283 * bash says "bash: cd: HOME not set" and does nothing 7430 * bash says "bash: cd: HOME not set" and does nothing
@@ -7301,7 +7448,8 @@ static int FAST_FUNC builtin_cd(char **argv)
7301 7448
7302static int FAST_FUNC builtin_exec(char **argv) 7449static int FAST_FUNC builtin_exec(char **argv)
7303{ 7450{
7304 if (*++argv == NULL) 7451 argv = skip_dash_dash(argv);
7452 if (argv[0] == NULL)
7305 return EXIT_SUCCESS; /* bash does this */ 7453 return EXIT_SUCCESS; /* bash does this */
7306 7454
7307 /* Careful: we can end up here after [v]fork. Do not restore 7455 /* Careful: we can end up here after [v]fork. Do not restore
@@ -7334,12 +7482,13 @@ static int FAST_FUNC builtin_exit(char **argv)
7334 */ 7482 */
7335 7483
7336 /* note: EXIT trap is run by hush_exit */ 7484 /* note: EXIT trap is run by hush_exit */
7337 if (*++argv == NULL) 7485 argv = skip_dash_dash(argv);
7486 if (argv[0] == NULL)
7338 hush_exit(G.last_exitcode); 7487 hush_exit(G.last_exitcode);
7339 /* mimic bash: exit 123abc == exit 255 + error msg */ 7488 /* mimic bash: exit 123abc == exit 255 + error msg */
7340 xfunc_error_retval = 255; 7489 xfunc_error_retval = 255;
7341 /* bash: exit -2 == exit 254, no error msg */ 7490 /* bash: exit -2 == exit 254, no error msg */
7342 hush_exit(xatoi(*argv) & 0xff); 7491 hush_exit(xatoi(argv[0]) & 0xff);
7343} 7492}
7344 7493
7345static void print_escaped(const char *s) 7494static void print_escaped(const char *s)
@@ -7668,7 +7817,7 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
7668 "------------------\n"); 7817 "------------------\n");
7669 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) { 7818 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
7670 if (x->b_descr) 7819 if (x->b_descr)
7671 printf("%s\t%s\n", x->b_cmd, x->b_descr); 7820 printf("%-10s%s\n", x->b_cmd, x->b_descr);
7672 } 7821 }
7673 bb_putchar('\n'); 7822 bb_putchar('\n');
7674 return EXIT_SUCCESS; 7823 return EXIT_SUCCESS;
@@ -7851,8 +8000,9 @@ static int FAST_FUNC builtin_set(char **argv)
7851static int FAST_FUNC builtin_shift(char **argv) 8000static int FAST_FUNC builtin_shift(char **argv)
7852{ 8001{
7853 int n = 1; 8002 int n = 1;
7854 if (argv[1]) { 8003 argv = skip_dash_dash(argv);
7855 n = atoi(argv[1]); 8004 if (argv[0]) {
8005 n = atoi(argv[0]);
7856 } 8006 }
7857 if (n >= 0 && n < G.global_argc) { 8007 if (n >= 0 && n < G.global_argc) {
7858 if (G.global_args_malloced) { 8008 if (G.global_args_malloced) {
@@ -7877,12 +8027,13 @@ static int FAST_FUNC builtin_source(char **argv)
7877 smallint sv_flg; 8027 smallint sv_flg;
7878#endif 8028#endif
7879 8029
7880 arg_path = NULL; 8030 argv = skip_dash_dash(argv);
7881 filename = *++argv; 8031 filename = argv[0];
7882 if (!filename) { 8032 if (!filename) {
7883 /* bash says: "bash: .: filename argument required" */ 8033 /* bash says: "bash: .: filename argument required" */
7884 return 2; /* bash compat */ 8034 return 2; /* bash compat */
7885 } 8035 }
8036 arg_path = NULL;
7886 if (!strchr(filename, '/')) { 8037 if (!strchr(filename, '/')) {
7887 arg_path = find_in_path(filename); 8038 arg_path = find_in_path(filename);
7888 if (arg_path) 8039 if (arg_path)
@@ -7920,11 +8071,12 @@ static int FAST_FUNC builtin_umask(char **argv)
7920 mode_t mask; 8071 mode_t mask;
7921 8072
7922 mask = umask(0); 8073 mask = umask(0);
7923 if (argv[1]) { 8074 argv = skip_dash_dash(argv);
8075 if (argv[0]) {
7924 mode_t old_mask = mask; 8076 mode_t old_mask = mask;
7925 8077
7926 mask ^= 0777; 8078 mask ^= 0777;
7927 rc = bb_parse_mode(argv[1], &mask); 8079 rc = bb_parse_mode(argv[0], &mask);
7928 mask ^= 0777; 8080 mask ^= 0777;
7929 if (rc == 0) { 8081 if (rc == 0) {
7930 mask = old_mask; 8082 mask = old_mask;
@@ -7932,7 +8084,7 @@ static int FAST_FUNC builtin_umask(char **argv)
7932 * bash: umask: 'q': invalid symbolic mode operator 8084 * bash: umask: 'q': invalid symbolic mode operator
7933 * bash: umask: 999: octal number out of range 8085 * bash: umask: 999: octal number out of range
7934 */ 8086 */
7935 bb_error_msg("%s: '%s' invalid mode", argv[0], argv[1]); 8087 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
7936 } 8088 }
7937 } else { 8089 } else {
7938 rc = 1; 8090 rc = 1;
@@ -7988,7 +8140,8 @@ static int FAST_FUNC builtin_wait(char **argv)
7988 int ret = EXIT_SUCCESS; 8140 int ret = EXIT_SUCCESS;
7989 int status, sig; 8141 int status, sig;
7990 8142
7991 if (*++argv == NULL) { 8143 argv = skip_dash_dash(argv);
8144 if (argv[0] == NULL) {
7992 /* Don't care about wait results */ 8145 /* Don't care about wait results */
7993 /* Note 1: must wait until there are no more children */ 8146 /* Note 1: must wait until there are no more children */
7994 /* Note 2: must be interruptible */ 8147 /* Note 2: must be interruptible */
diff --git a/shell/hush_test/hush-arith/arith.right b/shell/hush_test/hush-arith/arith.right
index 83155fb03..718c26ad0 100644
--- a/shell/hush_test/hush-arith/arith.right
+++ b/shell/hush_test/hush-arith/arith.right
@@ -43,21 +43,30 @@ Format: 'expected actual'
434 4 434 4
4429 29 4429 29
455 5 455 5
46unary plus, minus
46-4 -4 47-4 -4
474 4 484 4
49conditional expressions
481 1 501 1
4932 32 5132 32
5032 32 5232 32
511 1 531 1
521 1 541 1
5332 32 5532 32
56check that parentheses in `cmd` are interpreted correctly
573 3
58check that the unevaluated part of the ternary operator does not do evaluation or assignment
5420 20 5920 20
5530 30 6030 30
5620 20 6120 20
5730 30 6230 30
63check precedence of assignment vs. conditional operator
58hush: error in arithmetic 64hush: error in arithmetic
65check precedence of assignment vs. conditional operator
66associativity of assignment-operator operator
596 6 676 6
606,5,3 6,5,3 686,5,3 6,5,3
69octal, hex
61263 263 70263 263
62255 255 71255 255
6340 40 7240 40
diff --git a/shell/hush_test/hush-arith/arith.tests b/shell/hush_test/hush-arith/arith.tests
index 57e66e888..bc6b341d1 100755
--- a/shell/hush_test/hush-arith/arith.tests
+++ b/shell/hush_test/hush-arith/arith.tests
@@ -75,11 +75,11 @@ echo 4 $(( iv &= 4 ))
75echo 29 $(( iv += (jv + 9))) 75echo 29 $(( iv += (jv + 9)))
76echo 5 $(( (iv + 4) % 7 )) 76echo 5 $(( (iv + 4) % 7 ))
77 77
78# unary plus, minus 78echo unary plus, minus
79echo -4 $(( +4 - 8 )) 79echo -4 $(( +4 - 8 ))
80echo 4 $(( -4 + 8 )) 80echo 4 $(( -4 + 8 ))
81 81
82# conditional expressions 82echo conditional expressions
83echo 1 $(( 4<5 ? 1 : 32)) 83echo 1 $(( 4<5 ? 1 : 32))
84echo 32 $(( 4>5 ? 1 : 32)) 84echo 32 $(( 4>5 ? 1 : 32))
85echo 32 $(( 4>(2+3) ? 1 : 32)) 85echo 32 $(( 4>(2+3) ? 1 : 32))
@@ -87,8 +87,11 @@ echo 1 $(( 4<(2+3) ? 1 : 32))
87echo 1 $(( (2+2)<(2+3) ? 1 : 32)) 87echo 1 $(( (2+2)<(2+3) ? 1 : 32))
88echo 32 $(( (2+2)>(2+3) ? 1 : 32)) 88echo 32 $(( (2+2)>(2+3) ? 1 : 32))
89 89
90# check that the unevaluated part of the ternary operator does not do 90echo 'check that parentheses in `cmd` are interpreted correctly'
91# evaluation or assignment 91# \x28 is '('
92echo 3 $(( ( `printf '(\x28 1'` + `echo 2\)\)` ) ))
93
94echo check that the unevaluated part of the ternary operator does not do evaluation or assignment
92x=i+=2 95x=i+=2
93y=j+=2 96y=j+=2
94#ash# declare -i i=1 j=1 97#ash# declare -i i=1 j=1
@@ -109,20 +112,20 @@ echo 20 $((1 ? 20 : (x+=2)))
109echo 30 $((0 ? (y+=2) : 30)) 112echo 30 $((0 ? (y+=2) : 30))
110#ash# echo $i,$y # ash mishandles this 113#ash# echo $i,$y # ash mishandles this
111 114
112# check precedence of assignment vs. conditional operator 115echo check precedence of assignment vs. conditional operator
113# should be an error 116# should be an error
114#ash# declare -i x=2 117#ash# declare -i x=2
115 x=2 118 x=2
116#ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash: 119#ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash:
117( y=$((1 ? 20 : x+=2)) ) 120( y=$((1 ? 20 : x+=2)) )
118 121
119# check precedence of assignment vs. conditional operator 122echo check precedence of assignment vs. conditional operator
120#ash# declare -i x=2 123#ash# declare -i x=2
121 x=2 124 x=2
122# ash says "line NNN: syntax error: 0 ? x+=2 : 20" 125# ash says "line NNN: syntax error: 0 ? x+=2 : 20"
123#ash# echo 20 $((0 ? x+=2 : 20)) 126#ash# echo 20 $((0 ? x+=2 : 20))
124 127
125# associativity of assignment-operator operator 128echo associativity of assignment-operator operator
126#ash# declare -i i=1 j=2 k=3 129#ash# declare -i i=1 j=2 k=3
127i=1 130i=1
128j=2 131j=2
@@ -130,7 +133,7 @@ k=3
130echo 6 $((i += j += k)) 133echo 6 $((i += j += k))
131echo 6,5,3 $i,$j,$k 134echo 6,5,3 $i,$j,$k
132 135
133# octal, hex 136echo octal, hex
134echo 263 $(( 0x100 | 007 )) 137echo 263 $(( 0x100 | 007 ))
135echo 255 $(( 0xff )) 138echo 255 $(( 0xff ))
136#ash# echo 255 $(( 16#ff )) 139#ash# echo 255 $(( 16#ff ))
diff --git a/shell/hush_test/hush-psubst/falsetick.right b/shell/hush_test/hush-psubst/falsetick.right
new file mode 100644
index 000000000..0b98fb778
--- /dev/null
+++ b/shell/hush_test/hush-psubst/falsetick.right
@@ -0,0 +1,27 @@
10
20
30
40
52
62
72
82
9hush: can't open '/does/not/exist': No such file or directory
101
11hush: can't open '/does/not/exist': No such file or directory
121
13hush: can't open '/does/not/exist': No such file or directory
141
15hush: can't open '/does/not/exist': No such file or directory
161
17hush: can't open '/does/not/exist': No such file or directory
181
19hush: can't open '/does/not/exist': No such file or directory
201
21hush: can't open '/does/not/exist': No such file or directory
221
23hush: can't open '/does/not/exist': No such file or directory
241
25hush: can't open '/does/not/exist': No such file or directory
261
27Done: a=b
diff --git a/shell/hush_test/hush-psubst/falsetick.tests b/shell/hush_test/hush-psubst/falsetick.tests
new file mode 100755
index 000000000..44d2eae8b
--- /dev/null
+++ b/shell/hush_test/hush-psubst/falsetick.tests
@@ -0,0 +1,22 @@
1# Exitcode 0 (`` has no exitcode, but assignment has):
2true; a=``; echo $?
3false; a=``; echo $?
4true; a=$(); echo $?
5false; a=$(); echo $?
6# Exitcode 2 (`cmd` expansion sets exitcode after assignment set it to 0):
7true; a=`exit 2`; echo $?
8false; a=`exit 2`; echo $?
9true; a=$(exit 2); echo $?
10false; a=$(exit 2); echo $?
11# Exitcode 1 (redirect sets exitcode to 1 on error after them):
12true; a=`` >/does/not/exist; echo $?
13false; a=`` >/does/not/exist; echo $?
14true; a=$() >/does/not/exist; echo $?
15false; a=$() >/does/not/exist; echo $?
16true; a=`exit 2` >/does/not/exist; echo $?
17false; a=`exit 2` >/does/not/exist; echo $?
18true; a=$(exit 2) >/does/not/exist; echo $?
19false; a=$(exit 2) >/does/not/exist; echo $?
20# ...and assignment still happens despite redirect error:
21true; a=$(echo b) >/does/not/exist; echo $?
22echo "Done: a=$a"
diff --git a/shell/hush_test/hush-vars/param_expand_alt.right b/shell/hush_test/hush-vars/param_expand_alt.right
index 4d2197a5e..67f18d69c 100644
--- a/shell/hush_test/hush-vars/param_expand_alt.right
+++ b/shell/hush_test/hush-vars/param_expand_alt.right
@@ -1,6 +1,6 @@
1hush: syntax error: unterminated ${name} 1hush: syntax error: unterminated ${name}
2hush: syntax error: unterminated ${name} 2hush: syntax error: unterminated ${name}
3_0 _0 3__ __
4_ _ _ _ _ 4_ _ _ _ _
5_aaaa _ _ _word _word 5_aaaa _ _ _word _word
6_ _ _ _ _ 6_ _ _ _ _
diff --git a/shell/hush_test/hush-vars/param_expand_alt.tests b/shell/hush_test/hush-vars/param_expand_alt.tests
index dcdca86d4..3b646b142 100755
--- a/shell/hush_test/hush-vars/param_expand_alt.tests
+++ b/shell/hush_test/hush-vars/param_expand_alt.tests
@@ -2,8 +2,8 @@
2"$THIS_SH" -c 'echo ${+} ; echo moo' 2"$THIS_SH" -c 'echo ${+} ; echo moo'
3"$THIS_SH" -c 'echo ${:+} ; echo moo' 3"$THIS_SH" -c 'echo ${:+} ; echo moo'
4 4
5# now some funky ones 5# now some funky ones. (bash doesn't accept ${#+})
6echo _${#+} _${#:+} 6echo _${#+}_ _${#:+}_
7 7
8# now some valid ones 8# now some valid ones
9set -- 9set --
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.right b/shell/hush_test/hush-vars/param_expand_bash_substring.right
new file mode 100644
index 000000000..2f4c51d06
--- /dev/null
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right
@@ -0,0 +1,64 @@
1hush: syntax error: unterminated ${name}
2hush: syntax error: unterminated ${name}
3hush: syntax error: unterminated ${name}
4hush: syntax error: unterminated ${name}
50123456789
61 =||
71:1 =||
81:1:2=||
91::2 =||
101:1: =||
111:: =||
121 =|0123|
131:1 =|123|
141:1:2=|12|
151::2 =|01|
161:1: =||
171:: =||
18f =||
19f:1 =||
20f:1:2=||
21f::2 =||
22f:1: =||
23f:: =||
24f =||
25f:1 =||
26f:1:2=||
27f::2 =||
28f:1: =||
29f:: =||
30f =|a|
31f:1 =||
32f:1:2=||
33f::2 =|a|
34f:1: =||
35f:: =||
36f =|0123456789|
37f:1 =|123456789|
38f:1:2=|12|
39f::2 =|01|
40f:1: =||
41f:: =||
42Substrings from special vars
43? =|0|
44?:1 =||
45?:1:2=||
46?::2 =|0|
47?:1: =||
48?:: =||
49# =|11|
50#:1 =|1|
51#:1:2=|1|
52#::2 =|11|
53#:1: =||
54#:: =||
55Substrings with expressions
56f =|01234567|
57f:1+1:2+2 =|2345|
58f:-1:2+2 =|01234567|
59f:1:f =|1234567|
60f:1:$f =|1234567|
61f:1:${f} =|1234567|
62f:1:${f:3:1} =|123|
63f:1:1`echo 1`=|1|
64Done
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
new file mode 100755
index 000000000..5c9552dba
--- /dev/null
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
@@ -0,0 +1,83 @@
1# first try some invalid patterns
2# do all of these in subshells since it's supposed to error out
3export var=0123456789
4"$THIS_SH" -c 'echo ${:}'
5"$THIS_SH" -c 'echo ${::}'
6"$THIS_SH" -c 'echo ${:1}'
7"$THIS_SH" -c 'echo ${::1}'
8
9#this also is not valid in bash, but we accept it:
10"$THIS_SH" -c 'echo ${var:}'
11
12# then some funky ones
13# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}'
14
15# now some valid ones
16set --; echo "1 =|${1}|"
17set --; echo "1:1 =|${1:1}|"
18set --; echo "1:1:2=|${1:1:2}|"
19set --; echo "1::2 =|${1::2}|"
20set --; echo "1:1: =|${1:1:}|"
21set --; echo "1:: =|${1::}|"
22
23set -- 0123; echo "1 =|${1}|"
24set -- 0123; echo "1:1 =|${1:1}|"
25set -- 0123; echo "1:1:2=|${1:1:2}|"
26set -- 0123; echo "1::2 =|${1::2}|"
27set -- 0123; echo "1:1: =|${1:1:}|"
28set -- 0123; echo "1:: =|${1::}|"
29
30unset f; echo "f =|$f|"
31unset f; echo "f:1 =|${f:1}|"
32unset f; echo "f:1:2=|${f:1:2}|"
33unset f; echo "f::2 =|${f::2}|"
34unset f; echo "f:1: =|${f:1:}|"
35unset f; echo "f:: =|${f::}|"
36
37f=; echo "f =|$f|"
38f=; echo "f:1 =|${f:1}|"
39f=; echo "f:1:2=|${f:1:2}|"
40f=; echo "f::2 =|${f::2}|"
41f=; echo "f:1: =|${f:1:}|"
42f=; echo "f:: =|${f::}|"
43
44f=a; echo "f =|$f|"
45f=a; echo "f:1 =|${f:1}|"
46f=a; echo "f:1:2=|${f:1:2}|"
47f=a; echo "f::2 =|${f::2}|"
48f=a; echo "f:1: =|${f:1:}|"
49f=a; echo "f:: =|${f::}|"
50
51f=0123456789; echo "f =|$f|"
52f=0123456789; echo "f:1 =|${f:1}|"
53f=0123456789; echo "f:1:2=|${f:1:2}|"
54f=0123456789; echo "f::2 =|${f::2}|"
55f=0123456789; echo "f:1: =|${f:1:}|"
56f=0123456789; echo "f:: =|${f::}|"
57
58echo "Substrings from special vars"
59echo '? '"=|$?|"
60echo '?:1 '"=|${?:1}|"
61echo '?:1:2'"=|${?:1:2}|"
62echo '?::2 '"=|${?::2}|"
63echo '?:1: '"=|${?:1:}|"
64echo '?:: '"=|${?::}|"
65set -- 1 2 3 4 5 6 7 8 9 10 11
66echo '# '"=|$#|"
67echo '#:1 '"=|${#:1}|"
68echo '#:1:2'"=|${#:1:2}|"
69echo '#::2 '"=|${#::2}|"
70echo '#:1: '"=|${#:1:}|"
71echo '#:: '"=|${#::}|"
72
73echo "Substrings with expressions"
74f=01234567; echo 'f '"=|$f|"
75f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|"
76f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|"
77f=01234567; echo 'f:1:f '"=|${f:1:f}|"
78f=01234567; echo 'f:1:$f '"=|${f:1:$f}|"
79f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|"
80f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|"
81f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|"
82
83echo Done
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.right b/shell/hush_test/hush-vars/param_expand_indicate_error.right
index 590bb2001..06fcc5104 100644
--- a/shell/hush_test/hush-vars/param_expand_indicate_error.right
+++ b/shell/hush_test/hush-vars/param_expand_indicate_error.right
@@ -1,26 +1,41 @@
1hush: syntax error: unterminated ${name} 1hush: syntax error: unterminated ${name}
20 20
30 30
4====
4_ 5_
5hush: 1: parameter null or not set 6hush: 1: parameter null or not set
6hush: 1: parameter null or not set 7hush: 1: parameter null or not set
7hush: 1: message1 8hush: 1: message1
8hush: 1: message1 9hush: 1: message1
10hush: 1: unset!
11hush: 1: null or unset!
12====
9_aaaa 13_aaaa
10_aaaa 14_aaaa
11_aaaa 15_aaaa
12_aaaa 16_aaaa
13_aaaa 17_aaaa
18_aaaa
19_aaaa
20====
14_ 21_
15hush: f: parameter null or not set 22hush: f: parameter null or not set
16hush: f: parameter null or not set 23hush: f: parameter null or not set
17hush: f: message3 24hush: f: message3
18hush: f: message3 25hush: f: message3
26hush: f: unset!
27hush: f: null or unset!
28====
19_ 29_
20_ 30_
21hush: f: parameter null or not set 31hush: f: parameter null or not set
22_ 32_
23hush: f: message4 33hush: f: message4
34_
35hush: f: null or unset!
36====
37_fff
38_fff
24_fff 39_fff
25_fff 40_fff
26_fff 41_fff
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.tests b/shell/hush_test/hush-vars/param_expand_indicate_error.tests
index bccba3e1b..be14b1e37 100755
--- a/shell/hush_test/hush-vars/param_expand_indicate_error.tests
+++ b/shell/hush_test/hush-vars/param_expand_indicate_error.tests
@@ -5,36 +5,56 @@
5"$THIS_SH" -c 'echo ${:?}' 5"$THIS_SH" -c 'echo ${:?}'
6 6
7# then some funky ones 7# then some funky ones
8# note: bash prints 1 - treats it as "length of $#"? We print 0
8"$THIS_SH" -c 'echo ${#?}' 9"$THIS_SH" -c 'echo ${#?}'
10# bash prints 0
9"$THIS_SH" -c 'echo ${#:?}' 11"$THIS_SH" -c 'echo ${#:?}'
10 12
11# now some valid ones 13# now some valid ones
14export msg_unset="unset!"
15export msg_null_or_unset="null or unset!"
16
17echo ====
12"$THIS_SH" -c 'set --; echo _$1' 18"$THIS_SH" -c 'set --; echo _$1'
13"$THIS_SH" -c 'set --; echo _${1?}' 19"$THIS_SH" -c 'set --; echo _${1?}'
14"$THIS_SH" -c 'set --; echo _${1:?}' 20"$THIS_SH" -c 'set --; echo _${1:?}'
15"$THIS_SH" -c 'set --; echo _${1?message1}' 21"$THIS_SH" -c 'set --; echo _${1?message1}'
16"$THIS_SH" -c 'set --; echo _${1:?message1}' 22"$THIS_SH" -c 'set --; echo _${1:?message1}'
23"$THIS_SH" -c 'set --; echo _${1?$msg_unset}'
24"$THIS_SH" -c 'set --; echo _${1:?$msg_null_or_unset}'
17 25
26echo ====
18"$THIS_SH" -c 'set -- aaaa; echo _$1' 27"$THIS_SH" -c 'set -- aaaa; echo _$1'
19"$THIS_SH" -c 'set -- aaaa; echo _${1?}' 28"$THIS_SH" -c 'set -- aaaa; echo _${1?}'
20"$THIS_SH" -c 'set -- aaaa; echo _${1:?}' 29"$THIS_SH" -c 'set -- aaaa; echo _${1:?}'
21"$THIS_SH" -c 'set -- aaaa; echo _${1?word}' 30"$THIS_SH" -c 'set -- aaaa; echo _${1?word}'
22"$THIS_SH" -c 'set -- aaaa; echo _${1:?word}' 31"$THIS_SH" -c 'set -- aaaa; echo _${1:?word}'
32"$THIS_SH" -c 'set -- aaaa; echo _${1?$msg_unset}'
33"$THIS_SH" -c 'set -- aaaa; echo _${1:?$msg_null_or_unset}'
23 34
35echo ====
24"$THIS_SH" -c 'unset f; echo _$f' 36"$THIS_SH" -c 'unset f; echo _$f'
25"$THIS_SH" -c 'unset f; echo _${f?}' 37"$THIS_SH" -c 'unset f; echo _${f?}'
26"$THIS_SH" -c 'unset f; echo _${f:?}' 38"$THIS_SH" -c 'unset f; echo _${f:?}'
27"$THIS_SH" -c 'unset f; echo _${f?message3}' 39"$THIS_SH" -c 'unset f; echo _${f?message3}'
28"$THIS_SH" -c 'unset f; echo _${f:?message3}' 40"$THIS_SH" -c 'unset f; echo _${f:?message3}'
41"$THIS_SH" -c 'unset f; echo _${f?$msg_unset}'
42"$THIS_SH" -c 'unset f; echo _${f:?$msg_null_or_unset}'
29 43
44echo ====
30"$THIS_SH" -c 'f=; echo _$f' 45"$THIS_SH" -c 'f=; echo _$f'
31"$THIS_SH" -c 'f=; echo _${f?}' 46"$THIS_SH" -c 'f=; echo _${f?}'
32"$THIS_SH" -c 'f=; echo _${f:?}' 47"$THIS_SH" -c 'f=; echo _${f:?}'
33"$THIS_SH" -c 'f=; echo _${f?word}' 48"$THIS_SH" -c 'f=; echo _${f?word}'
34"$THIS_SH" -c 'f=; echo _${f:?message4}' 49"$THIS_SH" -c 'f=; echo _${f:?message4}'
50"$THIS_SH" -c 'f=; echo _${f?$msg_unset}'
51"$THIS_SH" -c 'f=; echo _${f:?$msg_null_or_unset}'
35 52
53echo ====
36"$THIS_SH" -c 'f=fff; echo _$f' 54"$THIS_SH" -c 'f=fff; echo _$f'
37"$THIS_SH" -c 'f=fff; echo _${f?}' 55"$THIS_SH" -c 'f=fff; echo _${f?}'
38"$THIS_SH" -c 'f=fff; echo _${f:?}' 56"$THIS_SH" -c 'f=fff; echo _${f:?}'
39"$THIS_SH" -c 'f=fff; echo _${f?word}' 57"$THIS_SH" -c 'f=fff; echo _${f?word}'
40"$THIS_SH" -c 'f=fff; echo _${f:?word}' 58"$THIS_SH" -c 'f=fff; echo _${f:?word}'
59"$THIS_SH" -c 'f=fff; echo _${f?$msg_unset}'
60"$THIS_SH" -c 'f=fff; echo _${f:?$msg_null_or_unset}'
diff --git a/shell/hush_test/hush-vars/param_expand_len.right b/shell/hush_test/hush-vars/param_expand_len.right
index 2d633a148..96e8cb59b 100644
--- a/shell/hush_test/hush-vars/param_expand_len.right
+++ b/shell/hush_test/hush-vars/param_expand_len.right
@@ -1,4 +1,9 @@
10
20
31
4Make sure len parsing doesnt break arg count
10 0 50 0
24 4 64 4
7Testing len op
34 3 2 1 0 0 84 3 2 1 0 0
40 3 0 90 3 0
diff --git a/shell/hush_test/hush-vars/param_expand_len.tests b/shell/hush_test/hush-vars/param_expand_len.tests
index 90f47d2fb..fe20a45e9 100755
--- a/shell/hush_test/hush-vars/param_expand_len.tests
+++ b/shell/hush_test/hush-vars/param_expand_len.tests
@@ -1,9 +1,14 @@
1# make sure len parsing doesnt break arg count 1"$THIS_SH" -c 'echo $#'
2"$THIS_SH" -c 'echo $#' arg0
3"$THIS_SH" -c 'echo $#' arg0 arg1
4
5echo Make sure len parsing doesnt break arg count
2set -- 6set --
3echo $# ${#} 7echo $# ${#}
4set -- aaaa bbb cc d 8set -- aaaa bbb cc d
5echo $# ${#} 9echo $# ${#}
6 10
11echo Testing len op
7echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6} 12echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6}
8 13
9unset e 14unset e
diff --git a/shell/hush_test/hush-vars/var3.right b/shell/hush_test/hush-vars/var3.right
index 5e28d2fab..40e67fdf5 100644
--- a/shell/hush_test/hush-vars/var3.right
+++ b/shell/hush_test/hush-vars/var3.right
@@ -1,2 +1,2 @@
1hush: syntax error: unterminated ${name} 1hush: invalid number '1q'
2hush: syntax error: unterminated ${name} 2hush: syntax error: unterminated ${name}
diff --git a/shell/hush_test/hush-vars/var_posix1.right b/shell/hush_test/hush-vars/var_posix1.right
index e6cba2758..7ff618ad0 100644
--- a/shell/hush_test/hush-vars/var_posix1.right
+++ b/shell/hush_test/hush-vars/var_posix1.right
@@ -23,6 +23,7 @@ babcdcd
23ababcdcd 23ababcdcd
24Empty: 24Empty:
25ababcdcd}_tail 25ababcdcd}_tail
26ababcdcd_tail
26ababcd 27ababcd
27ababcd 28ababcd
28ababcd 29ababcd
@@ -32,5 +33,9 @@ ababcdc
32ababcdcd 33ababcdcd
33Empty: 34Empty:
34ababcdcd}_tail 35ababcdcd}_tail
36ababcdcd_tail
35ababcdcd 37ababcdcd
36end 38ab
39ab
40ab
41End
diff --git a/shell/hush_test/hush-vars/var_posix1.tests b/shell/hush_test/hush-vars/var_posix1.tests
index c1f64094d..82abe8198 100755
--- a/shell/hush_test/hush-vars/var_posix1.tests
+++ b/shell/hush_test/hush-vars/var_posix1.tests
@@ -31,7 +31,7 @@ echo ${var##?}
31echo ${var#*} 31echo ${var#*}
32echo Empty:${var##*} 32echo Empty:${var##*}
33echo ${var#}}_tail 33echo ${var#}}_tail
34# UNFIXED BUG: echo ${var#\}}_tail 34echo ${var#\}}_tail
35 35
36echo ${var%cd} 36echo ${var%cd}
37echo ${var%%cd} 37echo ${var%%cd}
@@ -42,7 +42,11 @@ echo ${var%%?}
42echo ${var%*} 42echo ${var%*}
43echo Empty:${var%%*} 43echo Empty:${var%%*}
44echo ${var#}}_tail 44echo ${var#}}_tail
45# UNFIXED BUG: echo ${var#\}}_tail 45echo ${var#\}}_tail
46echo ${var%\\*} 46echo ${var%\\*}
47 47
48echo end 48a=ab}; echo ${a%\}};
49a=abc; c=c; echo ${a%${c}}
50a=ab{{c; echo ${a%`echo {{c`}
51
52echo End
diff --git a/util-linux/losetup.c b/util-linux/losetup.c
index e224a4d54..e44773a07 100644
--- a/util-linux/losetup.c
+++ b/util-linux/losetup.c
@@ -10,44 +10,48 @@
10#include "libbb.h" 10#include "libbb.h"
11 11
12int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 12int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
13int losetup_main(int argc, char **argv) 13int losetup_main(int argc UNUSED_PARAM, char **argv)
14{ 14{
15 char dev[] = LOOP_NAME"0";
16 unsigned opt; 15 unsigned opt;
16 int n;
17 char *opt_o; 17 char *opt_o;
18 char *s;
19 unsigned long long offset = 0; 18 unsigned long long offset = 0;
19 enum {
20 OPT_d = (1 << 0),
21 OPT_o = (1 << 1),
22 OPT_f = (1 << 2),
23 };
20 24
21 /* max 2 args, all opts are mutually exclusive */ 25 /* max 2 args, all opts are mutually exclusive */
22 opt_complementary = "?2:d--of:o--df:f-do"; 26 opt_complementary = "?2:d--of:o--df:f-do";
23 opt = getopt32(argv, "do:f", &opt_o); 27 opt = getopt32(argv, "do:f", &opt_o);
24 argc -= optind;
25 argv += optind; 28 argv += optind;
26 29
27 if (opt == 0x2) // -o 30 if (opt == OPT_o)
28 offset = xatoull(opt_o); 31 offset = xatoull(opt_o);
29 32
30 if (opt == 0x4 && argc) // -f does not take any argument 33 if (opt == OPT_d) {
31 bb_show_usage(); 34 /* -d BLOCKDEV */
32 35 if (!argv[0] || argv[1])
33 if (opt == 0x1) { // -d
34 /* detach takes exactly one argument */
35 if (argc != 1)
36 bb_show_usage(); 36 bb_show_usage();
37 if (del_loop(argv[0])) 37 if (del_loop(argv[0]))
38 bb_simple_perror_msg_and_die(argv[0]); 38 bb_simple_perror_msg_and_die(argv[0]);
39 return EXIT_SUCCESS; 39 return EXIT_SUCCESS;
40 } 40 }
41 41
42 if (argc == 2) { 42 if (argv[0]) {
43 /* -o or no option */ 43 char *s;
44 if (set_loop(&argv[0], argv[1], offset) < 0) 44
45 bb_simple_perror_msg_and_die(argv[0]); 45 if (opt == OPT_f) /* -f should not have arguments */
46 return EXIT_SUCCESS; 46 bb_show_usage();
47 }
48 47
49 if (argc == 1) { 48 if (argv[1]) {
50 /* -o or no option */ 49 /* [-o OFS] BLOCKDEV FILE */
50 if (set_loop(&argv[0], argv[1], offset) < 0)
51 bb_simple_perror_msg_and_die(argv[0]);
52 return EXIT_SUCCESS;
53 }
54 /* [-o OFS] BLOCKDEV */
51 s = query_loop(argv[0]); 55 s = query_loop(argv[0]);
52 if (!s) 56 if (!s)
53 bb_simple_perror_msg_and_die(argv[0]); 57 bb_simple_perror_msg_and_die(argv[0]);
@@ -57,23 +61,28 @@ int losetup_main(int argc, char **argv)
57 return EXIT_SUCCESS; 61 return EXIT_SUCCESS;
58 } 62 }
59 63
60 /* -o, -f or no option */ 64 /* [-o OFS|-f] with no params */
65 n = 0;
61 while (1) { 66 while (1) {
67 char *s;
68 char dev[sizeof(LOOP_NAME) + sizeof(int)*3];
69
70 sprintf(dev, LOOP_NAME"%u", n);
62 s = query_loop(dev); 71 s = query_loop(dev);
72 n++;
63 if (!s) { 73 if (!s) {
64 if (opt == 0x4) { 74 if (n > 9 && errno && errno != ENXIO)
75 return EXIT_SUCCESS;
76 if (opt == OPT_f) {
65 puts(dev); 77 puts(dev);
66 return EXIT_SUCCESS; 78 return EXIT_SUCCESS;
67 } 79 }
68 } else { 80 } else {
69 if (opt != 0x4) 81 if (opt != OPT_f)
70 printf("%s: %s\n", dev, s); 82 printf("%s: %s\n", dev, s);
71 if (ENABLE_FEATURE_CLEAN_UP) 83 if (ENABLE_FEATURE_CLEAN_UP)
72 free(s); 84 free(s);
73 } 85 }
74
75 if (++dev[sizeof(dev) - 2] > '9')
76 break;
77 } 86 }
78 return EXIT_SUCCESS; 87 return EXIT_SUCCESS;
79} 88}
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 8ec3071b1..aed6f798b 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -749,7 +749,15 @@ static const uint8_t nfs_err_stat[] = {
749 19, 20, 21, 22, 27, 28, 749 19, 20, 21, 22, 27, 28,
750 30, 63, 66, 69, 70, 71 750 30, 63, 66, 69, 70, 71
751}; 751};
752static const uint8_t nfs_err_errnum[] = { 752#if ( \
753 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
754 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
755 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
756typedef uint8_t nfs_err_type;
757#else
758typedef uint16_t nfs_err_type;
759#endif
760static const nfs_err_type nfs_err_errnum[] = {
753 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST, 761 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
754 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC, 762 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
755 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE 763 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE