From 4ac9819263114edb9b5b638ffa6d2e41a4bb46e7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 8 Oct 2009 03:06:04 +0200 Subject: [PATCH] apply post-1.15.1 fixes Signed-off-by: Denys Vlasenko --- coreutils/dd.c | 11 +- coreutils/uniq.c | 1 + editors/sed.c | 4 +- findutils/find.c | 68 ++-- include/libbb.h | 2 + libbb/lineedit.c | 49 +-- libbb/procps.c | 133 +++---- libbb/recursive_action.c | 17 +- miscutils/hdparm.c | 3 + shell/ash.c | 418 +++++++++++++--------- shell/ash_test/ash-redir/redir7.right | 3 + shell/ash_test/ash-redir/redir7.tests | 12 + shell/ash_test/ash-redir/redir8.right | 3 + shell/ash_test/ash-redir/redir8.tests | 15 + shell/ash_test/ash-signals/savetrap.right | 8 + shell/ash_test/ash-signals/savetrap.tests | 9 + shell/hush.c | 188 ++++++---- shell/hush_test/hush-trap/savetrap.right | 8 + shell/hush_test/hush-trap/savetrap.tests | 9 + shell/hush_test/hush-trap/subshell.right | 6 + shell/hush_test/hush-trap/subshell.tests | 20 ++ util-linux/fdisk.c | 5 +- 22 files changed, 636 insertions(+), 356 deletions(-) create mode 100644 shell/ash_test/ash-redir/redir7.right create mode 100755 shell/ash_test/ash-redir/redir7.tests create mode 100644 shell/ash_test/ash-redir/redir8.right create mode 100755 shell/ash_test/ash-redir/redir8.tests create mode 100644 shell/ash_test/ash-signals/savetrap.right create mode 100755 shell/ash_test/ash-signals/savetrap.tests create mode 100644 shell/hush_test/hush-trap/savetrap.right create mode 100755 shell/hush_test/hush-trap/savetrap.tests create mode 100644 shell/hush_test/hush-trap/subshell.right create mode 100755 shell/hush_test/hush-trap/subshell.tests diff --git a/coreutils/dd.c b/coreutils/dd.c index 5281d8118..381f60075 100644 --- a/coreutils/dd.c +++ b/coreutils/dd.c @@ -286,25 +286,26 @@ int dd_main(int argc UNUSED_PARAM, char **argv) } while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { - if (flags & FLAG_NOERROR) /* Pre-zero the buffer if conv=noerror */ - memset(ibuf, 0, ibs); n = safe_read(ifd, ibuf, ibs); if (n == 0) break; if (n < 0) { + /* "Bad block" */ if (!(flags & FLAG_NOERROR)) goto die_infile; - n = ibs; bb_simple_perror_msg(infile); - /* GNU dd with conv=noerror skips over "bad blocks" */ + /* GNU dd with conv=noerror skips over bad blocks */ xlseek(ifd, ibs, SEEK_CUR); + /* conv=noerror,sync writes NULs, + * conv=noerror just ignores input bad blocks */ + n = 0; } if ((size_t)n == ibs) G.in_full++; else { G.in_part++; if (flags & FLAG_SYNC) { - memset(ibuf + n, '\0', ibs - n); + memset(ibuf + n, 0, ibs - n); n = ibs; } } diff --git a/coreutils/uniq.c b/coreutils/uniq.c index 126eaeef9..e703659c6 100644 --- a/coreutils/uniq.c +++ b/coreutils/uniq.c @@ -84,6 +84,7 @@ int uniq_main(int argc UNUSED_PARAM, char **argv) break; } + free((char*)s1); ++dups; /* note: testing for overflow seems excessive. */ } diff --git a/editors/sed.c b/editors/sed.c index 8b4f60a8c..3f044caef 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -690,10 +690,8 @@ static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p) if (sed_cmd->which_match) break; - if (*line == '\0') - break; //maybe (G.regmatch[0].rm_eo ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL? - } while (regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH); + } while (*line && regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH); /* Copy rest of string into output pipeline */ while (1) { diff --git a/findutils/find.c b/findutils/find.c index 5e8193ffa..ff239dbdd 100644 --- a/findutils/find.c +++ b/findutils/find.c @@ -62,9 +62,6 @@ /* This is a NOEXEC applet. Be very careful! */ -IF_FEATURE_FIND_XDEV(static dev_t *xdev_dev;) -IF_FEATURE_FIND_XDEV(static int xdev_count;) - typedef int (*action_fp)(const char *fileName, struct stat *statbuf, void *) FAST_FUNC; typedef struct { @@ -100,9 +97,24 @@ IF_FEATURE_FIND_DELETE( ACTS(delete)) IF_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned *subst_count; int exec_argc;)) IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;)) -static action ***actions; -static bool need_print = 1; -static int recurse_flags = ACTION_RECURSE; +struct globals { + IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;) + IF_FEATURE_FIND_XDEV(int xdev_count;) + action ***actions; + bool need_print; + recurse_flags_t recurse_flags; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define INIT_G() do { \ + struct G_sizecheck { \ + char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ + }; \ + IF_FEATURE_FIND_XDEV(G.xdev_dev = NULL;) \ + IF_FEATURE_FIND_XDEV(G.xdev_count = 0;) \ + G.actions = NULL; \ + G.need_print = 1; \ + G.recurse_flags = ACTION_RECURSE; \ +} while (0) #if ENABLE_FEATURE_FIND_EXEC static unsigned count_subst(const char *str) @@ -363,7 +375,7 @@ ACTF(context) security_context_t con; int rc; - if (recurse_flags & ACTION_FOLLOWLINKS) { + if (G.recurse_flags & ACTION_FOLLOWLINKS) { rc = getfilecon(fileName, &con); } else { rc = lgetfilecon(fileName, &con); @@ -392,18 +404,18 @@ static int FAST_FUNC fileAction(const char *fileName, #endif #if ENABLE_FEATURE_FIND_XDEV - if (S_ISDIR(statbuf->st_mode) && xdev_count) { - for (i = 0; i < xdev_count; i++) { - if (xdev_dev[i] == statbuf->st_dev) + if (S_ISDIR(statbuf->st_mode) && G.xdev_count) { + for (i = 0; i < G.xdev_count; i++) { + if (G.xdev_dev[i] == statbuf->st_dev) break; } - if (i == xdev_count) + if (i == G.xdev_count) return SKIP; } #endif - i = exec_actions(actions, fileName, statbuf); + i = exec_actions(G.actions, fileName, statbuf); /* Had no explicit -print[0] or -exec? then print */ - if ((i & TRUE) && need_print) + if ((i & TRUE) && G.need_print) puts(fileName); /* Cannot return 0: our caller, recursive_action(), * will perror() and skip dirs (if called on dir) */ @@ -431,7 +443,7 @@ static int find_type(const char *type) else if (*type == 's') mask = S_IFSOCK; - if (mask == 0 || *(type + 1) != '\0') + if (mask == 0 || type[1] != '\0') bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type"); return mask; @@ -592,21 +604,21 @@ static action*** parse_params(char **argv) /* --- Tests and actions --- */ else if (parm == PARM_print) { - need_print = 0; + G.need_print = 0; /* GNU find ignores '!' here: "find ! -print" */ IF_FEATURE_FIND_NOT( invert_flag = 0; ) (void) ALLOC_ACTION(print); } #if ENABLE_FEATURE_FIND_PRINT0 else if (parm == PARM_print0) { - need_print = 0; + G.need_print = 0; IF_FEATURE_FIND_NOT( invert_flag = 0; ) (void) ALLOC_ACTION(print0); } #endif #if ENABLE_FEATURE_FIND_DEPTH else if (parm == PARM_depth) { - recurse_flags |= ACTION_DEPTHFIRST; + G.recurse_flags |= ACTION_DEPTHFIRST; } #endif #if ENABLE_FEATURE_FIND_PRUNE @@ -617,8 +629,8 @@ static action*** parse_params(char **argv) #endif #if ENABLE_FEATURE_FIND_DELETE else if (parm == PARM_delete) { - need_print = 0; - recurse_flags |= ACTION_DEPTHFIRST; + G.need_print = 0; + G.recurse_flags |= ACTION_DEPTHFIRST; (void) ALLOC_ACTION(delete); } #endif @@ -626,7 +638,7 @@ static action*** parse_params(char **argv) else if (parm == PARM_exec) { int i; action_exec *ap; - need_print = 0; + G.need_print = 0; IF_FEATURE_FIND_NOT( invert_flag = 0; ) ap = ALLOC_ACTION(exec); ap->exec_argv = ++argv; /* first arg after -exec */ @@ -834,6 +846,8 @@ IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,) #define minmaxdepth NULL #endif + INIT_G(); + for (firstopt = 1; firstopt < argc; firstopt++) { if (argv[firstopt][0] == '-') break; @@ -861,21 +875,21 @@ IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,) while ((arg = argp[0])) { int opt = index_in_strings(options, arg); if (opt == OPT_FOLLOW) { - recurse_flags |= ACTION_FOLLOWLINKS; + G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK; argp[0] = (char*)"-a"; } #if ENABLE_FEATURE_FIND_XDEV if (opt == OPT_XDEV) { struct stat stbuf; - if (!xdev_count) { - xdev_count = firstopt - 1; - xdev_dev = xmalloc(xdev_count * sizeof(dev_t)); + if (!G.xdev_count) { + G.xdev_count = firstopt - 1; + G.xdev_dev = xmalloc(G.xdev_count * sizeof(dev_t)); for (i = 1; i < firstopt; i++) { /* not xstat(): shouldn't bomb out on * "find not_exist exist -xdev" */ if (stat(argv[i], &stbuf)) stbuf.st_dev = -1L; - xdev_dev[i-1] = stbuf.st_dev; + G.xdev_dev[i-1] = stbuf.st_dev; } } argp[0] = (char*)"-a"; @@ -894,11 +908,11 @@ IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,) argp++; } - actions = parse_params(&argv[firstopt]); + G.actions = parse_params(&argv[firstopt]); for (i = 1; i < firstopt; i++) { if (!recursive_action(argv[i], - recurse_flags, /* flags */ + G.recurse_flags,/* flags */ fileAction, /* file action */ fileAction, /* dir action */ #if ENABLE_FEATURE_FIND_MAXDEPTH diff --git a/include/libbb.h b/include/libbb.h index c795e6aad..104a39904 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -286,7 +286,9 @@ enum { ACTION_DEPTHFIRST = (1 << 3), /*ACTION_REVERSE = (1 << 4), - unused */ ACTION_QUIET = (1 << 5), + ACTION_DANGLING_OK = (1 << 6), }; +typedef uint8_t recurse_flags_t; extern int recursive_action(const char *fileName, unsigned flags, int FAST_FUNC (*fileAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), int FAST_FUNC (*dirAction)(const char *fileName, struct stat* statbuf, void* userData, int depth), diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 9a773b4b8..071fc5bce 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -114,8 +114,8 @@ struct lineedit_statics { unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ unsigned cursor; - unsigned command_len; - /* *int* maxsize: we want x in "if (x > S.maxsize)" + int command_len; /* must be signed (^D returns -1 len) */ + /* signed maxsize: we want x in "if (x > S.maxsize)" * to _not_ be promoted to unsigned */ int maxsize; CHAR_T *command_ps; @@ -1095,15 +1095,15 @@ static void save_command_ps_at_cur_history(void) int cur = state->cur_history; free(state->history[cur]); -#if ENABLE_FEATURE_ASSUME_UNICODE +# if ENABLE_FEATURE_ASSUME_UNICODE { char tbuf[MAX_LINELEN]; save_string(tbuf, sizeof(tbuf)); state->history[cur] = xstrdup(tbuf); } -#else +# else state->history[cur] = xstrdup(command_ps); -#endif +# endif } } @@ -1131,7 +1131,7 @@ static int get_next_history(void) return 0; } -#if ENABLE_FEATURE_EDITING_SAVEHISTORY +# if ENABLE_FEATURE_EDITING_SAVEHISTORY /* We try to ensure that concurrent additions to the history * do not overwrite each other. * Otherwise shell users get unhappy. @@ -1256,10 +1256,10 @@ static void save_history(char *str) free_line_input_t(st_temp); } } -#else -#define load_history(a) ((void)0) -#define save_history(a) ((void)0) -#endif /* FEATURE_COMMAND_SAVEHISTORY */ +# else +# define load_history(a) ((void)0) +# define save_history(a) ((void)0) +# endif /* FEATURE_COMMAND_SAVEHISTORY */ static void remember_in_history(char *str) { @@ -1290,15 +1290,15 @@ static void remember_in_history(char *str) /* i <= MAX_HISTORY */ state->cur_history = i; state->cnt_history = i; -#if ENABLE_FEATURE_EDITING_SAVEHISTORY +# if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY if ((state->flags & SAVE_HISTORY) && state->hist_file) save_history(str); -#endif +# endif IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;) } #else /* MAX_HISTORY == 0 */ -#define remember_in_history(a) ((void)0) +# define remember_in_history(a) ((void)0) #endif /* MAX_HISTORY */ @@ -1476,11 +1476,11 @@ static void parse_and_put_prompt(const char *prmt_ptr) c = *prmt_ptr++; switch (c) { -#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR +# if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR case 'u': pbuf = user_buf ? user_buf : (char*)""; break; -#endif +# endif case 'h': pbuf = free_me = safe_gethostname(); *strchrnul(pbuf, '.') = '\0'; @@ -1488,7 +1488,7 @@ static void parse_and_put_prompt(const char *prmt_ptr) case '$': c = (geteuid() == 0 ? '#' : '$'); break; -#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR +# if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR case 'w': /* /home/user[/something] -> ~[/something] */ pbuf = cwd_buf; @@ -1501,7 +1501,7 @@ static void parse_and_put_prompt(const char *prmt_ptr) pbuf = free_me = xasprintf("~%s", cwd_buf + l); } break; -#endif +# endif case 'W': pbuf = cwd_buf; cp = strrchr(pbuf, '/'); @@ -1688,13 +1688,15 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li /* With null flags, no other fields are ever used */ state = st ? st : (line_input_t*) &const_int_0; -#if ENABLE_FEATURE_EDITING_SAVEHISTORY +#if MAX_HISTORY > 0 +# if ENABLE_FEATURE_EDITING_SAVEHISTORY if ((state->flags & SAVE_HISTORY) && state->hist_file) if (state->cnt_history == 0) load_history(state); -#endif +# endif if (state->flags & DO_HISTORY) state->cur_history = state->cnt_history; +#endif /* prepare before init handlers */ cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */ @@ -1716,7 +1718,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li new_settings.c_cc[VTIME] = 0; /* Turn off CTRL-C, so we can trap it */ #ifndef _POSIX_VDISABLE -#define _POSIX_VDISABLE '\0' +# define _POSIX_VDISABLE '\0' #endif new_settings.c_cc[VINTR] = _POSIX_VDISABLE; tcsetattr_stdin_TCSANOW(&new_settings); @@ -2037,7 +2039,8 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li rewrite_line: /* Rewrite the line with the selected history item */ /* change command */ - command_len = load_string(state->history[state->cur_history] ? : "", maxsize); + command_len = load_string(state->history[state->cur_history] ? + state->history[state->cur_history] : "", maxsize); /* redraw and go to eol (bol, in vi) */ redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); break; @@ -2121,7 +2124,9 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li #undef command #if ENABLE_FEATURE_ASSUME_UNICODE - command_len = save_string(command, maxsize - 1); + command[0] = '\0'; + if (command_len > 0) + command_len = save_string(command, maxsize - 1); free(command_ps); #endif diff --git a/libbb/procps.c b/libbb/procps.c index 307d8d622..7276f60ef 100644 --- a/libbb/procps.c +++ b/libbb/procps.c @@ -134,8 +134,8 @@ static unsigned long fast_strtoul_16(char **endptr) return n; } /* TOPMEM uses fast_strtoul_10, so... */ -#undef ENABLE_FEATURE_FAST_TOP -#define ENABLE_FEATURE_FAST_TOP 1 +# undef ENABLE_FEATURE_FAST_TOP +# define ENABLE_FEATURE_FAST_TOP 1 #endif #if ENABLE_FEATURE_FAST_TOP @@ -198,14 +198,16 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) if (errno) continue; - /* After this point we have to break, not continue - * ("continue" would mean that current /proc/NNN - * is not a valid process info) */ + /* After this point we can: + * "break": stop parsing, return the data + * "continue": try next /proc/XXX + */ memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz)); sp->pid = pid; - if (!(flags & ~PSSCAN_PID)) break; + if (!(flags & ~PSSCAN_PID)) + break; /* we needed only pid, we got it */ #if ENABLE_SELINUX if (flags & PSSCAN_CONTEXT) { @@ -218,7 +220,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) if (flags & PSSCAN_UIDGID) { if (stat(filename, &sb)) - break; + continue; /* process probably exited */ /* Effective UID/GID, not real */ sp->uid = sb.st_uid; sp->gid = sb.st_gid; @@ -234,10 +236,10 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) strcpy(filename_tail, "stat"); n = read_to_buf(filename, buf); if (n < 0) - break; + continue; /* process probably exited */ cp = strrchr(buf, ')'); /* split into "PID (cmd" and "" */ /*if (!cp || cp[1] != ' ') - break;*/ + continue;*/ cp[0] = '\0'; if (sizeof(sp->comm) < 16) BUG_comm_size(); @@ -257,12 +259,12 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) "%lu " /* start_time */ "%lu " /* vsize */ "%lu " /* rss */ -#if ENABLE_FEATURE_TOP_SMP_PROCESS +# if ENABLE_FEATURE_TOP_SMP_PROCESS "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ "%*s %*s %*s %*s " /*signal, blocked, sigignore, sigcatch */ "%*s %*s %*s %*s " /*wchan, nswap, cnswap, exit_signal */ "%d" /*cpu last seen on*/ -#endif +# endif , sp->state, &sp->ppid, &sp->pgid, &sp->sid, &tty, @@ -271,17 +273,17 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) &sp->start_time, &vsz, &rss -#if ENABLE_FEATURE_TOP_SMP_PROCESS +# if ENABLE_FEATURE_TOP_SMP_PROCESS , &sp->last_seen_on_cpu -#endif +# endif ); if (n < 11) - break; -#if ENABLE_FEATURE_TOP_SMP_PROCESS + continue; /* bogus data, get next /proc/XXX */ +# if ENABLE_FEATURE_TOP_SMP_PROCESS if (n < 11+15) sp->last_seen_on_cpu = 0; -#endif +# endif /* vsz is in bytes and we want kb */ sp->vsz = vsz >> 10; @@ -311,14 +313,14 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) sp->vsz = fast_strtoul_10(&cp) >> 10; /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; -#if ENABLE_FEATURE_TOP_SMP_PROCESS +# if ENABLE_FEATURE_TOP_SMP_PROCESS /* (6): rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ /* (4): signal, blocked, sigignore, sigcatch */ /* (4): wchan, nswap, cnswap, exit_signal */ cp = skip_fields(cp, 14); //FIXME: is it safe to assume this field exists? sp->last_seen_on_cpu = fast_strtoul_10(&cp); -#endif +# endif #endif /* end of !ENABLE_FEATURE_TOP_SMP_PROCESS */ #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS @@ -343,48 +345,48 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) strcpy(filename_tail, "smaps"); file = fopen_for_read(filename); - if (!file) - break; - while (fgets(buf, sizeof(buf), file)) { - unsigned long sz; - char *tp; - char w; + if (file) { + while (fgets(buf, sizeof(buf), file)) { + unsigned long sz; + char *tp; + char w; #define SCAN(str, name) \ if (strncmp(buf, str, sizeof(str)-1) == 0) { \ tp = skip_whitespace(buf + sizeof(str)-1); \ sp->name += fast_strtoul_10(&tp); \ continue; \ } - SCAN("Shared_Clean:" , shared_clean ); - SCAN("Shared_Dirty:" , shared_dirty ); - SCAN("Private_Clean:", private_clean); - SCAN("Private_Dirty:", private_dirty); + SCAN("Shared_Clean:" , shared_clean ); + SCAN("Shared_Dirty:" , shared_dirty ); + SCAN("Private_Clean:", private_clean); + SCAN("Private_Dirty:", private_dirty); #undef SCAN - // f7d29000-f7d39000 rw-s ADR M:m OFS FILE - tp = strchr(buf, '-'); - if (tp) { - *tp = ' '; - tp = buf; - sz = fast_strtoul_16(&tp); /* start */ - sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */ - // tp -> "rw-s" string - w = tp[1]; - // skipping "rw-s ADR M:m OFS " - tp = skip_whitespace(skip_fields(tp, 4)); - // filter out /dev/something (something != zero) - if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { - if (w == 'w') { - sp->mapped_rw += sz; - } else if (w == '-') { - sp->mapped_ro += sz; + // f7d29000-f7d39000 rw-s ADR M:m OFS FILE + tp = strchr(buf, '-'); + if (tp) { + *tp = ' '; + tp = buf; + sz = fast_strtoul_16(&tp); /* start */ + sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */ + // tp -> "rw-s" string + w = tp[1]; + // skipping "rw-s ADR M:m OFS " + tp = skip_whitespace(skip_fields(tp, 4)); + // filter out /dev/something (something != zero) + if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { + if (w == 'w') { + sp->mapped_rw += sz; + } else if (w == '-') { + sp->mapped_ro += sz; + } } - } //else printf("DROPPING %s (%s)\n", buf, tp); - if (strcmp(tp, "[stack]\n") == 0) - sp->stack += sz; + if (strcmp(tp, "[stack]\n") == 0) + sp->stack += sz; + } } + fclose(file); } - fclose(file); } #endif /* TOPMEM */ #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS @@ -393,23 +395,34 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) strcpy(filename_tail, "status"); file = fopen_for_read(filename); - if (!file) - break; - while (fgets(buf, sizeof(buf), file)) { - char *tp; + if (file) { + while (fgets(buf, sizeof(buf), file)) { + char *tp; #define SCAN_TWO(str, name, statement) \ if (strncmp(buf, str, sizeof(str)-1) == 0) { \ tp = skip_whitespace(buf + sizeof(str)-1); \ sscanf(tp, "%u", &sp->name); \ statement; \ } - SCAN_TWO("Uid:", ruid, continue); - SCAN_TWO("Gid:", rgid, break); + SCAN_TWO("Uid:", ruid, continue); + SCAN_TWO("Gid:", rgid, break); #undef SCAN_TWO + } + fclose(file); } - fclose(file); } #endif /* PS_ADDITIONAL_COLUMNS */ + if (flags & PSSCAN_EXE) { + strcpy(filename_tail, "exe"); + free(sp->exe); + sp->exe = xmalloc_readlink(filename); + } + /* Note: if /proc/PID/cmdline is empty, + * code below "breaks". Therefore it must be + * the last code to parse /proc/PID/xxx data + * (we used to have /proc/PID/exe parsing after it + * and were getting stale sp->exe). + */ #if 0 /* PSSCAN_CMD is not used */ if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) { free(sp->argv0); @@ -452,13 +465,9 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) } } #endif - if (flags & PSSCAN_EXE) { - strcpy(filename_tail, "exe"); - free(sp->exe); - sp->exe = xmalloc_readlink(filename); - } break; - } + } /* for (;;) */ + return sp; } diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c index 3ec596a35..3a4eb28db 100644 --- a/libbb/recursive_action.c +++ b/libbb/recursive_action.c @@ -61,6 +61,7 @@ int FAST_FUNC recursive_action(const char *fileName, unsigned depth) { struct stat statbuf; + unsigned follow; int status; DIR *dir; struct dirent *next; @@ -68,14 +69,22 @@ int FAST_FUNC recursive_action(const char *fileName, if (!fileAction) fileAction = true_action; if (!dirAction) dirAction = true_action; - status = ACTION_FOLLOWLINKS; /* hijack a variable for bitmask... */ - if (!depth) - status = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0; - status = ((flags & status) ? stat : lstat)(fileName, &statbuf); + follow = ACTION_FOLLOWLINKS; + if (depth == 0) + follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0; + follow &= flags; + status = (follow ? stat : lstat)(fileName, &statbuf); if (status < 0) { #ifdef DEBUG_RECURS_ACTION bb_error_msg("status=%d flags=%x", status, flags); #endif + if ((flags & ACTION_DANGLING_OK) + && errno == ENOENT + && lstat(fileName, &statbuf) == 0 + ) { + /* Dangling link */ + return fileAction(fileName, &statbuf, userData, depth); + } goto done_nak_warn; } diff --git a/miscutils/hdparm.c b/miscutils/hdparm.c index 60682231a..0917b4175 100644 --- a/miscutils/hdparm.c +++ b/miscutils/hdparm.c @@ -15,6 +15,9 @@ /* must be _after_ libbb.h: */ #include #include +#if !defined(BLKGETSIZE64) +# define BLKGETSIZE64 _IOR(0x12,114,size_t) +#endif /* device types */ /* ------------ */ diff --git a/shell/ash.c b/shell/ash.c index 4a163ffbc..952961373 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -112,7 +112,7 @@ enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; static const char homestr[] ALIGN1 = "HOME"; static const char snlfmt[] ALIGN1 = "%s\n"; -static const char illnum[] ALIGN1 = "Illegal number: %s"; +static const char msg_illnum[] ALIGN1 = "Illegal number: %s"; /* * We enclose jmp_buf in a structure so that we can declare pointers to @@ -142,17 +142,10 @@ struct globals_misc { struct jmploc *exception_handler; -// disabled by vda: cannot understand how it was supposed to work - -// cannot fix bugs. That's why you have to explain your non-trivial designs! -// /* do we generate EXSIG events */ -// int exsig; /* counter */ - volatile int suppressint; /* counter */ -// TODO: rename -// pendingsig -> pending_sig -// intpending -> pending_int - volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */ + volatile int suppress_int; /* counter */ + volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ /* last pending signal */ - volatile /*sig_atomic_t*/ smallint pendingsig; + volatile /*sig_atomic_t*/ smallint pending_sig; smallint exception_type; /* kind of exception (0..5) */ /* exceptions */ #define EXINT 0 /* SIGINT received */ @@ -200,6 +193,7 @@ struct globals_misc { /* indicates specified signal received */ uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ char *trap[NSIG]; + char **trap_ptr; /* used only by "trap hack" */ /* Rarely referenced stuff */ #if ENABLE_ASH_RANDOM_SUPPORT @@ -220,16 +214,16 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; #define arg0 (G_misc.arg0 ) #define exception_handler (G_misc.exception_handler) #define exception_type (G_misc.exception_type ) -#define suppressint (G_misc.suppressint ) -#define intpending (G_misc.intpending ) -//#define exsig (G_misc.exsig ) -#define pendingsig (G_misc.pendingsig ) +#define suppress_int (G_misc.suppress_int ) +#define pending_int (G_misc.pending_int ) +#define pending_sig (G_misc.pending_sig ) #define isloginsh (G_misc.isloginsh ) #define nullstr (G_misc.nullstr ) #define optlist (G_misc.optlist ) #define sigmode (G_misc.sigmode ) #define gotsig (G_misc.gotsig ) #define trap (G_misc.trap ) +#define trap_ptr (G_misc.trap_ptr ) #define random_galois_LFSR (G_misc.random_galois_LFSR) #define random_LCG (G_misc.random_LCG ) #define backgndpid (G_misc.backgndpid ) @@ -239,6 +233,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; barrier(); \ curdir = nullstr; \ physdir = nullstr; \ + trap_ptr = trap; \ } while (0) @@ -283,7 +278,7 @@ static int isdigit_str9(const char *str) * more fun than worrying about efficiency and portability. :-)) */ #define INT_OFF do { \ - suppressint++; \ + suppress_int++; \ xbarrier(); \ } while (0) @@ -324,11 +319,11 @@ raise_interrupt(void) { int ex_type; - intpending = 0; + pending_int = 0; /* Signal is not automatically unmasked after it is raised, * do it ourself - unmask all signals */ sigprocmask_allsigs(SIG_UNBLOCK); - /* pendingsig = 0; - now done in onsig() */ + /* pending_sig = 0; - now done in onsig() */ ex_type = EXSIG; if (gotsig[SIGINT - 1] && !trap[SIGINT]) { @@ -353,7 +348,7 @@ static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void int_on(void) { xbarrier(); - if (--suppressint == 0 && intpending) { + if (--suppress_int == 0 && pending_int) { raise_interrupt(); } } @@ -362,18 +357,18 @@ static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void force_int_on(void) { xbarrier(); - suppressint = 0; - if (intpending) + suppress_int = 0; + if (pending_int) raise_interrupt(); } #define FORCE_INT_ON force_int_on() -#define SAVE_INT(v) ((v) = suppressint) +#define SAVE_INT(v) ((v) = suppress_int) #define RESTORE_INT(v) do { \ xbarrier(); \ - suppressint = (v); \ - if (suppressint == 0 && intpending) \ + suppress_int = (v); \ + if (suppress_int == 0 && pending_int) \ raise_interrupt(); \ } while (0) @@ -461,15 +456,15 @@ out2str(const char *p) /* ============ Parser structures */ /* control characters in argument strings */ -#define CTLESC '\201' /* escape next character */ -#define CTLVAR '\202' /* variable defn */ -#define CTLENDVAR '\203' -#define CTLBACKQ '\204' +#define CTLESC ((unsigned char)'\201') /* escape next character */ +#define CTLVAR ((unsigned char)'\202') /* variable defn */ +#define CTLENDVAR ((unsigned char)'\203') +#define CTLBACKQ ((unsigned char)'\204') #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ /* CTLBACKQ | CTLQUOTE == '\205' */ -#define CTLARI '\206' /* arithmetic expression */ -#define CTLENDARI '\207' -#define CTLQUOTEMARK '\210' +#define CTLARI ((unsigned char)'\206') /* arithmetic expression */ +#define CTLENDARI ((unsigned char)'\207') +#define CTLQUOTEMARK ((unsigned char)'\210') /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ @@ -685,7 +680,7 @@ trace_printf(const char *fmt, ...) if (DEBUG_PID) fprintf(tracefile, "[%u] ", (int) getpid()); if (DEBUG_SIG) - fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); + fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); va_start(va, fmt); vfprintf(tracefile, fmt, va); va_end(va); @@ -701,7 +696,7 @@ trace_vprintf(const char *fmt, va_list va) if (DEBUG_PID) fprintf(tracefile, "[%u] ", (int) getpid()); if (DEBUG_SIG) - fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); + fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); vfprintf(tracefile, fmt, va); } @@ -1556,7 +1551,7 @@ static int number(const char *s) { if (!is_number(s)) - ash_msg_and_raise_error(illnum, s); + ash_msg_and_raise_error(msg_illnum, s); return atoi(s); } @@ -2351,8 +2346,6 @@ setprompt(int whichprompt) #define CD_PHYSICAL 1 #define CD_PRINT 2 -static int docd(const char *, int); - static int cdopt(void) { @@ -2360,7 +2353,7 @@ cdopt(void) int i, j; j = 'L'; - while ((i = nextopt("LP"))) { + while ((i = nextopt("LP")) != '\0') { if (i != j) { flags ^= CD_PHYSICAL; j = i; @@ -2710,8 +2703,8 @@ SIT(int c, int syntax) } else #endif { - if ((unsigned char)c >= (unsigned char)(CTLESC) - && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK) + if ((unsigned char)c >= CTLESC + && (unsigned char)c <= CTLQUOTEMARK ) { return CCTL; } @@ -3240,9 +3233,9 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) #define FORK_NOJOB 2 /* mode flags for showjob(s) */ -#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ -#define SHOW_PID 0x04 /* include process pid */ -#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ +#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */ +#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */ +#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */ /* * A job structure contains information about a job. A job is either a @@ -3250,7 +3243,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) * latter case, pidlist will be non-NULL, and will point to a -1 terminated * array of pids. */ - struct procstat { pid_t pid; /* process id */ int status; /* last process status from wait() */ @@ -3316,14 +3308,14 @@ onsig(int signo) { gotsig[signo - 1] = 1; - if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) { - if (!suppressint) { - pendingsig = 0; + if (signo == SIGINT && !trap[SIGINT]) { + if (!suppress_int) { + pending_sig = 0; raise_interrupt(); /* does not return */ } - intpending = 1; + pending_int = 1; } else { - pendingsig = signo; + pending_sig = signo; } } @@ -3546,7 +3538,6 @@ getjob(const char *name, int getctl) } if (is_number(p)) { -// TODO: number() instead? It does error checking... num = atoi(p); if (num < njobs) { jp = jobtab + num - 1; @@ -3918,7 +3909,7 @@ static int blocking_wait_with_raise_on_sig(struct job *job) { pid_t pid = dowait(DOWAIT_BLOCK, job); - if (pid <= 0 && pendingsig) + if (pid <= 0 && pending_sig) raise_exception(EXSIG); return pid; } @@ -3935,7 +3926,7 @@ showjob(FILE *out, struct job *jp, int mode) ps = jp->ps; - if (mode & SHOW_PGID) { + if (mode & SHOW_ONLY_PGID) { /* jobs -p */ /* just output process (group) id of pipeline */ fprintf(out, "%d\n", ps->pid); return; @@ -3945,11 +3936,11 @@ showjob(FILE *out, struct job *jp, int mode) indent_col = col; if (jp == curjob) - s[col - 2] = '+'; + s[col - 3] = '+'; else if (curjob && jp == curjob->prev_job) - s[col - 2] = '-'; + s[col - 3] = '-'; - if (mode & SHOW_PID) + if (mode & SHOW_PIDS) col += fmtstr(s + col, 16, "%d ", ps->pid); psend = ps + jp->nprocs; @@ -3963,25 +3954,32 @@ showjob(FILE *out, struct job *jp, int mode) status = jp->stopstatus; col += sprint_status(s + col, status, 0); } + /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ + /* This loop either prints " | | " line + * or prints several "PID | " lines, + * depending on SHOW_PIDS bit. + * We do not print status of individual processes + * between PID and . bash does it, but not very well: + * first line shows overall job status, not process status, + * making it impossible to know 1st process status. + */ goto start; - - do { + while (1) { /* for each process */ - col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3; + s[0] = '\0'; + col = 33; + if (mode & SHOW_PIDS) + col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->pid) - 1; start: - fprintf(out, "%s%*c%s", - s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd - ); - if (!(mode & SHOW_PID)) { - showpipe(jp, out); - break; - } - if (++ps == psend) { - outcslow('\n', out); + fprintf(out, "%s%*c", s, 33 - col >= 0 ? 33 - col : 0, ' '); + if (ps != jp->ps) + fprintf(out, "| "); + fprintf(out, "%s", ps->cmd); + if (++ps == psend) break; - } - } while (1); + } + outcslow('\n', out); jp->changed = 0; @@ -4019,17 +4017,17 @@ jobscmd(int argc UNUSED_PARAM, char **argv) int mode, m; mode = 0; - while ((m = nextopt("lp"))) { + while ((m = nextopt("lp")) != '\0') { if (m == 'l') - mode = SHOW_PID; + mode |= SHOW_PIDS; else - mode = SHOW_PGID; + mode |= SHOW_ONLY_PGID; } argv = argptr; if (*argv) { do - showjob(stdout, getjob(*argv,0), mode); + showjob(stdout, getjob(*argv, 0), mode); while (*++argv); } else showjobs(stdout, mode); @@ -4073,9 +4071,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv) int retval; struct job *jp; -// exsig++; -// xbarrier(); - if (pendingsig) + if (pending_sig) raise_exception(EXSIG); nextopt(nullstr); @@ -4311,7 +4307,7 @@ cmdputs(const char *s) if (!str) continue; dostr: - while ((c = *str++)) { + while ((c = *str++) != '\0') { USTPUTC(c, nextc); } } @@ -4530,9 +4526,11 @@ clear_traps(void) for (tp = trap; tp < &trap[NSIG]; tp++) { if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ INT_OFF; - free(*tp); + if (trap_ptr == trap) + free(*tp); + /* else: it "belongs" to trap_ptr vector, don't free */ *tp = NULL; - if (tp != &trap[0]) + if ((tp - trap) != 0) setsignal(tp - trap); INT_ON; } @@ -4546,7 +4544,7 @@ static void closescript(void); #if !JOBS # define forkchild(jp, n, mode) forkchild(jp, mode) #endif -static void +static NOINLINE void forkchild(struct job *jp, union node *n, int mode) { int oldlvl; @@ -4560,6 +4558,53 @@ forkchild(struct job *jp, union node *n, int mode) * Do we do it correctly? */ closescript(); + + if (mode == FORK_NOJOB /* is it `xxx` ? */ + && n && n->type == NCMD /* is it single cmd? */ + /* && n->ncmd.args->type == NARG - always true? */ + && strcmp(n->ncmd.args->narg.text, "trap") == 0 + && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */ + /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */ + ) { + TRACE(("Trap hack\n")); + /* Awful hack for `trap` or $(trap). + * + * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html + * contains an example where "trap" is executed in a subshell: + * + * save_traps=$(trap) + * ... + * eval "$save_traps" + * + * Standard does not say that "trap" in subshell shall print + * parent shell's traps. It only says that its output + * must have suitable form, but then, in the above example + * (which is not supposed to be normative), it implies that. + * + * bash (and probably other shell) does implement it + * (traps are reset to defaults, but "trap" still shows them), + * but as a result, "trap" logic is hopelessly messed up: + * + * # trap + * trap -- 'echo Ho' SIGWINCH <--- we have a handler + * # (trap) <--- trap is in subshell - no output (correct, traps are reset) + * # true | trap <--- trap is in subshell - no output (ditto) + * # echo `true | trap` <--- in subshell - output (but traps are reset!) + * trap -- 'echo Ho' SIGWINCH + * # echo `(trap)` <--- in subshell in subshell - output + * trap -- 'echo Ho' SIGWINCH + * # echo `true | (trap)` <--- in subshell in subshell in subshell - output! + * trap -- 'echo Ho' SIGWINCH + * + * The rules when to forget and when to not forget traps + * get really complex and nonsensical. + * + * Our solution: ONLY bare $(trap) or `trap` is special. + */ + /* Save trap handler strings for trap builtin to print */ + trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap)); + /* Fall through into clearing traps */ + } clear_traps(); #if JOBS /* do job control only in root shell */ @@ -4604,8 +4649,14 @@ forkchild(struct job *jp, union node *n, int mode) setsignal(SIGQUIT); } #if JOBS - if (n && n->type == NCMD && strcmp(n->ncmd.args->narg.text, "jobs") == 0) { + if (n && n->type == NCMD + && strcmp(n->ncmd.args->narg.text, "jobs") == 0 + ) { TRACE(("Job hack\n")); + /* "jobs": we do not want to clear job list for it, + * instead we remove only _its_ own_ job from job list. + * This makes "jobs .... | cat" more useful. + */ freejob(curjob); return; } @@ -4998,7 +5049,7 @@ struct redirtab { struct redirtab *next; int nullredirs; int pair_count; - struct two_fd_t two_fd[0]; + struct two_fd_t two_fd[]; }; #define redirlist (G_var.redirlist) @@ -5309,7 +5360,7 @@ ash_arith(const char *s) #define EXP_WORD 0x80 /* expand word in parameter expansion */ #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ /* - * _rmescape() flags + * rmescape() flags */ #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ @@ -5363,7 +5414,7 @@ esclen(const char *start, const char *p) { size_t esc = 0; - while (p > start && *--p == CTLESC) { + while (p > start && (unsigned char)*--p == CTLESC) { esc++; } return esc; @@ -5373,19 +5424,19 @@ esclen(const char *start, const char *p) * Remove any CTLESC characters from a string. */ static char * -_rmescapes(char *str, int flag) +rmescapes(char *str, int flag) { static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; char *p, *q, *r; unsigned inquotes; - int notescaped; - int globbing; + unsigned protect_against_glob; + unsigned globbing; p = strpbrk(str, qchars); - if (!p) { + if (!p) return str; - } + q = p; r = str; if (flag & RMESCAPE_ALLOC) { @@ -5404,28 +5455,33 @@ _rmescapes(char *str, int flag) q = (char *)memcpy(q, str, len) + len; } } + inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; globbing = flag & RMESCAPE_GLOB; - notescaped = globbing; + protect_against_glob = globbing; while (*p) { if (*p == CTLQUOTEMARK) { +// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0 +// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok? +// Note: both inquotes and protect_against_glob only affect whether +// CTLESC, gets converted to or to \ inquotes = ~inquotes; p++; - notescaped = globbing; + protect_against_glob = globbing; continue; } if (*p == '\\') { /* naked back slash */ - notescaped = 0; + protect_against_glob = 0; goto copy; } if (*p == CTLESC) { p++; - if (notescaped && inquotes && *p != '/') { + if (protect_against_glob && inquotes && *p != '/') { *q++ = '\\'; } } - notescaped = globbing; + protect_against_glob = globbing; copy: *q++ = *p++; } @@ -5436,8 +5492,6 @@ _rmescapes(char *str, int flag) } return r; } -#define rmescapes(p) _rmescapes((p), 0) - #define pmatch(a, b) !fnmatch((a), (b), 0) /* @@ -5452,7 +5506,7 @@ preglob(const char *pattern, int quoted, int flag) if (quoted) { flag |= RMESCAPE_QUOTED; } - return _rmescapes((char *)pattern, flag); + return rmescapes((char *)pattern, flag); } /* @@ -5463,14 +5517,17 @@ memtodest(const char *p, size_t len, int syntax, int quotes) { char *q = expdest; - q = makestrspace(len * 2, q); + q = makestrspace(quotes ? len * 2 : len, q); while (len--) { int c = signed_char2int(*p++); if (!c) continue; - if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK)) - USTPUTC(CTLESC, q); + if (quotes) { + int n = SIT(c, syntax); + if (n == CCTL || n == CBACK) + USTPUTC(CTLESC, q); + } USTPUTC(c, q); } @@ -5547,13 +5604,13 @@ removerecordregions(int endoff) } static char * -exptilde(char *startp, char *p, int flag) +exptilde(char *startp, char *p, int flags) { char c; char *name; struct passwd *pw; const char *home; - int quotes = flag & (EXP_FULL | EXP_CASE); + int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); int startloc; name = p + 1; @@ -5565,7 +5622,7 @@ exptilde(char *startp, char *p, int flag) case CTLQUOTEMARK: return startp; case ':': - if (flag & EXP_VARTILDE) + if (flags & EXP_VARTILDE) goto done; break; case '/': @@ -5770,7 +5827,7 @@ expari(int quotes) expdest = p; if (quotes) - rmescapes(p + 2); + rmescapes(p + 2, 0); len = cvtnum(ash_arith(p + 2)); @@ -5780,7 +5837,7 @@ expari(int quotes) #endif /* argstr needs it */ -static char *evalvar(char *p, int flag, struct strlist *var_str_list); +static char *evalvar(char *p, int flags, struct strlist *var_str_list); /* * Perform variable and command substitution. If EXP_FULL is set, output CTLESC @@ -5792,7 +5849,7 @@ static char *evalvar(char *p, int flag, struct strlist *var_str_list); * for correct expansion of "B=$A" word. */ static void -argstr(char *p, int flag, struct strlist *var_str_list) +argstr(char *p, int flags, struct strlist *var_str_list) { static const char spclchars[] ALIGN1 = { '=', @@ -5810,42 +5867,44 @@ argstr(char *p, int flag, struct strlist *var_str_list) }; const char *reject = spclchars; int c; - int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ - int breakall = flag & EXP_WORD; + int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ + int breakall = flags & EXP_WORD; int inquotes; size_t length; int startloc; - if (!(flag & EXP_VARTILDE)) { + if (!(flags & EXP_VARTILDE)) { reject += 2; - } else if (flag & EXP_VARTILDE2) { + } else if (flags & EXP_VARTILDE2) { reject++; } inquotes = 0; length = 0; - if (flag & EXP_TILDE) { + if (flags & EXP_TILDE) { char *q; - flag &= ~EXP_TILDE; + flags &= ~EXP_TILDE; tilde: q = p; - if (*q == CTLESC && (flag & EXP_QWORD)) + if (*q == CTLESC && (flags & EXP_QWORD)) q++; if (*q == '~') - p = exptilde(p, q, flag); + p = exptilde(p, q, flags); } start: startloc = expdest - (char *)stackblock(); for (;;) { length += strcspn(p + length, reject); - c = p[length]; - if (c && (!(c & 0x80) + c = (unsigned char) p[length]; + if (c) { + if (!(c & 0x80) #if ENABLE_SH_MATH_SUPPORT - || c == CTLENDARI + || c == CTLENDARI #endif - )) { - /* c == '=' || c == ':' || c == CTLENDARI */ - length++; + ) { + /* c == '=' || c == ':' || c == CTLENDARI */ + length++; + } } if (length > 0) { int newloc; @@ -5863,11 +5922,11 @@ argstr(char *p, int flag, struct strlist *var_str_list) case '\0': goto breakloop; case '=': - if (flag & EXP_VARTILDE2) { + if (flags & EXP_VARTILDE2) { p--; continue; } - flag |= EXP_VARTILDE2; + flags |= EXP_VARTILDE2; reject++; /* fall through */ case ':': @@ -5886,15 +5945,13 @@ argstr(char *p, int flag, struct strlist *var_str_list) goto breakloop; case CTLQUOTEMARK: /* "$@" syntax adherence hack */ - if ( - !inquotes && - !memcmp(p, dolatstr, 4) && - (p[4] == CTLQUOTEMARK || ( - p[4] == CTLENDVAR && - p[5] == CTLQUOTEMARK - )) + if (!inquotes + && memcmp(p, dolatstr, 4) == 0 + && ( p[4] == CTLQUOTEMARK + || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK) + ) ) { - p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1; + p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1; goto start; } inquotes = !inquotes; @@ -5910,10 +5967,10 @@ argstr(char *p, int flag, struct strlist *var_str_list) length++; goto addquote; case CTLVAR: - p = evalvar(p, flag, var_str_list); + p = evalvar(p, flags, var_str_list); goto start; case CTLBACKQ: - c = 0; + c = '\0'; case CTLBACKQ|CTLQUOTE: expbackq(argbackq->n, c, quotes); argbackq = argbackq->next; @@ -6119,15 +6176,15 @@ subevalvar(char *p, char *str, int strloc, int subtype, #if ENABLE_ASH_BASH_COMPAT case VSSUBSTR: loc = str = stackblock() + strloc; -// TODO: number() instead? It does error checking... - pos = atoi(loc); + /* Read POS in ${var:POS:LEN} */ + pos = atoi(loc); /* number(loc) errors out on "1:4" */ len = str - startp - 1; /* *loc != '\0', guaranteed by parser */ if (quotes) { char *ptr; - /* We must adjust the length by the number of escapes we find. */ + /* Adjust the length by the number of escapes */ for (ptr = startp; ptr < (str - 1); ptr++) { if (*ptr == CTLESC) { len--; @@ -6138,15 +6195,22 @@ subevalvar(char *p, char *str, int strloc, int subtype, orig_len = len; if (*loc++ == ':') { -// TODO: number() instead? It does error checking... - len = atoi(loc); + /* ${var::LEN} */ + len = number(loc); } else { + /* Skip POS in ${var:POS:LEN} */ len = orig_len; - while (*loc && *loc != ':') + while (*loc && *loc != ':') { + /* TODO? + * bash complains on: var=qwe; echo ${var:1a:123} + if (!isdigit(*loc)) + ash_msg_and_raise_error(msg_illnum, str); + */ loc++; - if (*loc++ == ':') -// TODO: number() instead? It does error checking... - len = atoi(loc); + } + if (*loc++ == ':') { + len = number(loc); + } } if (pos >= orig_len) { pos = 0; @@ -6190,7 +6254,7 @@ subevalvar(char *p, char *str, int strloc, int subtype, rmesc = startp; rmescend = (char *)stackblock() + strloc; if (quotes) { - rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); + rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); if (rmesc != startp) { rmescend = expdest; startp = (char *)stackblock() + startloc; @@ -6321,7 +6385,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) int syntax; int quoted = varflags & VSQUOTE; int subtype = varflags & VSTYPE; - int quotes = flags & (EXP_FULL | EXP_CASE); + int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); if (quoted && (flags & EXP_FULL)) sep = 1 << CHAR_BIT; @@ -6365,7 +6429,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) ap = shellparam.p; if (!ap) return -1; - while ((p = *ap++)) { + while ((p = *ap++) != NULL) { size_t partlen; partlen = strlen(p); @@ -6399,8 +6463,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) case '7': case '8': case '9': -// TODO: number() instead? It does error checking... - num = atoi(name); + num = atoi(name); /* number(name) fails on ${N#str} etc */ if (num < 0 || num > shellparam.nparam) return -1; p = num ? shellparam.p[num - 1] : arg0; @@ -6452,7 +6515,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) * input string. */ static char * -evalvar(char *p, int flag, struct strlist *var_str_list) +evalvar(char *p, int flags, struct strlist *var_str_list) { char varflags; char subtype; @@ -6463,7 +6526,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) int startloc; ssize_t varlen; - varflags = *p++; + varflags = (unsigned char) *p++; subtype = varflags & VSTYPE; quoted = varflags & VSQUOTE; var = p; @@ -6472,7 +6535,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) p = strchr(p, '=') + 1; again: - varlen = varvalue(var, varflags, flag, var_str_list); + varlen = varvalue(var, varflags, flags, var_str_list); if (varflags & VSNUL) varlen--; @@ -6485,8 +6548,8 @@ evalvar(char *p, int flag, struct strlist *var_str_list) vsplus: if (varlen < 0) { argstr( - p, flag | EXP_TILDE | - (quoted ? EXP_QWORD : EXP_WORD), + p, flags | EXP_TILDE | + (quoted ? EXP_QWORD : EXP_WORD), var_str_list ); goto end; @@ -6558,7 +6621,8 @@ evalvar(char *p, int flag, struct strlist *var_str_list) patloc = expdest - (char *)stackblock(); if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, startloc, varflags, - /* quotes: */ flag & (EXP_FULL | EXP_CASE), +//TODO: | EXP_REDIR too? All other such places do it too + /* quotes: */ flags & (EXP_FULL | EXP_CASE), var_str_list) ) { int amount = expdest - ( @@ -6812,7 +6876,7 @@ expmeta(char *enddir, char *name) p++; if (*p == '.') matchdot++; - while (!intpending && (dp = readdir(dirp)) != NULL) { + while (!pending_int && (dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.' && !matchdot) continue; if (pmatch(start, dp->d_name)) { @@ -6933,7 +6997,7 @@ expandmeta(struct strlist *str /*, int flag*/) */ nometa: *exparg.lastp = str; - rmescapes(str->text); + rmescapes(str->text, 0); exparg.lastp = &str->next; } else { *exparg.lastp = NULL; @@ -6981,7 +7045,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag) expandmeta(exparg.list /*, flag*/); } else { if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ - rmescapes(p); + rmescapes(p, 0); sp = stzalloc(sizeof(*sp)); sp->text = p; *exparg.lastp = sp; @@ -7206,8 +7270,8 @@ shellexec(char **argv, const char *path, int idx) break; } exitstatus = exerrno; - TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", - argv[0], e, suppressint)); + TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", + argv[0], e, suppress_int)); ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); /* NOTREACHED */ } @@ -8009,7 +8073,7 @@ dotrap(void) uint8_t savestatus; savestatus = exitstatus; - pendingsig = 0; + pending_sig = 0; xbarrier(); TRACE(("dotrap entered\n")); @@ -8189,7 +8253,7 @@ evaltree(union node *n, int flags) out1: if (checkexit & exitstatus) evalskip |= SKIPEVAL; - else if (pendingsig && dotrap()) + else if (pending_sig && dotrap()) goto exexit; if (flags & EV_EXIT) { @@ -9109,7 +9173,7 @@ evalcommand(union node *cmd, int flags) if (i == EXINT) exit_status = 128 + SIGINT; if (i == EXSIG) - exit_status = 128 + pendingsig; + exit_status = 128 + pending_sig; exitstatus = exit_status; if (i == EXINT || spclbltin > 0) { raise: @@ -9163,7 +9227,6 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv) exitstatus |= ferror(stdout); clearerr(stdout); commandname = savecmdname; -// exsig = 0; exception_handler = savehandler; return i; @@ -9214,7 +9277,7 @@ breakcmd(int argc UNUSED_PARAM, char **argv) int n = argv[1] ? number(argv[1]) : 1; if (n <= 0) - ash_msg_and_raise_error(illnum, argv[1]); + ash_msg_and_raise_error(msg_illnum, argv[1]); if (n > loopnest) n = loopnest; if (n > 0) { @@ -10022,7 +10085,7 @@ change_random(const char *value) vrandom.flags &= ~VNOFUNC; } else { /* set/reset */ - random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10); + random_galois_LFSR = random_LCG = strtoul(value, NULL, 10); } } #endif @@ -10406,7 +10469,7 @@ parsefname(void) TRACE(("Here document %d\n", n->type)); if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) raise_error_syntax("illegal eof marker for << redirection"); - rmescapes(wordtext); + rmescapes(wordtext, 0); here->eofmark = wordtext; here->next = NULL; if (heredoclist == NULL) @@ -12202,14 +12265,30 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) ap = argptr; if (!*ap) { for (signo = 0; signo < NSIG; signo++) { - if (trap[signo] != NULL) { + char *tr = trap_ptr[signo]; + if (tr) { + /* note: bash adds "SIG", but only if invoked + * as "bash". If called as "sh", or if set -o posix, + * then it prints short signal names. + * We are printing short names: */ out1fmt("trap -- %s %s\n", - single_quote(trap[signo]), + single_quote(tr), get_signame(signo)); + /* trap_ptr != trap only if we are in special-cased `trap` code. + * In this case, we will exit very soon, no need to free(). */ + /* if (trap_ptr != trap && tp[0]) */ + /* free(tr); */ } } + /* + if (trap_ptr != trap) { + free(trap_ptr); + trap_ptr = trap; + } + */ return 0; } + action = NULL; if (ap[1]) action = *ap++; @@ -12705,7 +12784,7 @@ umaskcmd(int argc UNUSED_PARAM, char **argv) mask = 0; do { if (*ap >= '8' || *ap < '0') - ash_msg_and_raise_error(illnum, argv[1]); + ash_msg_and_raise_error(msg_illnum, argv[1]); mask = (mask << 3) + (*ap - '0'); } while (*++ap != '\0'); umask(mask); @@ -12966,6 +13045,7 @@ exitshell(void) if (p) { trap[0] = NULL; evalstring(p, 0); + free(p); } flush_stdout_stderr(); out: @@ -12986,7 +13066,7 @@ init(void) /* from var.c: */ { char **envp; - char ppid[sizeof(int)*3 + 1]; + char ppid[sizeof(int)*3 + 2]; const char *p; struct stat st1, st2; @@ -12997,7 +13077,7 @@ init(void) } } - snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid()); + sprintf(ppid, "%u", (unsigned) getppid()); setvar("PPID", ppid, 0); p = lookupvar("PWD"); @@ -13237,7 +13317,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) } if (sflag || minusc == NULL) { -#if ENABLE_FEATURE_EDITING_SAVEHISTORY +#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY if (iflag) { const char *hp = lookupvar("HISTFILE"); if (hp) diff --git a/shell/ash_test/ash-redir/redir7.right b/shell/ash_test/ash-redir/redir7.right new file mode 100644 index 000000000..6430b0211 --- /dev/null +++ b/shell/ash_test/ash-redir/redir7.right @@ -0,0 +1,3 @@ +Ok +Ok +Done diff --git a/shell/ash_test/ash-redir/redir7.tests b/shell/ash_test/ash-redir/redir7.tests new file mode 100755 index 000000000..17d1040e0 --- /dev/null +++ b/shell/ash_test/ash-redir/redir7.tests @@ -0,0 +1,12 @@ +# Chars above 0x7f are used as special codes. +# 0x81 is CTLESC (see ash.c). +# The bug was that quoting and unquoting of them +# was out of sync for redirect filenames. + +>unicode.sh +echo -e 'echo Ok >uni\x81code' >>unicode.sh +echo -e 'cat uni\x81code' >>unicode.sh +echo -e 'cat uni?code' >>unicode.sh +. unicode.sh +rm uni*code* +echo Done diff --git a/shell/ash_test/ash-redir/redir8.right b/shell/ash_test/ash-redir/redir8.right new file mode 100644 index 000000000..6430b0211 --- /dev/null +++ b/shell/ash_test/ash-redir/redir8.right @@ -0,0 +1,3 @@ +Ok +Ok +Done diff --git a/shell/ash_test/ash-redir/redir8.tests b/shell/ash_test/ash-redir/redir8.tests new file mode 100755 index 000000000..32ab607b8 --- /dev/null +++ b/shell/ash_test/ash-redir/redir8.tests @@ -0,0 +1,15 @@ +# Chars above 0x7f are used as special codes. +# 0x81 is CTLESC (see ash.c). +# The bug was that quoting and unquoting of them +# was out of sync for redirect filenames. + +# Subcase when redirect filename is specified in a variable. + +>unicode.sh +echo -e 'v=uni\x81code' >>unicode.sh +echo -e 'echo Ok >"$v"' >>unicode.sh +echo -e 'cat uni\x81code' >>unicode.sh +echo -e 'cat uni?code' >>unicode.sh +. unicode.sh +rm uni*code* +echo Done diff --git a/shell/ash_test/ash-signals/savetrap.right b/shell/ash_test/ash-signals/savetrap.right new file mode 100644 index 000000000..a59225be3 --- /dev/null +++ b/shell/ash_test/ash-signals/savetrap.right @@ -0,0 +1,8 @@ +trap -- 'echo Exiting' EXIT +trap -- 'echo WINCH!' WINCH +trap -- 'echo Exiting' EXIT +trap -- 'echo WINCH!' WINCH +trap -- 'echo Exiting' EXIT +trap -- 'echo WINCH!' WINCH +Done +Exiting diff --git a/shell/ash_test/ash-signals/savetrap.tests b/shell/ash_test/ash-signals/savetrap.tests new file mode 100755 index 000000000..c2b312fb8 --- /dev/null +++ b/shell/ash_test/ash-signals/savetrap.tests @@ -0,0 +1,9 @@ +trap 'echo Exiting' EXIT +trap 'echo WINCH!' SIGWINCH +v=` trap ` +echo "$v" +v=$( trap ) +echo "$v" +v=`trap` +echo "$v" +echo Done diff --git a/shell/hush.c b/shell/hush.c index 5794b1ddf..b515eabd2 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -78,6 +78,7 @@ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. */ #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ +#include /* for malloc_trim */ #include /* #include */ #if ENABLE_HUSH_CASE @@ -786,7 +787,7 @@ static void xxfree(void *ptr) * HUSH_DEBUG >= 2 prints line number in this file where it was detected. */ #if HUSH_DEBUG < 2 -# define die_if_script(lineno, fmt...) die_if_script(fmt) +# define die_if_script(lineno, ...) die_if_script(__VA_ARGS__) # define syntax_error(lineno, msg) syntax_error(msg) # define syntax_error_at(lineno, msg) syntax_error_at(msg) # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) @@ -855,7 +856,7 @@ static void syntax_error_unexpected_ch(unsigned lineno, int ch) # undef syntax_error_unterm_str # undef syntax_error_unexpected_ch #else -# define die_if_script(fmt...) die_if_script(__LINE__, fmt) +# define die_if_script(...) die_if_script(__LINE__, __VA_ARGS__) # define syntax_error(msg) syntax_error(__LINE__, msg) # define syntax_error_at(msg) syntax_error_at(__LINE__, msg) # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) @@ -898,7 +899,7 @@ static int is_well_formed_var_name(const char *s, char terminator) /* Replace each \x with x in place, return ptr past NUL. */ static char *unbackslash(char *src) { - char *dst = src; + char *dst = src = strchrnul(src, '\\'); while (1) { if (*src == '\\') src++; @@ -1037,7 +1038,7 @@ static void restore_G_args(save_arg_t *sv, char **argv) * is finished or backgrounded. It is the same in interactive and * non-interactive shells, and is the same regardless of whether * a user trap handler is installed or a shell special one is in effect. - * ^C or ^Z from keyboard seem to execute "at once" because it usually + * ^C or ^Z from keyboard seems to execute "at once" because it usually * backgrounds (i.e. stops) or kills all members of currently running * pipe. * @@ -1104,12 +1105,17 @@ static void restore_G_args(save_arg_t *sv, char **argv) * (child shell is not interactive), * unset all traps (note: regardless of child shell's type - {}, (), etc) * after [v]fork, if we plan to exec: - * POSIX says pending signal mask is cleared in child - no need to clear it. + * POSIX says fork clears pending signal mask in child - no need to clear it. * Restore blocked signal set to one inherited by shell just prior to exec. * * Note: as a result, we do not use signal handlers much. The only uses * are to count SIGCHLDs * and to restore tty pgrp on signal-induced exit. + * + * Note 2 (compat): + * Standard says "When a subshell is entered, traps that are not being ignored + * are set to the default actions". bash interprets it so that traps which + * are set to "" (ignore) are NOT reset to defaults. We do the same. */ enum { SPECIAL_INTERACTIVE_SIGS = 0 @@ -2596,43 +2602,51 @@ static void reset_traps_to_defaults(void) { /* This function is always called in a child shell * after fork (not vfork, NOMMU doesn't use this function). - * Child shells are not interactive. - * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling. - * Testcase: (while :; do :; done) + ^Z should background. - * Same goes for SIGTERM, SIGHUP, SIGINT. */ unsigned sig; unsigned mask; + /* Child shells are not interactive. + * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling. + * Testcase: (while :; do :; done) + ^Z should background. + * Same goes for SIGTERM, SIGHUP, SIGINT. + */ if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) - return; + return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ - /* Stupid. It can be done with *single* &= op, but we can't use - * the fact that G.blocked_set is implemented as a bitmask... */ + /* Switching off SPECIAL_INTERACTIVE_SIGS. + * Stupid. It can be done with *single* &= op, but we can't use + * the fact that G.blocked_set is implemented as a bitmask + * in libc... */ mask = (SPECIAL_INTERACTIVE_SIGS >> 1); sig = 1; while (1) { - if (mask & 1) - sigdelset(&G.blocked_set, sig); + if (mask & 1) { + /* Careful. Only if no trap or trap is not "" */ + if (!G.traps || !G.traps[sig] || G.traps[sig][0]) + sigdelset(&G.blocked_set, sig); + } mask >>= 1; if (!mask) break; sig++; } - + /* Our homegrown sig mask is saner to work with :) */ G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; + + /* Resetting all traps to default except empty ones */ mask = G.non_DFL_mask; if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { - if (!G.traps[sig]) + if (!G.traps[sig] || !G.traps[sig][0]) continue; free(G.traps[sig]); G.traps[sig] = NULL; /* There is no signal for 0 (EXIT) */ if (sig == 0) continue; - /* There was a trap handler, we are removing it. + /* There was a trap handler, we just removed it. * But if sig still has non-DFL handling, - * we should not unblock it. */ + * we should not unblock the sig. */ if (mask & 1) continue; sigdelset(&G.blocked_set, sig); @@ -3079,15 +3093,21 @@ static const struct built_in_command* find_builtin(const char *name) } #if ENABLE_HUSH_FUNCTIONS -static const struct function *find_function(const char *name) +static struct function **find_function_slot(const char *name) { - const struct function *funcp = G.top_func; - while (funcp) { - if (strcmp(name, funcp->name) == 0) { + struct function **funcpp = &G.top_func; + while (*funcpp) { + if (strcmp(name, (*funcpp)->name) == 0) { break; } - funcp = funcp->next; + funcpp = &(*funcpp)->next; } + return funcpp; +} + +static const struct function *find_function(const char *name) +{ + const struct function *funcp = *find_function_slot(name); if (funcp) debug_printf_exec("found function '%s'\n", name); return funcp; @@ -3096,18 +3116,11 @@ static const struct function *find_function(const char *name) /* Note: takes ownership on name ptr */ static struct function *new_function(char *name) { - struct function *funcp; - struct function **funcpp = &G.top_func; + struct function **funcpp = find_function_slot(name); + struct function *funcp = *funcpp; - while ((funcp = *funcpp) != NULL) { - struct command *cmd; - - if (strcmp(funcp->name, name) != 0) { - funcpp = &funcp->next; - continue; - } - - cmd = funcp->parent_cmd; + if (funcp != NULL) { + struct command *cmd = funcp->parent_cmd; debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd); if (!cmd) { debug_printf_exec("freeing & replacing function '%s'\n", funcp->name); @@ -3129,39 +3142,36 @@ static struct function *new_function(char *name) cmd->group_as_string = funcp->body_as_string; # endif } - goto skip; + } else { + debug_printf_exec("remembering new function '%s'\n", name); + funcp = *funcpp = xzalloc(sizeof(*funcp)); + /*funcp->next = NULL;*/ } - debug_printf_exec("remembering new function '%s'\n", name); - funcp = *funcpp = xzalloc(sizeof(*funcp)); - /*funcp->next = NULL;*/ - skip: + funcp->name = name; return funcp; } static void unset_func(const char *name) { - struct function *funcp; - struct function **funcpp = &G.top_func; - - while ((funcp = *funcpp) != NULL) { - if (strcmp(funcp->name, name) == 0) { - *funcpp = funcp->next; - /* funcp is unlinked now, deleting it. - * Note: if !funcp->body, the function was created by - * "-F name body", do not free ->body_as_string - * and ->name as they were not malloced. */ - if (funcp->body) { - free_pipe_list(funcp->body); - free(funcp->name); + struct function **funcpp = find_function_slot(name); + struct function *funcp = *funcpp; + + if (funcp != NULL) { + debug_printf_exec("freeing function '%s'\n", funcp->name); + *funcpp = funcp->next; + /* funcp is unlinked now, deleting it. + * Note: if !funcp->body, the function was created by + * "-F name body", do not free ->body_as_string + * and ->name as they were not malloced. */ + if (funcp->body) { + free_pipe_list(funcp->body); + free(funcp->name); # if !BB_MMU - free(funcp->body_as_string); + free(funcp->body_as_string); # endif - } - free(funcp); - break; } - funcpp = &funcp->next; + free(funcp); } } @@ -3628,9 +3638,9 @@ static int checkjobs(struct pipe* fg_pipe) /* Note: is WIFSIGNALED, WEXITSTATUS = sig + 128 */ rcode = WEXITSTATUS(status); IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) - /* bash prints killing signal's name for *last* + /* bash prints killer signal's name for *last* * process in pipe (prints just newline for SIGINT). - * Mimic this. Example: "sleep 5" + ^\ + * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) */ if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); @@ -5183,6 +5193,47 @@ static FILE *generate_stream_from_string(const char *s) xmove_fd(channel[1], 1); /* Prevent it from trying to handle ctrl-z etc */ IF_HUSH_JOB(G.run_list_level = 1;) + /* Awful hack for `trap` or $(trap). + * + * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html + * contains an example where "trap" is executed in a subshell: + * + * save_traps=$(trap) + * ... + * eval "$save_traps" + * + * Standard does not say that "trap" in subshell shall print + * parent shell's traps. It only says that its output + * must have suitable form, but then, in the above example + * (which is not supposed to be normative), it implies that. + * + * bash (and probably other shell) does implement it + * (traps are reset to defaults, but "trap" still shows them), + * but as a result, "trap" logic is hopelessly messed up: + * + * # trap + * trap -- 'echo Ho' SIGWINCH <--- we have a handler + * # (trap) <--- trap is in subshell - no output (correct, traps are reset) + * # true | trap <--- trap is in subshell - no output (ditto) + * # echo `true | trap` <--- in subshell - output (but traps are reset!) + * trap -- 'echo Ho' SIGWINCH + * # echo `(trap)` <--- in subshell in subshell - output + * trap -- 'echo Ho' SIGWINCH + * # echo `true | (trap)` <--- in subshell in subshell in subshell - output! + * trap -- 'echo Ho' SIGWINCH + * + * The rules when to forget and when to not forget traps + * get really complex and nonsensical. + * + * Our solution: ONLY bare $(trap) or `trap` is special. + */ + s = skip_whitespace(s); + if (strncmp(s, "trap", 4) == 0 && (*skip_whitespace(s + 4) == '\0')) + { + static const char *const argv[] = { NULL, NULL }; + builtin_trap((char**)argv); + exit(0); /* not _exit() - we need to fflush */ + } #if BB_MMU reset_traps_to_defaults(); parse_and_run_string(s); @@ -5676,8 +5727,10 @@ static int handle_dollar(o_string *as_string, goto make_var; } /* else: it's $_ */ - /* TODO: */ - /* $_ Shell or shell script name; or last cmd name */ + /* TODO: $_ and $-: */ + /* $_ Shell or shell script name; or last argument of last command + * (if last command wasn't a pipe; if it was, bash sets $_ to ""); + * but in command's env, set to full pathname used to invoke it */ /* $- Option flags set by set builtin or shell options (-i etc) */ default: o_addQchr(dest, '$'); @@ -5794,7 +5847,7 @@ static struct pipe *parse_stream(char **pstring, * found. When recursing, quote state is passed in via dest->o_escape. */ debug_printf_parse("parse_stream entered, end_trigger='%c'\n", - end_trigger ? : 'X'); + end_trigger ? end_trigger : 'X'); debug_enter(); G.ifs = get_local_var_value("IFS"); @@ -6860,7 +6913,8 @@ static int FAST_FUNC builtin_cd(char **argv) * bash says "bash: cd: HOME not set" and does nothing * (exitcode 1) */ - newdir = get_local_var_value("HOME") ? : "/"; + const char *home = get_local_var_value("HOME"); + newdir = home ? home : "/"; } if (chdir(newdir)) { /* Mimic bash message exactly */ @@ -7057,6 +7111,10 @@ static int FAST_FUNC builtin_trap(char **argv) if (G.traps[i]) { printf("trap -- "); print_escaped(G.traps[i]); + /* note: bash adds "SIG", but only if invoked + * as "bash". If called as "sh", or if set -o posix, + * then it prints short signal names. + * We are printing short names: */ printf(" %s\n", get_signame(i)); } } @@ -7268,6 +7326,10 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) void *p; unsigned long l; +#ifdef M_TRIM_THRESHOLD + /* Optional. Reduces probability of false positives */ + malloc_trim(0); +#endif /* Crude attempt to find where "free memory" starts, * sans fragmentation. */ p = malloc(240); diff --git a/shell/hush_test/hush-trap/savetrap.right b/shell/hush_test/hush-trap/savetrap.right new file mode 100644 index 000000000..a59225be3 --- /dev/null +++ b/shell/hush_test/hush-trap/savetrap.right @@ -0,0 +1,8 @@ +trap -- 'echo Exiting' EXIT +trap -- 'echo WINCH!' WINCH +trap -- 'echo Exiting' EXIT +trap -- 'echo WINCH!' WINCH +trap -- 'echo Exiting' EXIT +trap -- 'echo WINCH!' WINCH +Done +Exiting diff --git a/shell/hush_test/hush-trap/savetrap.tests b/shell/hush_test/hush-trap/savetrap.tests new file mode 100755 index 000000000..c2b312fb8 --- /dev/null +++ b/shell/hush_test/hush-trap/savetrap.tests @@ -0,0 +1,9 @@ +trap 'echo Exiting' EXIT +trap 'echo WINCH!' SIGWINCH +v=` trap ` +echo "$v" +v=$( trap ) +echo "$v" +v=`trap` +echo "$v" +echo Done diff --git a/shell/hush_test/hush-trap/subshell.right b/shell/hush_test/hush-trap/subshell.right new file mode 100644 index 000000000..0d20ed4e9 --- /dev/null +++ b/shell/hush_test/hush-trap/subshell.right @@ -0,0 +1,6 @@ +Ok +Ok +Ok +Ok +TERM +Done diff --git a/shell/hush_test/hush-trap/subshell.tests b/shell/hush_test/hush-trap/subshell.tests new file mode 100755 index 000000000..4564c2ee2 --- /dev/null +++ b/shell/hush_test/hush-trap/subshell.tests @@ -0,0 +1,20 @@ +# Non-empty traps should be reset in subshell + +# HUP is special in interactive shells +trap '' HUP +# QUIT is always special +trap '' QUIT +# SYS is not special +trap '' SYS +# WINCH is harmless +trap 'bad: caught WINCH' WINCH +# With TERM we'll check whether it is reset +trap 'bad: caught TERM' TERM + +# using bash, because we don't have $PPID (yet) +(bash -c 'kill -HUP $PPID'; echo Ok) +(bash -c 'kill -QUIT $PPID'; echo Ok) +(bash -c 'kill -SYS $PPID'; echo Ok) +(bash -c 'kill -WINCH $PPID'; echo Ok) +(bash -c 'kill -TERM $PPID'; echo Bad: TERM is not reset) +echo Done diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 441640831..9f766ecd3 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -9,13 +9,16 @@ #ifndef _LARGEFILE64_SOURCE /* For lseek64 */ -#define _LARGEFILE64_SOURCE +# define _LARGEFILE64_SOURCE #endif #include /* assert */ #include #if !defined(BLKSSZGET) # define BLKSSZGET _IO(0x12, 104) #endif +#if !defined(BLKGETSIZE64) +# define BLKGETSIZE64 _IOR(0x12,114,size_t) +#endif #include "libbb.h" /* Looks like someone forgot to add this to config system */ -- 2.25.1