diff options
Diffstat (limited to 'bin')
-rw-r--r-- | bin/rm/rm.1 | 21 | ||||
-rw-r--r-- | bin/sh/eval.c | 155 | ||||
-rw-r--r-- | bin/sh/expand.c | 64 | ||||
-rw-r--r-- | bin/sh/expand.h | 2 | ||||
-rw-r--r-- | bin/sh/histedit.c | 4 | ||||
-rw-r--r-- | bin/sh/mksyntax.c | 1 | ||||
-rw-r--r-- | bin/sh/parser.c | 98 | ||||
-rw-r--r-- | bin/sh/parser.h | 1 | ||||
-rw-r--r-- | bin/sh/sh.1 | 28 | ||||
-rw-r--r-- | bin/sh/var.c | 2 |
10 files changed, 225 insertions, 151 deletions
diff --git a/bin/rm/rm.1 b/bin/rm/rm.1 index 11bc77d..ad81066 100644 --- a/bin/rm/rm.1 +++ b/bin/rm/rm.1 @@ -32,7 +32,7 @@ .\" @(#)rm.1 8.5 (Berkeley) 12/5/94 .\" $FreeBSD$ .\" -.Dd October 8, 2010 +.Dd October 31, 2010 .Dt RM 1 .Os .Sh NAME @@ -100,6 +100,11 @@ Specifying this flag for a read only file will cause .Nm to generate an error message and exit. The file will not be removed or overwritten. +.Pp +N.B.: The +.Fl P +flag is not considered a security feature +.Pq see Sx BUGS . .It Fl R Attempt to remove the file hierarchy rooted in each .Ar file @@ -229,8 +234,12 @@ command appeared in .Sh BUGS The .Fl P -option assumes that the underlying file system updates existing blocks -in-place and does not store new data in a new location. -This is true for UFS but not for ZFS, which is using a Copy-On-Write strategy. -In addition, only regular files are overwritten, other types of files -are not. +option assumes that the underlying storage overwrites file blocks +when data is written to an existing offset. +Several factors including the file system and its backing store could defeat +this assumption. +This includes, but is not limited to file systems that use a +Copy-On-Write strategy (e.g. ZFS or UFS when snapshots are being used), Flash +media that are using a wear leveling algorithm, or when the backing datastore +does journaling, etc. +In addition, only regular files are overwritten, other types of files are not. diff --git a/bin/sh/eval.c b/bin/sh/eval.c index 499751d..415b9eb 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -196,6 +196,7 @@ void evaltree(union node *n, int flags) { int do_etest; + union node *next; do_etest = 0; if (n == NULL) { @@ -203,84 +204,88 @@ evaltree(union node *n, int flags) exitstatus = 0; goto out; } + do { + next = NULL; #ifndef NO_HISTORY - displayhist = 1; /* show history substitutions done with fc */ + displayhist = 1; /* show history substitutions done with fc */ #endif - TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type)); - switch (n->type) { - case NSEMI: - evaltree(n->nbinary.ch1, flags & ~EV_EXIT); - if (evalskip) - goto out; - evaltree(n->nbinary.ch2, flags); - break; - case NAND: - evaltree(n->nbinary.ch1, EV_TESTED); - if (evalskip || exitstatus != 0) { - goto out; + TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type)); + switch (n->type) { + case NSEMI: + evaltree(n->nbinary.ch1, flags & ~EV_EXIT); + if (evalskip) + goto out; + next = n->nbinary.ch2; + break; + case NAND: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus != 0) { + goto out; + } + next = n->nbinary.ch2; + break; + case NOR: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus == 0) + goto out; + next = n->nbinary.ch2; + break; + case NREDIR: + evalredir(n, flags); + break; + case NSUBSHELL: + evalsubshell(n, flags); + do_etest = !(flags & EV_TESTED); + break; + case NBACKGND: + evalsubshell(n, flags); + break; + case NIF: { + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) + next = n->nif.ifpart; + else if (n->nif.elsepart) + next = n->nif.elsepart; + else + exitstatus = 0; + break; } - evaltree(n->nbinary.ch2, flags); - break; - case NOR: - evaltree(n->nbinary.ch1, EV_TESTED); - if (evalskip || exitstatus == 0) - goto out; - evaltree(n->nbinary.ch2, flags); - break; - case NREDIR: - evalredir(n, flags); - break; - case NSUBSHELL: - evalsubshell(n, flags); - do_etest = !(flags & EV_TESTED); - break; - case NBACKGND: - evalsubshell(n, flags); - break; - case NIF: { - evaltree(n->nif.test, EV_TESTED); - if (evalskip) - goto out; - if (exitstatus == 0) - evaltree(n->nif.ifpart, flags); - else if (n->nif.elsepart) - evaltree(n->nif.elsepart, flags); - else + case NWHILE: + case NUNTIL: + evalloop(n, flags & ~EV_EXIT); + break; + case NFOR: + evalfor(n, flags & ~EV_EXIT); + break; + case NCASE: + evalcase(n, flags); + break; + case NDEFUN: + defun(n->narg.text, n->narg.next); exitstatus = 0; - break; - } - case NWHILE: - case NUNTIL: - evalloop(n, flags & ~EV_EXIT); - break; - case NFOR: - evalfor(n, flags & ~EV_EXIT); - break; - case NCASE: - evalcase(n, flags); - break; - case NDEFUN: - defun(n->narg.text, n->narg.next); - exitstatus = 0; - break; - case NNOT: - evaltree(n->nnot.com, EV_TESTED); - exitstatus = !exitstatus; - break; - - case NPIPE: - evalpipe(n); - do_etest = !(flags & EV_TESTED); - break; - case NCMD: - evalcommand(n, flags, (struct backcmd *)NULL); - do_etest = !(flags & EV_TESTED); - break; - default: - out1fmt("Node type = %d\n", n->type); - flushout(&output); - break; - } + break; + case NNOT: + evaltree(n->nnot.com, EV_TESTED); + exitstatus = !exitstatus; + break; + + case NPIPE: + evalpipe(n); + do_etest = !(flags & EV_TESTED); + break; + case NCMD: + evalcommand(n, flags, (struct backcmd *)NULL); + do_etest = !(flags & EV_TESTED); + break; + default: + out1fmt("Node type = %d\n", n->type); + flushout(&output); + break; + } + n = next; + } while (n != NULL); out: if (pendingsigs) dotrap(); @@ -1130,7 +1135,7 @@ commandcmd(int argc, char **argv) return typecmd_impl(2, argv - 1, cmd, path); } if (argc != 0) - error("commandcmd() called while it should not be"); + error("commandcmd bad call"); /* * Do nothing successfully if no command was specified; diff --git a/bin/sh/expand.c b/bin/sh/expand.c index d33f0ac..200da3f 100644 --- a/bin/sh/expand.c +++ b/bin/sh/expand.c @@ -98,7 +98,7 @@ static struct arglist exparg; /* holds expanded arg list */ static void argstr(char *, int); static char *exptilde(char *, int); static void expbackq(union node *, int, int); -static int subevalvar(char *, char *, int, int, int, int); +static int subevalvar(char *, char *, int, int, int, int, int); static char *evalvar(char *, int); static int varisset(char *, int); static void varvalue(char *, int, int, int); @@ -216,7 +216,12 @@ argstr(char *p, int flag) char c; int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ int firsteq = 1; + int split_lit; + int lit_quoted; + split_lit = flag & EXP_SPLIT_LIT; + lit_quoted = flag & EXP_LIT_QUOTED; + flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(p, flag); for (;;) { @@ -225,17 +230,25 @@ argstr(char *p, int flag) case CTLENDVAR: goto breakloop; case CTLQUOTEMARK: + lit_quoted = 1; /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_FULL) != 0) STPUTC(c, expdest); break; + case CTLQUOTEEND: + lit_quoted = 0; + break; case CTLESC: if (quotes) STPUTC(c, expdest); c = *p++; STPUTC(c, expdest); + if (split_lit && !lit_quoted) + recordregion(expdest - stackblock() - + (quotes ? 2 : 1), + expdest - stackblock(), 0); break; case CTLVAR: p = evalvar(p, flag); @@ -255,18 +268,21 @@ argstr(char *p, int flag) * assignments (after the first '=' and after ':'s). */ STPUTC(c, expdest); - if (flag & EXP_VARTILDE && *p == '~') { - if (c == '=') { - if (firsteq) - firsteq = 0; - else - break; - } + if (split_lit && !lit_quoted) + recordregion(expdest - stackblock() - 1, + expdest - stackblock(), 0); + if (flag & EXP_VARTILDE && *p == '~' && + (c != '=' || firsteq)) { + if (c == '=') + firsteq = 0; p = exptilde(p, flag); } break; default: STPUTC(c, expdest); + if (split_lit && !lit_quoted) + recordregion(expdest - stackblock() - 1, + expdest - stackblock(), 0); } } breakloop:; @@ -510,7 +526,7 @@ expbackq(union node *cmd, int quoted, int flag) static int subevalvar(char *p, char *str, int strloc, int subtype, int startloc, - int varflags) + int varflags, int quotes) { char *startp; char *loc = NULL; @@ -555,12 +571,12 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, for (loc = startp; loc < str; loc++) { c = *loc; *loc = '\0'; - if (patmatch(str, startp, varflags & VSQUOTE)) { + if (patmatch(str, startp, quotes)) { *loc = c; goto recordleft; } *loc = c; - if ((varflags & VSQUOTE) && *loc == CTLESC) + if (quotes && *loc == CTLESC) loc++; } return 0; @@ -569,14 +585,13 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, for (loc = str - 1; loc >= startp;) { c = *loc; *loc = '\0'; - if (patmatch(str, startp, varflags & VSQUOTE)) { + if (patmatch(str, startp, quotes)) { *loc = c; goto recordleft; } *loc = c; loc--; - if ((varflags & VSQUOTE) && loc > startp && - *(loc - 1) == CTLESC) { + if (quotes && loc > startp && *(loc - 1) == CTLESC) { for (q = startp; q < loc; q++) if (*q == CTLESC) q++; @@ -588,14 +603,13 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, case VSTRIMRIGHT: for (loc = str - 1; loc >= startp;) { - if (patmatch(str, loc, varflags & VSQUOTE)) { + if (patmatch(str, loc, quotes)) { amount = loc - expdest; STADJUST(amount, expdest); return 1; } loc--; - if ((varflags & VSQUOTE) && loc > startp && - *(loc - 1) == CTLESC) { + if (quotes && loc > startp && *(loc - 1) == CTLESC) { for (q = startp; q < loc; q++) if (*q == CTLESC) q++; @@ -607,12 +621,12 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, case VSTRIMRIGHTMAX: for (loc = startp; loc < str - 1; loc++) { - if (patmatch(str, loc, varflags & VSQUOTE)) { + if (patmatch(str, loc, quotes)) { amount = loc - expdest; STADJUST(amount, expdest); return 1; } - if ((varflags & VSQUOTE) && *loc == CTLESC) + if (quotes && *loc == CTLESC) loc++; } return 0; @@ -742,7 +756,8 @@ record: case VSPLUS: case VSMINUS: if (!set) { - argstr(p, flag); + argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) | + (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0)); break; } if (easy) @@ -762,7 +777,7 @@ record: STPUTC('\0', expdest); patloc = expdest - stackblock(); if (subevalvar(p, NULL, patloc, subtype, - startloc, varflags) == 0) { + startloc, varflags, quotes) == 0) { int amount = (expdest - stackblock() - patloc) + 1; STADJUST(-amount, expdest); } @@ -773,7 +788,8 @@ record: case VSASSIGN: case VSQUESTION: if (!set) { - if (subevalvar(p, var, 0, subtype, startloc, varflags)) { + if (subevalvar(p, var, 0, subtype, startloc, varflags, + quotes)) { varflags &= ~VSNUL; /* * Remove any recorded regions beyond @@ -1495,13 +1511,13 @@ rmescapes(char *str) char *p, *q; p = str; - while (*p != CTLESC && *p != CTLQUOTEMARK) { + while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) { if (*p++ == '\0') return; } q = p; while (*p) { - if (*p == CTLQUOTEMARK) { + if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { p++; continue; } diff --git a/bin/sh/expand.h b/bin/sh/expand.h index a3e079a..a4bb198 100644 --- a/bin/sh/expand.h +++ b/bin/sh/expand.h @@ -52,6 +52,8 @@ struct arglist { #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ +#define EXP_SPLIT_LIT 0x20 /* IFS split literal text ${v+-a b c} */ +#define EXP_LIT_QUOTED 0x40 /* for EXP_SPLIT_LIT, start off quoted */ union node; diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c index dab69f1..3fc1782 100644 --- a/bin/sh/histedit.c +++ b/bin/sh/histedit.c @@ -297,7 +297,7 @@ histcmd(int argc, char **argv) laststr = argv[1]; break; default: - error("too many args"); + error("too many arguments"); } /* * Turn into event numbers. @@ -329,7 +329,7 @@ histcmd(int argc, char **argv) editfile = editfilestr; if ((efp = fdopen(fd, "w")) == NULL) { close(fd); - error("can't allocate stdio buffer for temp"); + error("Out of space"); } } diff --git a/bin/sh/mksyntax.c b/bin/sh/mksyntax.c index e4c4d14..07e8eb7 100644 --- a/bin/sh/mksyntax.c +++ b/bin/sh/mksyntax.c @@ -285,6 +285,7 @@ init(void) syntax[base + CTLARI] = "CCTL"; syntax[base + CTLENDARI] = "CCTL"; syntax[base + CTLQUOTEMARK] = "CCTL"; + syntax[base + CTLQUOTEEND] = "CCTL"; } diff --git a/bin/sh/parser.c b/bin/sh/parser.c index 9500d91..23db5e7 100644 --- a/bin/sh/parser.c +++ b/bin/sh/parser.c @@ -106,7 +106,7 @@ static struct parser_temp *parser_temp; static int noaliases = 0; -static union node *list(int); +static union node *list(int, int); static union node *andor(void); static union node *pipeline(void); static union node *command(void); @@ -220,20 +220,20 @@ parsecmd(int interact) if (t == TNL) return NULL; tokpushback++; - return list(1); + return list(1, 1); } static union node * -list(int nlflag) +list(int nlflag, int erflag) { - union node *n1, *n2, *n3; + union node *ntop, *n1, *n2, *n3; int tok; checkkwd = 2; - if (nlflag == 0 && tokendlist[peektoken()]) + if (!nlflag && !erflag && tokendlist[peektoken()]) return NULL; - n1 = NULL; + ntop = n1 = NULL; for (;;) { n2 = andor(); tok = readtoken(); @@ -250,14 +250,21 @@ list(int nlflag) n2 = n3; } } - if (n1 == NULL) { - n1 = n2; + if (ntop == NULL) + ntop = n2; + else if (n1 == NULL) { + n1 = (union node *)stalloc(sizeof (struct nbinary)); + n1->type = NSEMI; + n1->nbinary.ch1 = ntop; + n1->nbinary.ch2 = n2; + ntop = n1; } else { n3 = (union node *)stalloc(sizeof (struct nbinary)); n3->type = NSEMI; - n3->nbinary.ch1 = n1; + n3->nbinary.ch1 = n1->nbinary.ch2; n3->nbinary.ch2 = n2; + n1->nbinary.ch2 = n3; n1 = n3; } switch (tok) { @@ -269,28 +276,28 @@ list(int nlflag) if (tok == TNL) { parseheredoc(); if (nlflag) - return n1; + return ntop; } else if (tok == TEOF && nlflag) { parseheredoc(); - return n1; + return ntop; } else { tokpushback++; } checkkwd = 2; - if (tokendlist[peektoken()]) - return n1; + if (!nlflag && !erflag && tokendlist[peektoken()]) + return ntop; break; case TEOF: if (heredoclist) parseheredoc(); else pungetc(); /* push back EOF on input */ - return n1; + return ntop; default: - if (nlflag) + if (nlflag || erflag) synexpect(-1); tokpushback++; - return n1; + return ntop; } } } @@ -398,24 +405,24 @@ command(void) case TIF: n1 = (union node *)stalloc(sizeof (struct nif)); n1->type = NIF; - if ((n1->nif.test = list(0)) == NULL) + if ((n1->nif.test = list(0, 0)) == NULL) synexpect(-1); if (readtoken() != TTHEN) synexpect(TTHEN); - n1->nif.ifpart = list(0); + n1->nif.ifpart = list(0, 0); n2 = n1; while (readtoken() == TELIF) { n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); n2 = n2->nif.elsepart; n2->type = NIF; - if ((n2->nif.test = list(0)) == NULL) + if ((n2->nif.test = list(0, 0)) == NULL) synexpect(-1); if (readtoken() != TTHEN) synexpect(TTHEN); - n2->nif.ifpart = list(0); + n2->nif.ifpart = list(0, 0); } if (lasttoken == TELSE) - n2->nif.elsepart = list(0); + n2->nif.elsepart = list(0, 0); else { n2->nif.elsepart = NULL; tokpushback++; @@ -429,13 +436,13 @@ command(void) int got; n1 = (union node *)stalloc(sizeof (struct nbinary)); n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; - if ((n1->nbinary.ch1 = list(0)) == NULL) + if ((n1->nbinary.ch1 = list(0, 0)) == NULL) synexpect(-1); if ((got=readtoken()) != TDO) { TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); synexpect(TDO); } - n1->nbinary.ch2 = list(0); + n1->nbinary.ch2 = list(0, 0); if (readtoken() != TDONE) synexpect(TDONE); checkkwd = 1; @@ -487,7 +494,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); t = TEND; else synexpect(-1); - n1->nfor.body = list(0); + n1->nfor.body = list(0, 0); if (readtoken() != t) synexpect(t); checkkwd = 1; @@ -527,7 +534,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); ap->narg.next = NULL; if (lasttoken != TRP) noaliases = 0, synexpect(TRP); - cp->nclist.body = list(0); + cp->nclist.body = list(0, 0); checkkwd = 2; if ((t = readtoken()) != TESAC) { @@ -545,14 +552,14 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); case TLP: n1 = (union node *)stalloc(sizeof (struct nredir)); n1->type = NSUBSHELL; - n1->nredir.n = list(0); + n1->nredir.n = list(0, 0); n1->nredir.redirect = NULL; if (readtoken() != TRP) synexpect(TRP); checkkwd = 1; break; case TBEGIN: - n1 = list(0); + n1 = list(0, 0); if (readtoken() != TEND) synexpect(TEND); checkkwd = 1; @@ -644,9 +651,13 @@ simplecmd(union node **rpp, union node *redir) /* * - Require plain text. * - Functions with '/' cannot be called. + * - Reject name=(). + * - Reject ksh extended glob patterns. */ if (!noexpand(n->narg.text) || quoteflag || - strchr(n->narg.text, '/')) + strchr(n->narg.text, '/') || + strchr("!%*+-=?@}~", + n->narg.text[strlen(n->narg.text) - 1])) synerror("Bad function name"); rmescapes(n->narg.text); if (find_builtin(n->narg.text, &special) >= 0 && @@ -1066,7 +1077,7 @@ done: doprompt = 0; } - n = list(0); + n = list(0, oldstyle); if (oldstyle) doprompt = saveprompt; @@ -1161,7 +1172,7 @@ readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs) loop: { /* for each line, until end of word */ CHECKEND(); /* set c to PEOF if at end of here document */ for (;;) { /* until end of line or end of word */ - CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ + CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ synentry = state[level].syntax[c]; @@ -1203,12 +1214,18 @@ readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs) newvarnest == 0)) && (c != '}' || state[level].category != TSTATE_VAR_OLD)) USTPUTC('\\', out); + if ((eofmark == NULL || + newvarnest > 0) && + state[level].syntax == BASESYNTAX) + USTPUTC(CTLQUOTEMARK, out); if (SQSYNTAX[c] == CCTL) USTPUTC(CTLESC, out); - else if (eofmark == NULL || - newvarnest > 0) - USTPUTC(CTLQUOTEMARK, out); USTPUTC(c, out); + if ((eofmark == NULL || + newvarnest > 0) && + state[level].syntax == BASESYNTAX && + state[level].category == TSTATE_VAR_OLD) + USTPUTC(CTLQUOTEEND, out); quotef++; } break; @@ -1224,6 +1241,8 @@ readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs) if (eofmark != NULL && newvarnest == 0) USTPUTC(c, out); else { + if (state[level].category == TSTATE_VAR_OLD) + USTPUTC(CTLQUOTEEND, out); state[level].syntax = BASESYNTAX; quotef++; } @@ -1233,11 +1252,12 @@ readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs) break; case CENDVAR: /* '}' */ if (level > 0 && - (state[level].category == TSTATE_VAR_OLD || - state[level].category == TSTATE_VAR_NEW)) { - if (state[level].category == TSTATE_VAR_OLD) - state[level - 1].syntax = state[level].syntax; - else + ((state[level].category == TSTATE_VAR_OLD && + state[level].syntax == + state[level - 1].syntax) || + (state[level].category == TSTATE_VAR_NEW && + state[level].syntax == BASESYNTAX))) { + if (state[level].category == TSTATE_VAR_NEW) newvarnest--; level--; USTPUTC(CTLENDVAR, out); @@ -1725,7 +1745,7 @@ getprompt(void *unused __unused) char *fmt; const char *pwd; int i, trim; - static char internal_error[] = "<internal prompt error>"; + static char internal_error[] = "??"; /* * Select prompt format. diff --git a/bin/sh/parser.h b/bin/sh/parser.h index 453711f..64ec97f 100644 --- a/bin/sh/parser.h +++ b/bin/sh/parser.h @@ -43,6 +43,7 @@ #define CTLARI '\206' #define CTLENDARI '\207' #define CTLQUOTEMARK '\210' +#define CTLQUOTEEND '\211' /* only for ${v+-...} */ /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 index 3499969..d529692 100644 --- a/bin/sh/sh.1 +++ b/bin/sh/sh.1 @@ -32,7 +32,7 @@ .\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95 .\" $FreeBSD$ .\" -.Dd October 16, 2010 +.Dd October 31, 2010 .Dt SH 1 .Os .Sh NAME @@ -40,9 +40,24 @@ .Nd command interpreter (shell) .Sh SYNOPSIS .Nm -.Op Fl /+abCEefIimnPpsTuVvx +.Op Fl /+abCEefIimnPpTuVvx .Op Fl /+o Ar longname -.Op Fl c Ar string +.Oo +.Ar script +.Op Ar arg ... +.Oc +.Nm +.Op Fl /+abCEefIimnPpTuVvx +.Op Fl /+o Ar longname +.Fl c Ar string +.Oo +.Ar name +.Op Ar arg ... +.Oc +.Nm +.Op Fl /+abCEefIimnPpTuVvx +.Op Fl /+o Ar longname +.Fl s .Op Ar arg ... .Sh DESCRIPTION The @@ -1109,7 +1124,12 @@ the process ID and its exit status until the .Ic wait built-in command reports completion of the process. .It Li $0 -(zero) Expands to the name of the shell or shell script. +(zero) Expands to the name of the shell script if passed on the command line, +the +.Ar name +operand if given (with +.Fl c ) +or otherwise argument 0 passed to the shell. .El .Ss Special Variables The following variables are set by the shell or diff --git a/bin/sh/var.c b/bin/sh/var.c index 27bbf50..f5ea775 100644 --- a/bin/sh/var.c +++ b/bin/sh/var.c @@ -826,7 +826,7 @@ setvarcmd(int argc, char **argv) else if (argc == 3) setvar(argv[1], argv[2], 0); else - error("List assignment not implemented"); + error("too many arguments"); return 0; } |