diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 12:17:00 +1000 |
---|---|---|
committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 12:17:00 +1000 |
commit | 38a1e6da34ffe9f0c6cdb8661b263eb80d72981f (patch) | |
tree | 434501acda4fa6c635e058eca4eea449dcfbbd08 | |
parent | adcaaebedad72991d917fc57348a1b7a87067c2d (diff) | |
parent | 4f63c7931c42351e38619842681026ff2c20c7ee (diff) | |
download | busybox-w32-38a1e6da34ffe9f0c6cdb8661b263eb80d72981f.tar.gz busybox-w32-38a1e6da34ffe9f0c6cdb8661b263eb80d72981f.tar.bz2 busybox-w32-38a1e6da34ffe9f0c6cdb8661b263eb80d72981f.zip |
Merge branch 'origin/master' (early part)
32 files changed, 996 insertions, 545 deletions
@@ -1003,8 +1003,8 @@ $(mrproper-dirs): | |||
1003 | mrproper: clean archmrproper $(mrproper-dirs) | 1003 | mrproper: 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 { | |||
21 | int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 21 | int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
22 | int setkeycodes_main(int argc, char **argv) | 22 | int 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)) | |||
79 | IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP)) | 79 | IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP)) |
80 | IF_AWK(APPLET_NOEXEC(awk, awk, _BB_DIR_USR_BIN, _BB_SUID_DROP, awk)) | 80 | IF_AWK(APPLET_NOEXEC(awk, awk, _BB_DIR_USR_BIN, _BB_SUID_DROP, awk)) |
81 | IF_BASENAME(APPLET_NOFORK(basename, basename, _BB_DIR_USR_BIN, _BB_SUID_DROP, basename)) | 81 | IF_BASENAME(APPLET_NOFORK(basename, basename, _BB_DIR_USR_BIN, _BB_SUID_DROP, basename)) |
82 | IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash)) | ||
83 | IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash)) | ||
82 | IF_BBCONFIG(APPLET(bbconfig, _BB_DIR_BIN, _BB_SUID_DROP)) | 84 | IF_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)) |
84 | IF_BEEP(APPLET(beep, _BB_DIR_USR_BIN, _BB_SUID_DROP)) | 86 | IF_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 | |||
35 | struct 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 | |||
34 | static dev_t find_socket_dev(void) | 43 | static 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 | ||
47 | static 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 | |||
57 | static char *parse_net_arg(const char *arg, unsigned *port) | 56 | static 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 | ||
69 | static pid_list *add_pid(pid_list *plist, pid_t pid) | 68 | static 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 | ||
83 | static inode_list *add_inode(inode_list *ilist, dev_t dev, ino_t inode) | 82 | static 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 | ||
98 | static inode_list *scan_proc_net(const char *proto, | 100 | static 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 | ||
136 | static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode) | 134 | static 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 | ||
150 | static pid_list *scan_pid_maps(const char *fname, pid_t pid, | 150 | static 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 | ||
177 | static pid_list *scan_link(const char *lname, pid_t pid, | 175 | static 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 | ||
190 | static pid_list *scan_dir_links(const char *dname, pid_t pid, | 185 | static 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 */ |
212 | static pid_list *scan_proc_pids(inode_list *ilist) | 206 | static 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 | ||
244 | static 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 | |||
254 | static 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 | ||
271 | int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 237 | int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
272 | int fuser_main(int argc UNUSED_PARAM, char **argv) | 238 | int 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 | /* |
284 | fuser [options] FILEs or PORT/PROTOs | 249 | fuser [OPTIONS] FILE or PORT/PROTO |
285 | Find processes which use FILEs or PORTs | 250 | Find 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 | ||
238 | static void func_rgroup(char *buf, int size, const procps_status_t *ps) | 237 | static 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 | |||
256 | static void func_etime(char *buf, int size, const procps_status_t *ps) | 256 | static 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) | |||
337 | static const ps_out_t* find_out_spec(const char *name) | 338 | static 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 | ||
347 | static void parse_o(char* opt) | 361 | static 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? |
20 | ccflags() | 20 | ccflags() |
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 |
86 | SORT_SECTION=`check_cc "-Wl,--sort-section,alignment" ""` | 86 | SORT_SECTION=`check_cc "-Wl,--sort-section,alignment" ""` |
87 | 87 | ||
88 | # gold may not support --sort-common (yet) | ||
89 | SORT_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$//'` | |||
114 | test "x$l_list" != "x" && l_list="-Wl,--start-group $l_list -Wl,--end-group" | 117 | test "x$l_list" != "x" && l_list="-Wl,--start-group $l_list -Wl,--end-group" |
115 | try $CC $CFLAGS $LDFLAGS \ | 118 | try $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" | |||
171 | if ! test -f busybox_ldscript; then | 174 | if ! 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 @@ | |||
6 | menu "Shells" | 6 | menu "Shells" |
7 | 7 | ||
8 | choice | 8 | choice |
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 | ||
15 | config FEATURE_SH_IS_ASH | 15 | config 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 | 24 | config FEATURE_SH_IS_NONE |
25 | #### select LASH | 25 | bool "none" |
26 | #### bool "lash" | ||
27 | 26 | ||
28 | ####config FEATURE_SH_IS_MSH | 27 | endchoice |
29 | #### select MSH | ||
30 | #### bool "msh" | ||
31 | 28 | ||
32 | config FEATURE_SH_IS_NONE | 29 | choice |
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 | |||
46 | config FEATURE_BASH_IS_ASH | ||
47 | select ASH | ||
48 | bool "ash" | ||
49 | depends on !NOMMU | ||
50 | |||
51 | config FEATURE_BASH_IS_HUSH | ||
52 | select HUSH | ||
53 | bool "hush" | ||
54 | |||
55 | config FEATURE_BASH_IS_NONE | ||
33 | bool "none" | 56 | bool "none" |
34 | 57 | ||
35 | endchoice | 58 | endchoice |
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 | ||
35 | It says that shell must implement special built-ins. Special built-ins | 35 | It says that shell must implement special built-ins. Special built-ins |
36 | differ from regular ones by the fact that variable assignments | 36 | differ from regular ones by the fact that variable assignments |
37 | done on special builtin is *PRESERVED*. That is, | 37 | done on special builtin are *PRESERVED*. That is, |
38 | 38 | ||
39 | VAR=VAL special_builtin; echo $VAR | 39 | VAR=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 |
44 | abort the shell, but this is not such a critical difference, | 44 | abort the shell, but this is not such a critical difference, |
45 | and moreover, at least bash's "set" does not follow this rule, | 45 | and moreover, at least bash's "set" does not follow this rule, |
46 | which is even codified in autoconf now...). | 46 | which is even codified in autoconf configure logic now...) |
47 | 47 | ||
48 | List of special builtins: | 48 | List of special builtins: |
49 | 49 | ||
@@ -73,7 +73,7 @@ unset [-fv] name... | |||
73 | In practice, no one uses this obscure feature - none of these builtins | 73 | In practice, no one uses this obscure feature - none of these builtins |
74 | gives any special reasons to play such dirty tricks. | 74 | gives any special reasons to play such dirty tricks. |
75 | 75 | ||
76 | However. This section says that *function invocation* should act | 76 | However. This section also says that *function invocation* should act |
77 | similar to special built-in. That is, variable assignments | 77 | similar to special built-in. That is, variable assignments |
78 | done on function invocation should be preserved after function invocation. | 78 | done 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 | ||
7957 | static const char * | ||
7958 | tokname(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/... */ |
7975 | static int | 7961 | static int |
7976 | pstrcmp(const void *a, const void *b) | 7962 | pstrcmp(const void *a, const void *b) |
@@ -10745,7 +10731,16 @@ static struct nodelist *backquotelist; | |||
10745 | static union node *redirnode; | 10731 | static union node *redirnode; |
10746 | static struct heredoc *heredoc; | 10732 | static struct heredoc *heredoc; |
10747 | 10733 | ||
10748 | /* | 10734 | static const char * |
10735 | tokname(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 | |||
10755 | raise_error_unexpected_syntax(int token) | 10750 | raise_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 | ||
184 | struct variable; | 191 | struct 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))) |
1516 | static char* FAST_FUNC endofname(const char *name) | 1527 | static 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 | */ |
1597 | static int FAST_FUNC static_get(struct in_str *i) | 1608 | static 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 | ||
1820 | static void o_addstr(o_string *o, const char *str) | 1831 | static 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 | ||
1824 | static void nommu_addchr(o_string *o, int ch) | 1837 | static 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 */ |
2215 | static int glob_needed(const char *s) | 2228 | static 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 | ||
2421 | static 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 | */ |
5805 | static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl) | 5930 | #define DOUBLE_CLOSE_CHAR_FLAG 0x80 |
5931 | static 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 |
5856 | static int handle_dollar(o_string *as_string, | 5998 | static 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 |
6049 | static int parse_stream_dquoted(o_string *as_string, | 6186 | static 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 | ||
7396 | static 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 | |||
7261 | static int FAST_FUNC builtin_eval(char **argv) | 7404 | static 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 | ||
7278 | static int FAST_FUNC builtin_cd(char **argv) | 7422 | static 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 | ||
7302 | static int FAST_FUNC builtin_exec(char **argv) | 7449 | static 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 | ||
7345 | static void print_escaped(const char *s) | 7494 | static 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) | |||
7851 | static int FAST_FUNC builtin_shift(char **argv) | 8000 | static 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' | |||
43 | 4 4 | 43 | 4 4 |
44 | 29 29 | 44 | 29 29 |
45 | 5 5 | 45 | 5 5 |
46 | unary plus, minus | ||
46 | -4 -4 | 47 | -4 -4 |
47 | 4 4 | 48 | 4 4 |
49 | conditional expressions | ||
48 | 1 1 | 50 | 1 1 |
49 | 32 32 | 51 | 32 32 |
50 | 32 32 | 52 | 32 32 |
51 | 1 1 | 53 | 1 1 |
52 | 1 1 | 54 | 1 1 |
53 | 32 32 | 55 | 32 32 |
56 | check that parentheses in `cmd` are interpreted correctly | ||
57 | 3 3 | ||
58 | check that the unevaluated part of the ternary operator does not do evaluation or assignment | ||
54 | 20 20 | 59 | 20 20 |
55 | 30 30 | 60 | 30 30 |
56 | 20 20 | 61 | 20 20 |
57 | 30 30 | 62 | 30 30 |
63 | check precedence of assignment vs. conditional operator | ||
58 | hush: error in arithmetic | 64 | hush: error in arithmetic |
65 | check precedence of assignment vs. conditional operator | ||
66 | associativity of assignment-operator operator | ||
59 | 6 6 | 67 | 6 6 |
60 | 6,5,3 6,5,3 | 68 | 6,5,3 6,5,3 |
69 | octal, hex | ||
61 | 263 263 | 70 | 263 263 |
62 | 255 255 | 71 | 255 255 |
63 | 40 40 | 72 | 40 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 )) | |||
75 | echo 29 $(( iv += (jv + 9))) | 75 | echo 29 $(( iv += (jv + 9))) |
76 | echo 5 $(( (iv + 4) % 7 )) | 76 | echo 5 $(( (iv + 4) % 7 )) |
77 | 77 | ||
78 | # unary plus, minus | 78 | echo unary plus, minus |
79 | echo -4 $(( +4 - 8 )) | 79 | echo -4 $(( +4 - 8 )) |
80 | echo 4 $(( -4 + 8 )) | 80 | echo 4 $(( -4 + 8 )) |
81 | 81 | ||
82 | # conditional expressions | 82 | echo conditional expressions |
83 | echo 1 $(( 4<5 ? 1 : 32)) | 83 | echo 1 $(( 4<5 ? 1 : 32)) |
84 | echo 32 $(( 4>5 ? 1 : 32)) | 84 | echo 32 $(( 4>5 ? 1 : 32)) |
85 | echo 32 $(( 4>(2+3) ? 1 : 32)) | 85 | echo 32 $(( 4>(2+3) ? 1 : 32)) |
@@ -87,8 +87,11 @@ echo 1 $(( 4<(2+3) ? 1 : 32)) | |||
87 | echo 1 $(( (2+2)<(2+3) ? 1 : 32)) | 87 | echo 1 $(( (2+2)<(2+3) ? 1 : 32)) |
88 | echo 32 $(( (2+2)>(2+3) ? 1 : 32)) | 88 | echo 32 $(( (2+2)>(2+3) ? 1 : 32)) |
89 | 89 | ||
90 | # check that the unevaluated part of the ternary operator does not do | 90 | echo 'check that parentheses in `cmd` are interpreted correctly' |
91 | # evaluation or assignment | 91 | # \x28 is '(' |
92 | echo 3 $(( ( `printf '(\x28 1'` + `echo 2\)\)` ) )) | ||
93 | |||
94 | echo check that the unevaluated part of the ternary operator does not do evaluation or assignment | ||
92 | x=i+=2 | 95 | x=i+=2 |
93 | y=j+=2 | 96 | y=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))) | |||
109 | echo 30 $((0 ? (y+=2) : 30)) | 112 | echo 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 | 115 | echo 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 | 122 | echo 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 | 128 | echo 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 |
127 | i=1 | 130 | i=1 |
128 | j=2 | 131 | j=2 |
@@ -130,7 +133,7 @@ k=3 | |||
130 | echo 6 $((i += j += k)) | 133 | echo 6 $((i += j += k)) |
131 | echo 6,5,3 $i,$j,$k | 134 | echo 6,5,3 $i,$j,$k |
132 | 135 | ||
133 | # octal, hex | 136 | echo octal, hex |
134 | echo 263 $(( 0x100 | 007 )) | 137 | echo 263 $(( 0x100 | 007 )) |
135 | echo 255 $(( 0xff )) | 138 | echo 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 @@ | |||
1 | 0 | ||
2 | 0 | ||
3 | 0 | ||
4 | 0 | ||
5 | 2 | ||
6 | 2 | ||
7 | 2 | ||
8 | 2 | ||
9 | hush: can't open '/does/not/exist': No such file or directory | ||
10 | 1 | ||
11 | hush: can't open '/does/not/exist': No such file or directory | ||
12 | 1 | ||
13 | hush: can't open '/does/not/exist': No such file or directory | ||
14 | 1 | ||
15 | hush: can't open '/does/not/exist': No such file or directory | ||
16 | 1 | ||
17 | hush: can't open '/does/not/exist': No such file or directory | ||
18 | 1 | ||
19 | hush: can't open '/does/not/exist': No such file or directory | ||
20 | 1 | ||
21 | hush: can't open '/does/not/exist': No such file or directory | ||
22 | 1 | ||
23 | hush: can't open '/does/not/exist': No such file or directory | ||
24 | 1 | ||
25 | hush: can't open '/does/not/exist': No such file or directory | ||
26 | 1 | ||
27 | Done: 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): | ||
2 | true; a=``; echo $? | ||
3 | false; a=``; echo $? | ||
4 | true; a=$(); echo $? | ||
5 | false; a=$(); echo $? | ||
6 | # Exitcode 2 (`cmd` expansion sets exitcode after assignment set it to 0): | ||
7 | true; a=`exit 2`; echo $? | ||
8 | false; a=`exit 2`; echo $? | ||
9 | true; a=$(exit 2); echo $? | ||
10 | false; a=$(exit 2); echo $? | ||
11 | # Exitcode 1 (redirect sets exitcode to 1 on error after them): | ||
12 | true; a=`` >/does/not/exist; echo $? | ||
13 | false; a=`` >/does/not/exist; echo $? | ||
14 | true; a=$() >/does/not/exist; echo $? | ||
15 | false; a=$() >/does/not/exist; echo $? | ||
16 | true; a=`exit 2` >/does/not/exist; echo $? | ||
17 | false; a=`exit 2` >/does/not/exist; echo $? | ||
18 | true; a=$(exit 2) >/does/not/exist; echo $? | ||
19 | false; a=$(exit 2) >/does/not/exist; echo $? | ||
20 | # ...and assignment still happens despite redirect error: | ||
21 | true; a=$(echo b) >/does/not/exist; echo $? | ||
22 | echo "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 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | hush: syntax error: unterminated ${name} |
2 | hush: syntax error: unterminated ${name} | 2 | hush: 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 ${#+}) |
6 | echo _${#+} _${#:+} | 6 | echo _${#+}_ _${#:+}_ |
7 | 7 | ||
8 | # now some valid ones | 8 | # now some valid ones |
9 | set -- | 9 | set -- |
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 @@ | |||
1 | hush: syntax error: unterminated ${name} | ||
2 | hush: syntax error: unterminated ${name} | ||
3 | hush: syntax error: unterminated ${name} | ||
4 | hush: syntax error: unterminated ${name} | ||
5 | 0123456789 | ||
6 | 1 =|| | ||
7 | 1:1 =|| | ||
8 | 1:1:2=|| | ||
9 | 1::2 =|| | ||
10 | 1:1: =|| | ||
11 | 1:: =|| | ||
12 | 1 =|0123| | ||
13 | 1:1 =|123| | ||
14 | 1:1:2=|12| | ||
15 | 1::2 =|01| | ||
16 | 1:1: =|| | ||
17 | 1:: =|| | ||
18 | f =|| | ||
19 | f:1 =|| | ||
20 | f:1:2=|| | ||
21 | f::2 =|| | ||
22 | f:1: =|| | ||
23 | f:: =|| | ||
24 | f =|| | ||
25 | f:1 =|| | ||
26 | f:1:2=|| | ||
27 | f::2 =|| | ||
28 | f:1: =|| | ||
29 | f:: =|| | ||
30 | f =|a| | ||
31 | f:1 =|| | ||
32 | f:1:2=|| | ||
33 | f::2 =|a| | ||
34 | f:1: =|| | ||
35 | f:: =|| | ||
36 | f =|0123456789| | ||
37 | f:1 =|123456789| | ||
38 | f:1:2=|12| | ||
39 | f::2 =|01| | ||
40 | f:1: =|| | ||
41 | f:: =|| | ||
42 | Substrings 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 | #:: =|| | ||
55 | Substrings with expressions | ||
56 | f =|01234567| | ||
57 | f:1+1:2+2 =|2345| | ||
58 | f:-1:2+2 =|01234567| | ||
59 | f:1:f =|1234567| | ||
60 | f:1:$f =|1234567| | ||
61 | f:1:${f} =|1234567| | ||
62 | f:1:${f:3:1} =|123| | ||
63 | f:1:1`echo 1`=|1| | ||
64 | Done | ||
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 | ||
3 | export 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 | ||
16 | set --; echo "1 =|${1}|" | ||
17 | set --; echo "1:1 =|${1:1}|" | ||
18 | set --; echo "1:1:2=|${1:1:2}|" | ||
19 | set --; echo "1::2 =|${1::2}|" | ||
20 | set --; echo "1:1: =|${1:1:}|" | ||
21 | set --; echo "1:: =|${1::}|" | ||
22 | |||
23 | set -- 0123; echo "1 =|${1}|" | ||
24 | set -- 0123; echo "1:1 =|${1:1}|" | ||
25 | set -- 0123; echo "1:1:2=|${1:1:2}|" | ||
26 | set -- 0123; echo "1::2 =|${1::2}|" | ||
27 | set -- 0123; echo "1:1: =|${1:1:}|" | ||
28 | set -- 0123; echo "1:: =|${1::}|" | ||
29 | |||
30 | unset f; echo "f =|$f|" | ||
31 | unset f; echo "f:1 =|${f:1}|" | ||
32 | unset f; echo "f:1:2=|${f:1:2}|" | ||
33 | unset f; echo "f::2 =|${f::2}|" | ||
34 | unset f; echo "f:1: =|${f:1:}|" | ||
35 | unset f; echo "f:: =|${f::}|" | ||
36 | |||
37 | f=; echo "f =|$f|" | ||
38 | f=; echo "f:1 =|${f:1}|" | ||
39 | f=; echo "f:1:2=|${f:1:2}|" | ||
40 | f=; echo "f::2 =|${f::2}|" | ||
41 | f=; echo "f:1: =|${f:1:}|" | ||
42 | f=; echo "f:: =|${f::}|" | ||
43 | |||
44 | f=a; echo "f =|$f|" | ||
45 | f=a; echo "f:1 =|${f:1}|" | ||
46 | f=a; echo "f:1:2=|${f:1:2}|" | ||
47 | f=a; echo "f::2 =|${f::2}|" | ||
48 | f=a; echo "f:1: =|${f:1:}|" | ||
49 | f=a; echo "f:: =|${f::}|" | ||
50 | |||
51 | f=0123456789; echo "f =|$f|" | ||
52 | f=0123456789; echo "f:1 =|${f:1}|" | ||
53 | f=0123456789; echo "f:1:2=|${f:1:2}|" | ||
54 | f=0123456789; echo "f::2 =|${f::2}|" | ||
55 | f=0123456789; echo "f:1: =|${f:1:}|" | ||
56 | f=0123456789; echo "f:: =|${f::}|" | ||
57 | |||
58 | echo "Substrings from special vars" | ||
59 | echo '? '"=|$?|" | ||
60 | echo '?:1 '"=|${?:1}|" | ||
61 | echo '?:1:2'"=|${?:1:2}|" | ||
62 | echo '?::2 '"=|${?::2}|" | ||
63 | echo '?:1: '"=|${?:1:}|" | ||
64 | echo '?:: '"=|${?::}|" | ||
65 | set -- 1 2 3 4 5 6 7 8 9 10 11 | ||
66 | echo '# '"=|$#|" | ||
67 | echo '#:1 '"=|${#:1}|" | ||
68 | echo '#:1:2'"=|${#:1:2}|" | ||
69 | echo '#::2 '"=|${#::2}|" | ||
70 | echo '#:1: '"=|${#:1:}|" | ||
71 | echo '#:: '"=|${#::}|" | ||
72 | |||
73 | echo "Substrings with expressions" | ||
74 | f=01234567; echo 'f '"=|$f|" | ||
75 | f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|" | ||
76 | f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|" | ||
77 | f=01234567; echo 'f:1:f '"=|${f:1:f}|" | ||
78 | f=01234567; echo 'f:1:$f '"=|${f:1:$f}|" | ||
79 | f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|" | ||
80 | f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|" | ||
81 | f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|" | ||
82 | |||
83 | echo 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 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | hush: syntax error: unterminated ${name} |
2 | 0 | 2 | 0 |
3 | 0 | 3 | 0 |
4 | ==== | ||
4 | _ | 5 | _ |
5 | hush: 1: parameter null or not set | 6 | hush: 1: parameter null or not set |
6 | hush: 1: parameter null or not set | 7 | hush: 1: parameter null or not set |
7 | hush: 1: message1 | 8 | hush: 1: message1 |
8 | hush: 1: message1 | 9 | hush: 1: message1 |
10 | hush: 1: unset! | ||
11 | hush: 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 | _ |
15 | hush: f: parameter null or not set | 22 | hush: f: parameter null or not set |
16 | hush: f: parameter null or not set | 23 | hush: f: parameter null or not set |
17 | hush: f: message3 | 24 | hush: f: message3 |
18 | hush: f: message3 | 25 | hush: f: message3 |
26 | hush: f: unset! | ||
27 | hush: f: null or unset! | ||
28 | ==== | ||
19 | _ | 29 | _ |
20 | _ | 30 | _ |
21 | hush: f: parameter null or not set | 31 | hush: f: parameter null or not set |
22 | _ | 32 | _ |
23 | hush: f: message4 | 33 | hush: f: message4 |
34 | _ | ||
35 | hush: 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 |
14 | export msg_unset="unset!" | ||
15 | export msg_null_or_unset="null or unset!" | ||
16 | |||
17 | echo ==== | ||
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 | ||
26 | echo ==== | ||
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 | ||
35 | echo ==== | ||
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 | ||
44 | echo ==== | ||
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 | ||
53 | echo ==== | ||
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 @@ | |||
1 | 0 | ||
2 | 0 | ||
3 | 1 | ||
4 | Make sure len parsing doesnt break arg count | ||
1 | 0 0 | 5 | 0 0 |
2 | 4 4 | 6 | 4 4 |
7 | Testing len op | ||
3 | 4 3 2 1 0 0 | 8 | 4 3 2 1 0 0 |
4 | 0 3 0 | 9 | 0 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 | |||
5 | echo Make sure len parsing doesnt break arg count | ||
2 | set -- | 6 | set -- |
3 | echo $# ${#} | 7 | echo $# ${#} |
4 | set -- aaaa bbb cc d | 8 | set -- aaaa bbb cc d |
5 | echo $# ${#} | 9 | echo $# ${#} |
6 | 10 | ||
11 | echo Testing len op | ||
7 | echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6} | 12 | echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6} |
8 | 13 | ||
9 | unset e | 14 | unset 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 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | hush: invalid number '1q' |
2 | hush: syntax error: unterminated ${name} | 2 | hush: 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 | |||
23 | ababcdcd | 23 | ababcdcd |
24 | Empty: | 24 | Empty: |
25 | ababcdcd}_tail | 25 | ababcdcd}_tail |
26 | ababcdcd_tail | ||
26 | ababcd | 27 | ababcd |
27 | ababcd | 28 | ababcd |
28 | ababcd | 29 | ababcd |
@@ -32,5 +33,9 @@ ababcdc | |||
32 | ababcdcd | 33 | ababcdcd |
33 | Empty: | 34 | Empty: |
34 | ababcdcd}_tail | 35 | ababcdcd}_tail |
36 | ababcdcd_tail | ||
35 | ababcdcd | 37 | ababcdcd |
36 | end | 38 | ab |
39 | ab | ||
40 | ab | ||
41 | End | ||
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##?} | |||
31 | echo ${var#*} | 31 | echo ${var#*} |
32 | echo Empty:${var##*} | 32 | echo Empty:${var##*} |
33 | echo ${var#}}_tail | 33 | echo ${var#}}_tail |
34 | # UNFIXED BUG: echo ${var#\}}_tail | 34 | echo ${var#\}}_tail |
35 | 35 | ||
36 | echo ${var%cd} | 36 | echo ${var%cd} |
37 | echo ${var%%cd} | 37 | echo ${var%%cd} |
@@ -42,7 +42,11 @@ echo ${var%%?} | |||
42 | echo ${var%*} | 42 | echo ${var%*} |
43 | echo Empty:${var%%*} | 43 | echo Empty:${var%%*} |
44 | echo ${var#}}_tail | 44 | echo ${var#}}_tail |
45 | # UNFIXED BUG: echo ${var#\}}_tail | 45 | echo ${var#\}}_tail |
46 | echo ${var%\\*} | 46 | echo ${var%\\*} |
47 | 47 | ||
48 | echo end | 48 | a=ab}; echo ${a%\}}; |
49 | a=abc; c=c; echo ${a%${c}} | ||
50 | a=ab{{c; echo ${a%`echo {{c`} | ||
51 | |||
52 | echo 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 | ||
12 | int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 12 | int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
13 | int losetup_main(int argc, char **argv) | 13 | int 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 | }; |
752 | static 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 | ||
756 | typedef uint8_t nfs_err_type; | ||
757 | #else | ||
758 | typedef uint16_t nfs_err_type; | ||
759 | #endif | ||
760 | static 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 |