aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2015-10-13 14:45:51 +0100
committerRon Yorston <rmy@pobox.com>2015-10-13 14:45:51 +0100
commit8e509f11bceeec419abc718300bef7422d1fee4c (patch)
treefdfbc752ad94102e3613a5d7254f14a93eaf7f56 /shell
parent420f5edfe7676fe6e7cddbbf15c04649d096e422 (diff)
parent4d0c1ea4784c9844f8468d97ca5c26d3c70f9921 (diff)
downloadbusybox-w32-8e509f11bceeec419abc718300bef7422d1fee4c.tar.gz
busybox-w32-8e509f11bceeec419abc718300bef7422d1fee4c.tar.bz2
busybox-w32-8e509f11bceeec419abc718300bef7422d1fee4c.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c255
-rw-r--r--shell/ash_test/ash-glob/glob1.right2
-rwxr-xr-xshell/ash_test/ash-glob/glob1.tests2
-rw-r--r--shell/ash_test/ash-glob/glob2.right18
-rwxr-xr-xshell/ash_test/ash-glob/glob2.tests27
-rw-r--r--shell/ash_test/ash-glob/glob3.right2
-rwxr-xr-xshell/ash_test/ash-glob/glob3.tests2
-rw-r--r--shell/ash_test/ash-glob/glob_and_assign.right6
-rwxr-xr-xshell/ash_test/ash-glob/glob_and_assign.tests10
-rw-r--r--shell/ash_test/ash-glob/glob_redir.right2
-rwxr-xr-xshell/ash_test/ash-glob/glob_redir.tests9
-rw-r--r--shell/hush.c82
-rw-r--r--shell/hush_test/hush-glob/glob3.right2
-rwxr-xr-xshell/hush_test/hush-glob/glob3.tests2
-rw-r--r--shell/hush_test/hush-misc/nommu3.right2
-rwxr-xr-xshell/hush_test/hush-misc/nommu3.tests15
-rw-r--r--shell/shell_common.c2
17 files changed, 274 insertions, 166 deletions
diff --git a/shell/ash.c b/shell/ash.c
index e9850f493..21fc05f01 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -607,11 +607,12 @@ flush_stdout_stderr(void)
607 INT_ON; 607 INT_ON;
608} 608}
609 609
610/* Was called outcslow(c,FILE*), but c was always '\n' */
610static void 611static void
611outcslow(int c, FILE *dest) 612newline_and_flush(FILE *dest)
612{ 613{
613 INT_OFF; 614 INT_OFF;
614 putc(c, dest); 615 putc('\n', dest);
615 fflush(dest); 616 fflush(dest);
616 INT_ON; 617 INT_ON;
617} 618}
@@ -1272,7 +1273,7 @@ ash_vmsg(const char *msg, va_list ap)
1272 fprintf(stderr, "line %d: ", startlinno); 1273 fprintf(stderr, "line %d: ", startlinno);
1273 } 1274 }
1274 vfprintf(stderr, msg, ap); 1275 vfprintf(stderr, msg, ap);
1275 outcslow('\n', stderr); 1276 newline_and_flush(stderr);
1276} 1277}
1277 1278
1278/* 1279/*
@@ -3528,6 +3529,7 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3528#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */ 3529#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3529#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */ 3530#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3530#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */ 3531#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
3532#define SHOW_STDERR 0x08 /* print to stderr (else stdout) */
3531 3533
3532/* 3534/*
3533 * A job structure contains information about a job. A job is either a 3535 * A job structure contains information about a job. A job is either a
@@ -4046,7 +4048,7 @@ showpipe(struct job *jp /*, FILE *out*/)
4046 psend = jp->ps + jp->nprocs; 4048 psend = jp->ps + jp->nprocs;
4047 for (ps = jp->ps + 1; ps < psend; ps++) 4049 for (ps = jp->ps + 1; ps < psend; ps++)
4048 printf(" | %s", ps->ps_cmd); 4050 printf(" | %s", ps->ps_cmd);
4049 outcslow('\n', stdout); 4051 newline_and_flush(stdout);
4050 flush_stdout_stderr(); 4052 flush_stdout_stderr();
4051} 4053}
4052 4054
@@ -4106,39 +4108,33 @@ fg_bgcmd(int argc UNUSED_PARAM, char **argv)
4106#endif 4108#endif
4107 4109
4108static int 4110static int
4109sprint_status(char *s, int status, int sigonly) 4111sprint_status48(char *s, int status, int sigonly)
4110{ 4112{
4111 int col; 4113 int col;
4112 int st; 4114 int st;
4113 4115
4114 col = 0; 4116 col = 0;
4115 if (!WIFEXITED(status)) { 4117 if (!WIFEXITED(status)) {
4116#if JOBS 4118 if (JOBS && WIFSTOPPED(status))
4117 if (WIFSTOPPED(status))
4118 st = WSTOPSIG(status); 4119 st = WSTOPSIG(status);
4119 else 4120 else
4120#endif
4121 st = WTERMSIG(status); 4121 st = WTERMSIG(status);
4122 if (sigonly) { 4122 if (sigonly) {
4123 if (st == SIGINT || st == SIGPIPE) 4123 if (st == SIGINT || st == SIGPIPE)
4124 goto out; 4124 goto out;
4125#if JOBS 4125 if (JOBS && WIFSTOPPED(status))
4126 if (WIFSTOPPED(status))
4127 goto out; 4126 goto out;
4128#endif
4129 } 4127 }
4130 st &= 0x7f; 4128 st &= 0x7f;
4131//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata 4129//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
4132 col = fmtstr(s, 32, strsignal(st)); 4130 col = fmtstr(s, 32, strsignal(st));
4133 if (WCOREDUMP(status)) { 4131 if (WCOREDUMP(status)) {
4134 col += fmtstr(s + col, 16, " (core dumped)"); 4132 strcpy(s + col, " (core dumped)");
4133 col += sizeof(" (core dumped)")-1;
4135 } 4134 }
4136 } else if (!sigonly) { 4135 } else if (!sigonly) {
4137 st = WEXITSTATUS(status); 4136 st = WEXITSTATUS(status);
4138 if (st) 4137 col = fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st);
4139 col = fmtstr(s, 16, "Done(%d)", st);
4140 else
4141 col = fmtstr(s, 16, "Done");
4142 } 4138 }
4143 out: 4139 out:
4144 return col; 4140 return col;
@@ -4224,7 +4220,6 @@ dowait(int wait_flags, struct job *job)
4224 int status; 4220 int status;
4225 struct job *jp; 4221 struct job *jp;
4226 struct job *thisjob; 4222 struct job *thisjob;
4227 int state;
4228 4223
4229 TRACE(("dowait(0x%x) called\n", wait_flags)); 4224 TRACE(("dowait(0x%x) called\n", wait_flags));
4230 4225
@@ -4246,11 +4241,12 @@ dowait(int wait_flags, struct job *job)
4246 INT_OFF; 4241 INT_OFF;
4247 thisjob = NULL; 4242 thisjob = NULL;
4248 for (jp = curjob; jp; jp = jp->prev_job) { 4243 for (jp = curjob; jp; jp = jp->prev_job) {
4244 int jobstate;
4249 struct procstat *ps; 4245 struct procstat *ps;
4250 struct procstat *psend; 4246 struct procstat *psend;
4251 if (jp->state == JOBDONE) 4247 if (jp->state == JOBDONE)
4252 continue; 4248 continue;
4253 state = JOBDONE; 4249 jobstate = JOBDONE;
4254 ps = jp->ps; 4250 ps = jp->ps;
4255 psend = ps + jp->nprocs; 4251 psend = ps + jp->nprocs;
4256 do { 4252 do {
@@ -4264,41 +4260,41 @@ dowait(int wait_flags, struct job *job)
4264 ps->ps_pid = -1; 4260 ps->ps_pid = -1;
4265 } 4261 }
4266 if (ps->ps_status == -1) 4262 if (ps->ps_status == -1)
4267 state = JOBRUNNING; 4263 jobstate = JOBRUNNING;
4268#if JOBS 4264#if JOBS
4269 if (state == JOBRUNNING) 4265 if (jobstate == JOBRUNNING)
4270 continue; 4266 continue;
4271 if (WIFSTOPPED(ps->ps_status)) { 4267 if (WIFSTOPPED(ps->ps_status)) {
4272 jp->stopstatus = ps->ps_status; 4268 jp->stopstatus = ps->ps_status;
4273 state = JOBSTOPPED; 4269 jobstate = JOBSTOPPED;
4274 } 4270 }
4275#endif 4271#endif
4276 } while (++ps < psend); 4272 } while (++ps < psend);
4277 if (thisjob) 4273 if (!thisjob)
4278 goto gotjob; 4274 continue;
4279 }
4280#if JOBS
4281 if (!WIFSTOPPED(status))
4282#endif
4283 jobless--;
4284 goto out;
4285
4286 gotjob:
4287 if (state != JOBRUNNING) {
4288 thisjob->changed = 1;
4289 4275
4290 if (thisjob->state != state) { 4276 /* Found the job where one of its processes changed its state.
4291 TRACE(("Job %d: changing state from %d to %d\n", 4277 * Is there at least one live and running process in this job? */
4292 jobno(thisjob), thisjob->state, state)); 4278 if (jobstate != JOBRUNNING) {
4293 thisjob->state = state; 4279 /* No. All live processes in the job are stopped
4280 * (JOBSTOPPED) or there are no live processes (JOBDONE)
4281 */
4282 thisjob->changed = 1;
4283 if (thisjob->state != jobstate) {
4284 TRACE(("Job %d: changing state from %d to %d\n",
4285 jobno(thisjob), thisjob->state, jobstate));
4286 thisjob->state = jobstate;
4294#if JOBS 4287#if JOBS
4295 if (state == JOBSTOPPED) { 4288 if (jobstate == JOBSTOPPED)
4296 set_curjob(thisjob, CUR_STOPPED); 4289 set_curjob(thisjob, CUR_STOPPED);
4297 }
4298#endif 4290#endif
4291 }
4299 } 4292 }
4293 goto out;
4300 } 4294 }
4301 4295 /* The process wasn't found in job list */
4296 if (JOBS && !WIFSTOPPED(status))
4297 jobless--;
4302 out: 4298 out:
4303 INT_ON; 4299 INT_ON;
4304 4300
@@ -4306,7 +4302,7 @@ dowait(int wait_flags, struct job *job)
4306 char s[48 + 1]; 4302 char s[48 + 1];
4307 int len; 4303 int len;
4308 4304
4309 len = sprint_status(s, status, 1); 4305 len = sprint_status48(s, status, 1);
4310 if (len) { 4306 if (len) {
4311 s[len] = '\n'; 4307 s[len] = '\n';
4312 s[len + 1] = '\0'; 4308 s[len + 1] = '\0';
@@ -4327,13 +4323,14 @@ blocking_wait_with_raise_on_sig(void)
4327 4323
4328#if JOBS 4324#if JOBS
4329static void 4325static void
4330showjob(FILE *out, struct job *jp, int mode) 4326showjob(struct job *jp, int mode)
4331{ 4327{
4332 struct procstat *ps; 4328 struct procstat *ps;
4333 struct procstat *psend; 4329 struct procstat *psend;
4334 int col; 4330 int col;
4335 int indent_col; 4331 int indent_col;
4336 char s[80]; 4332 char s[16 + 16 + 48];
4333 FILE *out = (mode & SHOW_STDERR ? stderr : stdout);
4337 4334
4338 ps = jp->ps; 4335 ps = jp->ps;
4339 4336
@@ -4363,7 +4360,7 @@ showjob(FILE *out, struct job *jp, int mode)
4363 int status = psend[-1].ps_status; 4360 int status = psend[-1].ps_status;
4364 if (jp->state == JOBSTOPPED) 4361 if (jp->state == JOBSTOPPED)
4365 status = jp->stopstatus; 4362 status = jp->stopstatus;
4366 col += sprint_status(s + col, status, 0); 4363 col += sprint_status48(s + col, status, 0);
4367 } 4364 }
4368 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ 4365 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
4369 4366
@@ -4390,7 +4387,7 @@ showjob(FILE *out, struct job *jp, int mode)
4390 ps->ps_cmd 4387 ps->ps_cmd
4391 ); 4388 );
4392 } while (++ps != psend); 4389 } while (++ps != psend);
4393 outcslow('\n', out); 4390 newline_and_flush(out);
4394 4391
4395 jp->changed = 0; 4392 jp->changed = 0;
4396 4393
@@ -4405,7 +4402,7 @@ showjob(FILE *out, struct job *jp, int mode)
4405 * statuses have changed since the last call to showjobs. 4402 * statuses have changed since the last call to showjobs.
4406 */ 4403 */
4407static void 4404static void
4408showjobs(FILE *out, int mode) 4405showjobs(int mode)
4409{ 4406{
4410 struct job *jp; 4407 struct job *jp;
4411 4408
@@ -4417,7 +4414,7 @@ showjobs(FILE *out, int mode)
4417 4414
4418 for (jp = curjob; jp; jp = jp->prev_job) { 4415 for (jp = curjob; jp; jp = jp->prev_job) {
4419 if (!(mode & SHOW_CHANGED) || jp->changed) { 4416 if (!(mode & SHOW_CHANGED) || jp->changed) {
4420 showjob(out, jp, mode); 4417 showjob(jp, mode);
4421 } 4418 }
4422 } 4419 }
4423} 4420}
@@ -4438,10 +4435,10 @@ jobscmd(int argc UNUSED_PARAM, char **argv)
4438 argv = argptr; 4435 argv = argptr;
4439 if (*argv) { 4436 if (*argv) {
4440 do 4437 do
4441 showjob(stdout, getjob(*argv, 0), mode); 4438 showjob(getjob(*argv, 0), mode);
4442 while (*++argv); 4439 while (*++argv);
4443 } else { 4440 } else {
4444 showjobs(stdout, mode); 4441 showjobs(mode);
4445 } 4442 }
4446 4443
4447 return 0; 4444 return 0;
@@ -5894,8 +5891,8 @@ cvtnum(arith_t num)
5894{ 5891{
5895 int len; 5892 int len;
5896 5893
5897 expdest = makestrspace(32, expdest); 5894 expdest = makestrspace(sizeof(arith_t)*3 + 2, expdest);
5898 len = fmtstr(expdest, 32, ARITH_FMT, num); 5895 len = fmtstr(expdest, sizeof(arith_t)*3 + 2, ARITH_FMT, num);
5899 STADJUST(len, expdest); 5896 STADJUST(len, expdest);
5900 return len; 5897 return len;
5901} 5898}
@@ -7326,10 +7323,11 @@ expmeta(char *expdir, char *enddir, char *name)
7326 struct dirent *dp; 7323 struct dirent *dp;
7327 int atend; 7324 int atend;
7328 int matchdot; 7325 int matchdot;
7326 int esc;
7329 7327
7330 metaflag = 0; 7328 metaflag = 0;
7331 start = name; 7329 start = name;
7332 for (p = name; *p; p++) { 7330 for (p = name; esc = 0, *p; p += esc + 1) {
7333 if (*p == '*' || *p == '?') 7331 if (*p == '*' || *p == '?')
7334 metaflag = 1; 7332 metaflag = 1;
7335 else if (*p == '[') { 7333 else if (*p == '[') {
@@ -7346,15 +7344,16 @@ expmeta(char *expdir, char *enddir, char *name)
7346 break; 7344 break;
7347 } 7345 }
7348 } 7346 }
7349 } else if (*p == '\\') 7347 } else {
7350 p++; 7348 if (*p == '\\')
7351 else if (*p == '/') { 7349 esc++;
7352 if (metaflag) 7350 if (p[esc] == '/') {
7353 goto out; 7351 if (metaflag)
7354 start = p + 1; 7352 break;
7353 start = p + esc + 1;
7354 }
7355 } 7355 }
7356 } 7356 }
7357 out:
7358 if (metaflag == 0) { /* we've reached the end of the file name */ 7357 if (metaflag == 0) { /* we've reached the end of the file name */
7359 if (enddir != expdir) 7358 if (enddir != expdir)
7360 metaflag++; 7359 metaflag++;
@@ -7394,7 +7393,8 @@ expmeta(char *expdir, char *enddir, char *name)
7394 atend = 1; 7393 atend = 1;
7395 } else { 7394 } else {
7396 atend = 0; 7395 atend = 0;
7397 *endname++ = '\0'; 7396 *endname = '\0';
7397 endname += esc + 1;
7398 } 7398 }
7399 matchdot = 0; 7399 matchdot = 0;
7400 p = start; 7400 p = start;
@@ -7419,7 +7419,7 @@ expmeta(char *expdir, char *enddir, char *name)
7419 } 7419 }
7420 closedir(dirp); 7420 closedir(dirp);
7421 if (!atend) 7421 if (!atend)
7422 endname[-1] = '/'; 7422 endname[-esc - 1] = esc ? '\\' : '/';
7423} 7423}
7424 7424
7425static struct strlist * 7425static struct strlist *
@@ -10118,7 +10118,7 @@ preadfd(void)
10118 } 10118 }
10119# if ENABLE_ASH_IDLE_TIMEOUT 10119# if ENABLE_ASH_IDLE_TIMEOUT
10120 else if (errno == EAGAIN && timeout > 0) { 10120 else if (errno == EAGAIN && timeout > 0) {
10121 printf("\007timed out waiting for input: auto-logout\n"); 10121 puts("\007timed out waiting for input: auto-logout");
10122 exitshell(); 10122 exitshell();
10123 } 10123 }
10124# endif 10124# endif
@@ -10472,10 +10472,8 @@ setinputstring(char *string)
10472 10472
10473#if ENABLE_ASH_MAIL 10473#if ENABLE_ASH_MAIL
10474 10474
10475#define MAXMBOXES 10 10475/* Hash of mtimes of mailboxes */
10476 10476static unsigned mailtime_hash;
10477/* times of mailboxes */
10478static time_t mailtime[MAXMBOXES];
10479/* Set if MAIL or MAILPATH is changed. */ 10477/* Set if MAIL or MAILPATH is changed. */
10480static smallint mail_var_path_changed; 10478static smallint mail_var_path_changed;
10481 10479
@@ -10491,13 +10489,14 @@ chkmail(void)
10491 const char *mpath; 10489 const char *mpath;
10492 char *p; 10490 char *p;
10493 char *q; 10491 char *q;
10494 time_t *mtp; 10492 unsigned new_hash;
10495 struct stackmark smark; 10493 struct stackmark smark;
10496 struct stat statb; 10494 struct stat statb;
10497 10495
10498 setstackmark(&smark); 10496 setstackmark(&smark);
10499 mpath = mpathset() ? mpathval() : mailval(); 10497 mpath = mpathset() ? mpathval() : mailval();
10500 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) { 10498 new_hash = 0;
10499 for (;;) {
10501 p = path_advance(&mpath, nullstr); 10500 p = path_advance(&mpath, nullstr);
10502 if (p == NULL) 10501 if (p == NULL)
10503 break; 10502 break;
@@ -10511,16 +10510,15 @@ chkmail(void)
10511#endif 10510#endif
10512 q[-1] = '\0'; /* delete trailing '/' */ 10511 q[-1] = '\0'; /* delete trailing '/' */
10513 if (stat(p, &statb) < 0) { 10512 if (stat(p, &statb) < 0) {
10514 *mtp = 0;
10515 continue; 10513 continue;
10516 } 10514 }
10517 if (!mail_var_path_changed && statb.st_mtime != *mtp) { 10515 /* Very simplistic "hash": just a sum of all mtimes */
10518 fprintf( 10516 new_hash += (unsigned)statb.st_mtime;
10519 stderr, "%s\n", 10517 }
10520 pathopt ? pathopt : "you have mail" 10518 if (!mail_var_path_changed && mailtime_hash != new_hash) {
10521 ); 10519 if (mailtime_hash != 0)
10522 } 10520 out2str("you have mail\n");
10523 *mtp = statb.st_mtime; 10521 mailtime_hash = new_hash;
10524 } 10522 }
10525 mail_var_path_changed = 0; 10523 mail_var_path_changed = 0;
10526 popstackmark(&smark); 10524 popstackmark(&smark);
@@ -10792,9 +10790,11 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt
10792 char c = '?'; 10790 char c = '?';
10793 int done = 0; 10791 int done = 0;
10794 int err = 0; 10792 int err = 0;
10795 char s[12]; 10793 char sbuf[2];
10796 char **optnext; 10794 char **optnext;
10797 10795
10796 sbuf[1] = '\0';
10797
10798 if (*param_optind < 1) 10798 if (*param_optind < 1)
10799 return 1; 10799 return 1;
10800 optnext = optfirst + *param_optind - 1; 10800 optnext = optfirst + *param_optind - 1;
@@ -10821,9 +10821,9 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt
10821 for (q = optstr; *q != c;) { 10821 for (q = optstr; *q != c;) {
10822 if (*q == '\0') { 10822 if (*q == '\0') {
10823 if (optstr[0] == ':') { 10823 if (optstr[0] == ':') {
10824 s[0] = c; 10824 sbuf[0] = c;
10825 s[1] = '\0'; 10825 /*sbuf[1] = '\0'; - already is */
10826 err |= setvarsafe("OPTARG", s, 0); 10826 err |= setvarsafe("OPTARG", sbuf, 0);
10827 } else { 10827 } else {
10828 fprintf(stderr, "Illegal option -%c\n", c); 10828 fprintf(stderr, "Illegal option -%c\n", c);
10829 unsetvar("OPTARG"); 10829 unsetvar("OPTARG");
@@ -10838,9 +10838,9 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt
10838 if (*++q == ':') { 10838 if (*++q == ':') {
10839 if (*p == '\0' && (p = *optnext) == NULL) { 10839 if (*p == '\0' && (p = *optnext) == NULL) {
10840 if (optstr[0] == ':') { 10840 if (optstr[0] == ':') {
10841 s[0] = c; 10841 sbuf[0] = c;
10842 s[1] = '\0'; 10842 /*sbuf[1] = '\0'; - already is */
10843 err |= setvarsafe("OPTARG", s, 0); 10843 err |= setvarsafe("OPTARG", sbuf, 0);
10844 c = ':'; 10844 c = ':';
10845 } else { 10845 } else {
10846 fprintf(stderr, "No arg for -%c option\n", c); 10846 fprintf(stderr, "No arg for -%c option\n", c);
@@ -10859,11 +10859,10 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt
10859 out: 10859 out:
10860 *optoff = p ? p - *(optnext - 1) : -1; 10860 *optoff = p ? p - *(optnext - 1) : -1;
10861 *param_optind = optnext - optfirst + 1; 10861 *param_optind = optnext - optfirst + 1;
10862 fmtstr(s, sizeof(s), "%d", *param_optind); 10862 err |= setvarsafe("OPTIND", itoa(*param_optind), VNOFUNC);
10863 err |= setvarsafe("OPTIND", s, VNOFUNC); 10863 sbuf[0] = c;
10864 s[0] = c; 10864 /*sbuf[1] = '\0'; - already is */
10865 s[1] = '\0'; 10865 err |= setvarsafe(optvar, sbuf, 0);
10866 err |= setvarsafe(optvar, s, 0);
10867 if (err) { 10866 if (err) {
10868 *param_optind = 1; 10867 *param_optind = 1;
10869 *optoff = -1; 10868 *optoff = -1;
@@ -12559,7 +12558,7 @@ cmdloop(int top)
12559 setstackmark(&smark); 12558 setstackmark(&smark);
12560#if JOBS 12559#if JOBS
12561 if (doing_jobctl) 12560 if (doing_jobctl)
12562 showjobs(stderr, SHOW_CHANGED); 12561 showjobs(SHOW_CHANGED|SHOW_STDERR);
12563#endif 12562#endif
12564 inter = 0; 12563 inter = 0;
12565 if (iflag && top) { 12564 if (iflag && top) {
@@ -13298,21 +13297,11 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13298} 13297}
13299 13298
13300static int FAST_FUNC 13299static int FAST_FUNC
13301umaskcmd(int argc UNUSED_PARAM, char **argv) 13300umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13302{ 13301{
13303 static const char permuser[3] ALIGN1 = "ugo"; 13302 static const char permuser[3] ALIGN1 = "ogu";
13304 static const char permmode[3] ALIGN1 = "rwx";
13305 static const short permmask[] ALIGN2 = {
13306 S_IRUSR, S_IWUSR, S_IXUSR,
13307 S_IRGRP, S_IWGRP, S_IXGRP,
13308 S_IROTH, S_IWOTH, S_IXOTH
13309 };
13310
13311 /* TODO: use bb_parse_mode() instead */
13312 13303
13313 char *ap;
13314 mode_t mask; 13304 mode_t mask;
13315 int i;
13316 int symbolic_mode = 0; 13305 int symbolic_mode = 0;
13317 13306
13318 while (nextopt("S") != '\0') { 13307 while (nextopt("S") != '\0') {
@@ -13324,45 +13313,43 @@ umaskcmd(int argc UNUSED_PARAM, char **argv)
13324 umask(mask); 13313 umask(mask);
13325 INT_ON; 13314 INT_ON;
13326 13315
13327 ap = *argptr; 13316 if (*argptr == NULL) {
13328 if (ap == NULL) {
13329 if (symbolic_mode) { 13317 if (symbolic_mode) {
13330 char buf[18]; 13318 char buf[sizeof(",u=rwx,g=rwx,o=rwx")];
13331 char *p = buf; 13319 char *p = buf;
13320 int i;
13332 13321
13333 for (i = 0; i < 3; i++) { 13322 i = 2;
13334 int j; 13323 for (;;) {
13335 13324 *p++ = ',';
13336 *p++ = permuser[i]; 13325 *p++ = permuser[i];
13337 *p++ = '='; 13326 *p++ = '=';
13338 for (j = 0; j < 3; j++) { 13327 /* mask is 0..0uuugggooo. i=2 selects uuu bits */
13339 if ((mask & permmask[3 * i + j]) == 0) { 13328 if (!(mask & 0400)) *p++ = 'r';
13340 *p++ = permmode[j]; 13329 if (!(mask & 0200)) *p++ = 'w';
13341 } 13330 if (!(mask & 0100)) *p++ = 'x';
13342 } 13331 mask <<= 3;
13343 *p++ = ','; 13332 if (--i < 0)
13333 break;
13344 } 13334 }
13345 *--p = 0; 13335 *p = '\0';
13346 puts(buf); 13336 puts(buf + 1);
13347 } else { 13337 } else {
13348 out1fmt("%.4o\n", mask); 13338 out1fmt("%04o\n", mask);
13349 } 13339 }
13350 } else { 13340 } else {
13351 if (isdigit((unsigned char) *ap)) { 13341 char *modestr = *argptr;
13352 mask = 0; 13342 /* numeric umasks are taken as-is */
13353 do { 13343 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
13354 if (*ap >= '8' || *ap < '0') 13344 if (!isdigit(modestr[0]))
13355 ash_msg_and_raise_error(msg_illnum, argv[1]); 13345 mask ^= 0777;
13356 mask = (mask << 3) + (*ap - '0'); 13346 mask = bb_parse_mode(modestr, mask);
13357 } while (*++ap != '\0'); 13347 if ((unsigned)mask > 0777) {
13358 umask(mask); 13348 ash_msg_and_raise_error("illegal mode: %s", modestr);
13359 } else { 13349 }
13360 mask = ~mask & 0777; 13350 if (!isdigit(modestr[0]))
13361 if (!bb_parse_mode(ap, &mask)) { 13351 mask ^= 0777;
13362 ash_msg_and_raise_error("illegal mode: %s", ap); 13352 umask(mask);
13363 }
13364 umask(~mask & 0777);
13365 }
13366 } 13353 }
13367 return 0; 13354 return 0;
13368} 13355}
@@ -13713,7 +13700,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
13713 exitshell(); 13700 exitshell();
13714 } 13701 }
13715 if (e == EXINT) { 13702 if (e == EXINT) {
13716 outcslow('\n', stderr); 13703 newline_and_flush(stderr);
13717 } 13704 }
13718 13705
13719 popstackmark(&smark); 13706 popstackmark(&smark);
diff --git a/shell/ash_test/ash-glob/glob1.right b/shell/ash_test/ash-glob/glob1.right
new file mode 100644
index 000000000..f29ab4e65
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob1.right
@@ -0,0 +1,2 @@
1glob1.tests
2glob1.tests
diff --git a/shell/ash_test/ash-glob/glob1.tests b/shell/ash_test/ash-glob/glob1.tests
new file mode 100755
index 000000000..f980ce064
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob1.tests
@@ -0,0 +1,2 @@
1echo *glob1?t[e]sts*
2echo "glob1"?'t'[e]s*
diff --git a/shell/ash_test/ash-glob/glob2.right b/shell/ash_test/ash-glob/glob2.right
new file mode 100644
index 000000000..7a70c2263
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob2.right
@@ -0,0 +1,18 @@
1Expected Actual
2Z\* : Z\*
3Z* : Z*
4Z\f : Z\f
5Z\* : Z\*
6
7Z\z : Z\z
8Zz : Zz
9Z\z : Z\z
10Z\z : Z\z
11
12Z\ : Z\
13Z\ : Z\
14
15Z\f Zf : Z\f Zf
16Z\f Zf : Z\f Zf
17
18Done: 0
diff --git a/shell/ash_test/ash-glob/glob2.tests b/shell/ash_test/ash-glob/glob2.tests
new file mode 100755
index 000000000..00618b9db
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob2.tests
@@ -0,0 +1,27 @@
1# This test demonstrates that in unquoted $v, backslashes expand by this rule:
2# \z -> \\\z; \<eol> -> \\<eol> (for any z, special or not),
3# and subsequently globbing converts \\ to \ and treats \z as literal z
4# even if it is a special char.
5
6>'Zf'
7>'Z\f'
8 echo 'Expected' 'Actual'
9v='\*'; echo 'Z\* :' Z$v # ash is buggy here: prints 'Z\f'
10 echo 'Z* :' Z\*
11 echo 'Z\f :' Z\\*
12 echo 'Z\* :' Z\\\* # NB! only this matches Z$v output
13echo
14v='\z'; echo 'Z\z :' Z$v
15 echo 'Zz :' Z\z
16 echo 'Z\z :' Z\\z
17 echo 'Z\z :' Z\\\z
18echo
19v='\'; echo 'Z\ :' Z$v
20 echo 'Z\ :' Z\\
21echo
22v='*'; echo 'Z\f Zf :' Z$v
23 echo 'Z\f Zf :' Z*
24echo
25
26rm 'Z\f' 'Zf'
27echo Done: $?
diff --git a/shell/ash_test/ash-glob/glob3.right b/shell/ash_test/ash-glob/glob3.right
new file mode 100644
index 000000000..161b589e0
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob3.right
@@ -0,0 +1,2 @@
1glob3.tests
2./glob3.tests
diff --git a/shell/ash_test/ash-glob/glob3.tests b/shell/ash_test/ash-glob/glob3.tests
new file mode 100755
index 000000000..bdf54001e
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob3.tests
@@ -0,0 +1,2 @@
1echo "glob3.test"*
2echo "./glob3.test"*
diff --git a/shell/ash_test/ash-glob/glob_and_assign.right b/shell/ash_test/ash-glob/glob_and_assign.right
new file mode 100644
index 000000000..d46e44363
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_and_assign.right
@@ -0,0 +1,6 @@
1ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
2ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
3*.tmp
4ZVAR=z.tmp z.tmp
5ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
6ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
diff --git a/shell/ash_test/ash-glob/glob_and_assign.tests b/shell/ash_test/ash-glob/glob_and_assign.tests
new file mode 100755
index 000000000..0b158f20f
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_and_assign.tests
@@ -0,0 +1,10 @@
1>ZVAR=z.tmp
2>z.tmp
3ZVAR=*.tmp echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
4ZVAR=*.tmp /bin/echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
5ZVAR=*.tmp
6echo "$ZVAR"
7echo $ZVAR
8echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
9/bin/echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
10rm ZVAR=z.tmp z.tmp
diff --git a/shell/ash_test/ash-glob/glob_redir.right b/shell/ash_test/ash-glob/glob_redir.right
new file mode 100644
index 000000000..fbd0309b0
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_redir.right
@@ -0,0 +1,2 @@
1z.tmp:
2?.tmp: TEST
diff --git a/shell/ash_test/ash-glob/glob_redir.tests b/shell/ash_test/ash-glob/glob_redir.tests
new file mode 100755
index 000000000..621d12017
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_redir.tests
@@ -0,0 +1,9 @@
1# Redirections are not globbed.
2# bash:
3# if run as "sh", they are not globbed, but
4# if run as "bash", they are!
5>z.tmp
6echo TEST >?.tmp
7echo 'z.tmp:' `cat 'z.tmp'`
8echo '?.tmp:' `cat '?.tmp'`
9rm 'z.tmp' '?.tmp'
diff --git a/shell/hush.c b/shell/hush.c
index 3ca04494c..eabe83ac6 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1479,10 +1479,11 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
1479 1479
1480#if ENABLE_HUSH_JOB 1480#if ENABLE_HUSH_JOB
1481 1481
1482static void xfunc_has_died(void);
1482/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 1483/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
1483# define disable_restore_tty_pgrp_on_exit() (die_sleep = 0) 1484# define disable_restore_tty_pgrp_on_exit() (die_func = NULL)
1484/* After [v]fork, in parent: restore tty pgrp on xfunc death */ 1485/* After [v]fork, in parent: restore tty pgrp on xfunc death */
1485# define enable_restore_tty_pgrp_on_exit() (die_sleep = -1) 1486# define enable_restore_tty_pgrp_on_exit() (die_func = xfunc_has_died)
1486 1487
1487/* Restores tty foreground process group, and exits. 1488/* Restores tty foreground process group, and exits.
1488 * May be called as signal handler for fatal signal 1489 * May be called as signal handler for fatal signal
@@ -1587,6 +1588,15 @@ static void hush_exit(int exitcode)
1587#endif 1588#endif
1588} 1589}
1589 1590
1591static void xfunc_has_died(void) NORETURN;
1592static void xfunc_has_died(void)
1593{
1594 /* xfunc has failed! die die die */
1595 /* no EXIT traps, this is an escape hatch! */
1596 G.exiting = 1;
1597 hush_exit(xfunc_error_retval);
1598}
1599
1590 1600
1591//TODO: return a mask of ALL handled sigs? 1601//TODO: return a mask of ALL handled sigs?
1592static int check_and_run_traps(void) 1602static int check_and_run_traps(void)
@@ -3161,11 +3171,29 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
3161 old->command->group = ctx->list_head; 3171 old->command->group = ctx->list_head;
3162 old->command->cmd_type = CMD_NORMAL; 3172 old->command->cmd_type = CMD_NORMAL;
3163# if !BB_MMU 3173# if !BB_MMU
3164 o_addstr(&old->as_string, ctx->as_string.data); 3174 /* At this point, the compound command's string is in
3165 o_free_unsafe(&ctx->as_string); 3175 * ctx->as_string... except for the leading keyword!
3166 old->command->group_as_string = xstrdup(old->as_string.data); 3176 * Consider this example: "echo a | if true; then echo a; fi"
3167 debug_printf_parse("pop, remembering as:'%s'\n", 3177 * ctx->as_string will contain "true; then echo a; fi",
3168 old->command->group_as_string); 3178 * with "if " remaining in old->as_string!
3179 */
3180 {
3181 char *str;
3182 int len = old->as_string.length;
3183 /* Concatenate halves */
3184 o_addstr(&old->as_string, ctx->as_string.data);
3185 o_free_unsafe(&ctx->as_string);
3186 /* Find where leading keyword starts in first half */
3187 str = old->as_string.data + len;
3188 if (str > old->as_string.data)
3189 str--; /* skip whitespace after keyword */
3190 while (str > old->as_string.data && isalpha(str[-1]))
3191 str--;
3192 /* Ugh, we're done with this horrid hack */
3193 old->command->group_as_string = xstrdup(str);
3194 debug_printf_parse("pop, remembering as:'%s'\n",
3195 old->command->group_as_string);
3196 }
3169# endif 3197# endif
3170 *ctx = *old; /* physical copy */ 3198 *ctx = *old; /* physical copy */
3171 free(old); 3199 free(old);
@@ -4248,7 +4276,7 @@ static struct pipe *parse_stream(char **pstring,
4248 pi = NULL; 4276 pi = NULL;
4249 } 4277 }
4250#if !BB_MMU 4278#if !BB_MMU
4251 debug_printf_parse("as_string '%s'\n", ctx.as_string.data); 4279 debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
4252 if (pstring) 4280 if (pstring)
4253 *pstring = ctx.as_string.data; 4281 *pstring = ctx.as_string.data;
4254 else 4282 else
@@ -4399,7 +4427,7 @@ static struct pipe *parse_stream(char **pstring,
4399 ) { 4427 ) {
4400 o_free(&dest); 4428 o_free(&dest);
4401#if !BB_MMU 4429#if !BB_MMU
4402 debug_printf_parse("as_string '%s'\n", ctx.as_string.data); 4430 debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
4403 if (pstring) 4431 if (pstring)
4404 *pstring = ctx.as_string.data; 4432 *pstring = ctx.as_string.data;
4405 else 4433 else
@@ -4639,9 +4667,6 @@ static struct pipe *parse_stream(char **pstring,
4639 * with redirect_opt_num(), but bash doesn't do it. 4667 * with redirect_opt_num(), but bash doesn't do it.
4640 * "echo foo 2| cat" yields "foo 2". */ 4668 * "echo foo 2| cat" yields "foo 2". */
4641 done_command(&ctx); 4669 done_command(&ctx);
4642#if !BB_MMU
4643 o_reset_to_empty_unquoted(&ctx.as_string);
4644#endif
4645 } 4670 }
4646 goto new_cmd; 4671 goto new_cmd;
4647 case '(': 4672 case '(':
@@ -6779,7 +6804,7 @@ static int checkjobs(struct pipe *fg_pipe)
6779 int sig = WTERMSIG(status); 6804 int sig = WTERMSIG(status);
6780 if (i == fg_pipe->num_cmds-1) 6805 if (i == fg_pipe->num_cmds-1)
6781 /* TODO: use strsignal() instead for bash compat? but that's bloat... */ 6806 /* TODO: use strsignal() instead for bash compat? but that's bloat... */
6782 printf("%s\n", sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); 6807 puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
6783 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ 6808 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
6784 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? 6809 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
6785 * Maybe we need to use sig | 128? */ 6810 * Maybe we need to use sig | 128? */
@@ -7851,12 +7876,7 @@ int hush_main(int argc, char **argv)
7851 /* Initialize some more globals to non-zero values */ 7876 /* Initialize some more globals to non-zero values */
7852 cmdedit_update_prompt(); 7877 cmdedit_update_prompt();
7853 7878
7854 if (setjmp(die_jmp)) { 7879 die_func = xfunc_has_died;
7855 /* xfunc has failed! die die die */
7856 /* no EXIT traps, this is an escape hatch! */
7857 G.exiting = 1;
7858 hush_exit(xfunc_error_retval);
7859 }
7860 7880
7861 /* Shell is non-interactive at first. We need to call 7881 /* Shell is non-interactive at first. We need to call
7862 * install_special_sighandlers() if we are going to execute "sh <script>", 7882 * install_special_sighandlers() if we are going to execute "sh <script>",
@@ -8114,9 +8134,7 @@ int hush_main(int argc, char **argv)
8114 /* Grab control of the terminal */ 8134 /* Grab control of the terminal */
8115 tcsetpgrp(G_interactive_fd, getpid()); 8135 tcsetpgrp(G_interactive_fd, getpid());
8116 } 8136 }
8117 /* -1 is special - makes xfuncs longjmp, not exit 8137 enable_restore_tty_pgrp_on_exit();
8118 * (we reset die_sleep = 0 whereever we [v]fork) */
8119 enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
8120 8138
8121# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 8139# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
8122 { 8140 {
@@ -8950,24 +8968,29 @@ static int FAST_FUNC builtin_umask(char **argv)
8950 int rc; 8968 int rc;
8951 mode_t mask; 8969 mode_t mask;
8952 8970
8971 rc = 1;
8953 mask = umask(0); 8972 mask = umask(0);
8954 argv = skip_dash_dash(argv); 8973 argv = skip_dash_dash(argv);
8955 if (argv[0]) { 8974 if (argv[0]) {
8956 mode_t old_mask = mask; 8975 mode_t old_mask = mask;
8957 8976
8958 mask ^= 0777; 8977 /* numeric umasks are taken as-is */
8959 rc = bb_parse_mode(argv[0], &mask); 8978 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
8960 mask ^= 0777; 8979 if (!isdigit(argv[0][0]))
8961 if (rc == 0) { 8980 mask ^= 0777;
8981 mask = bb_parse_mode(argv[0], mask);
8982 if (!isdigit(argv[0][0]))
8983 mask ^= 0777;
8984 if ((unsigned)mask > 0777) {
8962 mask = old_mask; 8985 mask = old_mask;
8963 /* bash messages: 8986 /* bash messages:
8964 * bash: umask: 'q': invalid symbolic mode operator 8987 * bash: umask: 'q': invalid symbolic mode operator
8965 * bash: umask: 999: octal number out of range 8988 * bash: umask: 999: octal number out of range
8966 */ 8989 */
8967 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]); 8990 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
8991 rc = 0;
8968 } 8992 }
8969 } else { 8993 } else {
8970 rc = 1;
8971 /* Mimic bash */ 8994 /* Mimic bash */
8972 printf("%04o\n", (unsigned) mask); 8995 printf("%04o\n", (unsigned) mask);
8973 /* fall through and restore mask which we set to 0 */ 8996 /* fall through and restore mask which we set to 0 */
@@ -9097,12 +9120,9 @@ static int FAST_FUNC builtin_wait(char **argv)
9097 return EXIT_FAILURE; 9120 return EXIT_FAILURE;
9098 } 9121 }
9099 if (waitpid(pid, &status, 0) == pid) { 9122 if (waitpid(pid, &status, 0) == pid) {
9123 ret = WEXITSTATUS(status);
9100 if (WIFSIGNALED(status)) 9124 if (WIFSIGNALED(status))
9101 ret = 128 + WTERMSIG(status); 9125 ret = 128 + WTERMSIG(status);
9102 else if (WIFEXITED(status))
9103 ret = WEXITSTATUS(status);
9104 else /* wtf? */
9105 ret = EXIT_FAILURE;
9106 } else { 9126 } else {
9107 bb_perror_msg("wait %s", *argv); 9127 bb_perror_msg("wait %s", *argv);
9108 ret = 127; 9128 ret = 127;
diff --git a/shell/hush_test/hush-glob/glob3.right b/shell/hush_test/hush-glob/glob3.right
new file mode 100644
index 000000000..161b589e0
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob3.right
@@ -0,0 +1,2 @@
1glob3.tests
2./glob3.tests
diff --git a/shell/hush_test/hush-glob/glob3.tests b/shell/hush_test/hush-glob/glob3.tests
new file mode 100755
index 000000000..bdf54001e
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob3.tests
@@ -0,0 +1,2 @@
1echo "glob3.test"*
2echo "./glob3.test"*
diff --git a/shell/hush_test/hush-misc/nommu3.right b/shell/hush_test/hush-misc/nommu3.right
new file mode 100644
index 000000000..da1534bef
--- /dev/null
+++ b/shell/hush_test/hush-misc/nommu3.right
@@ -0,0 +1,2 @@
1Ok
20
diff --git a/shell/hush_test/hush-misc/nommu3.tests b/shell/hush_test/hush-misc/nommu3.tests
new file mode 100755
index 000000000..ac82a6a11
--- /dev/null
+++ b/shell/hush_test/hush-misc/nommu3.tests
@@ -0,0 +1,15 @@
1#!/bin/sh
2
3func()
4{
5 while read p; do echo "$p"; done
6}
7
8pipe_to_func()
9{
10 # We had a NOMMU bug which caused "echo Ok |" part to be lost
11 echo Ok | func
12}
13
14pipe_to_func | cat
15echo $?
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 1567d1de4..dbd4286bf 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -387,7 +387,7 @@ static void printlim(unsigned opts, const struct rlimit *limit,
387 val = limit->rlim_cur; 387 val = limit->rlim_cur;
388 388
389 if (val == RLIM_INFINITY) 389 if (val == RLIM_INFINITY)
390 printf("unlimited\n"); 390 puts("unlimited");
391 else { 391 else {
392 val >>= l->factor_shift; 392 val >>= l->factor_shift;
393 printf("%llu\n", (long long) val); 393 printf("%llu\n", (long long) val);