diff --git a/configure.ac b/configure.ac index 52aa429..5524650 100644 --- a/configure.ac +++ b/configure.ac @@ -177,6 +177,17 @@ if test "$have_st_mtim" = "yes"; then [Define if your `struct stat' has `st_mtim']) fi +dnl F_DUPFD_CLOEXEC is a mandatory part of POSIX since Issue 7 +AC_MSG_CHECKING(for F_DUPFD_CLOEXEC) +AC_COMPILE_IFELSE( +[AC_LANG_PROGRAM([#include +#include ], +[return fcntl(0, F_DUPFD_CLOEXEC, 0)])], +have_dupfd_cloexec=1, have_dupfd_cloexec=0) +AC_MSG_RESULT($(expr yes \& $have_dupfd_cloexec \| no)) +AC_DEFINE_UNQUOTED([HAVE_F_DUPFD_CLOEXEC], [$have_dupfd_cloexec], + [Define to 1 your system supports F_DUPFD_CLOEXEC]) + AC_ARG_WITH(libedit, AS_HELP_STRING(--with-libedit, [Compile with libedit support])) use_libedit= if test "$with_libedit" = "yes"; then diff --git a/src/alias.c b/src/alias.c index daeacbb..fcad43b 100644 --- a/src/alias.c +++ b/src/alias.c @@ -197,7 +197,8 @@ freealias(struct alias *ap) { void printalias(const struct alias *ap) { - out1fmt("%s=%s\n", ap->name, single_quote(ap->val)); + out1str(single_quote(ap->name)); + out1fmt("=%s\n", single_quote(ap->val)); } STATIC struct alias ** diff --git a/src/dash.1 b/src/dash.1 index ff02237..d3893bc 100644 --- a/src/dash.1 +++ b/src/dash.1 @@ -1095,6 +1095,8 @@ etc). .It : .It true A null command that returns a 0 (true) exit value. +.It false +A null command that returns a 1 (false) exit value. .It \&. file The commands in the specified file are read and executed by the shell. .It alias Op Ar name Ns Op Ar "=string ..." @@ -1143,8 +1145,8 @@ Do not execute the command but search for the command and print the absolute pathname of utilities, the name for builtins or the expansion of aliases. .El -.It cd Ar - -.It Xo cd Op Fl LP +.It cd|chdir Ar - +.It Xo cd|chdir Op Fl LP .Op Ar directory .Xc Switch to the specified directory (default @@ -1342,13 +1344,12 @@ The number of previous commands that are accessible. .El .It fg Op Ar job Move the specified job or the current job to the foreground. -.It getopts Ar optstring var +.It getopts Ar optstring var Op Ar arg ... The .Tn POSIX .Ic getopts command, not to be confused with the -.Em Bell Labs --derived +.Em Bell Labs Ns -derived .Xr getopt 1 . .Pp The first argument should be a series of letters, each of which may be @@ -1386,6 +1387,12 @@ then .Ev OPTARG will be unset. .Pp +By default, the variables +.Va $1 , ... , $n +are inspected; if +.Ar arg Ns s +are specified, they'll be parsed instead. +.Pp .Va optstring is a string of recognized option letters (see .Xr getopt 3 ) . @@ -1430,7 +1437,7 @@ do \\?) echo $USAGE; exit 1;; esac done -shift `expr $OPTIND - 1` +shift $((OPTIND - 1)) .Ed .Pp This code will accept any of the following as equivalent: @@ -1441,7 +1448,8 @@ cmd \-a \-c arg file file cmd \-carg -a file file cmd \-a \-carg \-\- file file .Ed -.It hash Fl rv Ar command ... +.It hash Op Ar command ... +.It hash Fl r The shell maintains a hash table which remembers the locations of commands. With no arguments whatsoever, @@ -1457,13 +1465,51 @@ With arguments, the .Ic hash command removes the specified commands from the hash table (unless they are functions) and then locates them. -With the -.Fl v -option, hash prints the locations of the commands as it finds them. The .Fl r option causes the hash command to delete all the entries in the hash table except for functions. +.It jobs Oo Fl lp Oc Op Ar job ... +Display the status of all, or just the specified, +.Ar job Ns s : +.Bl -tag -compact -offset 5n -width "By default" +.It By default +display the job number, currency +.Pq Sy +- +status, if any, the job state, and its shell command. +.It Fl l +also output the PID of the group leader, and just the PID and shell commands +of other members of the job. +.It Fl p +Display only leader PIDs, one per line. +.El +.It kill Oo Fl s Ar sigspec | Fl Ns Ar signum | Fl Ns Ar sigspec Oc Op Ar pid | job ... +Equivalent to +.Xr kill 1 , +but a +.Ar job +spec may also be specified. +Signals can be either case-insensitive names without +.Dv SIG +prefixes or decimal numbers; the default is +.Dv TERM . +.It kill Fl l Op Ar signum | exitstatus +List available signal names without the +.Dv SIG +prefix +.Pq Ar sigspec Ns s . +If +.Ar signum +specified, display just the +.Ar sigspec +for that signal. +If +.Ar exitstatus +specified +.Pq > Sy 128 , +display just the +.Ar sigspec +that caused it. .It pwd Op Fl LP builtin command remembers what the current directory is rather than recomputing it each time. @@ -1528,41 +1574,36 @@ With the option specified the output will be formatted suitably for non-interactive use. .\".Pp .It Xo printf Ar format -.Op Ar arguments ... +.Oo Ar value Oc Ns ... .Xc .Ic printf -formats and prints its arguments, after the first, under control -of the -.Ar format . -The -.Ar format -is a character string which contains three types of objects: plain characters, +formats and prints its arguments according to +.Ar format , +a character string which contains three types of objects: plain characters, which are simply copied to standard output, character escape sequences which are converted and copied to the standard output, and format specifications, each of which causes printing of the next successive -.Ar argument . +.Ar value . .Pp -The -.Ar arguments -after the first are treated as strings if the corresponding format is +Each +.Ar value +is treated as a string if the corresponding format specification is either .Cm b , -.Cm c +.Cm c , or .Cm s ; -otherwise it is evaluated as a C constant, with the following extensions: -.Pp +otherwise it is evaluated as a C constant, with the following additions: .Bl -bullet -offset indent -compact .It A leading plus or minus sign is allowed. .It -If the leading character is a single or double quote, the value is the -.Tn ASCII -code of the next character. +If the leading character is a single or double quote, the value of the next byte. .El .Pp -The format string is reused as often as necessary to satisfy the -.Ar arguments . +The format string is reused as often as necessary until all +.Ar value Ns s +are consumed. Any extra format specifications are evaluated with zero or the null string. .Pp @@ -2121,7 +2162,7 @@ printed; for commands and tracked aliases the complete pathname of the command is printed. .It ulimit Xo .Op Fl H \*(Ba Fl S -.Op Fl a \*(Ba Fl tfdscmlpnv Op Ar value +.Op Fl a \*(Ba Fl tfdscmlpnvwr Op Ar value .Xc Inquire about or set the hard or soft limits on processes or set new limits. @@ -2174,6 +2215,8 @@ show or set the limit on the number files a process can have open at once .It Fl v show or set the limit on the total virtual memory that can be in use by a process (in kilobytes) +.It Fl w +show or set the limit on the total number of locks held by a process .It Fl r show or set the limit on the real-time scheduling priority of a process .El diff --git a/src/exec.c b/src/exec.c index 87354d4..83cba94 100644 --- a/src/exec.c +++ b/src/exec.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #ifdef HAVE_PATHS_H #include @@ -271,11 +272,16 @@ hashcmd(int argc, char **argv) int c; struct cmdentry entry; char *name; + bool clear; - while ((c = nextopt("r")) != '\0') { + clear = false; + while ((c = nextopt("r")) != '\0') + clear = true; + if(clear) { clearcmdentry(); return 0; } + if (*argptr == NULL) { for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { @@ -760,11 +766,11 @@ unsetfunc(const char *name) int typecmd(int argc, char **argv) { - int i; int err = 0; - for (i = 1; i < argc; i++) { - err |= describe_command(out1, argv[i], NULL, 1); + nextopt(nullstr); + while (*argptr) { + err |= describe_command(out1, *argptr++, NULL, 1); } return err; } diff --git a/src/input.c b/src/input.c index ec075f5..38969a7 100644 --- a/src/input.c +++ b/src/input.c @@ -54,9 +54,7 @@ #include "alias.h" #include "parser.h" #include "main.h" -#ifndef SMALL #include "myhistedit.h" -#endif #define IBUFSIZ (BUFSIZ + 1) @@ -77,6 +75,7 @@ INCLUDE INCLUDE INCLUDE "input.h" INCLUDE "error.h" +INCLUDE "syntax.h" INIT { basepf.nextc = basepf.buf = basebuf; @@ -85,9 +84,12 @@ INIT { RESET { /* clear input buffer */ - basepf.lleft = basepf.nleft = 0; - basepf.unget = 0; popallfiles(); + basepf.unget = 0; + while (basepf.lastc[0] != '\n' && + basepf.lastc[0] != PEOF && + !int_pending()) + pgetc(); } FORKRESET { @@ -159,6 +161,17 @@ int pgetc(void) return __pgetc(); } +static int stdin_clear_nonblock(void) +{ + int flags = fcntl(0, F_GETFL, 0); + + if (flags >= 0) { + flags &=~ O_NONBLOCK; + flags = fcntl(0, F_SETFL, flags); + } + + return flags; +} static int preadfd(void) @@ -195,22 +208,38 @@ retry: } else #endif + if (parsefile->fd) nr = read(parsefile->fd, buf, IBUFSIZ - 1); + else { + unsigned len = IBUFSIZ - 1; + + nr = 0; + do { + int err; + + err = read(0, buf, 1); + if (err <= 0) { + if (nr) + break; + + nr = err; + if (errno != EWOULDBLOCK) + break; + if (stdin_clear_nonblock() < 0) + break; + + out2str("sh: turning off NDELAY mode\n"); + goto retry; + } + + nr++; + } while (!IS_DEFINED_SMALL && *buf++ != '\n' && --len); + } if (nr < 0) { if (errno == EINTR) goto retry; - if (parsefile->fd == 0 && errno == EWOULDBLOCK) { - int flags = fcntl(0, F_GETFL, 0); - if (flags >= 0 && flags & O_NONBLOCK) { - flags &=~ O_NONBLOCK; - if (fcntl(0, F_SETFL, flags) >= 0) { - out2str("sh: turning off NDELAY mode\n"); - goto retry; - } - } - } } return nr; } @@ -226,12 +255,11 @@ retry: static int preadbuffer(void) { - char *q; - int more; -#ifndef SMALL + int first = whichprompt == 1; int something; -#endif char savec; + int more; + char *q; if (unlikely(parsefile->strpush)) { popstring(); @@ -241,11 +269,11 @@ static int preadbuffer(void) return PEOF; flushall(); - more = parsefile->lleft; + more = input_get_lleft(parsefile); if (more <= 0) { again: if ((more = preadfd()) <= 0) { - parsefile->lleft = parsefile->nleft = 0; + input_set_lleft(parsefile, parsefile->nleft = 0); return PEOF; } } @@ -253,37 +281,38 @@ again: q = parsefile->nextc; /* delete nul characters */ -#ifndef SMALL - something = 0; -#endif + something = !first; for (;;) { int c; more--; c = *q; - if (!c) + if (!c) { memmove(q, q + 1, more); - else { - q++; + goto check; + } - if (c == '\n') { - parsefile->nleft = q - parsefile->nextc - 1; - break; - } + q++; -#ifndef SMALL - switch (c) { - default: - something = 1; - /* fall through */ - case '\t': - case ' ': - break; - } -#endif + if (IS_DEFINED_SMALL) + goto check; + + switch (c) { + case '\n': + parsefile->nleft = q - parsefile->nextc - 1; + goto check; + + default: + something = 1; + /* fall through */ + + case '\t': + case ' ': + break; } +check: if (more <= 0) { parsefile->nleft = q - parsefile->nextc - 1; if (parsefile->nleft < 0) @@ -291,20 +320,19 @@ again: break; } } - parsefile->lleft = more; + input_set_lleft(parsefile, more); - savec = *q; + if (!IS_DEFINED_SMALL) + savec = *q; *q = '\0'; -#ifndef SMALL if (parsefile->fd == 0 && hist && something) { HistEvent he; INTOFF; - history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, + history(hist, &he, first ? H_ENTER : H_APPEND, parsefile->nextc); INTON; } -#endif if (vflag) { out2str(parsefile->nextc); @@ -313,7 +341,8 @@ again: #endif } - *q = savec; + if (!IS_DEFINED_SMALL) + *q = savec; return (signed char)*parsefile->nextc++; } @@ -428,7 +457,7 @@ setinputfd(int fd, int push) parsefile->fd = fd; if (parsefile->buf == NULL) parsefile->buf = ckmalloc(IBUFSIZ); - parsefile->lleft = parsefile->nleft = 0; + input_set_lleft(parsefile, parsefile->nleft = 0); plinno = 1; } diff --git a/src/input.h b/src/input.h index 8c39f33..1ff5773 100644 --- a/src/input.h +++ b/src/input.h @@ -34,6 +34,12 @@ * @(#)input.h 8.2 (Berkeley) 5/4/95 */ +#ifdef SMALL +#define IS_DEFINED_SMALL 1 +#else +#define IS_DEFINED_SMALL 0 +#endif + /* PEOF (the end of file marker) is defined in syntax.h */ enum { @@ -70,7 +76,9 @@ struct parsefile { int linno; /* current line */ int fd; /* file descriptor (or -1 if string) */ int nleft; /* number of chars left in this line */ +#ifndef SMALL int lleft; /* number of chars left in this buffer */ +#endif char *nextc; /* next char in buffer */ char *buf; /* input buffer */ struct strpush *strpush; /* for pushing strings at this level */ @@ -104,3 +112,19 @@ void setinputstring(char *); void popfile(void); void unwindfiles(struct parsefile *); void popallfiles(void); + +static inline int input_get_lleft(struct parsefile *pf) +{ +#ifdef SMALL + return 0; +#else + return pf->lleft; +#endif +} + +static inline void input_set_lleft(struct parsefile *pf, int len) +{ +#ifndef SMALL + pf->lleft = len; +#endif +} diff --git a/src/miscbltin.c b/src/miscbltin.c index 5ccbbcb..e553f9e 100644 --- a/src/miscbltin.c +++ b/src/miscbltin.c @@ -440,6 +440,9 @@ ulimitcmd(int argc, char **argv) #endif #ifdef RLIMIT_LOCKS "w" +#endif +#ifdef RLIMIT_RTPRIO + "r" #endif )) != '\0') switch (optc) { diff --git a/src/myhistedit.h b/src/myhistedit.h index 22e5c43..1736f62 100644 --- a/src/myhistedit.h +++ b/src/myhistedit.h @@ -31,9 +31,27 @@ * @(#)myhistedit.h 8.2 (Berkeley) 5/4/95 */ +#ifdef SMALL +typedef void History; +typedef void EditLine; +typedef int HistEvent; + +enum { + H_APPEND, + H_ENTER, +}; + +#define hist NULL + +static inline void history(History *h, HistEvent *he, int action, char *p) +{ +} +#else #include extern History *hist; +#endif + extern EditLine *el; extern int displayhist; diff --git a/src/options.c b/src/options.c index a46c23b..2d4bd3b 100644 --- a/src/options.c +++ b/src/options.c @@ -409,8 +409,11 @@ getoptscmd(int argc, char **argv) { char **optbase; + nextopt(nullstr); + argc -= argptr - argv - 1; + argv = argptr - 1; if (argc < 3) - sh_error("Usage: getopts optstring var [arg]"); + sh_error("Usage: getopts optstring var [arg...]"); else if (argc == 3) { optbase = shellparam.p; if ((unsigned)shellparam.optind > shellparam.nparam + 1) { diff --git a/src/parser.c b/src/parser.c index a552c47..299c260 100644 --- a/src/parser.c +++ b/src/parser.c @@ -615,7 +615,7 @@ void fixredir(union node *n, const char *text, int err) else { if (err) - synerror("Bad fd number"); + sh_error("Bad fd number: %s", text); else n->ndup.vname = makename(); } @@ -1360,12 +1360,10 @@ parsebackq: { struct heredoc *saveheredoclist; int uninitialized_var(saveprompt); - str = NULL; + USTPUTC(CTLBACKQ, out); + str = stackblock(); savelen = out - (char *)stackblock(); - if (savelen > 0) { - str = alloca(savelen); - memcpy(str, stackblock(), savelen); - } + grabstackblock(savelen); if (oldstyle) { /* We must read until the closing backquote, giving special treatment to some slashes, and then push the string and @@ -1445,12 +1443,7 @@ done: /* Ignore any pushed back tokens left from the backquote parsing. */ if (oldstyle) tokpushback = 0; - out = growstackto(savelen + 1); - if (str) { - memcpy(out, str, savelen); - STADJUST(savelen, out); - } - USTPUTC(CTLBACKQ, out); + out = stnputs(str, savelen, stackblock()); if (oldstyle) goto parsebackq_oldreturn; else diff --git a/src/redir.c b/src/redir.c index 5a5835c..631ddc9 100644 --- a/src/redir.c +++ b/src/redir.c @@ -446,13 +446,18 @@ savefd(int from, int ofd) int newfd; int err; +#if HAVE_F_DUPFD_CLOEXEC + newfd = fcntl(from, F_DUPFD_CLOEXEC, 10); +#else newfd = fcntl(from, F_DUPFD, 10); +#endif + err = newfd < 0 ? errno : 0; if (err != EBADF) { close(ofd); if (err) sh_error("%d: %s", from, strerror(err)); - else + else if(!HAVE_F_DUPFD_CLOEXEC) fcntl(newfd, F_SETFD, FD_CLOEXEC); } diff --git a/src/var.c b/src/var.c index ef9c2bd..b70d72c 100644 --- a/src/var.c +++ b/src/var.c @@ -154,6 +154,10 @@ RESET { } #endif +static char *varnull(const char *s) +{ + return (strchr(s, '=') ?: nullstr - 1) + 1; +} /* * This routine initializes the builtin variables. It is called when the @@ -266,7 +270,7 @@ struct var *setvareq(char *s, int flags) goto out; if (vp->func && (flags & VNOFUNC) == 0) - (*vp->func)(strchrnul(s, '=') + 1); + (*vp->func)(varnull(s)); if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) ckfree(vp->text); @@ -531,7 +535,7 @@ poplocalvars(void) unsetvar(vp->text); } else { if (vp->func) - (*vp->func)(strchrnul(lvp->text, '=') + 1); + (*vp->func)(varnull(lvp->text)); if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) ckfree(vp->text); vp->flags = lvp->flags;