From 92510141e24251a1d72fbdeef4e2ed2b2b25b433 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 19 May 2010 00:39:17 +0200 Subject: losetup: support /dev/loop10 and higher. closes bug 1627 function old new delta query_loop 91 95 +4 losetup_main 288 285 -3 Signed-off-by: Denys Vlasenko --- libbb/loop.c | 2 +- util-linux/losetup.c | 59 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 35 insertions(+), 26 deletions(-) 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) fd = open(device, O_RDONLY); if (fd >= 0) { if (ioctl(fd, BB_LOOP_GET_STATUS, &loopinfo) == 0) { - dev = xasprintf("%lu %s", (long) loopinfo.lo_offset, + dev = xasprintf("%"OFF_FMT"u %s", (off_t) loopinfo.lo_offset, (char *)loopinfo.lo_file_name); } close(fd); 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 @@ #include "libbb.h" int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int losetup_main(int argc, char **argv) +int losetup_main(int argc UNUSED_PARAM, char **argv) { - char dev[] = LOOP_NAME"0"; unsigned opt; + int n; char *opt_o; - char *s; unsigned long long offset = 0; + enum { + OPT_d = (1 << 0), + OPT_o = (1 << 1), + OPT_f = (1 << 2), + }; /* max 2 args, all opts are mutually exclusive */ opt_complementary = "?2:d--of:o--df:f-do"; opt = getopt32(argv, "do:f", &opt_o); - argc -= optind; argv += optind; - if (opt == 0x2) // -o + if (opt == OPT_o) offset = xatoull(opt_o); - if (opt == 0x4 && argc) // -f does not take any argument - bb_show_usage(); - - if (opt == 0x1) { // -d - /* detach takes exactly one argument */ - if (argc != 1) + if (opt == OPT_d) { + /* -d BLOCKDEV */ + if (!argv[0] || argv[1]) bb_show_usage(); if (del_loop(argv[0])) bb_simple_perror_msg_and_die(argv[0]); return EXIT_SUCCESS; } - if (argc == 2) { - /* -o or no option */ - if (set_loop(&argv[0], argv[1], offset) < 0) - bb_simple_perror_msg_and_die(argv[0]); - return EXIT_SUCCESS; - } + if (argv[0]) { + char *s; + + if (opt == OPT_f) /* -f should not have arguments */ + bb_show_usage(); - if (argc == 1) { - /* -o or no option */ + if (argv[1]) { + /* [-o OFS] BLOCKDEV FILE */ + if (set_loop(&argv[0], argv[1], offset) < 0) + bb_simple_perror_msg_and_die(argv[0]); + return EXIT_SUCCESS; + } + /* [-o OFS] BLOCKDEV */ s = query_loop(argv[0]); if (!s) bb_simple_perror_msg_and_die(argv[0]); @@ -57,23 +61,28 @@ int losetup_main(int argc, char **argv) return EXIT_SUCCESS; } - /* -o, -f or no option */ + /* [-o OFS|-f] with no params */ + n = 0; while (1) { + char *s; + char dev[sizeof(LOOP_NAME) + sizeof(int)*3]; + + sprintf(dev, LOOP_NAME"%u", n); s = query_loop(dev); + n++; if (!s) { - if (opt == 0x4) { + if (n > 9 && errno && errno != ENXIO) + return EXIT_SUCCESS; + if (opt == OPT_f) { puts(dev); return EXIT_SUCCESS; } } else { - if (opt != 0x4) + if (opt != OPT_f) printf("%s: %s\n", dev, s); if (ENABLE_FEATURE_CLEAN_UP) free(s); } - - if (++dev[sizeof(dev) - 2] > '9') - break; } return EXIT_SUCCESS; } -- cgit v1.2.3-55-g6feb From 9f82d0be615540ffc1fe4551750e6cb0bc76f737 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 19 May 2010 01:54:37 +0200 Subject: vi: fix "set ic" command; code shrink while at it. closes bug 1765 function old new delta colon 2970 2848 -122 Signed-off-by: Denys Vlasenko --- editors/vi.c | 86 ++++++++++++++++++++++++++---------------------------------- 1 file changed, 37 insertions(+), 49 deletions(-) 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) // if (!buf[0]) - goto vc1; + goto ret; if (*buf == ':') buf++; // move past the ':' @@ -881,7 +881,7 @@ static void colon(char *buf) // don't edit, if the current file has been modified if (file_modified && !useforce) { status_line_bold("No write since last change (:edit! overrides)"); - goto vc1; + goto ret; } if (args[0]) { // the user supplied a file name @@ -892,11 +892,11 @@ static void colon(char *buf) } else { // no user file name, no current name- punt status_line_bold("No current filename"); - goto vc1; + goto ret; } if (init_text_buffer(fn) < 0) - goto vc1; + goto ret; #if ENABLE_FEATURE_VI_YANKMARK if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) { @@ -921,7 +921,7 @@ static void colon(char *buf) } else if (strncmp(cmd, "file", i) == 0) { // what File is this if (b != -1 || e != -1) { status_line_bold("No address allowed on this command"); - goto vc1; + goto ret; } if (args[0]) { // user wants a new filename @@ -967,11 +967,8 @@ static void colon(char *buf) if (c_is_no_print) standout_end(); } -#if ENABLE_FEATURE_VI_SET - vc2: -#endif Hit_Return(); - } else if (strncmp(cmd, "quit", i) == 0 // Quit + } else if (strncmp(cmd, "quit", i) == 0 // quit || strncmp(cmd, "next", i) == 0 // edit next file ) { int n; @@ -981,30 +978,30 @@ static void colon(char *buf) optind = save_argc; } editing = 0; - goto vc1; + goto ret; } // don't exit if the file been modified if (file_modified) { status_line_bold("No write since last change (:%s! overrides)", (*cmd == 'q' ? "quit" : "next")); - goto vc1; + goto ret; } // are there other file to edit n = save_argc - optind - 1; if (*cmd == 'q' && n > 0) { status_line_bold("%d more file(s) to edit", n); - goto vc1; + goto ret; } if (*cmd == 'n' && n <= 0) { status_line_bold("No more files to edit"); - goto vc1; + goto ret; } editing = 0; } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] fn = args; if (!fn[0]) { status_line_bold("No filename given"); - goto vc1; + goto ret; } if (b < 0) { // no addr given- use defaults q = begin_line(dot); // assume "dot" @@ -1018,7 +1015,7 @@ static void colon(char *buf) q = text + ofs; } if (ch < 0) - goto vc1; // nothing was inserted + goto ret; // nothing was inserted // how many lines in text[]? li = count_lines(q, q + ch - 1); status_line("\"%s\"" @@ -1049,25 +1046,21 @@ static void colon(char *buf) // only blank is regarded as args delmiter. What about tab '\t' ? if (!args[0] || strcasecmp(args, "all") == 0) { // print out values of all options - go_bottom_and_clear_to_eol(); - printf("----------------------------------------\r\n"); #if ENABLE_FEATURE_VI_SETOPTS - if (!autoindent) - printf("no"); - printf("autoindent "); - if (!err_method) - printf("no"); - printf("flash "); - if (!ignorecase) - printf("no"); - printf("ignorecase "); - if (!showmatch) - printf("no"); - printf("showmatch "); - printf("tabstop=%d ", tabstop); -#endif - printf("\r\n"); - goto vc2; + status_line_bold( + "%sautoindent " + "%sflash " + "%signorecase " + "%sshowmatch " + "tabstop=%u", + autoindent ? "" : "no", + err_method ? "" : "no", + ignorecase ? "" : "no", + showmatch ? "" : "no", + tabstop + ); +#endif + goto ret; } #if ENABLE_FEATURE_VI_SETOPTS argp = args; @@ -1075,19 +1068,17 @@ static void colon(char *buf) if (strncmp(argp, "no", 2) == 0) i = 2; // ":set noautoindent" setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT); - setops(argp, "flash ", i, "fl", VI_ERR_METHOD); + setops(argp, "flash " , i, "fl", VI_ERR_METHOD); setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE); - setops(argp, "showmatch ", i, "ic", VI_SHOWMATCH); - /* tabstopXXXX */ - if (strncmp(argp + i, "tabstop=%d ", 7) == 0) { - sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch); - if (ch > 0 && ch <= MAX_TABSTOP) - tabstop = ch; + setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH ); + if (strncmp(argp + i, "tabstop=", 8) == 0) { + int t = 0; + sscanf(argp + i+8, "%u", &t); + if (t > 0 && t <= MAX_TABSTOP) + tabstop = t; } - while (*argp && *argp != ' ') - argp++; // skip to arg delimiter (i.e. blank) - while (*argp && *argp == ' ') - argp++; // skip all delimiting blanks + argp = skip_non_whitespace(argp); + argp = skip_whitespace(argp); } #endif /* FEATURE_VI_SETOPTS */ #endif /* FEATURE_VI_SET */ @@ -1159,7 +1150,7 @@ static void colon(char *buf) #if ENABLE_FEATURE_VI_READONLY if (readonly_mode && !useforce) { status_line_bold("\"%s\" File is read only", fn); - goto vc3; + goto ret; } #endif // how many lines in text[]? @@ -1196,9 +1187,6 @@ static void colon(char *buf) editing = 0; } } -#if ENABLE_FEATURE_VI_READONLY - vc3:; -#endif #if ENABLE_FEATURE_VI_YANKMARK } else if (strncmp(cmd, "yank", i) == 0) { // yank lines if (b < 0) { // no addr given- use defaults @@ -1214,7 +1202,7 @@ static void colon(char *buf) // cmd unknown not_implemented(cmd); } - vc1: + ret: dot = bound_dot(dot); // make sure "dot" is valid return; #if ENABLE_FEATURE_VI_SEARCH -- cgit v1.2.3-55-g6feb From dc1fd2e52aa7d24fd609399e94b78c8c4dd79229 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 19 May 2010 17:01:29 +0200 Subject: mount: handle EDQUOT > 255 properly. closes bug 1579 Signed-off-by: Denys Vlasenko --- util-linux/mount.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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[] = { 19, 20, 21, 22, 27, 28, 30, 63, 66, 69, 70, 71 }; -static const uint8_t nfs_err_errnum[] = { +#if ( \ + EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \ + ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \ + EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256 +typedef uint8_t nfs_err_type; +#else +typedef uint16_t nfs_err_type; +#endif +static const nfs_err_type nfs_err_errnum[] = { EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST, ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC, EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE -- cgit v1.2.3-55-g6feb From 2e068e725ce693afe5e89e10feaa9f24bef5f789 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 19 May 2010 17:15:54 +0200 Subject: setkeycodes: fix handling of 0exx scancodes function old new delta setkeycodes_main 143 130 -13 Signed-off-by: Denys Vlasenko --- console-tools/setkeycodes.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 { int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int setkeycodes_main(int argc, char **argv) { - int fd, sc; + int fd; struct kbkeycode a; if (!(argc & 1) /* if even */ || argc < 2) { @@ -30,17 +30,17 @@ int setkeycodes_main(int argc, char **argv) fd = get_console_fd_or_die(); - while (argc > 2) { - a.keycode = xatou_range(argv[2], 0, 127); - a.scancode = sc = xstrtoul_range(argv[1], 16, 0, 255); - if (a.scancode > 127) { - a.scancode -= 0xe000; - a.scancode += 128; + while (argv[1]) { + int sc = xstrtoul_range(argv[1], 16, 0, 0xe07f); + if (sc >= 0xe000) { + sc -= 0xe000; + sc += 0x0080; } + a.scancode = sc; + a.keycode = xatou_range(argv[2], 0, 255); ioctl_or_perror_and_die(fd, KDSETKEYCODE, &a, "can't set SCANCODE %x to KEYCODE %d", sc, a.keycode); - argc -= 2; argv += 2; } return EXIT_SUCCESS; -- cgit v1.2.3-55-g6feb From 40477e2fdb3d32f4d368ee4f7c72ded4a2398082 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 May 2010 02:09:45 +0200 Subject: shell: make it possible to alias one of shells to "bash" function old new delta packed_usage 27047 27054 +7 applet_names 2227 2232 +5 applet_main 1304 1308 +4 applet_nameofs 652 654 +2 applet_install_loc 163 164 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 5/0 up/down: 19/0) Total: 19 bytes Signed-off-by: Denys Vlasenko --- include/applets.h | 2 ++ include/usage.h | 2 ++ shell/Config.in | 43 +++++++++++++++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 10 deletions(-) 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)) IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP)) IF_AWK(APPLET_NOEXEC(awk, awk, _BB_DIR_USR_BIN, _BB_SUID_DROP, awk)) IF_BASENAME(APPLET_NOFORK(basename, basename, _BB_DIR_USR_BIN, _BB_SUID_DROP, basename)) +IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash)) +IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash)) IF_BBCONFIG(APPLET(bbconfig, _BB_DIR_BIN, _BB_SUID_DROP)) //IF_BBSH(APPLET(bbsh, _BB_DIR_BIN, _BB_SUID_DROP)) IF_BEEP(APPLET(beep, _BB_DIR_USR_BIN, _BB_SUID_DROP)) diff --git a/include/usage.h b/include/usage.h index 3fce939bb..a295ab865 100644 --- a/include/usage.h +++ b/include/usage.h @@ -123,6 +123,8 @@ #define lash_full_usage "" #define msh_trivial_usage NOUSAGE_STR #define msh_full_usage "" +#define bash_trivial_usage NOUSAGE_STR +#define bash_full_usage "" #define awk_trivial_usage \ "[OPTIONS] [AWK_PROGRAM] [FILE]..." 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 @@ menu "Shells" choice - prompt "Choose your default shell" + prompt "Choose which shell is aliased to 'sh' name" default FEATURE_SH_IS_NONE help - Choose a shell. The ash shell is the most bash compatible - and full featured one. + Choose which shell you want to be executed by 'sh' alias. + The ash shell is the most bash compatible and full featured one. config FEATURE_SH_IS_ASH select ASH @@ -21,15 +21,38 @@ config FEATURE_SH_IS_HUSH select HUSH bool "hush" -####config FEATURE_SH_IS_LASH -#### select LASH -#### bool "lash" +config FEATURE_SH_IS_NONE + bool "none" -####config FEATURE_SH_IS_MSH -#### select MSH -#### bool "msh" +endchoice -config FEATURE_SH_IS_NONE +choice + prompt "Choose which shell is aliased to 'bash' name" + default FEATURE_BASH_IS_NONE + help + Choose which shell you want to be executed by 'bash' alias. + The ash shell is the most bash compatible and full featured one. + + Note that selecting this option does not switch on any bash + compatibility code. It merely makes it possible to install + /bin/bash (sym)link and run scripts which start with + #!/bin/bash line. + + Many systems use it in scripts which use bash-specific features, + even simple ones like $RANDOM. Without this option, busybox + can't be used for running them because it won't recongnize + "bash" as a supported applet name. + +config FEATURE_BASH_IS_ASH + select ASH + bool "ash" + depends on !NOMMU + +config FEATURE_BASH_IS_HUSH + select HUSH + bool "hush" + +config FEATURE_BASH_IS_NONE bool "none" endchoice -- cgit v1.2.3-55-g6feb From b131ccec9c917efd735a353cb0f2cb14862192f1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 May 2010 03:39:43 +0200 Subject: hush: support "cd -- DIR" and such function old new delta skip_dash_dash - 33 +33 builtin_exit 43 48 +5 builtin_umask 121 125 +4 builtin_shift 115 119 +4 builtin_cd 71 75 +4 builtin_wait 271 274 +3 builtin_source 171 174 +3 builtin_exec 57 60 +3 builtin_eval 46 45 -1 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 7/1 up/down: 59/-1) Total: 58 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 8baccf246..fcfbd06f3 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7258,11 +7258,20 @@ static int FAST_FUNC builtin_printf(char **argv) } #endif +static char **skip_dash_dash(char **argv) +{ + argv++; + if (argv[0] && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0') + argv++; + return argv; +} + static int FAST_FUNC builtin_eval(char **argv) { int rcode = EXIT_SUCCESS; - if (*++argv) { + argv = skip_dash_dash(argv); + if (*argv) { char *str = expand_strvec_to_string(argv); /* bash: * eval "echo Hi; done" ("done" is syntax error): @@ -7277,7 +7286,10 @@ static int FAST_FUNC builtin_eval(char **argv) static int FAST_FUNC builtin_cd(char **argv) { - const char *newdir = argv[1]; + const char *newdir; + + argv = skip_dash_dash(argv); + newdir = argv[0]; if (newdir == NULL) { /* bash does nothing (exitcode 0) if HOME is ""; if it's unset, * bash says "bash: cd: HOME not set" and does nothing @@ -7301,7 +7313,8 @@ static int FAST_FUNC builtin_cd(char **argv) static int FAST_FUNC builtin_exec(char **argv) { - if (*++argv == NULL) + argv = skip_dash_dash(argv); + if (argv[0] == NULL) return EXIT_SUCCESS; /* bash does this */ /* Careful: we can end up here after [v]fork. Do not restore @@ -7334,12 +7347,13 @@ static int FAST_FUNC builtin_exit(char **argv) */ /* note: EXIT trap is run by hush_exit */ - if (*++argv == NULL) + argv = skip_dash_dash(argv); + if (argv[0] == NULL) hush_exit(G.last_exitcode); /* mimic bash: exit 123abc == exit 255 + error msg */ xfunc_error_retval = 255; /* bash: exit -2 == exit 254, no error msg */ - hush_exit(xatoi(*argv) & 0xff); + hush_exit(xatoi(argv[0]) & 0xff); } static void print_escaped(const char *s) @@ -7668,7 +7682,7 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) "------------------\n"); for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) { if (x->b_descr) - printf("%s\t%s\n", x->b_cmd, x->b_descr); + printf("%-10s%s\n", x->b_cmd, x->b_descr); } bb_putchar('\n'); return EXIT_SUCCESS; @@ -7851,8 +7865,9 @@ static int FAST_FUNC builtin_set(char **argv) static int FAST_FUNC builtin_shift(char **argv) { int n = 1; - if (argv[1]) { - n = atoi(argv[1]); + argv = skip_dash_dash(argv); + if (argv[0]) { + n = atoi(argv[0]); } if (n >= 0 && n < G.global_argc) { if (G.global_args_malloced) { @@ -7877,12 +7892,13 @@ static int FAST_FUNC builtin_source(char **argv) smallint sv_flg; #endif - arg_path = NULL; - filename = *++argv; + argv = skip_dash_dash(argv); + filename = argv[0]; if (!filename) { /* bash says: "bash: .: filename argument required" */ return 2; /* bash compat */ } + arg_path = NULL; if (!strchr(filename, '/')) { arg_path = find_in_path(filename); if (arg_path) @@ -7920,11 +7936,12 @@ static int FAST_FUNC builtin_umask(char **argv) mode_t mask; mask = umask(0); - if (argv[1]) { + argv = skip_dash_dash(argv); + if (argv[0]) { mode_t old_mask = mask; mask ^= 0777; - rc = bb_parse_mode(argv[1], &mask); + rc = bb_parse_mode(argv[0], &mask); mask ^= 0777; if (rc == 0) { mask = old_mask; @@ -7932,7 +7949,7 @@ static int FAST_FUNC builtin_umask(char **argv) * bash: umask: 'q': invalid symbolic mode operator * bash: umask: 999: octal number out of range */ - bb_error_msg("%s: '%s' invalid mode", argv[0], argv[1]); + bb_error_msg("umask: '%s' invalid mode", argv[0]); } } else { rc = 1; @@ -7988,7 +8005,8 @@ static int FAST_FUNC builtin_wait(char **argv) int ret = EXIT_SUCCESS; int status, sig; - if (*++argv == NULL) { + argv = skip_dash_dash(argv); + if (argv[0] == NULL) { /* Don't care about wait results */ /* Note 1: must wait until there are no more children */ /* Note 2: must be interruptible */ -- cgit v1.2.3-55-g6feb From 44c86ce5d7ee7c641a2c8c392f059e19050abd11 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 May 2010 04:22:55 +0200 Subject: hush: code shrink text data bss dec hex filename 843121 453 6828 850402 cf9e2 busybox_old 843108 453 6828 850389 cf9d5 busybox_unstripped Signed-off-by: Denys Vlasenko --- shell/hush.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/hush.c b/shell/hush.c index fcfbd06f3..df81eaae7 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7949,7 +7949,7 @@ static int FAST_FUNC builtin_umask(char **argv) * bash: umask: 'q': invalid symbolic mode operator * bash: umask: 999: octal number out of range */ - bb_error_msg("umask: '%s' invalid mode", argv[0]); + bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]); } } else { rc = 1; -- cgit v1.2.3-55-g6feb From a0ec4f500c7b8b2ac1c7e34c9a2ee7504c7f8914 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 May 2010 12:50:42 +0200 Subject: ash: eliminate 16 bytes in bss text data bss dec hexfilename 841423 441 7572 849436 cf61cbusybox_old 841430 441 7556 849427 cf613busybox_unstripped Signed-off-by: Denys Vlasenko --- shell/ash.c | 48 ++++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 83886c610..7efd477d1 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -7547,8 +7547,10 @@ changepath(const char *new) if (*old != *new) { firstchange = idx; if ((*old == '\0' && *new == ':') - || (*old == ':' && *new == '\0')) + || (*old == ':' && *new == '\0') + ) { firstchange++; + } old = new; /* ignore subsequent differences */ } if (*new == '\0') @@ -7557,7 +7559,8 @@ changepath(const char *new) idx_bltin = idx; if (*new == ':') idx++; - new++, old++; + new++; + old++; } if (builtinloc < 0 && idx_bltin >= 0) builtinloc = idx_bltin; /* zap builtins */ @@ -7633,23 +7636,6 @@ static const char *const tokname_array[] = { "\1}", }; -static const char * -tokname(int tok) -{ - static char buf[16]; - -//try this: -//if (tok < TSEMI) return tokname_array[tok] + 1; -//sprintf(buf, "\"%s\"", tokname_array[tok] + 1); -//return buf; - - if (tok >= TSEMI) - buf[0] = '"'; - sprintf(buf + (tok >= TSEMI), "%s%c", - tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0)); - return buf; -} - /* Wrapper around strcmp for qsort/bsearch/... */ static int pstrcmp(const void *a, const void *b) @@ -10280,7 +10266,16 @@ static struct nodelist *backquotelist; static union node *redirnode; static struct heredoc *heredoc; -/* +static const char * +tokname(char *buf, int tok) +{ + if (tok < TSEMI) + return tokname_array[tok] + 1; + sprintf(buf, "\"%s\"", tokname_array[tok] + 1); + return buf; +} + +/* raise_error_unexpected_syntax: * Called when an unexpected token is read during the parse. The argument * is the token that is expected, or -1 if more than one type of token can * occur at this point. @@ -10290,11 +10285,12 @@ static void raise_error_unexpected_syntax(int token) { char msg[64]; + char buf[16]; int l; - l = sprintf(msg, "unexpected %s", tokname(lasttoken)); + l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken)); if (token >= 0) - sprintf(msg + l, " (expecting %s)", tokname(token)); + sprintf(msg + l, " (expecting %s)", tokname(buf, token)); raise_error_syntax(msg); /* NOTREACHED */ } @@ -10682,7 +10678,7 @@ parse_command(void) n1->nbinary.ch1 = list(0); got = readtoken(); if (got != TDO) { - TRACE(("expecting DO got %s %s\n", tokname(got), + TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1, got == TWORD ? wordtext : "")); raise_error_unexpected_syntax(TDO); } @@ -11766,7 +11762,7 @@ readtoken(void) pp = findkwd(wordtext); if (pp) { lasttoken = t = pp - tokname_array; - TRACE(("keyword %s recognized\n", tokname(t))); + TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1)); goto out; } } @@ -11787,9 +11783,9 @@ readtoken(void) checkkwd = 0; #if DEBUG if (!alreadyseen) - TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); + TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); else - TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); + TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); #endif return t; } -- cgit v1.2.3-55-g6feb From 131ed3bcc9c9eabcb4bd6a063c24c6f9922f1491 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 May 2010 12:56:14 +0200 Subject: update shell/README Signed-off-by: Denys Vlasenko --- shell/README | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 It says that shell must implement special built-ins. Special built-ins differ from regular ones by the fact that variable assignments -done on special builtin is *PRESERVED*. That is, +done on special builtin are *PRESERVED*. That is, VAR=VAL special_builtin; echo $VAR @@ -43,7 +43,7 @@ should print VAL. (Another distinction is that an error in special built-in should abort the shell, but this is not such a critical difference, and moreover, at least bash's "set" does not follow this rule, -which is even codified in autoconf now...). +which is even codified in autoconf configure logic now...) List of special builtins: @@ -73,7 +73,7 @@ unset [-fv] name... In practice, no one uses this obscure feature - none of these builtins gives any special reasons to play such dirty tricks. -However. This section says that *function invocation* should act +However. This section also says that *function invocation* should act similar to special built-in. That is, variable assignments done on function invocation should be preserved after function invocation. -- cgit v1.2.3-55-g6feb From cddbb610cb0ea8d74668653aeaded710d2d13768 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 May 2010 14:27:09 +0200 Subject: hush: fix var=`exit 2` not setting $? to 2 Signed-off-by: Denys Vlasenko --- shell/hush.c | 15 ++++++++++----- shell/hush_test/hush-psubst/falsetick.right | 27 +++++++++++++++++++++++++++ shell/hush_test/hush-psubst/falsetick.tests | 22 ++++++++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 shell/hush_test/hush-psubst/falsetick.right create mode 100755 shell/hush_test/hush-psubst/falsetick.tests diff --git a/shell/hush.c b/shell/hush.c index df81eaae7..8da9439c1 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2523,7 +2523,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char * and $IFS-splitted */ debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); G.last_exitcode = process_command_subs(&subst_result, arg); - debug_printf_subst("SUBST RES '%s'\n", subst_result.data); + debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); val = subst_result.data; goto store_val; #endif @@ -4117,9 +4117,9 @@ static NOINLINE int run_pipe(struct pipe *pi) if (argv[command->assignment_cnt] == NULL) { /* Assignments, but no command */ - /* Ensure redirects take effect. Try "a=t >file" */ + /* Ensure redirects take effect (that is, create files). + * Try "a=t >file": */ rcode = setup_redirects(command, squirrel); -//FIXME: "false; q=`false`; echo $?" should print 1 restore_redirects(squirrel); /* Set shell variables */ while (*argv) { @@ -4129,6 +4129,11 @@ static NOINLINE int run_pipe(struct pipe *pi) set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); argv++; } + /* Redirect error sets $? to 1. Othervise, + * if evaluating assignment value set $?, retain it. + * Try "false; q=`exit 2`; echo $?" - should print 2: */ + if (rcode == 0) + rcode = G.last_exitcode; /* Do we need to flag set_local_var() errors? * "assignment to readonly var" and "putenv error" */ @@ -4186,7 +4191,7 @@ static NOINLINE int run_pipe(struct pipe *pi) old_vars = set_vars_and_save_old(new_env); if (!funcp) { debug_printf_exec(": builtin '%s' '%s'...\n", - x->cmd, argv_expanded[1]); + x->b_cmd, argv_expanded[1]); rcode = x->b_function(argv_expanded) & 0xff; fflush_all(); } @@ -6814,7 +6819,7 @@ int hush_main(int argc, char **argv) struct variable *cur_var; INIT_G(); - if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, is already done */ + if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ G.last_exitcode = EXIT_SUCCESS; #if !BB_MMU G.argv0_for_re_execing = argv[0]; 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 @@ +0 +0 +0 +0 +2 +2 +2 +2 +hush: can't open '/does/not/exist': No such file or directory +1 +hush: can't open '/does/not/exist': No such file or directory +1 +hush: can't open '/does/not/exist': No such file or directory +1 +hush: can't open '/does/not/exist': No such file or directory +1 +hush: can't open '/does/not/exist': No such file or directory +1 +hush: can't open '/does/not/exist': No such file or directory +1 +hush: can't open '/does/not/exist': No such file or directory +1 +hush: can't open '/does/not/exist': No such file or directory +1 +hush: can't open '/does/not/exist': No such file or directory +1 +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 @@ +# Exitcode 0 (`` has no exitcode, but assignment has): +true; a=``; echo $? +false; a=``; echo $? +true; a=$(); echo $? +false; a=$(); echo $? +# Exitcode 2 (`cmd` expansion sets exitcode after assignment set it to 0): +true; a=`exit 2`; echo $? +false; a=`exit 2`; echo $? +true; a=$(exit 2); echo $? +false; a=$(exit 2); echo $? +# Exitcode 1 (redirect sets exitcode to 1 on error after them): +true; a=`` >/does/not/exist; echo $? +false; a=`` >/does/not/exist; echo $? +true; a=$() >/does/not/exist; echo $? +false; a=$() >/does/not/exist; echo $? +true; a=`exit 2` >/does/not/exist; echo $? +false; a=`exit 2` >/does/not/exist; echo $? +true; a=$(exit 2) >/does/not/exist; echo $? +false; a=$(exit 2) >/does/not/exist; echo $? +# ...and assignment still happens despite redirect error: +true; a=$(echo b) >/does/not/exist; echo $? +echo "Done: a=$a" -- cgit v1.2.3-55-g6feb From e3be7842be3ccac389efd2ac51b18773c58852c5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 May 2010 16:27:42 +0200 Subject: hush: shrink variable expansion code function old new delta expand_vars_to_list 2164 2012 -152 Signed-off-by: Denys Vlasenko --- shell/hush.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 8da9439c1..824a5b52e 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2568,35 +2568,33 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char #endif default: /* varname */ case_default: { - bool exp_len = false; - bool exp_null = false; char *var = arg; + bool exp_len; + char exp_op; char exp_save = exp_save; /* for compiler */ - char exp_op = exp_op; /* for compiler */ + char *exp_saveptr = exp_saveptr; /* points to expansion operator */ char *exp_word = exp_word; /* for compiler */ - size_t exp_off = 0; *p = '\0'; arg[0] = first_ch & 0x7f; /* prepare for expansions */ + exp_len = false; + exp_op = 0; if (var[0] == '#') { /* handle length expansion ${#var} */ exp_len = true; ++var; } else { /* maybe handle parameter expansion */ - exp_off = strcspn(var, ":-=+?%#"); - if (!var[exp_off]) - exp_off = 0; - if (exp_off) { - exp_save = var[exp_off]; - exp_null = exp_save == ':'; - exp_word = var + exp_off; - if (exp_null) - ++exp_word; + exp_saveptr = var + strcspn(var, ":-=+?%#"); + exp_save = *exp_saveptr; + if (exp_save) { + exp_word = exp_saveptr; + if (exp_save == ':') + exp_word++; exp_op = *exp_word++; - var[exp_off] = '\0'; + *exp_saveptr = '\0'; } } @@ -2615,7 +2613,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char debug_printf_expand("expand: length of '%s' = ", val); val = utoa(val ? strlen(val) : 0); debug_printf_expand("%s\n", val); - } else if (exp_off) { + } else if (exp_op) { if (exp_op == '%' || exp_op == '#') { if (val) { /* we need to do a pattern match */ @@ -2623,7 +2621,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char char *loc; scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left); if (exp_op == *exp_word) /* ## or %% */ - ++exp_word; + exp_word++; val = dyn_val = xstrdup(val); loc = scan(dyn_val, exp_word, match_at_left); if (match_at_left) /* # or ## */ @@ -2631,13 +2629,14 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char else if (loc) /* % or %% and match was found */ *loc = '\0'; } - } else { + } else { /* one of :-=+? */ +//TODO: handle ${VAR:N[:M]} here. N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc /* we need to do an expansion */ - int exp_test = (!val || (exp_null && !val[0])); + int exp_test = (!val || ((exp_save == ':') && !val[0])); if (exp_op == '+') exp_test = !exp_test; debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, - exp_null ? "true" : "false", exp_test); + (exp_save == ':') ? "true" : "false", exp_test); if (exp_test) { if (exp_op == '?') { //TODO: how interactive bash aborts expansion mid-command? @@ -2666,7 +2665,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char } } - var[exp_off] = exp_save; + *exp_saveptr = exp_save; } arg[0] = first_ch; -- cgit v1.2.3-55-g6feb From ee0775dd13a84316f6fd49c930237e6dec241dba Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 May 2010 16:37:53 +0200 Subject: hush: small code shrink function old new delta expand_vars_to_list 2012 1999 -13 Signed-off-by: Denys Vlasenko --- shell/hush.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 824a5b52e..d5cea07a1 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2569,7 +2569,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char default: /* varname */ case_default: { char *var = arg; - bool exp_len; + char exp_len; /* '#' if it's ${#var} */ char exp_op; char exp_save = exp_save; /* for compiler */ char *exp_saveptr = exp_saveptr; /* points to expansion operator */ @@ -2579,12 +2579,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char arg[0] = first_ch & 0x7f; /* prepare for expansions */ - exp_len = false; exp_op = 0; - if (var[0] == '#') { + exp_len = var[0]; + if (exp_len == '#') { /* handle length expansion ${#var} */ - exp_len = true; - ++var; + var++; } else { /* maybe handle parameter expansion */ exp_saveptr = var + strcspn(var, ":-=+?%#"); @@ -2609,8 +2608,8 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char val = get_local_var_value(var); /* handle any expansions */ - if (exp_len) { - debug_printf_expand("expand: length of '%s' = ", val); + if (exp_len == '#') { + debug_printf_expand("expand: length(%s)=", val); val = utoa(val ? strlen(val) : 0); debug_printf_expand("%s\n", val); } else if (exp_op) { -- cgit v1.2.3-55-g6feb From 53b513331add89e83c1327579f3da91ebbe97570 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 May 2010 21:46:45 +0200 Subject: hush: explain various parameter expansion ops in comments Signed-off-by: Denys Vlasenko --- shell/hush.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index d5cea07a1..06e9af21a 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2614,8 +2614,16 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char debug_printf_expand("%s\n", val); } else if (exp_op) { if (exp_op == '%' || exp_op == '#') { + /* Standard-mandated substring removal ops: + * ${parameter%word} - remove smallest suffix pattern + * ${parameter%%word} - remove largest suffix pattern + * ${parameter#word} - remove smallest prefix pattern + * ${parameter##word} - remove largest prefix pattern + * + * Word is expanded to produce a glob pattern. + * Then var's value is matched to it and matching part removed. + */ if (val) { - /* we need to do a pattern match */ bool match_at_left; char *loc; scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left); @@ -2629,18 +2637,38 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char *loc = '\0'; } } else { /* one of :-=+? */ +//TODO: check validity of exp_op. Currently it can be anything. //TODO: handle ${VAR:N[:M]} here. N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc - /* we need to do an expansion */ - int exp_test = (!val || ((exp_save == ':') && !val[0])); + /* Standard-mandated substitution ops: + * ${var?word} - indicate error if unset + * If var is unset, word (or a message indicating it is unset + * if word is null) is written to standard error + * and the shell exits with a non-zero exit status. + * Otherwise, the value of var is substituted. + * ${var-word} - use default value + * If var is unset, word is substituted. + * ${var=word} - assign and use default value + * If var is unset, word is assigned to var. + * In all cases, final value of var is substituted. + * ${var+word} - use alternative value + * If var is unset, null is substituted. + * Otherwise, word is substituted. + * + * Word is subjected to tilde expansion, parameter expansion, + * command substitution, and arithmetic expansion. + * If word is not needed, it is not expanded. + * + * Colon forms (${var:-word}, ${var:=word} etc) do the same, + * but also treat null var as if it is unset. + */ + int use_word = (!val || ((exp_save == ':') && !val[0])); if (exp_op == '+') - exp_test = !exp_test; + use_word = !use_word; debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, - (exp_save == ':') ? "true" : "false", exp_test); - if (exp_test) { + (exp_save == ':') ? "true" : "false", use_word); + if (use_word) { if (exp_op == '?') { //TODO: how interactive bash aborts expansion mid-command? - /* ${var?[error_msg_if_unset]} */ - /* ${var:?[error_msg_if_unset_or_null]} */ /* mimic bash message */ die_if_script("%s: %s", var, -- cgit v1.2.3-55-g6feb From 4d8e5fdc1d4bb14ebfecdf6ce19e15024103659f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 21 May 2010 01:15:42 +0200 Subject: hush: optional support for ${var:N:M} bashism function old new delta expand_vars_to_list 1999 2183 +184 handle_dollar 682 623 -59 Signed-off-by: Denys Vlasenko --- shell/hush.c | 84 ++++++++++++---------- .../hush-vars/param_expand_bash_substring.right | 29 ++++++++ .../hush-vars/param_expand_bash_substring.tests | 46 ++++++++++++ 3 files changed, 120 insertions(+), 39 deletions(-) create mode 100644 shell/hush_test/hush-vars/param_expand_bash_substring.right create mode 100755 shell/hush_test/hush-vars/param_expand_bash_substring.tests diff --git a/shell/hush.c b/shell/hush.c index 06e9af21a..0333395f8 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -46,7 +46,6 @@ * brace expansion: one/{two,three,four} * reserved words: function select * advanced test: [[ ]] - * substrings: ${var:1:5} * process substitution: <(list) and >(list) * =~: regex operator * let EXPR [EXPR...] @@ -2586,7 +2585,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char var++; } else { /* maybe handle parameter expansion */ - exp_saveptr = var + strcspn(var, ":-=+?%#"); + exp_saveptr = var + strcspn(var, "%#:-=+?"); exp_save = *exp_saveptr; if (exp_save) { exp_word = exp_saveptr; @@ -2636,9 +2635,38 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char else if (loc) /* % or %% and match was found */ *loc = '\0'; } - } else { /* one of :-=+? */ -//TODO: check validity of exp_op. Currently it can be anything. -//TODO: handle ${VAR:N[:M]} here. N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc + } else if (!strchr("%#:-=+?"+3, exp_op)) { +#if ENABLE_HUSH_BASH_COMPAT + /* exp_op is ':' and next char isn't a subst operator. + * Assuming it's ${var:[N][:M]} bashism. + * TODO: N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc + */ + char *end; + unsigned len = INT_MAX; + unsigned beg = 0; + end = --exp_word; + if (*exp_word != ':') /* not ${var::...} */ + beg = bb_strtou(exp_word, &end, 0); + //bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end); + if (*end == ':') { + len = bb_strtou(end + 1, &end, 0); + //bb_error_msg("len:%u end:'%s'", len, end); + } + if (*end == '\0' + && end != exp_word /* not "${var:}" */ + ) { + //bb_error_msg("from val:'%s'", val); + if (!val || beg >= strlen(val)) + val = ""; + else + val = dyn_val = xstrndup(val + beg, len); + //bb_error_msg("val:'%s'", val); + } else +#endif + { + die_if_script("malformed ${%s...}", var); + } + } else { /* one of "-=+?" */ /* Standard-mandated substitution ops: * ${var?word} - indicate error if unset * If var is unset, word (or a message indicating it is unset @@ -2693,7 +2721,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char } *exp_saveptr = exp_save; - } + } /* if (exp_op) */ arg[0] = first_ch; #if ENABLE_HUSH_TICK @@ -5926,14 +5954,15 @@ static int handle_dollar(o_string *as_string, goto make_one_char_var; case '{': { bool first_char, all_digits; - int expansion; + bool in_expansion_param; ch = i_getch(input); nommu_addchr(as_string, ch); o_addchr(dest, SPECIAL_VAR_SYMBOL); - /* TODO: maybe someone will try to escape the '}' */ - expansion = 0; +// TODO: need to handle "a=ab}; echo ${a%\}}" +// and "a=abc; c=c; echo ${a%${c}}" + in_expansion_param = false; first_char = true; all_digits = false; while (1) { @@ -5957,45 +5986,22 @@ static int handle_dollar(o_string *as_string, goto char_ok; } - if (expansion < 2 - && ( (all_digits && !isdigit(ch)) - || (!all_digits && !isalnum(ch) && ch != '_') + if (!in_expansion_param + && ( (all_digits && !isdigit(ch)) /* met non-digit: 123w */ + || (!all_digits && !isalnum(ch) && ch != '_') /* met non-name char: abc% */ ) ) { /* handle parameter expansions * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 */ - if (first_char) - goto case_default; - switch (ch) { - case ':': /* null modifier */ - if (expansion == 0) { - debug_printf_parse(": null modifier\n"); - ++expansion; - break; - } - goto case_default; - case '#': /* remove prefix */ - case '%': /* remove suffix */ - if (expansion == 0) { - debug_printf_parse(": remove suffix/prefix\n"); - expansion = 2; - break; - } - goto case_default; - case '-': /* default value */ - case '=': /* assign default */ - case '+': /* alternative */ - case '?': /* error indicate */ - debug_printf_parse(": parameter expansion\n"); - expansion = 2; - break; - default: - case_default: + if (first_char /* bad (empty var name): "${%..." */ + || !strchr("%#:-=+?", ch) /* bad: "${var..." */ + ) { syntax_error_unterm_str("${name}"); debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); return 1; } + in_expansion_param = true; } char_ok: debug_printf_parse(": '%c'\n", ch); 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..9cd465938 --- /dev/null +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right @@ -0,0 +1,29 @@ +hush: syntax error: unterminated ${name} +hush: syntax error: unterminated ${name} +hush: syntax error: unterminated ${name} +hush: syntax error: unterminated ${name} +0123456789 +1 =|| +1:1 =|| +1:1:2=|| +1::2 =|| +1 =|0123| +1:1 =|123| +1:1:2=|12| +1::2 =|01| +f =|| +f:1 =|| +f:1:2=|| +f::2 =|| +f =|| +f:1 =|| +f:1:2=|| +f::2 =|| +f =|a| +f:1 =|| +f:1:2=|| +f::2 =|a| +f =|0123456789| +f:1 =|123456789| +f:1:2=|12| +f::2 =|01| 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..6a1765559 --- /dev/null +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests @@ -0,0 +1,46 @@ +# do all of these in subshells since it's supposed to error out + +export var=0123456789 + +# first try some invalid patterns +"$THIS_SH" -c 'echo ${:}' +"$THIS_SH" -c 'echo ${::}' +"$THIS_SH" -c 'echo ${:1}' +"$THIS_SH" -c 'echo ${::1}' + +#this also is not valid in bash, but we accept it: +"$THIS_SH" -c 'echo ${var:}' + +# then some funky ones +# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' + +# now some valid ones +"$THIS_SH" -c 'set --; echo "1 =|${1}|"' +"$THIS_SH" -c 'set --; echo "1:1 =|${1:1}|"' +"$THIS_SH" -c 'set --; echo "1:1:2=|${1:1:2}|"' +"$THIS_SH" -c 'set --; echo "1::2 =|${1::2}|"' + +"$THIS_SH" -c 'set -- 0123; echo "1 =|${1}|"' +"$THIS_SH" -c 'set -- 0123; echo "1:1 =|${1:1}|"' +"$THIS_SH" -c 'set -- 0123; echo "1:1:2=|${1:1:2}|"' +"$THIS_SH" -c 'set -- 0123; echo "1::2 =|${1::2}|"' + +"$THIS_SH" -c 'unset f; echo "f =|$f|"' +"$THIS_SH" -c 'unset f; echo "f:1 =|${f:1}|"' +"$THIS_SH" -c 'unset f; echo "f:1:2=|${f:1:2}|"' +"$THIS_SH" -c 'unset f; echo "f::2 =|${f::2}|"' + +"$THIS_SH" -c 'f=; echo "f =|$f|"' +"$THIS_SH" -c 'f=; echo "f:1 =|${f:1}|"' +"$THIS_SH" -c 'f=; echo "f:1:2=|${f:1:2}|"' +"$THIS_SH" -c 'f=; echo "f::2 =|${f::2}|"' + +"$THIS_SH" -c 'f=a; echo "f =|$f|"' +"$THIS_SH" -c 'f=a; echo "f:1 =|${f:1}|"' +"$THIS_SH" -c 'f=a; echo "f:1:2=|${f:1:2}|"' +"$THIS_SH" -c 'f=a; echo "f::2 =|${f::2}|"' + +"$THIS_SH" -c 'f=0123456789; echo "f =|$f|"' +"$THIS_SH" -c 'f=0123456789; echo "f:1 =|${f:1}|"' +"$THIS_SH" -c 'f=0123456789; echo "f:1:2=|${f:1:2}|"' +"$THIS_SH" -c 'f=0123456789; echo "f::2 =|${f::2}|"' -- cgit v1.2.3-55-g6feb From 4f2c59b267a4840986c95e22bd302b4216bfb8cf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 21 May 2010 01:25:16 +0200 Subject: hush: remove extra comparison from prev commit Signed-off-by: Denys Vlasenko --- shell/hush.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 0333395f8..945077d87 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2647,14 +2647,12 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char end = --exp_word; if (*exp_word != ':') /* not ${var::...} */ beg = bb_strtou(exp_word, &end, 0); - //bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end); + //bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end); if (*end == ':') { len = bb_strtou(end + 1, &end, 0); //bb_error_msg("len:%u end:'%s'", len, end); } - if (*end == '\0' - && end != exp_word /* not "${var:}" */ - ) { + if (*end == '\0') { //bb_error_msg("from val:'%s'", val); if (!val || beg >= strlen(val)) val = ""; -- cgit v1.2.3-55-g6feb From e91bc53d0c2e8de7dc4fbdb888ab0a4923c2b475 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Fri, 21 May 2010 11:47:45 +0200 Subject: lxdialog: fix ncursesw include detection the lib was checked but the include not, do so. TODO: check if upstream fixed that already or fix it there, too. Signed-off-by: Bernhard Reutner-Fischer --- scripts/kconfig/lxdialog/check-lxdialog.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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() # Where is ncurses.h? ccflags() { - if [ -f /usr/include/ncurses/ncurses.h ]; then + if [ -f /usr/include/ncursesw/ncurses.h ]; then + echo '-I/usr/include/ncursesw -DCURSES_LOC=""' + elif [ -f /usr/include/ncursesw/curses.h ]; then + echo '-I/usr/include/ncursesw -DCURSES_LOC=""' + elif [ -f /usr/include/ncurses/ncurses.h ]; then echo '-I/usr/include/ncurses -DCURSES_LOC=""' elif [ -f /usr/include/ncurses/curses.h ]; then echo '-I/usr/include/ncurses -DCURSES_LOC=""' -- cgit v1.2.3-55-g6feb From a88585a931c7e81d4d3d393127d8ad8c0fe73fb5 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Fri, 21 May 2010 12:11:34 +0200 Subject: trylink: gold may not support --sort-common (yet) Signed-off-by: Bernhard Reutner-Fischer --- scripts/trylink | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/scripts/trylink b/scripts/trylink index 8c8854679..164f5274c 100755 --- a/scripts/trylink +++ b/scripts/trylink @@ -85,6 +85,9 @@ LDLIBS="$7" # The --sort-section option is not supported by older versions of ld SORT_SECTION=`check_cc "-Wl,--sort-section,alignment" ""` +# gold may not support --sort-common (yet) +SORT_COMMON=`check_cc "-Wl,--sort-common" ""` + # Static linking against glibc produces buggy executables # (glibc does not cope well with ld --gc-sections). # 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$//'` test "x$l_list" != "x" && l_list="-Wl,--start-group $l_list -Wl,--end-group" try $CC $CFLAGS $LDFLAGS \ -o $EXE \ - -Wl,--sort-common \ + $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ -Wl,--start-group $O_FILES $A_FILES -Wl,--end-group \ @@ -139,7 +142,7 @@ while test "$LDLIBS"; do $debug && echo "Trying -l options: '$l_list'" try $CC $CFLAGS $LDFLAGS \ -o $EXE \ - -Wl,--sort-common \ + $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ -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" if ! test -f busybox_ldscript; then try $CC $CFLAGS $LDFLAGS \ -o $EXE \ - -Wl,--sort-common \ + $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ -Wl,--start-group $O_FILES $A_FILES -Wl,--end-group \ @@ -193,7 +196,7 @@ else # Hmm, "ld --sort-section alignment" should do it too. try $CC $CFLAGS $LDFLAGS \ -o $EXE \ - -Wl,--sort-common \ + $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ -Wl,-T,busybox_ldscript \ @@ -228,7 +231,7 @@ if test "$CONFIG_BUILD_LIBBUSYBOX" = y; then -Wl,-z,combreloc \ -Wl,-soname="libbusybox.so.$BB_VER" \ -Wl,--undefined=lbb_main \ - -Wl,--sort-common \ + $SORT_COMMON \ $SORT_SECTION \ -Wl,--start-group $A_FILES -Wl,--end-group \ $l_list \ @@ -249,7 +252,7 @@ if test "$CONFIG_FEATURE_SHARED_BUSYBOX" = y; then EXE="$sharedlib_dir/busybox_unstripped" try $CC $CFLAGS $LDFLAGS \ -o $EXE \ - -Wl,--sort-common \ + $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ -Wl,--start-group $O_FILES -Wl,--end-group \ @@ -288,7 +291,7 @@ int main(int argc, char **argv) EXE="$sharedlib_dir/$name" try $CC $CFLAGS $LDFLAGS "$sharedlib_dir/applet.c" \ -o $EXE \ - -Wl,--sort-common \ + $SORT_COMMON \ $SORT_SECTION \ $GC_SECTIONS \ -L"$sharedlib_dir" -lbusybox \ -- cgit v1.2.3-55-g6feb From 73e013fca7afd2edc9ba8530df77c8210a14700b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 21 May 2010 15:24:12 +0200 Subject: hush: handle ${var:NUM:} too Signed-off-by: Denys Vlasenko --- shell/hush.c | 10 ++++++++-- shell/hush_test/hush-vars/param_expand_bash_substring.right | 12 ++++++++++++ shell/hush_test/hush-vars/param_expand_bash_substring.tests | 12 ++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 945077d87..6cf8899b0 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2649,12 +2649,17 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char beg = bb_strtou(exp_word, &end, 0); //bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end); if (*end == ':') { - len = bb_strtou(end + 1, &end, 0); + if (end[1] != '\0') /* not ${var:NUM:} */ + len = bb_strtou(end + 1, &end, 0); + else { + len = 0; + end++; + } //bb_error_msg("len:%u end:'%s'", len, end); } if (*end == '\0') { //bb_error_msg("from val:'%s'", val); - if (!val || beg >= strlen(val)) + if (len == 0 || !val || beg >= strlen(val)) val = ""; else val = dyn_val = xstrndup(val + beg, len); @@ -2663,6 +2668,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char #endif { die_if_script("malformed ${%s...}", var); + val = ""; } } else { /* one of "-=+?" */ /* Standard-mandated substitution ops: diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.right b/shell/hush_test/hush-vars/param_expand_bash_substring.right index 9cd465938..6e3eb3ba6 100644 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.right +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right @@ -7,23 +7,35 @@ hush: syntax error: unterminated ${name} 1:1 =|| 1:1:2=|| 1::2 =|| +1:1: =|| +1:: =|| 1 =|0123| 1:1 =|123| 1:1:2=|12| 1::2 =|01| +1:1: =|| +1:: =|| f =|| f:1 =|| f:1:2=|| f::2 =|| +f:1: =|| +f:: =|| f =|| f:1 =|| f:1:2=|| f::2 =|| +f:1: =|| +f:: =|| f =|a| f:1 =|| f:1:2=|| f::2 =|a| +f:1: =|| +f:: =|| f =|0123456789| f:1 =|123456789| f:1:2=|12| f::2 =|01| +f:1: =|| +f:: =|| diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests index 6a1765559..eedd435ed 100755 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.tests +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests @@ -19,28 +19,40 @@ export var=0123456789 "$THIS_SH" -c 'set --; echo "1:1 =|${1:1}|"' "$THIS_SH" -c 'set --; echo "1:1:2=|${1:1:2}|"' "$THIS_SH" -c 'set --; echo "1::2 =|${1::2}|"' +"$THIS_SH" -c 'set --; echo "1:1: =|${1:1:}|"' +"$THIS_SH" -c 'set --; echo "1:: =|${1::}|"' "$THIS_SH" -c 'set -- 0123; echo "1 =|${1}|"' "$THIS_SH" -c 'set -- 0123; echo "1:1 =|${1:1}|"' "$THIS_SH" -c 'set -- 0123; echo "1:1:2=|${1:1:2}|"' "$THIS_SH" -c 'set -- 0123; echo "1::2 =|${1::2}|"' +"$THIS_SH" -c 'set -- 0123; echo "1:1: =|${1:1:}|"' +"$THIS_SH" -c 'set -- 0123; echo "1:: =|${1::}|"' "$THIS_SH" -c 'unset f; echo "f =|$f|"' "$THIS_SH" -c 'unset f; echo "f:1 =|${f:1}|"' "$THIS_SH" -c 'unset f; echo "f:1:2=|${f:1:2}|"' "$THIS_SH" -c 'unset f; echo "f::2 =|${f::2}|"' +"$THIS_SH" -c 'unset f; echo "f:1: =|${f:1:}|"' +"$THIS_SH" -c 'unset f; echo "f:: =|${f::}|"' "$THIS_SH" -c 'f=; echo "f =|$f|"' "$THIS_SH" -c 'f=; echo "f:1 =|${f:1}|"' "$THIS_SH" -c 'f=; echo "f:1:2=|${f:1:2}|"' "$THIS_SH" -c 'f=; echo "f::2 =|${f::2}|"' +"$THIS_SH" -c 'f=; echo "f:1: =|${f:1:}|"' +"$THIS_SH" -c 'f=; echo "f:: =|${f::}|"' "$THIS_SH" -c 'f=a; echo "f =|$f|"' "$THIS_SH" -c 'f=a; echo "f:1 =|${f:1}|"' "$THIS_SH" -c 'f=a; echo "f:1:2=|${f:1:2}|"' "$THIS_SH" -c 'f=a; echo "f::2 =|${f::2}|"' +"$THIS_SH" -c 'f=a; echo "f:1: =|${f:1:}|"' +"$THIS_SH" -c 'f=a; echo "f:: =|${f::}|"' "$THIS_SH" -c 'f=0123456789; echo "f =|$f|"' "$THIS_SH" -c 'f=0123456789; echo "f:1 =|${f:1}|"' "$THIS_SH" -c 'f=0123456789; echo "f:1:2=|${f:1:2}|"' "$THIS_SH" -c 'f=0123456789; echo "f::2 =|${f::2}|"' +"$THIS_SH" -c 'f=0123456789; echo "f:1: =|${f:1:}|"' +"$THIS_SH" -c 'f=0123456789; echo "f:: =|${f::}|"' -- cgit v1.2.3-55-g6feb From 214b8ca3dcfd28d4aa2bb58cd89ac941ab23d6d0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 21 May 2010 15:35:44 +0200 Subject: hush: add a list of unsupported builtins Signed-off-by: Denys Vlasenko --- shell/hush.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/hush.c b/shell/hush.c index 6cf8899b0..c713ce808 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -61,6 +61,8 @@ * grep for "TODO" and fix (some of them are easy) * special variables (done: PWD, PPID, RANDOM) * follow IFS rules more precisely, including update semantics + * builtins mandated by standards we don't support: + * [un]alias, command, fc, getopts, newgrp, readonly, times * export builtin should be special, its arguments are assignments * and therefore expansion of them should be "one-word" expansion: * $ export i=`echo 'a b'` # export has one arg: "i=a b" -- cgit v1.2.3-55-g6feb From 349ef96bb5eae3c487884dd0e88c84a6ba0a1efa Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 21 May 2010 15:46:24 +0200 Subject: hush: reorganized TODO comment at top Signed-off-by: Denys Vlasenko --- shell/hush.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index c713ce808..1937d24e4 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -37,9 +37,14 @@ * handle the recursion implicit in the various substitutions, especially * across continuation lines. * - * POSIX syntax not implemented: + * TODOs: + * grep for "TODO" and fix (some of them are easy) + * special variables (done: PWD, PPID, RANDOM) + * tilde expansion * aliases - * Tilde Expansion + * follow IFS rules more precisely, including update semantics + * builtins mandated by standards we don't support: + * [un]alias, command, fc, getopts, newgrp, readonly, times * * Bash compat TODO: * redirection of stdout+stderr: &> and >& @@ -49,20 +54,13 @@ * process substitution: <(list) and >(list) * =~: regex operator * let EXPR [EXPR...] - * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) - * If the last arg evaluates to 0, let returns 1; 0 otherwise. - * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used) + * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) + * If the last arg evaluates to 0, let returns 1; 0 otherwise. + * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used) * ((EXPR)) - * The EXPR is evaluated according to ARITHMETIC EVALUATION. - * This is exactly equivalent to let "EXPR". + * The EXPR is evaluated according to ARITHMETIC EVALUATION. + * This is exactly equivalent to let "EXPR". * $[EXPR]: synonym for $((EXPR)) - * - * TODOs: - * grep for "TODO" and fix (some of them are easy) - * special variables (done: PWD, PPID, RANDOM) - * follow IFS rules more precisely, including update semantics - * builtins mandated by standards we don't support: - * [un]alias, command, fc, getopts, newgrp, readonly, times * export builtin should be special, its arguments are assignments * and therefore expansion of them should be "one-word" expansion: * $ export i=`echo 'a b'` # export has one arg: "i=a b" -- cgit v1.2.3-55-g6feb From 3f78cec34745069cf0a92a16dfccff66d98ef5ba Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 21 May 2010 17:54:46 +0200 Subject: hush: handle expansions in ${var?expanded_word} constructs function old new delta expand_vars_to_list 2209 2229 +20 Signed-off-by: Denys Vlasenko --- shell/hush.c | 42 ++++++++++++++-------- .../hush-vars/param_expand_indicate_error.right | 15 ++++++++ .../hush-vars/param_expand_indicate_error.tests | 20 +++++++++++ 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 1937d24e4..6d91a534a 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2403,6 +2403,23 @@ static char *expand_pseudo_dquoted(const char *str) return exp_str; } +#if ENABLE_SH_MATH_SUPPORT +static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p) +{ + arith_eval_hooks_t hooks; + arith_t res; + char *exp_str; + + hooks.lookupvar = get_local_var_value; + hooks.setvar = set_local_var_from_halves; + hooks.endofname = endofname; + exp_str = expand_pseudo_dquoted(arg); + res = arith(exp_str ? exp_str : arg, errcode_p, &hooks); + free(exp_str); + return res; +} +#endif + /* Expand all variable references in given string, adding words to list[] * at n, n+1,... positions. Return updated n (so that list[n] is next one * to be filled). This routine is extremely tricky: has to deal with @@ -2427,7 +2444,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { char first_ch; int i; - char *dyn_val = NULL; + char *to_be_freed = NULL; const char *val = NULL; #if ENABLE_HUSH_TICK o_string subst_result = NULL_O_STRING; @@ -2528,21 +2545,13 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char #endif #if ENABLE_SH_MATH_SUPPORT case '+': { /* +cmd */ - arith_eval_hooks_t hooks; arith_t res; int errcode; - char *exp_str; arg++; /* skip '+' */ *p = '\0'; /* replace trailing */ debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); - - exp_str = expand_pseudo_dquoted(arg); - hooks.lookupvar = get_local_var_value; - hooks.setvar = set_local_var_from_halves; - hooks.endofname = endofname; - res = arith(exp_str ? exp_str : arg, &errcode, &hooks); - free(exp_str); + res = expand_and_evaluate_arith(arg, &errcode); if (errcode < 0) { const char *msg = "error in arithmetic"; @@ -2628,8 +2637,8 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left); if (exp_op == *exp_word) /* ## or %% */ exp_word++; - val = dyn_val = xstrdup(val); - loc = scan(dyn_val, exp_word, match_at_left); + val = to_be_freed = xstrdup(val); + loc = scan(to_be_freed, exp_word, match_at_left); if (match_at_left) /* # or ## */ val = loc; else if (loc) /* % or %% and match was found */ @@ -2662,7 +2671,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char if (len == 0 || !val || beg >= strlen(val)) val = ""; else - val = dyn_val = xstrndup(val + beg, len); + val = to_be_freed = xstrndup(val + beg, len); //bb_error_msg("val:'%s'", val); } else #endif @@ -2699,13 +2708,16 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, (exp_save == ':') ? "true" : "false", use_word); if (use_word) { + to_be_freed = expand_pseudo_dquoted(exp_word); + if (to_be_freed) + exp_word = to_be_freed; if (exp_op == '?') { -//TODO: how interactive bash aborts expansion mid-command? /* mimic bash message */ die_if_script("%s: %s", var, exp_word[0] ? exp_word : "parameter null or not set" ); +//TODO: how interactive bash aborts expansion mid-command? } else { val = exp_word; } @@ -2750,7 +2762,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char if (val) { o_addQstr(output, val, strlen(val)); } - free(dyn_val); + free(to_be_freed); /* Do the check to avoid writing to a const string */ if (*p != SPECIAL_VAR_SYMBOL) *p = SPECIAL_VAR_SYMBOL; 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 @@ hush: syntax error: unterminated ${name} 0 0 +==== _ hush: 1: parameter null or not set hush: 1: parameter null or not set hush: 1: message1 hush: 1: message1 +hush: 1: unset! +hush: 1: null or unset! +==== _aaaa _aaaa _aaaa _aaaa _aaaa +_aaaa +_aaaa +==== _ hush: f: parameter null or not set hush: f: parameter null or not set hush: f: message3 hush: f: message3 +hush: f: unset! +hush: f: null or unset! +==== _ _ hush: f: parameter null or not set _ hush: f: message4 +_ +hush: f: null or unset! +==== +_fff +_fff _fff _fff _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 @@ "$THIS_SH" -c 'echo ${:?}' # then some funky ones +# note: bash prints 1 - treats it as "length of $#"? We print 0 "$THIS_SH" -c 'echo ${#?}' +# bash prints 0 "$THIS_SH" -c 'echo ${#:?}' # now some valid ones +export msg_unset="unset!" +export msg_null_or_unset="null or unset!" + +echo ==== "$THIS_SH" -c 'set --; echo _$1' "$THIS_SH" -c 'set --; echo _${1?}' "$THIS_SH" -c 'set --; echo _${1:?}' "$THIS_SH" -c 'set --; echo _${1?message1}' "$THIS_SH" -c 'set --; echo _${1:?message1}' +"$THIS_SH" -c 'set --; echo _${1?$msg_unset}' +"$THIS_SH" -c 'set --; echo _${1:?$msg_null_or_unset}' +echo ==== "$THIS_SH" -c 'set -- aaaa; echo _$1' "$THIS_SH" -c 'set -- aaaa; echo _${1?}' "$THIS_SH" -c 'set -- aaaa; echo _${1:?}' "$THIS_SH" -c 'set -- aaaa; echo _${1?word}' "$THIS_SH" -c 'set -- aaaa; echo _${1:?word}' +"$THIS_SH" -c 'set -- aaaa; echo _${1?$msg_unset}' +"$THIS_SH" -c 'set -- aaaa; echo _${1:?$msg_null_or_unset}' +echo ==== "$THIS_SH" -c 'unset f; echo _$f' "$THIS_SH" -c 'unset f; echo _${f?}' "$THIS_SH" -c 'unset f; echo _${f:?}' "$THIS_SH" -c 'unset f; echo _${f?message3}' "$THIS_SH" -c 'unset f; echo _${f:?message3}' +"$THIS_SH" -c 'unset f; echo _${f?$msg_unset}' +"$THIS_SH" -c 'unset f; echo _${f:?$msg_null_or_unset}' +echo ==== "$THIS_SH" -c 'f=; echo _$f' "$THIS_SH" -c 'f=; echo _${f?}' "$THIS_SH" -c 'f=; echo _${f:?}' "$THIS_SH" -c 'f=; echo _${f?word}' "$THIS_SH" -c 'f=; echo _${f:?message4}' +"$THIS_SH" -c 'f=; echo _${f?$msg_unset}' +"$THIS_SH" -c 'f=; echo _${f:?$msg_null_or_unset}' +echo ==== "$THIS_SH" -c 'f=fff; echo _$f' "$THIS_SH" -c 'f=fff; echo _${f?}' "$THIS_SH" -c 'f=fff; echo _${f:?}' "$THIS_SH" -c 'f=fff; echo _${f?word}' "$THIS_SH" -c 'f=fff; echo _${f:?word}' +"$THIS_SH" -c 'f=fff; echo _${f?$msg_unset}' +"$THIS_SH" -c 'f=fff; echo _${f:?$msg_null_or_unset}' -- cgit v1.2.3-55-g6feb From 7436950a7516d1f4498285ccc81bf6d926f3af5e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 21 May 2010 19:52:01 +0200 Subject: hush: fix a=abc; c=c; echo ${a%${c}} function old new delta expand_vars_to_list 2229 2302 +73 add_till_closing_paren 286 313 +27 handle_dollar 623 574 -49 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 100/-49) Total: 51 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 118 +++++++++++++++-------------- shell/hush_test/hush-vars/var3.right | 2 +- shell/hush_test/hush-vars/var_posix1.right | 6 +- shell/hush_test/hush-vars/var_posix1.tests | 9 ++- 4 files changed, 75 insertions(+), 60 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 6d91a534a..a3df5edcd 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2638,11 +2638,21 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char if (exp_op == *exp_word) /* ## or %% */ exp_word++; val = to_be_freed = xstrdup(val); - loc = scan(to_be_freed, exp_word, match_at_left); - if (match_at_left) /* # or ## */ - val = loc; - else if (loc) /* % or %% and match was found */ - *loc = '\0'; + { + char *exp_exp_word = expand_pseudo_dquoted(exp_word); + if (exp_exp_word) + exp_word = exp_exp_word; + loc = scan(to_be_freed, exp_word, match_at_left); + //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'", + // exp_op, to_be_freed, exp_word, loc); + free(exp_exp_word); + } + if (loc) { /* match was found */ + if (match_at_left) /* # or ## */ + val = loc; + else /* % or %% */ + *loc = '\0'; + } } } else if (!strchr("%#:-=+?"+3, exp_op)) { #if ENABLE_HUSH_BASH_COMPAT @@ -5876,20 +5886,28 @@ static void add_till_backquote(o_string *dest, struct in_str *input) * echo $(echo '(TEST)' BEST) (TEST) BEST * echo $(echo 'TEST)' BEST) TEST) BEST * echo $(echo \(\(TEST\) BEST) ((TEST) BEST + * + * BUG: enter: echo $(( `printf '(\x28 1'` + `echo 2))` )) + * on the command line, press Enter. You get > prompt which is impossible + * to exit with ^C. */ -static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl) +#define DOUBLE_CLOSE_CHAR_FLAG 0x80 +static void add_till_closing_paren(o_string *dest, struct in_str *input, char end_ch) { int count = 0; + char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; + end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1); while (1) { int ch = i_getch(input); if (ch == EOF) { syntax_error_unterm_ch(')'); /*xfunc_die(); - redundant */ } - if (ch == '(') + if (ch == '(' || ch == '{') count++; - if (ch == ')') { - if (--count < 0) { + if (ch == ')' || ch == '}') { + count--; + if (count < 0 && ch == end_ch) { if (!dbl) break; if (i_peek(input) == ')') { @@ -5969,62 +5987,52 @@ static int handle_dollar(o_string *as_string, case '@': /* args */ goto make_one_char_var; case '{': { - bool first_char, all_digits; - bool in_expansion_param; + o_addchr(dest, SPECIAL_VAR_SYMBOL); - ch = i_getch(input); + ch = i_getch(input); /* eat '{' */ nommu_addchr(as_string, ch); - o_addchr(dest, SPECIAL_VAR_SYMBOL); -// TODO: need to handle "a=ab}; echo ${a%\}}" -// and "a=abc; c=c; echo ${a%${c}}" - in_expansion_param = false; - first_char = true; - all_digits = false; + ch = i_getch(input); /* first char after '{' */ + nommu_addchr(as_string, ch); + /* It should be ${?}, or ${#var}, + * or even ${?+subst} - operator acting on a special variable, + * or the beginning of variable name. + */ + if (!strchr("$!?#*@_", ch) && !isalnum(ch)) { /* not one of those */ + bad_dollar_syntax: + syntax_error_unterm_str("${name}"); + debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); + return 1; + } + ch |= quote_mask; + + /* It's possible to just call add_till_closing_paren() at this point. + * However, this regresses some of our testsuite cases + * which check invalid constructs like ${%}. + * Oh well... let's check that the var name part is fine... */ + while (1) { + o_addchr(dest, ch); + debug_printf_parse(": '%c'\n", ch); + ch = i_getch(input); nommu_addchr(as_string, ch); - if (ch == '}') { + if (ch == '}') break; - } - if (first_char) { - if (ch == '#') { - /* ${#var}: length of var contents */ - goto char_ok; - } - if (isdigit(ch)) { - all_digits = true; - goto char_ok; - } - /* They're being verbose and doing ${?} */ - if (i_peek(input) == '}' && strchr("$!?#*@_", ch)) - goto char_ok; - } - - if (!in_expansion_param - && ( (all_digits && !isdigit(ch)) /* met non-digit: 123w */ - || (!all_digits && !isalnum(ch) && ch != '_') /* met non-name char: abc% */ - ) - ) { + if (!isalnum(ch) && ch != '_') { /* handle parameter expansions * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 */ - if (first_char /* bad (empty var name): "${%..." */ - || !strchr("%#:-=+?", ch) /* bad: "${var..." */ - ) { - syntax_error_unterm_str("${name}"); - debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); - return 1; - } - in_expansion_param = true; + if (!strchr("%#:-=+?", ch)) /* ${var... */ + goto bad_dollar_syntax; + /* Eat everything until closing '}' */ + o_addchr(dest, ch); +//TODO: add nommu_addchr hack here + add_till_closing_paren(dest, input, '}'); + break; } - char_ok: - debug_printf_parse(": '%c'\n", ch); - o_addchr(dest, ch | quote_mask); - quote_mask = 0; - first_char = false; - } /* while (1) */ + } o_addchr(dest, SPECIAL_VAR_SYMBOL); break; } @@ -6044,7 +6052,7 @@ static int handle_dollar(o_string *as_string, # if !BB_MMU pos = dest->length; # endif - add_till_closing_paren(dest, input, true); + add_till_closing_paren(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG); # if !BB_MMU if (as_string) { o_addstr(as_string, dest->data + pos); @@ -6062,7 +6070,7 @@ static int handle_dollar(o_string *as_string, # if !BB_MMU pos = dest->length; # endif - add_till_closing_paren(dest, input, false); + add_till_closing_paren(dest, input, ')'); # if !BB_MMU if (as_string) { o_addstr(as_string, dest->data + pos); 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 @@ -hush: syntax error: unterminated ${name} +hush: invalid number '1q' 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..813437e2f 100644 --- a/shell/hush_test/hush-vars/var_posix1.right +++ b/shell/hush_test/hush-vars/var_posix1.right @@ -23,6 +23,7 @@ babcdcd ababcdcd Empty: ababcdcd}_tail +ababcdcd_tail ababcd ababcd ababcd @@ -32,5 +33,8 @@ ababcdc ababcdcd Empty: ababcdcd}_tail +ababcdcd_tail ababcdcd -end +ab +ab +End diff --git a/shell/hush_test/hush-vars/var_posix1.tests b/shell/hush_test/hush-vars/var_posix1.tests index c1f64094d..e48fd98c7 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##?} echo ${var#*} echo Empty:${var##*} echo ${var#}}_tail -# UNFIXED BUG: echo ${var#\}}_tail +echo ${var#\}}_tail echo ${var%cd} echo ${var%%cd} @@ -42,7 +42,10 @@ echo ${var%%?} echo ${var%*} echo Empty:${var%%*} echo ${var#}}_tail -# UNFIXED BUG: echo ${var#\}}_tail +echo ${var#\}}_tail echo ${var%\\*} -echo end +a=ab}; echo ${a%\}}; +a=abc; c=c; echo ${a%${c}} + +echo End -- cgit v1.2.3-55-g6feb From a6ad397ea92cd9c53973243728d3e52640fe63ec Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 00:26:06 +0200 Subject: hush: fix more obscure ${var%...} cases function old new delta add_till_closing_paren 313 359 +46 builtin_exit 48 47 -1 Signed-off-by: Denys Vlasenko --- shell/hush.c | 39 ++++++++++++++++++------------ shell/hush_test/hush-arith/arith.right | 9 +++++++ shell/hush_test/hush-arith/arith.tests | 19 +++++++++------ shell/hush_test/hush-vars/var_posix1.right | 1 + shell/hush_test/hush-vars/var_posix1.tests | 1 + 5 files changed, 45 insertions(+), 24 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index a3df5edcd..32b90876f 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -45,6 +45,8 @@ * follow IFS rules more precisely, including update semantics * builtins mandated by standards we don't support: * [un]alias, command, fc, getopts, newgrp, readonly, times + * make complex ${var%...} constructs support optional + * make here documents optional * * Bash compat TODO: * redirection of stdout+stderr: &> and >& @@ -5887,36 +5889,36 @@ static void add_till_backquote(o_string *dest, struct in_str *input) * echo $(echo 'TEST)' BEST) TEST) BEST * echo $(echo \(\(TEST\) BEST) ((TEST) BEST * - * BUG: enter: echo $(( `printf '(\x28 1'` + `echo 2))` )) - * on the command line, press Enter. You get > prompt which is impossible - * to exit with ^C. + * Also adapted to eat ${var%...} constructs, since ... part + * can contain arbitrary constructs, just like $(cmd). */ #define DOUBLE_CLOSE_CHAR_FLAG 0x80 static void add_till_closing_paren(o_string *dest, struct in_str *input, char end_ch) { - int count = 0; char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1); while (1) { int ch = i_getch(input); if (ch == EOF) { - syntax_error_unterm_ch(')'); + syntax_error_unterm_ch(end_ch); /*xfunc_die(); - redundant */ } - if (ch == '(' || ch == '{') - count++; - if (ch == ')' || ch == '}') { - count--; - if (count < 0 && ch == end_ch) { - if (!dbl) - break; - if (i_peek(input) == ')') { - i_getch(input); - break; - } + if (ch == end_ch) { + if (!dbl) + break; + /* we look for closing )) of $((EXPR)) */ + if (i_peek(input) == end_ch) { + i_getch(input); /* eat second ')' */ + break; } } o_addchr(dest, ch); + if (ch == '(' || ch == '{') { + ch = (ch == '(' ? ')' : '}'); + add_till_closing_paren(dest, input, ch); + o_addchr(dest, ch); + continue; + } if (ch == '\'') { add_till_single_quote(dest, input); o_addchr(dest, ch); @@ -5927,6 +5929,11 @@ static void add_till_closing_paren(o_string *dest, struct in_str *input, char en o_addchr(dest, ch); continue; } + if (ch == '`') { + add_till_backquote(dest, input); + o_addchr(dest, ch); + continue; + } if (ch == '\\') { /* \x. Copy verbatim. Important for \(, \) */ ch = i_getch(input); 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' 4 4 29 29 5 5 +unary plus, minus -4 -4 4 4 +conditional expressions 1 1 32 32 32 32 1 1 1 1 32 32 +check that parentheses in `cmd` are interpreted correctly +3 3 +check that the unevaluated part of the ternary operator does not do evaluation or assignment 20 20 30 30 20 20 30 30 +check precedence of assignment vs. conditional operator hush: error in arithmetic +check precedence of assignment vs. conditional operator +associativity of assignment-operator operator 6 6 6,5,3 6,5,3 +octal, hex 263 263 255 255 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 )) echo 29 $(( iv += (jv + 9))) echo 5 $(( (iv + 4) % 7 )) -# unary plus, minus +echo unary plus, minus echo -4 $(( +4 - 8 )) echo 4 $(( -4 + 8 )) -# conditional expressions +echo conditional expressions echo 1 $(( 4<5 ? 1 : 32)) echo 32 $(( 4>5 ? 1 : 32)) echo 32 $(( 4>(2+3) ? 1 : 32)) @@ -87,8 +87,11 @@ echo 1 $(( 4<(2+3) ? 1 : 32)) echo 1 $(( (2+2)<(2+3) ? 1 : 32)) echo 32 $(( (2+2)>(2+3) ? 1 : 32)) -# check that the unevaluated part of the ternary operator does not do -# evaluation or assignment +echo 'check that parentheses in `cmd` are interpreted correctly' +# \x28 is '(' +echo 3 $(( ( `printf '(\x28 1'` + `echo 2\)\)` ) )) + +echo check that the unevaluated part of the ternary operator does not do evaluation or assignment x=i+=2 y=j+=2 #ash# declare -i i=1 j=1 @@ -109,20 +112,20 @@ echo 20 $((1 ? 20 : (x+=2))) echo 30 $((0 ? (y+=2) : 30)) #ash# echo $i,$y # ash mishandles this -# check precedence of assignment vs. conditional operator +echo check precedence of assignment vs. conditional operator # should be an error #ash# declare -i x=2 x=2 #ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash: ( y=$((1 ? 20 : x+=2)) ) -# check precedence of assignment vs. conditional operator +echo check precedence of assignment vs. conditional operator #ash# declare -i x=2 x=2 # ash says "line NNN: syntax error: 0 ? x+=2 : 20" #ash# echo 20 $((0 ? x+=2 : 20)) -# associativity of assignment-operator operator +echo associativity of assignment-operator operator #ash# declare -i i=1 j=2 k=3 i=1 j=2 @@ -130,7 +133,7 @@ k=3 echo 6 $((i += j += k)) echo 6,5,3 $i,$j,$k -# octal, hex +echo octal, hex echo 263 $(( 0x100 | 007 )) echo 255 $(( 0xff )) #ash# echo 255 $(( 16#ff )) diff --git a/shell/hush_test/hush-vars/var_posix1.right b/shell/hush_test/hush-vars/var_posix1.right index 813437e2f..7ff618ad0 100644 --- a/shell/hush_test/hush-vars/var_posix1.right +++ b/shell/hush_test/hush-vars/var_posix1.right @@ -37,4 +37,5 @@ ababcdcd_tail ababcdcd ab ab +ab End diff --git a/shell/hush_test/hush-vars/var_posix1.tests b/shell/hush_test/hush-vars/var_posix1.tests index e48fd98c7..82abe8198 100755 --- a/shell/hush_test/hush-vars/var_posix1.tests +++ b/shell/hush_test/hush-vars/var_posix1.tests @@ -47,5 +47,6 @@ echo ${var%\\*} a=ab}; echo ${a%\}}; a=abc; c=c; echo ${a%${c}} +a=ab{{c; echo ${a%`echo {{c`} echo End -- cgit v1.2.3-55-g6feb From ddc62f64baad8abdeb587b13afe8d47fb347981c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 00:53:32 +0200 Subject: hush: fix a nommu todo in recent commits Signed-off-by: Denys Vlasenko --- shell/hush.c | 62 ++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 32b90876f..0f0151a21 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5767,7 +5767,9 @@ static int parse_group(o_string *dest, struct parse_context *ctx, } { -#if !BB_MMU +#if BB_MMU +# define as_string NULL +#else char *as_string = NULL; #endif pipe_list = parse_stream(&as_string, input, endch); @@ -5778,9 +5780,8 @@ static int parse_group(o_string *dest, struct parse_context *ctx, /* empty ()/{} or parse error? */ if (!pipe_list || pipe_list == ERR_PTR) { /* parse_stream already emitted error msg */ -#if !BB_MMU - free(as_string); -#endif + if (!BB_MMU) + free(as_string); debug_printf_parse("parse_group return 1: " "parse_stream returned %p\n", pipe_list); return 1; @@ -5792,6 +5793,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx, debug_printf_parse("end of group, remembering as:'%s'\n", command->group_as_string); #endif +#undef as_string } debug_printf_parse("parse_group return 0\n"); return 0; @@ -5893,7 +5895,7 @@ static void add_till_backquote(o_string *dest, struct in_str *input) * can contain arbitrary constructs, just like $(cmd). */ #define DOUBLE_CLOSE_CHAR_FLAG 0x80 -static void add_till_closing_paren(o_string *dest, struct in_str *input, char end_ch) +static void add_till_closing_bracket(o_string *dest, struct in_str *input, char end_ch) { char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1); @@ -5915,7 +5917,7 @@ static void add_till_closing_paren(o_string *dest, struct in_str *input, char en o_addchr(dest, ch); if (ch == '(' || ch == '{') { ch = (ch == '(' ? ')' : '}'); - add_till_closing_paren(dest, input, ch); + add_till_closing_bracket(dest, input, ch); o_addchr(dest, ch); continue; } @@ -5952,6 +5954,7 @@ static void add_till_closing_paren(o_string *dest, struct in_str *input, char en #if BB_MMU #define handle_dollar(as_string, dest, input) \ handle_dollar(dest, input) +#define as_string NULL #endif static int handle_dollar(o_string *as_string, o_string *dest, @@ -6013,12 +6016,14 @@ static int handle_dollar(o_string *as_string, } ch |= quote_mask; - /* It's possible to just call add_till_closing_paren() at this point. + /* It's possible to just call add_till_closing_bracket() at this point. * However, this regresses some of our testsuite cases * which check invalid constructs like ${%}. * Oh well... let's check that the var name part is fine... */ while (1) { + unsigned pos; + o_addchr(dest, ch); debug_printf_parse(": '%c'\n", ch); @@ -6035,8 +6040,15 @@ static int handle_dollar(o_string *as_string, goto bad_dollar_syntax; /* Eat everything until closing '}' */ o_addchr(dest, ch); -//TODO: add nommu_addchr hack here - add_till_closing_paren(dest, input, '}'); + if (!BB_MMU) + pos = dest->length; + add_till_closing_bracket(dest, input, '}'); +#if !BB_MMU + if (as_string) { + o_addstr(as_string, dest->data + pos); + o_addchr(as_string, '}'); + } +#endif break; } } @@ -6045,9 +6057,8 @@ static int handle_dollar(o_string *as_string, } #if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK case '(': { -# if !BB_MMU - int pos; -# endif + unsigned pos; + ch = i_getch(input); nommu_addchr(as_string, ch); # if ENABLE_SH_MATH_SUPPORT @@ -6056,17 +6067,16 @@ static int handle_dollar(o_string *as_string, nommu_addchr(as_string, ch); o_addchr(dest, SPECIAL_VAR_SYMBOL); o_addchr(dest, /*quote_mask |*/ '+'); -# if !BB_MMU - pos = dest->length; -# endif - add_till_closing_paren(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG); -# if !BB_MMU + if (!BB_MMU) + pos = dest->length; + add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG); +#if !BB_MMU if (as_string) { o_addstr(as_string, dest->data + pos); o_addchr(as_string, ')'); o_addchr(as_string, ')'); } -# endif +#endif o_addchr(dest, SPECIAL_VAR_SYMBOL); break; } @@ -6074,16 +6084,15 @@ static int handle_dollar(o_string *as_string, # if ENABLE_HUSH_TICK o_addchr(dest, SPECIAL_VAR_SYMBOL); o_addchr(dest, quote_mask | '`'); -# if !BB_MMU - pos = dest->length; -# endif - add_till_closing_paren(dest, input, ')'); -# if !BB_MMU + if (!BB_MMU) + pos = dest->length; + add_till_closing_bracket(dest, input, ')'); +#if !BB_MMU if (as_string) { o_addstr(as_string, dest->data + pos); o_addchr(as_string, ')'); } -# endif +#endif o_addchr(dest, SPECIAL_VAR_SYMBOL); # endif break; @@ -6108,11 +6117,13 @@ static int handle_dollar(o_string *as_string, } debug_printf_parse("handle_dollar return 0\n"); return 0; +#undef as_string } #if BB_MMU #define parse_stream_dquoted(as_string, dest, input, dquote_end) \ parse_stream_dquoted(dest, input, dquote_end) +#define as_string NULL #endif static int parse_stream_dquoted(o_string *as_string, o_string *dest, @@ -6176,7 +6187,7 @@ static int parse_stream_dquoted(o_string *as_string, } #if ENABLE_HUSH_TICK if (ch == '`') { - //int pos = dest->length; + //unsigned pos = dest->length; o_addchr(dest, SPECIAL_VAR_SYMBOL); o_addchr(dest, 0x80 | '`'); add_till_backquote(dest, input); @@ -6194,6 +6205,7 @@ static int parse_stream_dquoted(o_string *as_string, dest->o_assignment = DEFINITELY_ASSIGNMENT; } goto again; +#undef as_string } /* -- cgit v1.2.3-55-g6feb From 1e811b12317d0eab4e78d848caa640cca497a0a7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 03:12:29 +0200 Subject: hush: support ${var:EXPR:EXPR}! function old new delta handle_dollar 574 681 +107 expand_and_evaluate_arith - 77 +77 expand_vars_to_list 2302 2374 +72 add_till_closing_bracket 359 368 +9 builtin_exit 48 47 -1 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 3/1 up/down: 265/-1) Total: 264 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 130 ++++++++++++++------- .../hush-vars/param_expand_bash_substring.right | 10 ++ .../hush-vars/param_expand_bash_substring.tests | 98 +++++++++------- 3 files changed, 152 insertions(+), 86 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 0f0151a21..41d5fcab2 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -171,6 +171,7 @@ #define debug_printf_env(...) do {} while (0) #define debug_printf_jobs(...) do {} while (0) #define debug_printf_expand(...) do {} while (0) +#define debug_printf_varexp(...) do {} while (0) #define debug_printf_glob(...) do {} while (0) #define debug_printf_list(...) do {} while (0) #define debug_printf_subst(...) do {} while (0) @@ -743,6 +744,10 @@ static const struct built_in_command bltins2[] = { # define DEBUG_EXPAND 0 #endif +#ifndef debug_printf_varexp +# define debug_printf_varexp(...) (indent(), fprintf(stderr, __VA_ARGS__)) +#endif + #ifndef debug_printf_glob # define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__)) # define DEBUG_GLOB 1 @@ -1817,11 +1822,11 @@ static void o_addblock(o_string *o, const char *str, int len) o->data[o->length] = '\0'; } -#if !BB_MMU static void o_addstr(o_string *o, const char *str) { o_addblock(o, str, strlen(str)); } +#if !BB_MMU static void nommu_addchr(o_string *o, int ch) { if (o) @@ -2597,12 +2602,19 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char } else { /* maybe handle parameter expansion */ exp_saveptr = var + strcspn(var, "%#:-=+?"); - exp_save = *exp_saveptr; - if (exp_save) { - exp_word = exp_saveptr; - if (exp_save == ':') - exp_word++; - exp_op = *exp_word++; + exp_op = exp_save = *exp_saveptr; + if (exp_op) { + exp_word = exp_saveptr + 1; + if (exp_op == ':') { + exp_op = *exp_word++; + if (ENABLE_HUSH_BASH_COMPAT + && (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op)) + ) { + /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ + exp_op = ':'; + exp_word--; + } + } *exp_saveptr = '\0'; } } @@ -2656,39 +2668,42 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char *loc = '\0'; } } - } else if (!strchr("%#:-=+?"+3, exp_op)) { + } else if (exp_op == ':') { #if ENABLE_HUSH_BASH_COMPAT - /* exp_op is ':' and next char isn't a subst operator. - * Assuming it's ${var:[N][:M]} bashism. - * TODO: N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc + /* It's ${var:N[:M]} bashism. + * Note that in encoded form it has TWO parts: + * var:NM */ - char *end; - unsigned len = INT_MAX; - unsigned beg = 0; - end = --exp_word; - if (*exp_word != ':') /* not ${var::...} */ - beg = bb_strtou(exp_word, &end, 0); - //bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end); - if (*end == ':') { - if (end[1] != '\0') /* not ${var:NUM:} */ - len = bb_strtou(end + 1, &end, 0); - else { - len = 0; - end++; - } - //bb_error_msg("len:%u end:'%s'", len, end); - } - if (*end == '\0') { - //bb_error_msg("from val:'%s'", val); + arith_t beg, len; + int errcode = 0; + + beg = expand_and_evaluate_arith(exp_word, &errcode); + debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); + *p++ = SPECIAL_VAR_SYMBOL; + exp_word = p; + p = strchr(p, SPECIAL_VAR_SYMBOL); + *p = '\0'; + len = expand_and_evaluate_arith(exp_word, &errcode); + debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); + + if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */ + if (beg < 0) /* bash compat */ + beg = 0; + debug_printf_varexp("from val:'%s'\n", val); if (len == 0 || !val || beg >= strlen(val)) val = ""; - else + else { + /* Paranoia. What if user entered 9999999999999 + * which fits in arith_t but not int? */ + if (len >= INT_MAX) + len = INT_MAX; val = to_be_freed = xstrndup(val + beg, len); - //bb_error_msg("val:'%s'", val); + } + debug_printf_varexp("val:'%s'\n", val); } else #endif { - die_if_script("malformed ${%s...}", var); + die_if_script("malformed ${%s:...}", var); val = ""; } } else { /* one of "-=+?" */ @@ -5891,21 +5906,28 @@ static void add_till_backquote(o_string *dest, struct in_str *input) * echo $(echo 'TEST)' BEST) TEST) BEST * echo $(echo \(\(TEST\) BEST) ((TEST) BEST * - * Also adapted to eat ${var%...} constructs, since ... part + * Also adapted to eat ${var%...} and $((...)) constructs, since ... part * can contain arbitrary constructs, just like $(cmd). + * In bash compat mode, it needs to also be able to stop on '}' or ':' + * for ${var:N[:M]} parsing. */ #define DOUBLE_CLOSE_CHAR_FLAG 0x80 -static void add_till_closing_bracket(o_string *dest, struct in_str *input, char end_ch) +static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch) { + int ch; char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; - end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1); +#if ENABLE_HUSH_BASH_COMPAT + char end_char2 = end_ch >> 8; +#endif + end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); + while (1) { - int ch = i_getch(input); + ch = i_getch(input); if (ch == EOF) { syntax_error_unterm_ch(end_ch); /*xfunc_die(); - redundant */ } - if (ch == end_ch) { + if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { if (!dbl) break; /* we look for closing )) of $((EXPR)) */ @@ -5947,6 +5969,7 @@ static void add_till_closing_bracket(o_string *dest, struct in_str *input, char continue; } } + return ch; } #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ @@ -6033,22 +6056,45 @@ static int handle_dollar(o_string *as_string, break; if (!isalnum(ch) && ch != '_') { + unsigned end_ch; + unsigned char last_ch; /* handle parameter expansions * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 */ if (!strchr("%#:-=+?", ch)) /* ${var... */ goto bad_dollar_syntax; - /* Eat everything until closing '}' */ o_addchr(dest, ch); + + /* Eat everything until closing '}' (or ':') */ + end_ch = '}'; + if (ENABLE_HUSH_BASH_COMPAT + && ch == ':' + && !strchr("%#:-=+?"+3, i_peek(input)) + ) { + /* It's ${var:N[:M]} thing */ + end_ch = '}' * 0x100 + ':'; + } + again: if (!BB_MMU) pos = dest->length; - add_till_closing_bracket(dest, input, '}'); -#if !BB_MMU + last_ch = add_till_closing_bracket(dest, input, end_ch); if (as_string) { o_addstr(as_string, dest->data + pos); - o_addchr(as_string, '}'); + o_addchr(as_string, last_ch); + } + + if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { + /* close the first block: */ + o_addchr(dest, SPECIAL_VAR_SYMBOL); + /* while parsing N from ${var:N[:M]}... */ + if ((end_ch & 0xff) == last_ch) { + /* ...got ':' - parse the rest */ + end_ch = '}'; + goto again; + } + /* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */ + o_addstr(dest, "999999999"); } -#endif break; } } diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.right b/shell/hush_test/hush-vars/param_expand_bash_substring.right index 6e3eb3ba6..53b8836ff 100644 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.right +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right @@ -39,3 +39,13 @@ f:1:2=|12| f::2 =|01| f:1: =|| f:: =|| +Substrings with expressions +f =|01234567| +f:1+1:2+2 =|2345| +f:-1:2+2 =|01234567| +f:1:f =|1234567| +f:1:$f =|1234567| +f:1:${f} =|1234567| +f:1:${f:3:1} =|123| +f:1:1`echo 1`=|1| +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 index eedd435ed..a80523add 100755 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.tests +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests @@ -1,8 +1,6 @@ +# first try some invalid patterns # do all of these in subshells since it's supposed to error out - export var=0123456789 - -# first try some invalid patterns "$THIS_SH" -c 'echo ${:}' "$THIS_SH" -c 'echo ${::}' "$THIS_SH" -c 'echo ${:1}' @@ -15,44 +13,56 @@ export var=0123456789 # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' # now some valid ones -"$THIS_SH" -c 'set --; echo "1 =|${1}|"' -"$THIS_SH" -c 'set --; echo "1:1 =|${1:1}|"' -"$THIS_SH" -c 'set --; echo "1:1:2=|${1:1:2}|"' -"$THIS_SH" -c 'set --; echo "1::2 =|${1::2}|"' -"$THIS_SH" -c 'set --; echo "1:1: =|${1:1:}|"' -"$THIS_SH" -c 'set --; echo "1:: =|${1::}|"' - -"$THIS_SH" -c 'set -- 0123; echo "1 =|${1}|"' -"$THIS_SH" -c 'set -- 0123; echo "1:1 =|${1:1}|"' -"$THIS_SH" -c 'set -- 0123; echo "1:1:2=|${1:1:2}|"' -"$THIS_SH" -c 'set -- 0123; echo "1::2 =|${1::2}|"' -"$THIS_SH" -c 'set -- 0123; echo "1:1: =|${1:1:}|"' -"$THIS_SH" -c 'set -- 0123; echo "1:: =|${1::}|"' - -"$THIS_SH" -c 'unset f; echo "f =|$f|"' -"$THIS_SH" -c 'unset f; echo "f:1 =|${f:1}|"' -"$THIS_SH" -c 'unset f; echo "f:1:2=|${f:1:2}|"' -"$THIS_SH" -c 'unset f; echo "f::2 =|${f::2}|"' -"$THIS_SH" -c 'unset f; echo "f:1: =|${f:1:}|"' -"$THIS_SH" -c 'unset f; echo "f:: =|${f::}|"' - -"$THIS_SH" -c 'f=; echo "f =|$f|"' -"$THIS_SH" -c 'f=; echo "f:1 =|${f:1}|"' -"$THIS_SH" -c 'f=; echo "f:1:2=|${f:1:2}|"' -"$THIS_SH" -c 'f=; echo "f::2 =|${f::2}|"' -"$THIS_SH" -c 'f=; echo "f:1: =|${f:1:}|"' -"$THIS_SH" -c 'f=; echo "f:: =|${f::}|"' - -"$THIS_SH" -c 'f=a; echo "f =|$f|"' -"$THIS_SH" -c 'f=a; echo "f:1 =|${f:1}|"' -"$THIS_SH" -c 'f=a; echo "f:1:2=|${f:1:2}|"' -"$THIS_SH" -c 'f=a; echo "f::2 =|${f::2}|"' -"$THIS_SH" -c 'f=a; echo "f:1: =|${f:1:}|"' -"$THIS_SH" -c 'f=a; echo "f:: =|${f::}|"' - -"$THIS_SH" -c 'f=0123456789; echo "f =|$f|"' -"$THIS_SH" -c 'f=0123456789; echo "f:1 =|${f:1}|"' -"$THIS_SH" -c 'f=0123456789; echo "f:1:2=|${f:1:2}|"' -"$THIS_SH" -c 'f=0123456789; echo "f::2 =|${f::2}|"' -"$THIS_SH" -c 'f=0123456789; echo "f:1: =|${f:1:}|"' -"$THIS_SH" -c 'f=0123456789; echo "f:: =|${f::}|"' +set --; echo "1 =|${1}|" +set --; echo "1:1 =|${1:1}|" +set --; echo "1:1:2=|${1:1:2}|" +set --; echo "1::2 =|${1::2}|" +set --; echo "1:1: =|${1:1:}|" +set --; echo "1:: =|${1::}|" + +set -- 0123; echo "1 =|${1}|" +set -- 0123; echo "1:1 =|${1:1}|" +set -- 0123; echo "1:1:2=|${1:1:2}|" +set -- 0123; echo "1::2 =|${1::2}|" +set -- 0123; echo "1:1: =|${1:1:}|" +set -- 0123; echo "1:: =|${1::}|" + +unset f; echo "f =|$f|" +unset f; echo "f:1 =|${f:1}|" +unset f; echo "f:1:2=|${f:1:2}|" +unset f; echo "f::2 =|${f::2}|" +unset f; echo "f:1: =|${f:1:}|" +unset f; echo "f:: =|${f::}|" + +f=; echo "f =|$f|" +f=; echo "f:1 =|${f:1}|" +f=; echo "f:1:2=|${f:1:2}|" +f=; echo "f::2 =|${f::2}|" +f=; echo "f:1: =|${f:1:}|" +f=; echo "f:: =|${f::}|" + +f=a; echo "f =|$f|" +f=a; echo "f:1 =|${f:1}|" +f=a; echo "f:1:2=|${f:1:2}|" +f=a; echo "f::2 =|${f::2}|" +f=a; echo "f:1: =|${f:1:}|" +f=a; echo "f:: =|${f::}|" + +f=0123456789; echo "f =|$f|" +f=0123456789; echo "f:1 =|${f:1}|" +f=0123456789; echo "f:1:2=|${f:1:2}|" +f=0123456789; echo "f::2 =|${f::2}|" +f=0123456789; echo "f:1: =|${f:1:}|" +f=0123456789; echo "f:: =|${f::}|" + +echo "Substrings with expressions" +f=01234567; echo 'f '"=|$f|" +f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|" +f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|" +f=01234567; echo 'f:1:f '"=|${f:1:f}|" +f=01234567; echo 'f:1:$f '"=|${f:1:$f}|" +f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|" +f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|" +f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|" + +echo Done -- cgit v1.2.3-55-g6feb From 8a33679694b0fdf459d69868f85c081cab5687cb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 06:05:02 +0200 Subject: hush: fix "hush -c 'echo $#'" showing -1 Signed-off-by: Denys Vlasenko --- shell/hush.c | 1 - shell/hush_test/hush-vars/param_expand_len.right | 5 +++++ shell/hush_test/hush-vars/param_expand_len.tests | 7 ++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 41d5fcab2..7645a34a4 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7080,7 +7080,6 @@ int hush_main(int argc, char **argv) /* -c 'script' (no params): prevent empty $0 */ G.global_argv--; /* points to argv[i] of 'script' */ G.global_argv[0] = argv[0]; - G.global_argc--; } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ init_sigmasks(); parse_and_run_string(optarg); 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 @@ +0 +0 +1 +Make sure len parsing doesnt break arg count 0 0 4 4 +Testing len op 4 3 2 1 0 0 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 @@ -# make sure len parsing doesnt break arg count +"$THIS_SH" -c 'echo $#' +"$THIS_SH" -c 'echo $#' arg0 +"$THIS_SH" -c 'echo $#' arg0 arg1 + +echo Make sure len parsing doesnt break arg count set -- echo $# ${#} set -- aaaa bbb cc d echo $# ${#} +echo Testing len op echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6} unset e -- cgit v1.2.3-55-g6feb From e85248afa23434b78e48fe09b57eea5f6657410d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 06:20:26 +0200 Subject: hush: fix segfault in ${?:N:M} function old new delta expand_vars_to_list 2374 2409 +35 builtin_umask 132 133 +1 builtin_exit 47 48 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/0 up/down: 37/0) Total: 37 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 81 +++++++++++++--------- shell/hush_test/hush-vars/param_expand_alt.right | 2 +- shell/hush_test/hush-vars/param_expand_alt.tests | 4 +- .../hush-vars/param_expand_bash_substring.right | 13 ++++ .../hush-vars/param_expand_bash_substring.tests | 15 ++++ 5 files changed, 78 insertions(+), 37 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 7645a34a4..08e63785d 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -179,9 +179,13 @@ #define ERR_PTR ((void*)(long)1) -#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" +#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" -#define SPECIAL_VAR_SYMBOL 3 +#define _SPECIAL_VARS_STR "_*@$!?#" +#define SPECIAL_VARS_STR ("_*@$!?#" + 1) +#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) + +#define SPECIAL_VAR_SYMBOL 3 struct variable; @@ -2472,21 +2476,6 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char switch (first_ch & 0x7f) { /* Highest bit in first_ch indicates that var is double-quoted */ - case '$': /* pid */ - val = utoa(G.root_pid); - break; - case '!': /* bg pid */ - val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)""; - break; - case '?': /* exitcode */ - val = utoa(G.last_exitcode); - break; - case '#': /* argc */ - if (arg[1] != SPECIAL_VAR_SYMBOL) - /* actually, it's a ${#var} */ - goto case_default; - val = utoa(G.global_argc ? G.global_argc-1 : 0); - break; case '*': case '@': i = 1; @@ -2581,27 +2570,35 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char break; } #endif - default: /* varname */ - case_default: { - char *var = arg; - char exp_len; /* '#' if it's ${#var} */ + default: { /* varname */ + char *var; + char first_char; char exp_op; char exp_save = exp_save; /* for compiler */ - char *exp_saveptr = exp_saveptr; /* points to expansion operator */ + char *exp_saveptr; /* points to expansion operator */ char *exp_word = exp_word; /* for compiler */ + var = arg; *p = '\0'; - arg[0] = first_ch & 0x7f; - - /* prepare for expansions */ + exp_saveptr = arg[1] ? strchr("%#:-=+?", arg[1]) : NULL; + first_char = arg[0] = first_ch & 0x7f; exp_op = 0; - exp_len = var[0]; - if (exp_len == '#') { + + if (first_char == '#' && arg[1] && !exp_saveptr) { /* handle length expansion ${#var} */ var++; + exp_op = 'L'; } else { /* maybe handle parameter expansion */ - exp_saveptr = var + strcspn(var, "%#:-=+?"); + if (exp_saveptr /* if 2nd char is one of expansion operators */ + && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */ + ) { + /* ${?:0}, ${#[:]%0} etc */ + exp_saveptr = var + 1; + } else { + /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */ + exp_saveptr = var+1 + strcspn(var+1, "%#:-=+?"); + } exp_op = exp_save = *exp_saveptr; if (exp_op) { exp_word = exp_saveptr + 1; @@ -2616,7 +2613,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char } } *exp_saveptr = '\0'; - } + } /* else: it's not an expansion op, but bare ${var} */ } /* lookup the variable in question */ @@ -2626,11 +2623,27 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char if (i < G.global_argc) val = G.global_argv[i]; /* else val remains NULL: $N with too big N */ - } else - val = get_local_var_value(var); + } else { + switch (var[0]) { + case '$': /* pid */ + val = utoa(G.root_pid); + break; + case '!': /* bg pid */ + val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)""; + break; + case '?': /* exitcode */ + val = utoa(G.last_exitcode); + break; + case '#': /* argc */ + val = utoa(G.global_argc ? G.global_argc-1 : 0); + break; + default: + val = get_local_var_value(var); + } + } /* handle any expansions */ - if (exp_len == '#') { + if (exp_op == 'L') { debug_printf_expand("expand: length(%s)=", val); val = utoa(val ? strlen(val) : 0); debug_printf_expand("%s\n", val); @@ -2761,7 +2774,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char } } } - } + } /* one of "-=+?" */ *exp_saveptr = exp_save; } /* if (exp_op) */ @@ -6031,7 +6044,7 @@ static int handle_dollar(o_string *as_string, * or even ${?+subst} - operator acting on a special variable, * or the beginning of variable name. */ - if (!strchr("$!?#*@_", ch) && !isalnum(ch)) { /* not one of those */ + if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */ bad_dollar_syntax: syntax_error_unterm_str("${name}"); debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); 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 @@ hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name} -_0 _0 +__ __ _ _ _ _ _ _aaaa _ _ _word _word _ _ _ _ _ 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 @@ "$THIS_SH" -c 'echo ${+} ; echo moo' "$THIS_SH" -c 'echo ${:+} ; echo moo' -# now some funky ones -echo _${#+} _${#:+} +# now some funky ones. (bash doesn't accept ${#+}) +echo _${#+}_ _${#:+}_ # now some valid ones 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 index 53b8836ff..2f4c51d06 100644 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.right +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right @@ -39,6 +39,19 @@ f:1:2=|12| f::2 =|01| f:1: =|| f:: =|| +Substrings from special vars +? =|0| +?:1 =|| +?:1:2=|| +?::2 =|0| +?:1: =|| +?:: =|| +# =|11| +#:1 =|1| +#:1:2=|1| +#::2 =|11| +#:1: =|| +#:: =|| Substrings with expressions f =|01234567| f:1+1:2+2 =|2345| diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests index a80523add..5c9552dba 100755 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.tests +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests @@ -55,6 +55,21 @@ f=0123456789; echo "f::2 =|${f::2}|" f=0123456789; echo "f:1: =|${f:1:}|" f=0123456789; echo "f:: =|${f::}|" +echo "Substrings from special vars" +echo '? '"=|$?|" +echo '?:1 '"=|${?:1}|" +echo '?:1:2'"=|${?:1:2}|" +echo '?::2 '"=|${?::2}|" +echo '?:1: '"=|${?:1:}|" +echo '?:: '"=|${?::}|" +set -- 1 2 3 4 5 6 7 8 9 10 11 +echo '# '"=|$#|" +echo '#:1 '"=|${#:1}|" +echo '#:1:2'"=|${#:1:2}|" +echo '#::2 '"=|${#::2}|" +echo '#:1: '"=|${#:1:}|" +echo '#:: '"=|${#::}|" + echo "Substrings with expressions" f=01234567; echo 'f '"=|$f|" f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|" -- cgit v1.2.3-55-g6feb From 5ae8f1cdbecd3bdf502992c21a77aae65299c410 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 06:32:11 +0200 Subject: hush: fix hush -c 'echo $0' - was showing empty string Signed-off-by: Denys Vlasenko --- shell/hush.c | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/hush.c b/shell/hush.c index 08e63785d..8125a63f1 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -7093,6 +7093,7 @@ int hush_main(int argc, char **argv) /* -c 'script' (no params): prevent empty $0 */ G.global_argv--; /* points to argv[i] of 'script' */ G.global_argv[0] = argv[0]; + G.global_argc++; } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ init_sigmasks(); parse_and_run_string(optarg); -- cgit v1.2.3-55-g6feb From 2e48d536cee3c2fb261780eb2a01972bd936a56c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 17:30:39 +0200 Subject: hush: reduce #ifdef forest a bit, rename handle_dollar -> parse_dollar Signed-off-by: Denys Vlasenko --- shell/hush.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 8125a63f1..e9d31b436 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -50,6 +50,7 @@ * * Bash compat TODO: * redirection of stdout+stderr: &> and >& + * subst operator: ${var/[/]expr/expr} * brace expansion: one/{two,three,four} * reserved words: function select * advanced test: [[ ]] @@ -1830,6 +1831,7 @@ static void o_addstr(o_string *o, const char *str) { o_addblock(o, str, strlen(str)); } + #if !BB_MMU static void nommu_addchr(o_string *o, int ch) { @@ -2618,7 +2620,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char /* lookup the variable in question */ if (isdigit(var[0])) { - /* handle_dollar() should have vetted var for us */ + /* parse_dollar() should have vetted var for us */ i = xatoi_u(var); if (i < G.global_argc) val = G.global_argv[i]; @@ -4545,11 +4547,11 @@ static void debug_print_tree(struct pipe *pi, int lvl) fprintf(stderr, " group %s: (argv=%p)%s%s\n", CMDTYPE[command->cmd_type], argv -#if !BB_MMU +# if !BB_MMU , " group_as_string:", command->group_as_string -#else +# else , "", "" -#endif +# endif ); debug_print_tree(command->group, lvl+1); prn++; @@ -5988,18 +5990,18 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign /* Return code: 0 for OK, 1 for syntax error */ #if BB_MMU -#define handle_dollar(as_string, dest, input) \ - handle_dollar(dest, input) +#define parse_dollar(as_string, dest, input) \ + parse_dollar(dest, input) #define as_string NULL #endif -static int handle_dollar(o_string *as_string, +static int parse_dollar(o_string *as_string, o_string *dest, struct in_str *input) { int ch = i_peek(input); /* first character after the $ */ unsigned char quote_mask = dest->o_escape ? 0x80 : 0; - debug_printf_parse("handle_dollar entered: ch='%c'\n", ch); + debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); if (isalpha(ch)) { ch = i_getch(input); nommu_addchr(as_string, ch); @@ -6047,7 +6049,7 @@ static int handle_dollar(o_string *as_string, if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */ bad_dollar_syntax: syntax_error_unterm_str("${name}"); - debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); + debug_printf_parse("parse_dollar return 1: unterminated ${name}\n"); return 1; } ch |= quote_mask; @@ -6129,13 +6131,11 @@ static int handle_dollar(o_string *as_string, if (!BB_MMU) pos = dest->length; add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG); -#if !BB_MMU if (as_string) { o_addstr(as_string, dest->data + pos); o_addchr(as_string, ')'); o_addchr(as_string, ')'); } -#endif o_addchr(dest, SPECIAL_VAR_SYMBOL); break; } @@ -6146,12 +6146,10 @@ static int handle_dollar(o_string *as_string, if (!BB_MMU) pos = dest->length; add_till_closing_bracket(dest, input, ')'); -#if !BB_MMU if (as_string) { o_addstr(as_string, dest->data + pos); o_addchr(as_string, ')'); } -#endif o_addchr(dest, SPECIAL_VAR_SYMBOL); # endif break; @@ -6174,7 +6172,7 @@ static int handle_dollar(o_string *as_string, default: o_addQchr(dest, '$'); } - debug_printf_parse("handle_dollar return 0\n"); + debug_printf_parse("parse_dollar return 0\n"); return 0; #undef as_string } @@ -6237,9 +6235,9 @@ static int parse_stream_dquoted(o_string *as_string, goto again; } if (ch == '$') { - if (handle_dollar(as_string, dest, input) != 0) { + if (parse_dollar(as_string, dest, input) != 0) { debug_printf_parse("parse_stream_dquoted return 1: " - "handle_dollar returned non-0\n"); + "parse_dollar returned non-0\n"); return 1; } goto again; @@ -6602,9 +6600,9 @@ static struct pipe *parse_stream(char **pstring, #endif break; case '$': - if (handle_dollar(&ctx.as_string, &dest, input) != 0) { + if (parse_dollar(&ctx.as_string, &dest, input) != 0) { debug_printf_parse("parse_stream parse error: " - "handle_dollar returned non-0\n"); + "parse_dollar returned non-0\n"); goto parse_error; } break; @@ -6630,19 +6628,16 @@ static struct pipe *parse_stream(char **pstring, break; #if ENABLE_HUSH_TICK case '`': { -#if !BB_MMU - int pos; -#endif + unsigned pos; + o_addchr(&dest, SPECIAL_VAR_SYMBOL); o_addchr(&dest, '`'); -#if !BB_MMU pos = dest.length; -#endif add_till_backquote(&dest, input); -#if !BB_MMU +# if !BB_MMU o_addstr(&ctx.as_string, dest.data + pos); o_addchr(&ctx.as_string, '`'); -#endif +# endif o_addchr(&dest, SPECIAL_VAR_SYMBOL); //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos); break; -- cgit v1.2.3-55-g6feb From 8391c4800cda0eda6e64c464cc0ea85d1785a768 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 17:50:43 +0200 Subject: hush: trivial code shrink function old new delta static_get 26 22 -4 Signed-off-by: Denys Vlasenko --- shell/hush.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index e9d31b436..72cfb232a 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -814,10 +814,10 @@ static void xxfree(void *ptr) fdprintf(2, "free %p\n", ptr); free(ptr); } -#define xmalloc(s) xxmalloc(__LINE__, s) -#define xrealloc(p, s) xxrealloc(__LINE__, p, s) -#define xstrdup(s) xxstrdup(__LINE__, s) -#define free(p) xxfree(p) +# define xmalloc(s) xxmalloc(__LINE__, s) +# define xrealloc(p, s) xxrealloc(__LINE__, p, s) +# define xstrdup(s) xxstrdup(__LINE__, s) +# define free(p) xxfree(p) #endif @@ -1161,9 +1161,9 @@ static void SIGCHLD_handler(int sig UNUSED_PARAM) #if ENABLE_HUSH_JOB /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ -#define disable_restore_tty_pgrp_on_exit() (die_sleep = 0) +# define disable_restore_tty_pgrp_on_exit() (die_sleep = 0) /* After [v]fork, in parent: restore tty pgrp on xfunc death */ -#define enable_restore_tty_pgrp_on_exit() (die_sleep = -1) +# define enable_restore_tty_pgrp_on_exit() (die_sleep = -1) /* Restores tty foreground process group, and exits. * May be called as signal handler for fatal signal @@ -1189,8 +1189,8 @@ static void sigexit(int sig) } #else -#define disable_restore_tty_pgrp_on_exit() ((void)0) -#define enable_restore_tty_pgrp_on_exit() ((void)0) +# define disable_restore_tty_pgrp_on_exit() ((void)0) +# define enable_restore_tty_pgrp_on_exit() ((void)0) #endif @@ -1522,8 +1522,8 @@ static void unset_vars(char **strings) } #if ENABLE_SH_MATH_SUPPORT -#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) -#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) +# define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) +# define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) static char* FAST_FUNC endofname(const char *name) { char *p; @@ -1607,10 +1607,11 @@ static struct variable *set_vars_and_save_old(char **strings) */ static int FAST_FUNC static_get(struct in_str *i) { - int ch = *i->p++; - if (ch != '\0') + int ch = *i->p; + if (ch != '\0') { + i->p++; return ch; - i->p--; + } return EOF; } @@ -1662,7 +1663,7 @@ static void get_user_input(struct in_str *i) const char *prompt_str; prompt_str = setup_prompt_string(i->promptmode); -#if ENABLE_FEATURE_EDITING +# if ENABLE_FEATURE_EDITING /* Enable command line editing only while a command line * is actually being read */ do { @@ -1678,7 +1679,7 @@ static void get_user_input(struct in_str *i) G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ G.user_input_buf[1] = '\0'; } -#else +# else do { G.flag_SIGINT = 0; fputs(prompt_str, stdout); @@ -1688,7 +1689,7 @@ static void get_user_input(struct in_str *i) //do we need check_and_run_traps(0)? (maybe only if stdin) } while (G.flag_SIGINT); i->eof_flag = (r == EOF); -#endif +# endif i->p = G.user_input_buf; } @@ -2221,7 +2222,7 @@ static int o_glob(o_string *o, int n) return n; } -#else +#else /* !HUSH_BRACE_EXP */ /* Helper */ static int glob_needed(const char *s) @@ -2298,7 +2299,7 @@ static int o_glob(o_string *o, int n) return n; } -#endif +#endif /* !HUSH_BRACE_EXP */ /* If o->o_glob == 1, glob the string so far remembered. * Otherwise, just finish current list[] and start new */ @@ -3033,7 +3034,7 @@ static void re_execute_shell(char ***to_free, const char *s, char *g_argv0, char **g_argv, char **builtin_argv) { -#define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x")) +# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x")) /* delims + 2 * (number of bytes in printed hex numbers) */ char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)]; char *heredoc_argv[4]; @@ -3078,7 +3079,7 @@ static void re_execute_shell(char ***to_free, const char *s, , empty_trap_mask IF_HUSH_LOOPS(, G.depth_of_loop) ); -#undef NOMMU_HACK_FMT +# undef NOMMU_HACK_FMT /* 1:hush 2:-$::: * 3:-c 4: 5: 6:NULL */ -- cgit v1.2.3-55-g6feb From fef9ee70727452954d2c5d28cc65e8b0fffcd6f1 Mon Sep 17 00:00:00 2001 From: Maksym Kryzhanovskyy Date: Sat, 22 May 2010 20:41:08 +0200 Subject: fuser: code shrink, fixing default signal, exitcode and PID display Defult signal is KILL, not TERM. We used to not display PIDs with -k but without -s, but upstream (fuser from PSmisc 22.7) still shows PIDs. Filtering out of our own PID was buggy. function old new delta fuser_main 669 918 +249 search_dev_inode 67 74 +7 add_pid 38 39 +1 scan_pid_maps 225 222 -3 add_inode 91 88 -3 packed_usage 27047 27039 -8 scan_dir_links 102 76 -26 scan_link 78 46 -32 file_to_dev_inode 64 - -64 scan_proc_net 307 - -307 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 3/5 up/down: 257/-443) Total: -186 bytes Signed-off-by: Maksym Kryzhanovskyy Signed-off-by: Denys Vlasenko --- include/usage.h | 6 +- procps/fuser.c | 258 +++++++++++++++++++++++++++----------------------------- 2 files changed, 125 insertions(+), 139 deletions(-) diff --git a/include/usage.h b/include/usage.h index a295ab865..8f695f8b8 100644 --- a/include/usage.h +++ b/include/usage.h @@ -1474,9 +1474,9 @@ "\n -m Find processes which use same fs as FILEs" \ "\n -4 Search only IPv4 space" \ "\n -6 Search only IPv6 space" \ - "\n -s Silent: just exit with 0 if any processes are found" \ - "\n -k Kill found processes (otherwise display PIDs)" \ - "\n -SIGNAL Signal to send (default: TERM)" \ + "\n -s Don't display PIDs" \ + "\n -k Kill found processes" \ + "\n -SIGNAL Signal to send (default: KILL)" \ #define getenforce_trivial_usage NOUSAGE_STR #define getenforce_full_usage "" 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 { pid_t pid; } pid_list; + +struct globals { + pid_list *pid_list_head; + inode_list *inode_list_head; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define INIT_G() do { } while (0) + + static dev_t find_socket_dev(void) { int fd = socket(AF_INET, SOCK_DGRAM, 0); @@ -44,16 +53,6 @@ static dev_t find_socket_dev(void) return 0; } -static int file_to_dev_inode(const char *filename, dev_t *dev, ino_t *inode) -{ - struct stat f_stat; - if (stat(filename, &f_stat)) - return 0; - *inode = f_stat.st_ino; - *dev = f_stat.st_dev; - return 1; -} - static char *parse_net_arg(const char *arg, unsigned *port) { char path[20], tproto[5]; @@ -63,54 +62,54 @@ static char *parse_net_arg(const char *arg, unsigned *port) sprintf(path, "/proc/net/%s", tproto); if (access(path, R_OK) != 0) return NULL; - return xstrdup(tproto); + return xstrdup(path); } -static pid_list *add_pid(pid_list *plist, pid_t pid) +static void add_pid(const pid_t pid) { - pid_list *curr = plist; - while (curr != NULL) { - if (curr->pid == pid) - return plist; - curr = curr->next; + pid_list **curr = &G.pid_list_head; + + while (*curr) { + if ((*curr)->pid == pid) + return; + curr = &(*curr)->next; } - curr = xmalloc(sizeof(pid_list)); - curr->pid = pid; - curr->next = plist; - return curr; + + *curr = xzalloc(sizeof(pid_list)); + (*curr)->pid = pid; } -static inode_list *add_inode(inode_list *ilist, dev_t dev, ino_t inode) +static void add_inode(const struct stat *st) { - inode_list *curr = ilist; - while (curr != NULL) { - if (curr->inode == inode && curr->dev == dev) - return ilist; - curr = curr->next; + inode_list **curr = &G.inode_list_head; + + while (*curr) { + if ((*curr)->dev == st->st_dev + && (*curr)->inode == st->st_ino + ) { + return; + } + curr = &(*curr)->next; } - curr = xmalloc(sizeof(inode_list)); - curr->dev = dev; - curr->inode = inode; - curr->next = ilist; - return curr; + + *curr = xzalloc(sizeof(inode_list)); + (*curr)->dev = st->st_dev; + (*curr)->inode = st->st_ino; } -static inode_list *scan_proc_net(const char *proto, - unsigned port, inode_list *ilist) +static void scan_proc_net(const char *path, unsigned port) { - char path[20], line[MAX_LINE + 1]; - ino_t tmp_inode; - dev_t tmp_dev; + char line[MAX_LINE + 1]; long long uint64_inode; unsigned tmp_port; FILE *f; + struct stat st; - tmp_dev = find_socket_dev(); + st.st_dev = find_socket_dev(); - sprintf(path, "/proc/net/%s", proto); f = fopen_for_read(path); if (!f) - return ilist; + return; while (fgets(line, MAX_LINE, f)) { char addr[68]; @@ -124,22 +123,23 @@ static inode_list *scan_proc_net(const char *proto, if (len > 8 && (option_mask32 & OPT_IP4)) continue; if (tmp_port == port) { - tmp_inode = uint64_inode; - ilist = add_inode(ilist, tmp_dev, tmp_inode); + st.st_ino = uint64_inode; + add_inode(&st); } } } fclose(f); - return ilist; } -static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode) +static int search_dev_inode(const struct stat *st) { + inode_list *ilist = G.inode_list_head; + while (ilist) { - if (ilist->dev == dev) { + if (ilist->dev == st->st_dev) { if (option_mask32 & OPT_MOUNT) return 1; - if (ilist->inode == inode) + if (ilist->inode == st->st_ino) return 1; } ilist = ilist->next; @@ -147,48 +147,42 @@ static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode) return 0; } -static pid_list *scan_pid_maps(const char *fname, pid_t pid, - inode_list *ilist, pid_list *plist) +static void scan_pid_maps(const char *fname, pid_t pid) { FILE *file; char line[MAX_LINE + 1]; int major, minor; - ino_t inode; long long uint64_inode; - dev_t dev; + struct stat st; file = fopen_for_read(fname); if (!file) - return plist; + return; + while (fgets(line, MAX_LINE, file)) { if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) continue; - inode = uint64_inode; - if (major == 0 && minor == 0 && inode == 0) + st.st_ino = uint64_inode; + if (major == 0 && minor == 0 && st.st_ino == 0) continue; - dev = makedev(major, minor); - if (search_dev_inode(ilist, dev, inode)) - plist = add_pid(plist, pid); + st.st_dev = makedev(major, minor); + if (search_dev_inode(&st)) + add_pid(pid); } fclose(file); - return plist; } -static pid_list *scan_link(const char *lname, pid_t pid, - inode_list *ilist, pid_list *plist) +static void scan_link(const char *lname, pid_t pid) { - ino_t inode; - dev_t dev; + struct stat st; - if (!file_to_dev_inode(lname, &dev, &inode)) - return plist; - if (search_dev_inode(ilist, dev, inode)) - plist = add_pid(plist, pid); - return plist; + if (stat(lname, &st) >= 0) { + if (search_dev_inode(&st)) + add_pid(pid); + } } -static pid_list *scan_dir_links(const char *dname, pid_t pid, - inode_list *ilist, pid_list *plist) +static void scan_dir_links(const char *dname, pid_t pid) { DIR *d; struct dirent *de; @@ -196,102 +190,73 @@ static pid_list *scan_dir_links(const char *dname, pid_t pid, d = opendir(dname); if (!d) - return plist; + return; + while ((de = readdir(d)) != NULL) { lname = concat_subpath_file(dname, de->d_name); if (lname == NULL) continue; - plist = scan_link(lname, pid, ilist, plist); + scan_link(lname, pid); free(lname); } closedir(d); - return plist; } /* NB: does chdir internally */ -static pid_list *scan_proc_pids(inode_list *ilist) +static void scan_proc_pids(void) { DIR *d; struct dirent *de; pid_t pid; - pid_list *plist; xchdir("/proc"); d = opendir("/proc"); if (!d) - return NULL; + return; - plist = NULL; while ((de = readdir(d)) != NULL) { pid = (pid_t)bb_strtou(de->d_name, NULL, 10); if (errno) continue; if (chdir(de->d_name) < 0) continue; - plist = scan_link("cwd", pid, ilist, plist); - plist = scan_link("exe", pid, ilist, plist); - plist = scan_link("root", pid, ilist, plist); - plist = scan_dir_links("fd", pid, ilist, plist); - plist = scan_dir_links("lib", pid, ilist, plist); - plist = scan_dir_links("mmap", pid, ilist, plist); - plist = scan_pid_maps("maps", pid, ilist, plist); - xchdir("/proc"); - } - closedir(d); - return plist; -} + scan_link("cwd", pid); + scan_link("exe", pid); + scan_link("root", pid); -static int print_pid_list(pid_list *plist) -{ - while (plist != NULL) { - printf("%u ", (unsigned)plist->pid); - plist = plist->next; - } - bb_putchar('\n'); - return 1; -} - -static int kill_pid_list(pid_list *plist, int sig) -{ - pid_t mypid = getpid(); - int success = 1; + scan_dir_links("fd", pid); + scan_dir_links("lib", pid); + scan_dir_links("mmap", pid); - while (plist != NULL) { - if (plist->pid != mypid) { - if (kill(plist->pid, sig) != 0) { - bb_perror_msg("kill pid %u", (unsigned)plist->pid); - success = 0; - } - } - plist = plist->next; + scan_pid_maps("maps", pid); + xchdir("/proc"); } - return success; + closedir(d); } int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fuser_main(int argc UNUSED_PARAM, char **argv) { pid_list *plist; - inode_list *ilist; + pid_t mypid; char **pp; - dev_t dev; - ino_t inode; + struct stat st; unsigned port; int opt; - int success; + int exitcode; int killsig; /* -fuser [options] FILEs or PORT/PROTOs +fuser [OPTIONS] FILE or PORT/PROTO Find processes which use FILEs or PORTs -m Find processes which use same fs as FILEs -4 Search only IPv4 space -6 Search only IPv6 space - -s Silent: just exit with 0 if any processes are found - -k Kill found processes (otherwise display PIDs) - -SIGNAL Signal to send (default: TERM) + -s Don't display PIDs + -k Kill found processes + -SIGNAL Signal to send (default: KILL) */ /* Handle -SIGNAL. Oh my... */ - killsig = SIGTERM; + killsig = SIGKILL; /* yes, the default is not SIGTERM */ pp = argv; while (*++pp) { char *arg = *pp; @@ -313,33 +278,54 @@ Find processes which use FILEs or PORTs break; } + opt_complementary = "-1"; /* at least one param */ opt = getopt32(argv, OPTION_STRING); argv += optind; - ilist = NULL; pp = argv; while (*pp) { - char *proto = parse_net_arg(*pp, &port); - if (proto) { /* PORT/PROTO */ - ilist = scan_proc_net(proto, port, ilist); - free(proto); + char *path = parse_net_arg(*pp, &port); + if (path) { /* PORT/PROTO */ + scan_proc_net(path, port); + free(path); } else { /* FILE */ - if (!file_to_dev_inode(*pp, &dev, &inode)) - bb_perror_msg_and_die("can't open '%s'", *pp); - ilist = add_inode(ilist, dev, inode); + xstat(*pp, &st); + add_inode(&st); } pp++; } - plist = scan_proc_pids(ilist); /* changes dir to "/proc" */ + scan_proc_pids(); /* changes dir to "/proc" */ - if (!plist) - return EXIT_FAILURE; - success = 1; - if (opt & OPT_KILL) { - success = kill_pid_list(plist, killsig); - } else if (!(opt & OPT_SILENT)) { - success = print_pid_list(plist); + mypid = getpid(); + plist = G.pid_list_head; + while (1) { + if (!plist) + return EXIT_FAILURE; + if (plist->pid != mypid) + break; + plist = plist->next; } - return (success != 1); /* 0 == success */ + + exitcode = EXIT_SUCCESS; + do { + if (plist->pid != mypid) { + if (opt & OPT_KILL) { + if (kill(plist->pid, killsig) != 0) { + bb_perror_msg("kill pid %u", (unsigned)plist->pid); + exitcode = EXIT_FAILURE; + } + } + if (!(opt & OPT_SILENT)) { + printf("%u ", (unsigned)plist->pid); + } + } + plist = plist->next; + } while (plist); + + if (!(opt & (OPT_SILENT))) { + bb_putchar('\n'); + } + + return exitcode; } -- cgit v1.2.3-55-g6feb From 765b0eed3ef29a80115708c3249d3a541509cd24 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 21:17:46 +0200 Subject: getty: do not emit bogus error message on EOF Signed-off-by: Denys Vlasenko --- loginutils/getty.c | 1 + 1 file changed, 1 insertion(+) 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, while (cp->eol == '\0') { /* Do not report trivial EINTR/EIO errors. */ + errno = EINTR; /* make read of 0 bytes be silent too */ if (read(STDIN_FILENO, &c, 1) < 1) { if (errno == EINTR || errno == EIO) exit(EXIT_SUCCESS); -- cgit v1.2.3-55-g6feb From 9e07219c7814972893d1f3bb67b43108fe83212a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 23 May 2010 00:34:42 +0200 Subject: ps: make "ps -o BAD" emit list of good -o params also make ps help more informative function old new delta find_out_spec 58 103 +45 packed_usage 27039 27079 +40 Signed-off-by: Denys Vlasenko --- include/usage.h | 8 ++++---- procps/ps.c | 22 ++++++++++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/include/usage.h b/include/usage.h index 8f695f8b8..d53b86731 100644 --- a/include/usage.h +++ b/include/usage.h @@ -3574,11 +3574,11 @@ #if ENABLE_DESKTOP #define ps_trivial_usage \ - "" + "[-o COL1,COL2=HEADER]" IF_FEATURE_SHOW_THREADS(" [-T]") #define ps_full_usage "\n\n" \ - "Report process status\n" \ + "Show list of processes\n" \ "\nOptions:" \ - "\n -o col1,col2=header Select columns for display" \ + "\n -o COL1,COL2=HEADER Select columns for display" \ IF_FEATURE_SHOW_THREADS( \ "\n -T Show threads" \ ) @@ -3594,7 +3594,7 @@ #define ps_trivial_usage \ "" #define ps_full_usage "\n\n" \ - "Report process status\n" \ + "Show list of processes\n" \ USAGE_PS \ IF_SELINUX( \ "\n -Z Show selinux context" \ 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) snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); } - #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 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) sprintf(buf, "%*d", size, ps->niceness); } -#endif /* FEATURE_PS_ADDITIONAL_COLUMNS */ +#endif #if ENABLE_FEATURE_PS_TIME + static void func_etime(char *buf, int size, const procps_status_t *ps) { /* 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) mm /= 60; snprintf(buf, size+1, "%3lu:%02u", mm, ss); } + #endif #if ENABLE_SELINUX @@ -337,11 +338,24 @@ static ps_out_t* new_out_t(void) static const ps_out_t* find_out_spec(const char *name) { unsigned i; +#if ENABLE_DESKTOP + char buf[ARRAY_SIZE(out_spec)*7 + 1]; + char *p = buf; +#endif + for (i = 0; i < ARRAY_SIZE(out_spec); i++) { - if (!strncmp(name, out_spec[i].name6, 6)) + if (strncmp(name, out_spec[i].name6, 6) == 0) return &out_spec[i]; +#if ENABLE_DESKTOP + p += sprintf(p, "%.6s,", out_spec[i].name6); +#endif } - bb_error_msg_and_die("bad -o argument '%s'", name); +#if ENABLE_DESKTOP + p[-1] = '\0'; + bb_error_msg_and_die("bad -o argument '%s', supported arguments: %s", name, buf); +#else + bb_error_msg_and_die("bad -o argument '%s'"); +#endif } static void parse_o(char* opt) -- cgit v1.2.3-55-g6feb From 4f63c7931c42351e38619842681026ff2c20c7ee Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 23 May 2010 16:14:06 +0200 Subject: fix unescaped $ in makefile Signed-off-by: Denys Vlasenko --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 14f5cd149..c231092be 100644 --- a/Makefile +++ b/Makefile @@ -1001,8 +1001,8 @@ $(mrproper-dirs): mrproper: clean archmrproper $(mrproper-dirs) $(call cmd,rmdirs) $(call cmd,rmfiles) - @find -name Config.src | sed 's/.src$/.in/' | xargs -r rm -f - @find -name Kbuild.src | sed 's/.src$//' | xargs -r rm -f + @find -name Config.src | sed 's/.src$$/.in/' | xargs -r rm -f + @find -name Kbuild.src | sed 's/.src$$//' | xargs -r rm -f # distclean # -- cgit v1.2.3-55-g6feb