sh: Sync with FreeBSD
authorPeter Avalos <pavalos@dragonflybsd.org>
Mon, 7 Feb 2011 08:05:28 +0000 (22:05 -1000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sat, 12 Feb 2011 07:08:36 +0000 (21:08 -1000)
This is a combination of 198 commits that enhances performance,
standards compliance, and bug fixes:

-Fix exit status of case statement.
-sh.1: Update markup.
-Fix PWD values.
-Fix null pointer dereferences.
-Fix bugs where arithmetic expansion$((...)) was truncated
-Pass the correct flags to expandarg() for NFROMFD and NTOFD.
-Add __dead2
-Fix $? at the first command of a function.
-Report error messages to stderr instead of stdout.
-Make alias builtin POSIX compliant.
-Improve the IFS handling of the read built-in.
-Don't let empty lines overwrite the result of the last command with 0.
-Fix the eval command in combination with set -e.
-Make read's timeout (-t) apply to the entire line.
-align local ckmalloc() with malloc(3) by using a size_t
-style(9) to enhance readability
-explicit 'unsigned int' instead of just 'unsigned'
-Mention the range for the exit status for the exit builtin
-Don't skip forking for an external command if any traps are active.
-Avoid leaving unnecessary waiting shells.
-Properly flush input after an error in backquotes
-Fix some issues with quoted output
-Fix race condition in noclobber option
-Improve handling of setjmp/longjmp volatile
-Do not fork for EV_EXIT.
-Quote -x tracing output so it is unambiguous.
-Designate special builtins as such in command -V and type.
-Fix crash when undefining or redefining an executing function.
-Fix memory leak when using a variable in arithmetic like $((x)).
-Use sigaction instead of signal/siginterrupt combination.
-Allow a newline before "in" in a for command, as required by POSIX.
-Some changes to stderr flushing.
-Handle current work directories of arbitrary length.
-trap: do not consider a bad signal name a fatal error.
-Ensure the same command input file is on top after execing builtin.
-Fix various things about SIGINT handling.
-Fix some cases where file descriptors from redirections leak to programs.
-Remove setting variables from dotcmd/exportcmd.
-Fix a memory leak when calling . with variable assignments.
-Constify various strings.
-Do not consider a tilde-prefix with expansions in it.
-Do not run callers' exception handlers in subshells.
-Remove declaration of function that no longer exists.
-WARNS fixes to reduce diffs to FreeBSD
-arith: Return only 0 and 1 from && and ||.
-Fix memory leak when parsing backticks (``).
-Ensure funcnest is decremented if there's an error in the function.
-Allow command -pv and command -pV
-Fix some bugs with backquoted builtins.
-Send the "not found" message for builtin <cmd> to redirected fd 2.
-Do not stat() $MAIL/$MAILPATH in non-interactive shells.
-Fix expansion of \W in prompt strings when the working directory is "/".
-Improve the command builtin:
-Make sure to popredir() even if a special builtin caused an error.
-Make sure to popredir() even if a function caused an error.
-Make parsebackq a function instead of an emulated nested function.
-Do not abort on a redirection error if there is no command word.
-Do not abort on a redirection error on a compound command.
-Treat unexpected newlines in substitutions as a syntax error.
-Fix various things about expansions.
-Remove special handling for ' and " in arithmetic.
-Allow quoting pattern match characters in ${v%pat} and ${v#pat}.
-Do tilde expansion in substitutions.
-Automatically enable -o emacs in interactive shells with terminals.
-On startup of the shell, use PWD from the environment if it is valid.
-Use stalloc for arith variable names.
-Apply locale vars on builtins, recognize LC_MESSAGES as a locale var.
-Have only one copy of _PATH_STDPATH in the binary.
-Fix "reserved word" vs "keyword" inconsistency.
-Fix pathname expansion with quoted slashes like *\/.
-Reap any zombies before forking for a background command.
-Rework documentation of shell variables.
-Recognize "--" in . and exec.
-Change interaction of command substitution and here documents.
-Fix a crash if a heredoc was not properly ended and parsing continued.
-Pass TERM changes to libedit.
-Pass through SIGINT from a child if interactive and job control is enabled.
-Forget about terminated background processes sooner.
-Use $PWD instead of getcwd() for the \w and \W prompt expansions.
-Allow a background command consisting solely of redirections.
-Fix crash due to uninitialized here-document.
-Return 0 from eval if no command was given.
-Remove unnecessary duplicate letters in mksyntax.c
-Fix heap-based buffer overflow in pathname generation.
-Fix shadowing of sigset.
-Fix break/continue/return sometimes not skipping the rest of dot script.
-Add a brief summary of arithmetic expressions.
-Remove remnants of '!!' to negate pattern.
-Do not use locale for determining if something is a name.
-Get rid of some magic numbers.
-Improve comments in expand.c.
-Fix 'read' if all chars before the first IFS char are backslash-escaped.
-Remove xrefs for expr(1) and getopt(1).
-Apply variable assignments left-to-right in bltinlookup().
-Fix exit status if return is used within a loop condition.
-Suggest that DEBUG_FLAGS be used to enable extra debugging
-Make DEBUG traces 64-bit clean.
-Remove the "STATIC" macro
-Fix a bug in STACKSTRNUL()
-Clarify subshells/processes for pipelines.
-There cannot be a TNOT in simplecmd(), remove checks.
-Change ! within a pipeline to start a new pipeline instead.
-Check whether dup2 was successful for >&FD and <&FD.
-Make sure defined functions can actually be called.
-Do not allow overriding a special builtin with a function.
-Ignore double-quotes in arithmetic rather than treating them as quotes.
-Make double-quotes quote a '}' inside ${v#...} and ${v%...}.
-Only accept a '}' inside ${v+-=?...} if double-quote state matches.
-Do IFS splitting on word in ${v+word} and ${v-word}.
-Fix some issues with CTL* bytes and ${var#pat}.
-Error out on various specials/keywords in the wrong place in backticks.
-Detect various additional errors in the parser.
-Reject function names ending in one of !%*+-=?@}~
-Tweak some string constants to reduce code size.
-Use iteration instead of recursion to evaluate semicolon lists.
-Reindent evaltree().
-Correct synopsis and make precise how $0 is set.
-Fix some issues with aliases and case, by importing dash checkkwd code.
-Modernize the introduction a bit.
-Remove unused man page for echo builtin.
-Do the additional actions if 'local -' restore changes -i/-m/-E/-V.
-Add binary buffered output for use by the printf builtin.
-document printf builtin
-Code size optimizations to buffered output.
-Remove the check that alpha/name/in_name chars are not CTL* bytes.
-Fix confusing behaviour if chdir succeeded but getcwd failed in cd -P.
-Code size optimizations to "stack string" memory allocation
-jobs -p: Do not ask the kernel for the pgid.
-Improve jobs output of pipelines.
-POSIX says there should not be a space between Done and (exitstatus).
-Improve internal-representation-to-text code to avoid binary output.
-Use vsnprintf() rather than crafting our own in fmtstr().
-Replace some macros and repeated code in expand.c with functions.
-Remove the herefd hack.
-Various simplifications to jobs.c
-Remove duplicate check, turning dead code into live code.
-Fix corruption of command substitutions with special chars after newline
-Remove dead code.
-arith: Disallow decimal constants starting with 0 (containing 8 or 9).
-Make warnings in the printf builtin non-fatal.
-Add a function to print warnings (with command name and newline).
-Add kill builtin.
-Explain why it is a bad idea to use aliases in scripts.
-Allow arbitrary large numbers in CHECKSTRSPACE.
-Make expansion errors in optimized command substitution non-fatal.
-Don't do optimized command substitution if expansions have side effects.
-Properly restore exception handler in fc.
-Avoid side effects from builtins in optimized command substitution.
-Check if dup2 for redirection from/to a file succeeds.
-Check readonly status for assignments on regular builtins.
-Do not call exitshell() from evalcommand() unless evalcommand() forked itself
-Make exit without parameters from EXIT trap POSIX-compliant.
-Remove special %builtin PATH entry.
-Make 'trap -l' look like 'kill -l'.
-Fix some things about -- in trap.
-If exit is used without args from a trap action, exit on the signal.
-Fix signal messages being sent to the wrong file sometimes.
-Send messages about signals to stderr.
-Return only 126 or 127 for execve() failures.
-Make sys_signame upper case.
-Remove special code for shell scripts without magic number.
-Do not try to execute binary files as scripts.
-Forget all cached command locations on any PATH change.
-Fix two things about {(...)} <redir.
-Import arithmetic expression code from dash.
-Install /bin/sh safely.

Obtained-from: FreeBSD

74 files changed:
bin/kill/kill.1
bin/kill/kill.c
bin/sh/Makefile
bin/sh/TOUR
bin/sh/alias.c
bin/sh/alias.h
bin/sh/arith.h
bin/sh/arith.y [deleted file]
bin/sh/arith_lex.l [deleted file]
bin/sh/arith_yacc.c [new file with mode: 0644]
bin/sh/arith_yacc.h [copied from bin/sh/alias.h with 56% similarity]
bin/sh/arith_yylex.c [new file with mode: 0644]
bin/sh/bltin/bltin.h
bin/sh/bltin/echo.1 [deleted file]
bin/sh/bltin/echo.c
bin/sh/builtins.def
bin/sh/cd.c
bin/sh/cd.h
bin/sh/error.c
bin/sh/error.h
bin/sh/eval.c
bin/sh/eval.h
bin/sh/exec.c
bin/sh/exec.h
bin/sh/expand.c
bin/sh/expand.h
bin/sh/funcs/cmv
bin/sh/funcs/dirs
bin/sh/funcs/kill
bin/sh/funcs/login
bin/sh/funcs/newgrp
bin/sh/funcs/popd
bin/sh/funcs/pushd
bin/sh/funcs/suspend
bin/sh/histedit.c
bin/sh/init.h
bin/sh/input.c
bin/sh/input.h
bin/sh/jobs.c
bin/sh/jobs.h
bin/sh/mail.c
bin/sh/mail.h
bin/sh/main.c
bin/sh/main.h
bin/sh/memalloc.c
bin/sh/memalloc.h
bin/sh/miscbltin.c
bin/sh/mkbuiltins
bin/sh/mkinit.c
bin/sh/mknodes.c
bin/sh/mksyntax.c
bin/sh/mktokens
bin/sh/myhistedit.h
bin/sh/mystring.c
bin/sh/mystring.h
bin/sh/nodes.c.pat
bin/sh/nodetypes
bin/sh/options.c
bin/sh/options.h
bin/sh/output.c
bin/sh/output.h
bin/sh/parser.c
bin/sh/parser.h
bin/sh/redir.c
bin/sh/sh.1
bin/sh/shell.h
bin/sh/show.c
bin/sh/show.h
bin/sh/trap.c
bin/sh/trap.h
bin/sh/var.c
bin/sh/var.h
bin/test/test.c
usr.bin/printf/printf.c

index 9b01fac..a674955 100644 (file)
@@ -140,6 +140,7 @@ Terminate the process group with pgid 117:
 .Xr csh 1 ,
 .Xr killall 1 ,
 .Xr ps 1 ,
+.Xr sh 1 ,
 .Xr kill 2 ,
 .Xr sigaction 2
 .Sh STANDARDS
index de40ec7..10330bf 100644 (file)
 
 #include <ctype.h>
 #include <err.h>
+#include <errno.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#ifdef SHELL
+#define main killcmd
+#include "bltin/bltin.h"
+#include "error.h"
+#endif
+
 static void    nosig(const char *);
 static void    printsignals(FILE *);
 static int     signame_to_signum(const char *);
@@ -70,16 +77,16 @@ main(int argc, char **argv)
                                usage();
                        numsig = strtol(*argv, &ep, 10);
                        if (**argv == '\0' || *ep != '\0')
-                               errx(1, "illegal signal number: %s", *argv);
+                               errx(2, "illegal signal number: %s", *argv);
                        if (numsig >= 128)
                                numsig -= 128;
                        if (numsig <= 0 || numsig >= sys_nsig)
                                nosig(*argv);
                        printf("%s\n", sys_signame[numsig]);
-                       exit(0);
+                       return (0);
                }
                printsignals(stdout);
-               exit(0);
+               return (0);
        }
 
        if (strcmp(*argv, "-s") == 0) {
@@ -102,7 +109,7 @@ main(int argc, char **argv)
                } else if (isdigit(**argv)) {
                        numsig = strtol(*argv, &ep, 10);
                        if (**argv == '\0' || *ep != '\0')
-                               errx(1, "illegal signal number: %s", *argv);
+                               errx(2, "illegal signal number: %s", *argv);
                        if (numsig < 0 || numsig >= sys_nsig)
                                nosig(*argv);
                } else
@@ -117,17 +124,25 @@ main(int argc, char **argv)
                usage();
 
        for (errors = 0; argc; argc--, argv++) {
-               pid = (pid_t)strtol(*argv, &ep, 10);
-               if (**argv == '\0' || *ep != '\0') {
-                       warnx("illegal process id: %s", *argv);
-                       errors = 1;
-               } else if (kill(pid, numsig) == -1) {
+#ifdef SHELL
+               if (**argv == '%')
+                       pid = getjobpgrp(*argv);
+               else
+#endif
+               {
+                       pid = (pid_t)strtol(*argv, &ep, 10);
+                       if (**argv == '\0' || *ep != '\0') {
+                               warnx("illegal process id: %s", *argv);
+                               errors = 1;
+                       }
+               }
+               if (kill(pid, numsig) == -1) {
                        warn("%s", *argv);
                        errors = 1;
                }
        }
 
-       exit(errors);
+       return (errors);
 }
 
 static int
@@ -149,7 +164,11 @@ nosig(const char *name)
 {
        warnx("unknown signal %s; valid signals:", name);
        printsignals(stderr);
-       exit(1);
+#ifdef SHELL
+       error(NULL);
+#else
+       exit(2);
+#endif
 }
 
 static void
@@ -174,5 +193,9 @@ usage(void)
                "       kill -l [exit_status]",
                "       kill -signal_name pid ...",
                "       kill -signal_number pid ...");
-       exit(1);
+#ifdef SHELL
+       error(NULL);
+#else
+       exit(2);
+#endif
 }
index d17a932..6ec094f 100644 (file)
@@ -1,35 +1,36 @@
 #      @(#)Makefile    8.4 (Berkeley) 5/5/95
-# $FreeBSD: src/bin/sh/Makefile,v 1.30.2.1 2001/12/15 10:05:18 knu Exp $
-# $DragonFly: src/bin/sh/Makefile,v 1.7 2006/09/28 22:29:44 pavalos Exp $
+# $FreeBSD: src/bin/sh/Makefile,v 1.56 2011/02/08 23:18:06 jilles Exp $
 
 PROG=  sh
-SHSRCS=        alias.c arith.y arith_lex.l cd.c echo.c error.c eval.c exec.c expand.c \
-       histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \
+INSTALLFLAGS= -S
+SHSRCS=        alias.c arith_yacc.c arith_yylex.c cd.c echo.c error.c eval.c \
+       exec.c expand.c \
+       histedit.c input.c jobs.c kill.c mail.c main.c memalloc.c miscbltin.c \
        mystring.c options.c output.c parser.c printf.c redir.c show.c \
        test.c trap.c var.c
 GENSRCS= builtins.c init.c nodes.c syntax.c
-GENHDRS= builtins.h nodes.h syntax.h token.h y.tab.h
-SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS} y.tab.h
+GENHDRS= builtins.h nodes.h syntax.h token.h
+SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS}
 
 # MLINKS for Shell built in commands for which there are no userland
 # utilities of the same name are handled with the associated manpage,
-# builtin.1 in share/man/man1/.  
+# builtin.1 in share/man/man1/.
 
-DPADD+= ${LIBL} ${LIBEDIT} ${LIBTERMCAP}
-LDADD+= -ll -ledit -ltermcap
+DPADD= ${LIBEDIT} ${LIBTERMCAP}
+LDADD= -ledit -ltermcap
 
-LFLAGS= -8     # 8-bit lex scanner for arithmetic
 CFLAGS+=-DSHELL -I. -I${.CURDIR}
 # for debug:
-# CFLAGS+= -g -DDEBUG=2
+# DEBUG_FLAGS+= -g -DDEBUG=2 -fno-inline
 
 .if defined(BOOTSTRAPPING)
 CFLAGS+= -DNO_HISTORY
 .endif
 
 .PATH: ${.CURDIR}/bltin \
-       ${.CURDIR}/../../usr.bin/printf \
-       ${.CURDIR}/../../bin/test
+       ${.CURDIR}/../kill \
+       ${.CURDIR}/../test \
+       ${.CURDIR}/../../usr.bin/printf
 
 CLEANFILES+= mkinit.nx mkinit.no mknodes.nx mknodes.no \
        mksyntax.nx mksyntax.no
index 82be886..a6114f1 100644 (file)
@@ -1,6 +1,5 @@
 #      @(#)TOUR        8.1 (Berkeley) 5/31/93
-# $FreeBSD: src/bin/sh/TOUR,v 1.7 2006/04/16 11:54:01 schweikh Exp $
-# $DragonFly: src/bin/sh/TOUR,v 1.3 2007/01/04 06:24:11 pavalos Exp $
+# $FreeBSD: src/bin/sh/TOUR,v 1.8 2011/02/04 22:47:55 jilles Exp $
 
 NOTE -- This is the original TOUR paper distributed with ash and
 does not represent the current state of the shell.  It is provided anyway
@@ -45,10 +44,6 @@ C source files for entries looking like:
                            back to the main command loop */
         }
 
-        SHELLPROC {
-              x = 3;    /* executed when the shell runs a shell procedure */
-        }
-
 It pulls this code out into routines which are when particular
 events occur.  The intent is to improve modularity by isolating
 the information about which modules need to be explicitly
@@ -81,12 +76,7 @@ EXCEPTIONS:  Code for dealing with exceptions appears in
 exceptions.c.  The C language doesn't include exception handling,
 so I implement it using setjmp and longjmp.  The global variable
 exception contains the type of exception.  EXERROR is raised by
-calling error.  EXINT is an interrupt.  EXSHELLPROC is an excep-
-tion which is raised when a shell procedure is invoked.  The pur-
-pose of EXSHELLPROC is to perform the cleanup actions associated
-with other exceptions.  After these cleanup actions, the shell
-can interpret a shell procedure itself without exec'ing a new
-copy of the shell.
+calling error.  EXINT is an interrupt.
 
 INTERRUPTS:  In an interactive shell, an interrupt will cause an
 EXINT exception to return to the main command loop.  (Exception:
@@ -271,14 +261,6 @@ When a program is run, the code in eval.c sticks any environment
 variables which precede the command (as in "PATH=xxx command") in
 the variable table as the simplest way to strip duplicates, and
 then calls "environment" to get the value of the environment.
-There are two consequences of this.  First, if an assignment to
-PATH precedes the command, the value of PATH before the assign-
-ment must be remembered and passed to shellexec.  Second, if the
-program turns out to be a shell procedure, the strings from the
-environment variables which preceded the command must be pulled
-out of the table and replaced with strings obtained from malloc,
-since the former will automatically be freed when the stack (see
-the entry on memalloc.c) is emptied.
 
 BUILTIN COMMANDS:  The procedures for handling these are scat-
 tered throughout the code, depending on which location appears
index fa91211..9fd0537 100644 (file)
  * SUCH DAMAGE.
  *
  * @(#)alias.c 8.3 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/alias.c,v 1.20 2005/09/02 22:43:28 stefanf Exp $
- * $DragonFly: src/bin/sh/alias.c,v 1.5 2007/01/04 06:35:12 pavalos Exp $
+ * $FreeBSD: src/bin/sh/alias.c,v 1.30 2011/02/04 22:47:55 jilles Exp $
  */
 
 #include <stdlib.h>
 #include "shell.h"
-#include "input.h"
 #include "output.h"
 #include "error.h"
 #include "memalloc.h"
 
 #define ATABSIZE 39
 
-STATIC struct alias *atab[ATABSIZE];
+static struct alias *atab[ATABSIZE];
+static int aliases;
 
-STATIC void setalias(char *, char *);
-STATIC int unalias(char *);
-STATIC struct alias **hashalias(char *);
+static void setalias(const char *, const char *);
+static int unalias(const char *);
+static struct alias **hashalias(const char *);
 
-STATIC
+static
 void
-setalias(char *name, char *val)
+setalias(const char *name, const char *val)
 {
        struct alias *ap, **app;
 
@@ -97,7 +96,7 @@ setalias(char *name, char *val)
        ap->val = savestr(val);
 #else /* hack */
        {
-       int len = strlen(val);
+       size_t len = strlen(val);
        ap->val = ckmalloc(len + 2);
        memcpy(ap->val, val, len);
        ap->val[len] = ' ';     /* fluff */
@@ -107,11 +106,12 @@ setalias(char *name, char *val)
        ap->flag = 0;
        ap->next = *app;
        *app = ap;
+       aliases++;
        INTON;
 }
 
-STATIC int
-unalias(char *name)
+static int
+unalias(const char *name)
 {
        struct alias *ap, **app;
 
@@ -136,6 +136,7 @@ unalias(char *name)
                                ckfree(ap);
                                INTON;
                        }
+                       aliases--;
                        return (0);
                }
        }
@@ -143,15 +144,7 @@ unalias(char *name)
        return (1);
 }
 
-#ifdef mkinit
-MKINIT void rmaliases(void);
-
-SHELLPROC {
-       rmaliases();
-}
-#endif
-
-void
+static void
 rmaliases(void)
 {
        struct alias *ap, *tmp;
@@ -169,11 +162,12 @@ rmaliases(void)
                        ckfree(tmp);
                }
        }
+       aliases = 0;
        INTON;
 }
 
 struct alias *
-lookupalias(char *name, int check)
+lookupalias(const char *name, int check)
 {
        struct alias *ap = *hashalias(name);
 
@@ -188,9 +182,47 @@ lookupalias(char *name, int check)
        return (NULL);
 }
 
-/*
- * TODO - sort output
- */
+static int
+comparealiases(const void *p1, const void *p2)
+{
+       const struct alias *const *a1 = p1;
+       const struct alias *const *a2 = p2;
+
+       return strcmp((*a1)->name, (*a2)->name);
+}
+
+static void
+printalias(const struct alias *a)
+{
+       char *p;
+
+       out1fmt("%s=", a->name);
+       /* Don't print the space added above. */
+       p = a->val + strlen(a->val) - 1;
+       *p = '\0';
+       out1qstr(a->val);
+       *p = ' ';
+       out1c('\n');
+}
+
+static void
+printaliases(void)
+{
+       int i, j;
+       struct alias **sorted, *ap;
+
+       sorted = ckmalloc(aliases * sizeof(*sorted));
+       j = 0;
+       for (i = 0; i < ATABSIZE; i++)
+               for (ap = atab[i]; ap; ap = ap->next)
+                       if (*ap->name != '\0')
+                               sorted[j++] = ap;
+       qsort(sorted, aliases, sizeof(*sorted), comparealiases);
+       for (i = 0; i < aliases; i++)
+               printalias(sorted[i]);
+       ckfree(sorted);
+}
+
 int
 aliascmd(int argc, char **argv)
 {
@@ -199,28 +231,16 @@ aliascmd(int argc, char **argv)
        struct alias *ap;
 
        if (argc == 1) {
-               int i;
-
-               for (i = 0; i < ATABSIZE; i++)
-                       for (ap = atab[i]; ap; ap = ap->next) {
-                               if (*ap->name != '\0') {
-                                       out1fmt("alias %s=", ap->name);
-                                       out1qstr(ap->val);
-                                       out1c('\n');
-                               }
-                       }
+               printaliases();
                return (0);
        }
        while ((n = *++argv) != NULL) {
                if ((v = strchr(n+1, '=')) == NULL) /* n+1: funny ksh stuff */
                        if ((ap = lookupalias(n, 0)) == NULL) {
-                               outfmt(out2, "alias: %s not found\n", n);
+                               warning("%s not found", n);
                                ret = 1;
-                       } else {
-                               out1fmt("alias %s=", n);
-                               out1qstr(ap->val);
-                               out1c('\n');
-                       }
+                       } else
+                               printalias(ap);
                else {
                        *v++ = '\0';
                        setalias(n, v);
@@ -247,8 +267,8 @@ unaliascmd(int argc __unused, char **argv __unused)
        return (i);
 }
 
-STATIC struct alias **
-hashalias(char *p)
+static struct alias **
+hashalias(const char *p)
 {
        unsigned int hashval;
 
index d4791f7..7779ada 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)alias.h     8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/alias.h,v 1.6.2.1 2002/07/19 04:38:51 tjr Exp $
- * $DragonFly: src/bin/sh/alias.h,v 1.2 2003/06/17 04:22:50 dillon Exp $
+ * $FreeBSD: src/bin/sh/alias.h,v 1.10 2011/02/04 22:47:55 jilles Exp $
  */
 
 #define ALIASINUSE     1
@@ -47,7 +46,6 @@ struct alias {
        int flag;
 };
 
-struct alias *lookupalias(char *, int);
+struct alias *lookupalias(const char *, int);
 int aliascmd(int, char **);
 int unaliascmd(int, char **);
-void rmaliases(void);
index 4f21033..82b797f 100644 (file)
  * SUCH DAMAGE.
  *
  *     @(#)arith.h     1.1 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/arith.h,v 1.11 2005/08/13 07:59:46 stefanf Exp $
- * $DragonFly: src/bin/sh/arith.h,v 1.4 2007/01/04 14:06:21 pavalos Exp $
+ * $FreeBSD: src/bin/sh/arith.h,v 1.14 2011/02/08 23:18:06 jilles Exp $
  */
 
-extern const char *arith_buf, *arith_startbuf;
+#include "shell.h"
 
-int arith(const char *);
-void arith_lex_reset(void);
-int expcmd(int , char **);
+#define        DIGITS(var)     (int)(3 + (2 + CHAR_BIT * sizeof((var))) / 3)
+
+arith_t        arith(const char *);
+void   arith_lex_reset(void);
+int    expcmd(int, char **);
diff --git a/bin/sh/arith.y b/bin/sh/arith.y
deleted file mode 100644 (file)
index a245ecc..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-%{
-/*-
- * Copyright (c) 1993
- *     The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)arith.y 8.3 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/arith.y,v 1.21 2005/08/13 07:59:46 stefanf Exp $
- * $DragonFly: src/bin/sh/arith.y,v 1.5 2007/01/04 14:06:21 pavalos Exp $
- */
-
-#include <limits.h>
-#include <stdio.h>
-
-#include "arith.h"
-#include "shell.h"
-#include "var.h"
-%}
-%union {
-       arith_t l_value;
-       char* s_value;
-}
-%token <l_value> ARITH_NUM ARITH_LPAREN ARITH_RPAREN
-%token <s_value> ARITH_VAR
-
-%type <l_value>        expr
-%right ARITH_ASSIGN
-%right ARITH_ADDASSIGN ARITH_SUBASSIGN
-%right ARITH_MULASSIGN ARITH_DIVASSIGN ARITH_REMASSIGN
-%right ARITH_RSHASSIGN ARITH_LSHASSIGN
-%right ARITH_BANDASSIGN ARITH_BXORASSIGN ARITH_BORASSIGN
-%left ARITH_OR
-%left ARITH_AND
-%left ARITH_BOR
-%left ARITH_BXOR
-%left ARITH_BAND
-%left ARITH_EQ ARITH_NE
-%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE
-%left ARITH_LSHIFT ARITH_RSHIFT
-%left ARITH_ADD ARITH_SUB
-%left ARITH_MUL ARITH_DIV ARITH_REM
-%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT
-%%
-
-exp:
-       expr
-               { return ($1); }
-       ;
-
-expr:
-       ARITH_LPAREN expr ARITH_RPAREN
-               { $$ = $2; } |
-       expr ARITH_OR expr
-               { $$ = $1 ? $1 : $3 ? $3 : 0; } |
-       expr ARITH_AND expr
-               { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; } |
-       expr ARITH_BOR expr
-               { $$ = $1 | $3; } |
-       expr ARITH_BXOR expr
-               { $$ = $1 ^ $3; } |
-       expr ARITH_BAND expr
-               { $$ = $1 & $3; } |
-       expr ARITH_EQ expr
-               { $$ = $1 == $3; } |
-       expr ARITH_GT expr
-               { $$ = $1 > $3; } |
-       expr ARITH_GE expr
-               { $$ = $1 >= $3; } |
-       expr ARITH_LT expr
-               { $$ = $1 < $3; } |
-       expr ARITH_LE expr
-               { $$ = $1 <= $3; } |
-       expr ARITH_NE expr
-               { $$ = $1 != $3; } |
-       expr ARITH_LSHIFT expr
-               { $$ = $1 << $3; } |
-       expr ARITH_RSHIFT expr
-               { $$ = $1 >> $3; } |
-       expr ARITH_ADD expr
-               { $$ = $1 + $3; } |
-       expr ARITH_SUB expr
-               { $$ = $1 - $3; } |
-       expr ARITH_MUL expr
-               { $$ = $1 * $3; } |
-       expr ARITH_DIV expr
-               {
-               if ($3 == 0)
-                       yyerror("division by zero");
-               $$ = $1 / $3;
-               } |
-       expr ARITH_REM expr
-               {
-               if ($3 == 0)
-                       yyerror("division by zero");
-               $$ = $1 % $3;
-               } |
-       ARITH_NOT expr
-               { $$ = !($2); } |
-       ARITH_BNOT expr
-               { $$ = ~($2); } |
-       ARITH_SUB expr %prec ARITH_UNARYMINUS
-               { $$ = -($2); } |
-       ARITH_ADD expr %prec ARITH_UNARYPLUS
-               { $$ = $2; } |
-       ARITH_NUM |
-       ARITH_VAR
-               {
-               char *p;
-               arith_t arith_val;
-               char *str_val;
-
-               if (lookupvar($1) == NULL)
-                       setvarsafe($1, "0", 0);
-               str_val = lookupvar($1);
-               arith_val = strtoarith_t(str_val, &p, 0);
-               /*
-                * Conversion is successful only in case
-                * we've converted _all_ characters.
-                */
-               if (*p != '\0')
-                       yyerror("variable conversion error");
-               $$ = arith_val;
-               } |
-       ARITH_VAR ARITH_ASSIGN expr
-               {
-               if (arith_assign($1, $3) != 0)
-                       yyerror("variable assignment error");
-               $$ = $3;
-               } |
-       ARITH_VAR ARITH_ADDASSIGN expr
-               {
-               arith_t value;
-
-               value = atoarith_t(lookupvar($1)) + $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } |
-       ARITH_VAR ARITH_SUBASSIGN expr
-               {
-               arith_t value;
-
-               value = atoarith_t(lookupvar($1)) - $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } |
-       ARITH_VAR ARITH_MULASSIGN expr
-               {
-               arith_t value;
-
-               value = atoarith_t(lookupvar($1)) * $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } |
-       ARITH_VAR ARITH_DIVASSIGN expr
-               {
-               arith_t value;
-
-               if ($3 == 0)
-                       yyerror("division by zero");
-
-               value = atoarith_t(lookupvar($1)) / $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } |
-       ARITH_VAR ARITH_REMASSIGN expr
-               {
-               arith_t value;
-
-               if ($3 == 0)
-                       yyerror("division by zero");
-
-               value = atoarith_t(lookupvar($1)) % $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } |
-       ARITH_VAR ARITH_RSHASSIGN expr
-               {
-               arith_t value;
-
-               value = atoarith_t(lookupvar($1)) >> $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } |
-       ARITH_VAR ARITH_LSHASSIGN expr
-               {
-               arith_t value;
-
-               value = atoarith_t(lookupvar($1)) << $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } |
-       ARITH_VAR ARITH_BANDASSIGN expr
-               {
-               arith_t value;
-
-               value = atoarith_t(lookupvar($1)) & $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } |
-       ARITH_VAR ARITH_BXORASSIGN expr
-               {
-               arith_t value;
-
-               value = atoarith_t(lookupvar($1)) ^ $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } |
-       ARITH_VAR ARITH_BORASSIGN expr
-               {
-               arith_t value;
-
-               value = atoarith_t(lookupvar($1)) | $3;
-               if (arith_assign($1, value) != 0)
-                       yyerror("variable assignment error");
-               $$ = value;
-               } ;
-%%
-#include "error.h"
-#include "output.h"
-#include "memalloc.h"
-
-#define lstrlen(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3)
-
-const char *arith_buf, *arith_startbuf;
-
-int yylex(void);
-int yyparse(void);
-static void yyerror(const char *s);
-
-static int
-arith_assign(char *name, arith_t value)
-{
-       char *str;
-       int ret;
-
-       str = (char *)ckmalloc(lstrlen(value));
-       sprintf(str, ARITH_FORMAT_STR, value);
-       ret = setvarsafe(name, str, 0);
-       free(str);
-       return (ret);
-}
-
-int
-arith(const char *s)
-{
-       long result;
-
-       arith_buf = arith_startbuf = s;
-
-       INTOFF;
-       result = yyparse();
-       arith_lex_reset();      /* Reprime lex. */
-       INTON;
-
-       return (result);
-}
-
-static void
-yyerror(const char *s)
-{
-
-       yyerrok;
-       yyclearin;
-       arith_lex_reset();      /* Reprime lex. */
-       error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
-}
-
-/*
- *  The exp(1) builtin.
- */
-int
-expcmd(int argc, char **argv)
-{
-       const char *p;
-       char *concat;
-       char **ap;
-       long i;
-
-       if (argc > 1) {
-               p = argv[1];
-               if (argc > 2) {
-                       /*
-                        * Concatenate arguments.
-                        */
-                       STARTSTACKSTR(concat);
-                       ap = argv + 2;
-                       for (;;) {
-                               while (*p)
-                                       STPUTC(*p++, concat);
-                               if ((p = *ap++) == NULL)
-                                       break;
-                               STPUTC(' ', concat);
-                       }
-                       STPUTC('\0', concat);
-                       p = grabstackstr(concat);
-               }
-       } else
-               p = "";
-
-       i = arith(p);
-
-       out1fmt("%ld\n", i);
-       return (! i);
-}
-
-/*************************/
-#ifdef TEST_ARITH
-#include <stdio.h>
-main(int argc, char *argv[])
-{
-       printf("%d\n", exp(argv[1]));
-}
-
-error(char *s)
-{
-       fprintf(stderr, "exp: %s\n", s);
-       exit(1);
-}
-#endif
diff --git a/bin/sh/arith_lex.l b/bin/sh/arith_lex.l
deleted file mode 100644 (file)
index 3eeddad..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-%{
-/*-
- * Copyright (c) 1993
- *     The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)arith_lex.l     8.3 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/arith_lex.l,v 1.24 2005/08/13 07:59:46 stefanf Exp $
- * $DragonFly: src/bin/sh/arith_lex.l,v 1.5 2007/01/04 14:06:21 pavalos Exp $
- */
-
-#include <string.h>
-#include <unistd.h>
-
-#include "arith.h"
-#include "shell.h"
-#include "y.tab.h"
-#include "error.h"
-#include "memalloc.h"
-#include "var.h"
-
-int yylex(void);
-
-#undef YY_INPUT
-#define YY_INPUT(buf,result,max) \
-       result = (*buf = *arith_buf++) ? 1 : YY_NULL;
-#define YY_NO_INPUT
-#define YY_NO_UNPUT
-%}
-
-%%
-[ \t\n]        { ; }
-
-0x[a-fA-F0-9]+ {
-                       yylval.l_value = strtoarith_t(yytext, NULL, 16);
-                       return ARITH_NUM;
-               }
-
-0[0-7]+                {
-                       yylval.l_value = strtoarith_t(yytext, NULL, 8);
-                       return ARITH_NUM;
-               }
-
-[0-9]+         {
-                       yylval.l_value = strtoarith_t(yytext, NULL, 10);
-                       return ARITH_NUM;
-               }
-
-[A-Za-z][A-Za-z0-9_]*  {
-                       /*
-                        * If variable doesn't exist, we should initialize
-                        * it to zero.
-                        */
-                       char *temp;
-                       if (lookupvar(yytext) == NULL)
-                               setvarsafe(yytext, "0", 0);
-                       temp = (char *)ckmalloc(strlen(yytext) + 1);
-                       yylval.s_value = strcpy(temp, yytext);
-
-                       return ARITH_VAR;
-               }
-
-"("            {       return ARITH_LPAREN;    }
-")"            {       return ARITH_RPAREN;    }
-"||"           {       return ARITH_OR;        }
-"&&"           {       return ARITH_AND;       }
-"|"            {       return ARITH_BOR;       }
-"^"            {       return ARITH_BXOR;      }
-"&"            {       return ARITH_BAND;      }
-"=="           {       return ARITH_EQ;        }
-"!="           {       return ARITH_NE;        }
-">"            {       return ARITH_GT;        }
-">="           {       return ARITH_GE;        }
-"<"            {       return ARITH_LT;        }
-"<="           {       return ARITH_LE;        }
-"<<"           {       return ARITH_LSHIFT;    }
-">>"           {       return ARITH_RSHIFT;    }
-"*"            {       return ARITH_MUL;       }
-"/"            {       return ARITH_DIV;       }
-"%"            {       return ARITH_REM;       }
-"+"            {       return ARITH_ADD;       }
-"-"            {       return ARITH_SUB;       }
-"~"            {       return ARITH_BNOT;      }
-"!"            {       return ARITH_NOT;       }
-"="            {       return ARITH_ASSIGN;    }
-"+="           {       return ARITH_ADDASSIGN; }
-"-="           {       return ARITH_SUBASSIGN; }
-"*="           {       return ARITH_MULASSIGN; }
-"/="           {       return ARITH_DIVASSIGN; }
-"%="           {       return ARITH_REMASSIGN; }
-">>="          {       return ARITH_RSHASSIGN; }
-"<<="          {       return ARITH_LSHASSIGN; }
-"&="           {       return ARITH_BANDASSIGN; }
-"^="           {       return ARITH_BXORASSIGN; }
-"|="           {       return ARITH_BORASSIGN; }
-.              {
-                       error("arith: syntax error: \"%s\"\n", arith_startbuf);
-               }
-%%
-
-void
-arith_lex_reset(void)
-{
-       YY_NEW_FILE;
-}
diff --git a/bin/sh/arith_yacc.c b/bin/sh/arith_yacc.c
new file mode 100644 (file)
index 0000000..baa588d
--- /dev/null
@@ -0,0 +1,388 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2007
+ *     Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/bin/sh/arith_yacc.c,v 1.1 2011/02/08 23:18:06 jilles Exp $
+ */
+
+#include <sys/limits.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "arith.h"
+#include "arith_yacc.h"
+#include "expand.h"
+#include "shell.h"
+#include "error.h"
+#include "memalloc.h"
+#include "output.h"
+#include "options.h"
+#include "var.h"
+
+#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
+#error Arithmetic tokens are out of order.
+#endif
+
+static const char *arith_startbuf;
+
+const char *arith_buf;
+union yystype yylval;
+
+static int last_token;
+
+#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec
+
+static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
+       ARITH_PRECEDENCE(ARITH_MUL, 0),
+       ARITH_PRECEDENCE(ARITH_DIV, 0),
+       ARITH_PRECEDENCE(ARITH_REM, 0),
+       ARITH_PRECEDENCE(ARITH_ADD, 1),
+       ARITH_PRECEDENCE(ARITH_SUB, 1),
+       ARITH_PRECEDENCE(ARITH_LSHIFT, 2),
+       ARITH_PRECEDENCE(ARITH_RSHIFT, 2),
+       ARITH_PRECEDENCE(ARITH_LT, 3),
+       ARITH_PRECEDENCE(ARITH_LE, 3),
+       ARITH_PRECEDENCE(ARITH_GT, 3),
+       ARITH_PRECEDENCE(ARITH_GE, 3),
+       ARITH_PRECEDENCE(ARITH_EQ, 4),
+       ARITH_PRECEDENCE(ARITH_NE, 4),
+       ARITH_PRECEDENCE(ARITH_BAND, 5),
+       ARITH_PRECEDENCE(ARITH_BXOR, 6),
+       ARITH_PRECEDENCE(ARITH_BOR, 7),
+};
+
+#define ARITH_MAX_PREC 8
+
+static __dead2 void
+yyerror(const char *s)
+{
+       error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
+       /* NOTREACHED */
+}
+
+static arith_t
+arith_lookupvarint(char *varname)
+{
+       const char *str;
+       char *p;
+       arith_t result;
+
+       str = lookupvar(varname);
+       if (str == NULL || *str == '\0')
+               str = "0";
+       errno = 0;
+       result = strtoarith_t(str, &p, 0);
+       if (errno != 0 || *p != '\0')
+               yyerror("variable conversion error");
+       return result;
+}
+
+static inline int
+arith_prec(int op)
+{
+       return prec[op - ARITH_BINOP_MIN];
+}
+
+static inline int
+higher_prec(int op1, int op2)
+{
+       return arith_prec(op1) < arith_prec(op2);
+}
+
+static arith_t
+do_binop(int op, arith_t a, arith_t b)
+{
+
+       switch (op) {
+       default:
+       case ARITH_REM:
+       case ARITH_DIV:
+               if (!b)
+                       yyerror("division by zero");
+               return op == ARITH_REM ? a % b : a / b;
+       case ARITH_MUL:
+               return a * b;
+       case ARITH_ADD:
+               return a + b;
+       case ARITH_SUB:
+               return a - b;
+       case ARITH_LSHIFT:
+               return a << b;
+       case ARITH_RSHIFT:
+               return a >> b;
+       case ARITH_LT:
+               return a < b;
+       case ARITH_LE:
+               return a <= b;
+       case ARITH_GT:
+               return a > b;
+       case ARITH_GE:
+               return a >= b;
+       case ARITH_EQ:
+               return a == b;
+       case ARITH_NE:
+               return a != b;
+       case ARITH_BAND:
+               return a & b;
+       case ARITH_BXOR:
+               return a ^ b;
+       case ARITH_BOR:
+               return a | b;
+       }
+}
+
+static arith_t assignment(int var, int noeval);
+
+static arith_t
+primary(int token, union yystype *val, int op, int noeval)
+{
+       arith_t result;
+
+again:
+       switch (token) {
+       case ARITH_LPAREN:
+               result = assignment(op, noeval);
+               if (last_token != ARITH_RPAREN)
+                       yyerror("expecting ')'");
+               last_token = yylex();
+               return result;
+       case ARITH_NUM:
+               last_token = op;
+               return val->val;
+       case ARITH_VAR:
+               last_token = op;
+               return noeval ? val->val : arith_lookupvarint(val->name);
+       case ARITH_ADD:
+               token = op;
+               *val = yylval;
+               op = yylex();
+               goto again;
+       case ARITH_SUB:
+               *val = yylval;
+               return -primary(op, val, yylex(), noeval);
+       case ARITH_NOT:
+               *val = yylval;
+               return !primary(op, val, yylex(), noeval);
+       case ARITH_BNOT:
+               *val = yylval;
+               return ~primary(op, val, yylex(), noeval);
+       default:
+               yyerror("expecting primary");
+       }
+}
+
+static arith_t
+binop2(arith_t a, int op, int lprec, int noeval)
+{
+       for (;;) {
+               union yystype val;
+               arith_t b;
+               int op2;
+               int token;
+
+               token = yylex();
+               val = yylval;
+
+               b = primary(token, &val, yylex(), noeval);
+
+               op2 = last_token;
+               if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX &&
+                   higher_prec(op2, op)) {
+                       b = binop2(b, op2, arith_prec(op), noeval);
+                       op2 = last_token;
+               }
+
+               a = noeval ? b : do_binop(op, a, b);
+
+               if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX ||
+                   arith_prec(op2) >= lprec)
+                       return a;
+
+               op = op2;
+       }
+}
+
+static arith_t
+binop(int token, union yystype *val, int op, int noeval)
+{
+       arith_t a = primary(token, val, op, noeval);
+
+       op = last_token;
+       if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX)
+               return a;
+
+       return binop2(a, op, ARITH_MAX_PREC, noeval);
+}
+
+static arith_t
+and(int token, union yystype *val, int op, int noeval)
+{
+       arith_t a = binop(token, val, op, noeval);
+       arith_t b;
+
+       op = last_token;
+       if (op != ARITH_AND)
+               return a;
+
+       token = yylex();
+       *val = yylval;
+
+       b = and(token, val, yylex(), noeval | !a);
+
+       return a && b;
+}
+
+static arith_t
+or(int token, union yystype *val, int op, int noeval)
+{
+       arith_t a = and(token, val, op, noeval);
+       arith_t b;
+
+       op = last_token;
+       if (op != ARITH_OR)
+               return a;
+
+       token = yylex();
+       *val = yylval;
+
+       b = or(token, val, yylex(), noeval | !!a);
+
+       return a || b;
+}
+
+static arith_t
+cond(int token, union yystype *val, int op, int noeval)
+{
+       arith_t a = or(token, val, op, noeval);
+       arith_t b;
+       arith_t c;
+
+       if (last_token != ARITH_QMARK)
+               return a;
+
+       b = assignment(yylex(), noeval | !a);
+
+       if (last_token != ARITH_COLON)
+               yyerror("expecting ':'");
+
+       token = yylex();
+       *val = yylval;
+
+       c = cond(token, val, yylex(), noeval | !!a);
+
+       return a ? b : c;
+}
+
+static arith_t
+assignment(int var, int noeval)
+{
+       union yystype val = yylval;
+       int op = yylex();
+       arith_t result;
+       char sresult[DIGITS(result) + 1];
+
+       if (var != ARITH_VAR)
+               return cond(var, &val, op, noeval);
+
+       if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX))
+               return cond(var, &val, op, noeval);
+
+       result = assignment(yylex(), noeval);
+       if (noeval)
+               return result;
+
+       if (op != ARITH_ASS)
+               result = do_binop(op - 11, arith_lookupvarint(val.name), result);
+       snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result);
+       setvar(val.name, sresult, 0);
+       return result;
+}
+
+arith_t
+arith(const char *s)
+{
+       struct stackmark smark;
+       arith_t result;
+
+       setstackmark(&smark);
+
+       arith_buf = arith_startbuf = s;
+
+       result = assignment(yylex(), 0);
+
+       if (last_token)
+               yyerror("expecting EOF");
+
+       popstackmark(&smark);
+
+       return result;
+}
+
+/*
+ *  The exp(1) builtin.
+ */
+int
+expcmd(int argc, char **argv)
+{
+       const char *p;
+       char *concat;
+       char **ap;
+       arith_t i;
+
+       if (argc > 1) {
+               p = argv[1];
+               if (argc > 2) {
+                       /*
+                        * Concatenate arguments.
+                        */
+                       STARTSTACKSTR(concat);
+                       ap = argv + 2;
+                       for (;;) {
+                               while (*p)
+                                       STPUTC(*p++, concat);
+                               if ((p = *ap++) == NULL)
+                                       break;
+                               STPUTC(' ', concat);
+                       }
+                       STPUTC('\0', concat);
+                       p = grabstackstr(concat);
+               }
+       } else
+               p = "";
+
+       i = arith(p);
+
+       out1fmt(ARITH_FORMAT_STR "\n", i);
+       return !i;
+}
+
similarity index 56%
copy from bin/sh/alias.h
copy to bin/sh/arith_yacc.h
index d4791f7..14c075e 100644 (file)
@@ -1,6 +1,8 @@
 /*-
  * Copyright (c) 1993
  *     The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2007
+ *     Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     @(#)alias.h     8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/alias.h,v 1.6.2.1 2002/07/19 04:38:51 tjr Exp $
- * $DragonFly: src/bin/sh/alias.h,v 1.2 2003/06/17 04:22:50 dillon Exp $
+ * $FreeBSD: src/bin/sh/arith_yacc.h,v 1.1 2011/02/08 23:18:06 jilles Exp $
  */
 
-#define ALIASINUSE     1
+#define ARITH_ASS 1
 
-struct alias {
-       struct alias *next;
+#define ARITH_OR 2
+#define ARITH_AND 3
+#define ARITH_BAD 4
+#define ARITH_NUM 5
+#define ARITH_VAR 6
+#define ARITH_NOT 7
+
+#define ARITH_BINOP_MIN 8
+#define ARITH_LE 8
+#define ARITH_GE 9
+#define ARITH_LT 10
+#define ARITH_GT 11
+#define ARITH_EQ 12
+#define ARITH_REM 13
+#define ARITH_BAND 14
+#define ARITH_LSHIFT 15
+#define ARITH_RSHIFT 16
+#define ARITH_MUL 17
+#define ARITH_ADD 18
+#define ARITH_BOR 19
+#define ARITH_SUB 20
+#define ARITH_BXOR 21
+#define ARITH_DIV 22
+#define ARITH_NE 23
+#define ARITH_BINOP_MAX 24
+
+#define ARITH_ASS_MIN 24
+#define ARITH_REMASS 24
+#define ARITH_BANDASS 25
+#define ARITH_LSHIFTASS 26
+#define ARITH_RSHIFTASS 27
+#define ARITH_MULASS 28
+#define ARITH_ADDASS 29
+#define ARITH_BORASS 30
+#define ARITH_SUBASS 31
+#define ARITH_BXORASS 32
+#define ARITH_DIVASS 33
+#define ARITH_ASS_MAX 34
+
+#define ARITH_LPAREN 34
+#define ARITH_RPAREN 35
+#define ARITH_BNOT 36
+#define ARITH_QMARK 37
+#define ARITH_COLON 38
+
+union yystype {
+       arith_t val;
        char *name;
-       char *val;
-       int flag;
 };
 
-struct alias *lookupalias(char *, int);
-int aliascmd(int, char **);
-int unaliascmd(int, char **);
-void rmaliases(void);
+extern union yystype yylval;
+
+int yylex(void);
diff --git a/bin/sh/arith_yylex.c b/bin/sh/arith_yylex.c
new file mode 100644 (file)
index 0000000..5452665
--- /dev/null
@@ -0,0 +1,242 @@
+/*-
+ * Copyright (c) 2002
+ *     Herbert Xu.
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include "shell.h"
+#include "arith_yacc.h"
+#include "expand.h"
+#include "error.h"
+#include "memalloc.h"
+#include "parser.h"
+#include "syntax.h"
+
+#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
+#error Arithmetic tokens are out of order.
+#endif
+
+extern const char *arith_buf;
+
+int
+yylex(void)
+{
+       int value;
+       const char *buf = arith_buf;
+       const char *p;
+
+       for (;;) {
+               value = *buf;
+               switch (value) {
+               case ' ':
+               case '\t':
+               case '\n':
+                       buf++;
+                       continue;
+               default:
+                       return ARITH_BAD;
+               case '0':
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       yylval.val = strtoarith_t(buf,
+                           __DECONST(char **, &arith_buf), 0);
+                       return ARITH_NUM;
+               case 'A':
+               case 'B':
+               case 'C':
+               case 'D':
+               case 'E':
+               case 'F':
+               case 'G':
+               case 'H':
+               case 'I':
+               case 'J':
+               case 'K':
+               case 'L':
+               case 'M':
+               case 'N':
+               case 'O':
+               case 'P':
+               case 'Q':
+               case 'R':
+               case 'S':
+               case 'T':
+               case 'U':
+               case 'V':
+               case 'W':
+               case 'X':
+               case 'Y':
+               case 'Z':
+               case '_':
+               case 'a':
+               case 'b':
+               case 'c':
+               case 'd':
+               case 'e':
+               case 'f':
+               case 'g':
+               case 'h':
+               case 'i':
+               case 'j':
+               case 'k':
+               case 'l':
+               case 'm':
+               case 'n':
+               case 'o':
+               case 'p':
+               case 'q':
+               case 'r':
+               case 's':
+               case 't':
+               case 'u':
+               case 'v':
+               case 'w':
+               case 'x':
+               case 'y':
+               case 'z':
+                       p = buf;
+                       while (buf++, is_in_name(*buf))
+                               ;
+                       yylval.name = stalloc(buf - p + 1);
+                       memcpy(yylval.name, p, buf - p);
+                       yylval.name[buf - p] = '\0';
+                       value = ARITH_VAR;
+                       goto out;
+               case '=':
+                       value += ARITH_ASS - '=';
+checkeq:
+                       buf++;
+checkeqcur:
+                       if (*buf != '=')
+                               goto out;
+                       value += 11;
+                       break;
+               case '>':
+                       switch (*++buf) {
+                       case '=':
+                               value += ARITH_GE - '>';
+                               break;
+                       case '>':
+                               value += ARITH_RSHIFT - '>';
+                               goto checkeq;
+                       default:
+                               value += ARITH_GT - '>';
+                               goto out;
+                       }
+                       break;
+               case '<':
+                       switch (*++buf) {
+                       case '=':
+                               value += ARITH_LE - '<';
+                               break;
+                       case '<':
+                               value += ARITH_LSHIFT - '<';
+                               goto checkeq;
+                       default:
+                               value += ARITH_LT - '<';
+                               goto out;
+                       }
+                       break;
+               case '|':
+                       if (*++buf != '|') {
+                               value += ARITH_BOR - '|';
+                               goto checkeqcur;
+                       }
+                       value += ARITH_OR - '|';
+                       break;
+               case '&':
+                       if (*++buf != '&') {
+                               value += ARITH_BAND - '&';
+                               goto checkeqcur;
+                       }
+                       value += ARITH_AND - '&';
+                       break;
+               case '!':
+                       if (*++buf != '=') {
+                               value += ARITH_NOT - '!';
+                               goto out;
+                       }
+                       value += ARITH_NE - '!';
+                       break;
+               case 0:
+                       goto out;
+               case '(':
+                       value += ARITH_LPAREN - '(';
+                       break;
+               case ')':
+                       value += ARITH_RPAREN - ')';
+                       break;
+               case '*':
+                       value += ARITH_MUL - '*';
+                       goto checkeq;
+               case '/':
+                       value += ARITH_DIV - '/';
+                       goto checkeq;
+               case '%':
+                       value += ARITH_REM - '%';
+                       goto checkeq;
+               case '+':
+                       value += ARITH_ADD - '+';
+                       goto checkeq;
+               case '-':
+                       value += ARITH_SUB - '-';
+                       goto checkeq;
+               case '~':
+                       value += ARITH_BNOT - '~';
+                       break;
+               case '^':
+                       value += ARITH_BXOR - '^';
+                       goto checkeq;
+               case '?':
+                       value += ARITH_QMARK - '?';
+                       break;
+               case ':':
+                       value += ARITH_COLON - ':';
+                       break;
+               }
+               break;
+       }
+
+       buf++;
+out:
+       arith_buf = buf;
+       return value;
+}
index 7028f8b..d0d9471 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)bltin.h     8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/bltin/bltin.h,v 1.10.2.2 2002/07/19 04:38:54 tjr Exp $
- * $DragonFly: src/bin/sh/bltin/bltin.h,v 1.4 2004/11/07 20:54:52 eirikn Exp $
+ * $FreeBSD: src/bin/sh/bltin/bltin.h,v 1.18 2010/12/21 22:47:34 jilles Exp $
  */
 
 /*
@@ -48,6 +47,7 @@
 #include "../mystring.h"
 #ifdef SHELL
 #include "../output.h"
+#define FILE struct output
 #undef stdout
 #define stdout out1
 #undef stderr
 #define putchar(c)     out1c(c)
 #define fprintf outfmt
 #define fputs outstr
+#define fwrite(ptr, size, nmemb, file) outbin(ptr, (size) * (nmemb), file)
 #define fflush flushout
 #define INITARGS(argv)
-#define warnx1(a, b, c) {                              \
-       char buf[64];                                   \
-       snprintf(buf, sizeof(buf), a);                  \
-       error("%s", buf);                               \
-}
-#define warnx2(a, b, c) {                              \
-       char buf[64];                                   \
-       snprintf(buf, sizeof(buf), a, b);               \
-       error("%s", buf);                               \
-}
-#define warnx3(a, b, c) {                              \
-       char buf[64];                                   \
-       snprintf(buf, sizeof(buf), a, b, c);            \
-       error("%s", buf);                               \
-}
+#define warnx warning
+#define warn(fmt, ...) warning(fmt ": %s", __VA_ARGS__, strerror(errno))
+#define errx(exitstatus, ...) error(__VA_ARGS__)
 
 int main(int, char *[]);
 
@@ -86,7 +75,9 @@ int main(int, char *[]);
 #define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
 #endif
 
-void error(const char *, ...) __printf0like(1, 2);
+#include <unistd.h>
+
+pid_t getjobpgrp(char *);
 
 
 extern char *commandname;
diff --git a/bin/sh/bltin/echo.1 b/bin/sh/bltin/echo.1
deleted file mode 100644 (file)
index 96e7e1a..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-.\"-
-.\" Copyright (c) 1991, 1993
-.\"    The Regents of the University of California.  All rights reserved.
-.\"
-.\" This code is derived from software contributed to Berkeley by
-.\" Kenneth Almquist.
-.\" Copyright 1989 by Kenneth Almquist
-.\"
-.\" Redistribution and use in source and binary forms, with or without
-.\" modification, are permitted provided that the following conditions
-.\" are met:
-.\" 1. Redistributions of source code must retain the above copyright
-.\"    notice, this list of conditions and the following disclaimer.
-.\" 2. Redistributions in binary form must reproduce the above copyright
-.\"    notice, this list of conditions and the following disclaimer in the
-.\"    documentation and/or other materials provided with the distribution.
-.\" 3. All advertising materials mentioning features or use of this software
-.\"    must display the following acknowledgement:
-.\"    This product includes software developed by the University of
-.\"    California, Berkeley and its contributors.
-.\" 4. Neither the name of the University nor the names of its contributors
-.\"    may be used to endorse or promote products derived from this software
-.\"    without specific prior written permission.
-.\"
-.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-.\" SUCH DAMAGE.
-.\"
-.\"    @(#)echo.1      8.2 (Berkeley) 5/4/95
-.\" $FreeBSD: src/bin/sh/bltin/echo.1,v 1.15 2005/01/10 08:39:25 imp Exp $
-.\" $DragonFly: src/bin/sh/bltin/echo.1,v 1.3 2007/01/05 03:59:34 pavalos Exp $
-.\"
-.Dd May 4, 1995
-.Dt ECHO 1
-.Os
-.Sh NAME
-.Nm echo
-.Nd produce message in a shell script
-.Sh SYNOPSIS
-.Nm
-.Op Fl n | Fl e
-.Ar args...
-.Sh DESCRIPTION
-The
-.Nm
-utility prints its arguments on the standard output, separated by spaces.
-Unless the
-.Fl n
-option is present, a newline is output following the arguments.
-The
-.Fl e
-option causes
-.Nm
-to treat the escape sequences specially, as described in the following
-paragraph.
-The
-.Fl e
-option is the default, and is provided solely for compatibility with
-other systems.
-Only one of the options
-.Fl n
-and
-.Fl e
-may be given.
-.Pp
-If any of the following sequences of characters is encountered during
-output, the sequence is not output.
-Instead, the specified action is
-performed:
-.Bl -tag -width indent
-.It Li \eb
-A backspace character is output.
-.It Li \ec
-Subsequent output is suppressed.
-This is normally used at the end of the
-last argument to suppress the trailing newline that
-.Nm
-would otherwise output.
-.It Li \ef
-Output a form feed.
-.It Li \en
-Output a newline character.
-.It Li \er
-Output a carriage return.
-.It Li \et
-Output a (horizontal) tab character.
-.It Li \ev
-Output a vertical tab.
-.It Li \e0 Ns Ar digits
-Output the character whose value is given by zero to three digits.
-If there are zero digits, a
-.Dv NUL
-character is output.
-.It Li \e\e
-Output a backslash.
-.El
-.Sh HINTS
-Remember that backslash is special to the shell and needs to be escaped.
-To output a message to standard error, say
-.Pp
-.D1 echo message >&2
-.Sh BUGS
-The octal character escape mechanism
-.Pq Li \e0 Ns Ar digits
-differs from the
-C language mechanism.
-.Pp
-There is no way to force
-.Nm
-to treat its arguments literally, rather than interpreting them as
-options and escape sequences.
index a6633d8..11f1091 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)echo.c      8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/bltin/echo.c,v 1.9.2.3 2003/01/31 10:40:27 dwmalone Exp $
- * $DragonFly: src/bin/sh/bltin/echo.c,v 1.2 2003/06/17 04:22:50 dillon Exp $
+ * $FreeBSD: src/bin/sh/bltin/echo.c,v 1.14 2004/04/06 20:06:53 markm Exp $
  */
 
 /*
index f2759ea..f13d255 100644 (file)
@@ -36,8 +36,7 @@
 # SUCH DAMAGE.
 #
 #      @(#)builtins.def        8.4 (Berkeley) 5/4/95
-# $FreeBSD: src/bin/sh/builtins.def,v 1.19 2006/04/02 18:43:33 stefanf Exp $
-# $DragonFly: src/bin/sh/builtins.def,v 1.5 2007/01/07 08:26:55 pavalos Exp $
+# $FreeBSD: src/bin/sh/builtins.def,v 1.21 2010/12/21 22:47:34 jilles Exp $
 
 #
 # This file lists all the builtin commands.  The first column is the name
@@ -74,6 +73,7 @@ hashcmd               hash
 histcmd -h     fc
 jobidcmd       jobid
 jobscmd                jobs
+killcmd                kill
 localcmd       local
 printfcmd      printf
 pwdcmd         pwd
index f281ca9..82e4f66 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)cd.c    8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/cd.c,v 1.35 2006/06/12 21:06:00 stefanf Exp $
- * $DragonFly: src/bin/sh/cd.c,v 1.5 2007/01/05 22:18:52 pavalos Exp $
+ * $FreeBSD: src/bin/sh/cd.c,v 1.45 2010/12/21 20:47:06 jilles Exp $
  */
 
 #include <sys/types.h>
 #include "show.h"
 #include "cd.h"
 
-STATIC int cdlogical(char *);
-STATIC int cdphysical(char *);
-STATIC int docd(char *, int, int);
-STATIC char *getcomponent(void);
-STATIC int updatepwd(char *);
+static int cdlogical(char *);
+static int cdphysical(char *);
+static int docd(char *, int, int);
+static char *getcomponent(void);
+static char *findcwd(char *);
+static void updatepwd(char *);
+static char *getpwd(void);
+static char *getpwd2(void);
 
-STATIC char *curdir = NULL;    /* current working directory */
-STATIC char *prevdir;          /* previous working directory */
-STATIC char *cdcomppath;
+static char *curdir = NULL;    /* current working directory */
+static char *prevdir;          /* previous working directory */
+static char *cdcomppath;
 
 int
 cdcmd(int argc, char **argv)
@@ -142,7 +144,7 @@ cdcmd(int argc, char **argv)
  * Actually change the directory.  In an interactive shell, print the
  * directory name if "print" is nonzero.
  */
-STATIC int
+static int
 docd(char *dest, int print, int phys)
 {
 
@@ -158,7 +160,7 @@ docd(char *dest, int print, int phys)
        return 0;
 }
 
-STATIC int
+static int
 cdlogical(char *dest)
 {
        char *p;
@@ -189,8 +191,7 @@ cdlogical(char *dest)
                        STPUTC('/', p);
                first = 0;
                component = q;
-               while (*q)
-                       STPUTC(*q++, p);
+               STPUTS(q, p);
                if (equal(component, ".."))
                        continue;
                STACKSTRNUL(p);
@@ -201,23 +202,29 @@ cdlogical(char *dest)
        }
 
        INTOFF;
-       if (updatepwd(badstat ? NULL : dest) < 0 || chdir(curdir) < 0) {
+       if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) {
                INTON;
                return (-1);
        }
+       updatepwd(p);
        INTON;
        return (0);
 }
 
-STATIC int
+static int
 cdphysical(char *dest)
 {
+       char *p;
 
        INTOFF;
-       if (chdir(dest) < 0 || updatepwd(NULL) < 0) {
+       if (chdir(dest) < 0) {
                INTON;
                return (-1);
        }
+       p = findcwd(NULL);
+       if (p == NULL)
+               warning("warning: failed to get name of current directory");
+       updatepwd(p);
        INTON;
        return (0);
 }
@@ -226,7 +233,7 @@ cdphysical(char *dest)
  * Get the next component of the path name pointed to by cdcomppath.
  * This routine overwrites the string pointed to by cdcomppath.
  */
-STATIC char *
+static char *
 getcomponent(void)
 {
        char *p;
@@ -247,47 +254,25 @@ getcomponent(void)
 }
 
 
-/*
- * Update curdir (the name of the current directory) in response to a
- * cd command.  We also call hashcd to let the routines in exec.c know
- * that the current directory has changed.
- */
-STATIC int
-updatepwd(char *dir)
+static char *
+findcwd(char *dir)
 {
        char *new;
        char *p;
 
-       hashcd();                               /* update command hash table */
-
        /*
         * If our argument is NULL, we don't know the current directory
         * any more because we traversed a symbolic link or something
         * we couldn't stat().
         */
-       if (dir == NULL || curdir == NULL)  {
-               if (prevdir)
-                       ckfree(prevdir);
-               INTOFF;
-               prevdir = curdir;
-               curdir = NULL;
-               if (getpwd() == NULL) {
-                       INTON;
-                       return (-1);
-               }
-               setvar("PWD", curdir, VEXPORT);
-               setvar("OLDPWD", prevdir, VEXPORT);
-               INTON;
-               return (0);
-       }
+       if (dir == NULL || curdir == NULL)
+               return getpwd2();
        cdcomppath = stalloc(strlen(dir) + 1);
        scopy(dir, cdcomppath);
        STARTSTACKSTR(new);
        if (*dir != '/') {
-               p = curdir;
-               while (*p)
-                       STPUTC(*p++, new);
-               if (p[-1] == '/')
+               STPUTS(curdir, new);
+               if (STTOPC(new) == '/')
                        STUNPUTC(new);
        }
        while ((p = getcomponent()) != NULL) {
@@ -295,29 +280,37 @@ updatepwd(char *dir)
                        while (new > stackblock() && (STUNPUTC(new), *new) != '/');
                } else if (*p != '\0' && ! equal(p, ".")) {
                        STPUTC('/', new);
-                       while (*p)
-                               STPUTC(*p++, new);
+                       STPUTS(p, new);
                }
        }
        if (new == stackblock())
                STPUTC('/', new);
        STACKSTRNUL(new);
-       INTOFF;
+       return stackblock();
+}
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.  We also call hashcd to let the routines in exec.c know
+ * that the current directory has changed.
+ */
+static void
+updatepwd(char *dir)
+{
+       hashcd();                               /* update command hash table */
+
        if (prevdir)
                ckfree(prevdir);
        prevdir = curdir;
-       curdir = savestr(stackblock());
+       curdir = dir ? savestr(dir) : NULL;
        setvar("PWD", curdir, VEXPORT);
        setvar("OLDPWD", prevdir, VEXPORT);
-       INTON;
-
-       return (0);
 }
 
 int
 pwdcmd(int argc, char **argv)
 {
-       char buf[PATH_MAX];
+       char *p;
        int ch, phys;
 
        optreset = 1; optind = 1; opterr = 0; /* initialize getopt */
@@ -345,9 +338,9 @@ pwdcmd(int argc, char **argv)
                out1str(curdir);
                out1c('\n');
        } else {
-               if (getcwd(buf, sizeof(buf)) == NULL)
+               if ((p = getpwd2()) == NULL)
                        error(".: %s", strerror(errno));
-               out1str(buf);
+               out1str(p);
                out1c('\n');
        }
 
@@ -355,30 +348,66 @@ pwdcmd(int argc, char **argv)
 }
 
 /*
- * Find out what the current directory is. If we already know the current
- * directory, this routine returns immediately.
+ * Get the current directory and cache the result in curdir.
  */
-char *
+static char *
 getpwd(void)
 {
-       char buf[PATH_MAX];
+       char *p;
 
        if (curdir)
                return curdir;
-       if (getcwd(buf, sizeof(buf)) == NULL) {
-               char *pwd = getenv("PWD");
-               struct stat stdot, stpwd;
-
-               if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
-                   stat(pwd, &stpwd) != -1 &&
-                   stdot.st_dev == stpwd.st_dev &&
-                   stdot.st_ino == stpwd.st_ino) {
-                       curdir = savestr(pwd);
-                       return curdir;
-               }
-               return NULL;
-       }
-       curdir = savestr(buf);
+
+       p = getpwd2();
+       if (p != NULL)
+               curdir = savestr(p);
 
        return curdir;
 }
+
+#define MAXPWD 256
+
+/*
+ * Return the current directory.
+ */
+static char *
+getpwd2(void)
+{
+       char *pwd;
+       int i;
+
+       for (i = MAXPWD;; i *= 2) {
+               pwd = stalloc(i);
+               if (getcwd(pwd, i) != NULL)
+                       return pwd;
+               stunalloc(pwd);
+               if (errno != ERANGE)
+                       break;
+       }
+
+       return NULL;
+}
+
+/*
+ * Initialize PWD in a new shell.
+ * If the shell is interactive, we need to warn if this fails.
+ */
+void
+pwd_init(int warn)
+{
+       char *pwd;
+       struct stat stdot, stpwd;
+
+       pwd = lookupvar("PWD");
+       if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
+           stat(pwd, &stpwd) != -1 &&
+           stdot.st_dev == stpwd.st_dev &&
+           stdot.st_ino == stpwd.st_ino) {
+               if (curdir)
+                       ckfree(curdir);
+               curdir = savestr(pwd);
+       }
+       if (getpwd() == NULL && warn)
+               out2fmt_flush("sh: cannot determine working directory\n");
+       setvar("PWD", curdir, VEXPORT);
+}
index 9746048..472c5fa 100644 (file)
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/bin/sh/cd.h,v 1.5.2.1 2002/07/19 04:38:51 tjr Exp $
- * $DragonFly: src/bin/sh/cd.h,v 1.2 2003/06/17 04:22:50 dillon Exp $
+ * $FreeBSD: src/bin/sh/cd.h,v 1.8 2010/04/17 14:35:46 jilles Exp $
  */
 
-char   *getpwd(void);
+void    pwd_init(int);
 int     cdcmd (int, char **);
 int     pwdcmd(int, char **);
index f1d7f28..f1cec2c 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)error.c 8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/error.c,v 1.26 2006/02/04 14:37:50 schweikh Exp $
- * $DragonFly: src/bin/sh/error.c,v 1.4 2007/01/06 03:44:04 pavalos Exp $
+ * $FreeBSD: src/bin/sh/error.c,v 1.33 2010/12/21 20:47:06 jilles Exp $
  */
 
 /*
@@ -67,17 +66,21 @@ volatile sig_atomic_t suppressint;
 volatile sig_atomic_t intpending;
 
 
-static void exverror(int, const char *, va_list) __printf0like(2, 0);
+static void exverror(int, const char *, va_list) __printf0like(2, 0) __dead2;
 
 /*
  * Called to raise an exception.  Since C doesn't include exceptions, we
  * just do a longjmp to the exception handler.  The type of exception is
  * stored in the global variable "exception".
+ *
+ * Interrupts are disabled; they should be reenabled when the exception is
+ * caught.
  */
 
 void
 exraise(int e)
 {
+       INTOFF;
        if (handler == NULL)
                abort();
        exception = e;
@@ -98,7 +101,7 @@ exraise(int e)
 void
 onint(void)
 {
-       sigset_t sigset;
+       sigset_t sigs;
 
        /*
         * The !in_dotrap here is safe.  The only way we can arrive here
@@ -111,8 +114,8 @@ onint(void)
                return;
        }
        intpending = 0;
-       sigemptyset(&sigset);
-       sigprocmask(SIG_SETMASK, &sigset, NULL);
+       sigemptyset(&sigs);
+       sigprocmask(SIG_SETMASK, &sigs, NULL);
 
        /*
         * This doesn't seem to be needed, since main() emits a newline.
@@ -130,6 +133,26 @@ onint(void)
 }
 
 
+static void
+vwarning(const char *msg, va_list ap)
+{
+       if (commandname)
+               outfmt(out2, "%s: ", commandname);
+       doformat(out2, msg, ap);
+       out2fmt_flush("\n");
+}
+
+
+void
+warning(const char *msg, ...)
+{
+       va_list ap;
+       va_start(ap, msg);
+       vwarning(msg, ap);
+       va_end(ap);
+}
+
+
 /*
  * Exverror is called to raise the error exception.  If the first argument
  * is not NULL then error prints an error message using printf style
@@ -138,8 +161,15 @@ onint(void)
 static void
 exverror(int cond, const char *msg, va_list ap)
 {
-       CLEAR_PENDING_INT;
-       INTOFF;
+       /*
+        * An interrupt trumps an error.  Certain places catch error
+        * exceptions or transform them to a plain nonzero exit code
+        * in child processes, and if an error exception can be handled,
+        * an interrupt can be handled as well.
+        *
+        * exraise() will disable interrupts for the exception handler.
+        */
+       FORCEINTON;
 
 #ifdef DEBUG
        if (msg)
@@ -147,12 +177,8 @@ exverror(int cond, const char *msg, va_list ap)
        else
                TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
 #endif
-       if (msg) {
-               if (commandname)
-                       outfmt(&errout, "%s: ", commandname);
-               doformat(&errout, msg, ap);
-               out2c('\n');
-       }
+       if (msg)
+               vwarning(msg, ap);
        flushall();
        exraise(cond);
 }
index 44a66fb..03badcf 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)error.h     8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/error.h,v 1.17 2004/04/06 20:06:51 markm Exp $
- * $DragonFly: src/bin/sh/error.h,v 1.4 2007/01/06 03:44:04 pavalos Exp $
+ * $FreeBSD: src/bin/sh/error.h,v 1.21 2011/02/04 22:47:55 jilles Exp $
  */
 
 /*
@@ -61,8 +60,7 @@ extern volatile sig_atomic_t exception;
 /* exceptions */
 #define EXINT 0                /* SIGINT received */
 #define EXERROR 1      /* a generic error */
-#define EXSHELLPROC 2  /* execute a shell procedure */
-#define EXEXEC 3       /* command execution failed */
+#define EXEXEC 2       /* command execution failed */
 
 
 /*
@@ -77,14 +75,17 @@ extern volatile sig_atomic_t intpending;
 
 #define INTOFF suppressint++
 #define INTON { if (--suppressint == 0 && intpending) onint(); }
+#define is_int_on() suppressint
+#define SETINTON(s) suppressint = (s)
 #define FORCEINTON {suppressint = 0; if (intpending) onint();}
 #define CLEAR_PENDING_INT intpending = 0
 #define int_pending() intpending
 
-void exraise(int);
+void exraise(int) __dead2;
 void onint(void);
-void error(const char *, ...) __printf0like(1, 2);
-void exerror(int, const char *, ...) __printf0like(2, 3);
+void warning(const char *, ...) __printflike(1, 2);
+void error(const char *, ...) __printf0like(1, 2) __dead2;
+void exerror(int, const char *, ...) __printf0like(2, 3) __dead2;
 
 
 /*
index af87caa..703d4c2 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)eval.c  8.9 (Berkeley) 6/8/95
- * $FreeBSD: src/bin/sh/eval.c,v 1.53 2006/06/15 07:57:05 stefanf Exp $
- * $DragonFly: src/bin/sh/eval.c,v 1.12 2007/02/04 19:45:24 corecode Exp $
+ * $FreeBSD: src/bin/sh/eval.c,v 1.101 2011/02/05 14:08:51 jilles Exp $
  */
 
 #include <sys/time.h>
 #endif
 
 
-/* flags in argument to evaltree */
-#define EV_EXIT 01             /* exit after evaluating tree */
-#define EV_TESTED 02           /* exit status is checked; ignore -e flag */
-#define EV_BACKCMD 04          /* command executing within back quotes */
-
 int evalskip;                  /* set if we are skipping commands */
-STATIC int skipcount;          /* number of levels to skip */
+static int skipcount;          /* number of levels to skip */
 MKINIT int loopnest;           /* current loop nesting level */
 int funcnest;                  /* depth of function calls */
+static int builtin_flags;      /* evalcommand flags for builtins */
 
 
 const char *commandname;
@@ -93,14 +88,16 @@ int exitstatus;                     /* exit status of last command */
 int oexitstatus;               /* saved exit status */
 
 
-STATIC void evalloop(union node *, int);
-STATIC void evalfor(union node *, int);
-STATIC void evalcase(union node *, int);
-STATIC void evalsubshell(union node *, int);
-STATIC void expredir(union node *);
-STATIC void evalpipe(union node *);
-STATIC void evalcommand(union node *, int, struct backcmd *);
-STATIC void prehash(union node *);
+static void evalloop(union node *, int);
+static void evalfor(union node *, int);
+static void evalcase(union node *, int);
+static void evalsubshell(union node *, int);
+static void evalredir(union node *, int);
+static void expredir(union node *);
+static void evalpipe(union node *);
+static int is_valid_fast_cmdsubst(union node *n);
+static void evalcommand(union node *, int, struct backcmd *);
+static void prehash(union node *);
 
 
 /*
@@ -115,10 +112,6 @@ RESET {
        loopnest = 0;
        funcnest = 0;
 }
-
-SHELLPROC {
-       exitstatus = 0;
-}
 #endif
 
 
@@ -140,8 +133,7 @@ evalcmd(int argc, char **argv)
                         STARTSTACKSTR(concat);
                         ap = argv + 2;
                         for (;;) {
-                                while (*p)
-                                        STPUTC(*p++, concat);
+                                STPUTS(p, concat);
                                 if ((p = *ap++) == NULL)
                                         break;
                                 STPUTC(' ', concat);
@@ -149,8 +141,9 @@ evalcmd(int argc, char **argv)
                         STPUTC('\0', concat);
                         p = grabstackstr(concat);
                 }
-                evalstring(p);
-        }
+                evalstring(p, builtin_flags & EV_TESTED);
+        } else
+                exitstatus = 0;
         return exitstatus;
 }
 
@@ -160,23 +153,37 @@ evalcmd(int argc, char **argv)
  */
 
 void
-evalstring(char *s)
+evalstring(char *s, int flags)
 {
        union node *n;
        struct stackmark smark;
+       int flags_exit;
+       int any;
 
+       flags_exit = flags & EV_EXIT;
+       flags &= ~EV_EXIT;
+       any = 0;
        setstackmark(&smark);
        setinputstring(s, 1);
        while ((n = parsecmd(0)) != NEOF) {
-               evaltree(n, 0);
+               if (n != NULL) {
+                       if (flags_exit && preadateof())
+                               evaltree(n, flags | EV_EXIT);
+                       else
+                               evaltree(n, flags);
+                       any = 1;
+               }
                popstackmark(&smark);
        }
        popfile();
        popstackmark(&smark);
+       if (!any)
+               exitstatus = 0;
+       if (flags_exit)
+               exitshell(exitstatus);
 }
 
 
-
 /*
  * Evaluate a parse tree.  The value is left in the global variable
  * exitstatus.
@@ -186,6 +193,7 @@ void
 evaltree(union node *n, int flags)
 {
        int do_etest;
+       union node *next;
 
        do_etest = 0;
        if (n == NULL) {
@@ -193,87 +201,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:
-               expredir(n->nredir.redirect);
-               redirect(n->nredir.redirect, REDIR_PUSH);
-               evaltree(n->nredir.n, flags);
-               popredir();
-               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, 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, 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();
@@ -282,7 +291,7 @@ out:
 }
 
 
-STATIC void
+static void
 evalloop(union node *n, int flags)
 {
        int status;
@@ -298,6 +307,8 @@ skipping:     if (evalskip == SKIPCONT && --skipcount <= 0) {
                        }
                        if (evalskip == SKIPBREAK && --skipcount <= 0)
                                evalskip = 0;
+                       if (evalskip == SKIPFUNC || evalskip == SKIPFILE)
+                               status = exitstatus;
                        break;
                }
                if (n->type == NWHILE) {
@@ -318,7 +329,7 @@ skipping:     if (evalskip == SKIPCONT && --skipcount <= 0) {
 
 
 
-STATIC void
+static void
 evalfor(union node *n, int flags)
 {
        struct arglist arglist;
@@ -358,7 +369,7 @@ out:
 
 
 
-STATIC void
+static void
 evalcase(union node *n, int flags)
 {
        union node *cp;
@@ -369,6 +380,7 @@ evalcase(union node *n, int flags)
        setstackmark(&smark);
        arglist.lastp = &arglist.list;
        oexitstatus = exitstatus;
+       exitstatus = 0;
        expandarg(n->ncase.expr, &arglist, EXP_TILDE);
        for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
                for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
@@ -390,21 +402,20 @@ out:
  * Kick off a subshell to evaluate a tree.
  */
 
-STATIC void
+static void
 evalsubshell(union node *n, int flags)
 {
        struct job *jp;
        int backgnd = (n->type == NBACKGND);
 
        expredir(n->nredir.redirect);
-       jp = makejob(n, 1);
-       if (forkshell(jp, n, backgnd) == 0) {
+       if ((!backgnd && flags & EV_EXIT && !have_traps()) ||
+           forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
                if (backgnd)
                        flags &=~ EV_TESTED;
                redirect(n->nredir.redirect, 0);
                evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
-       }
-       if (! backgnd) {
+       } else if (!backgnd) {
                INTOFF;
                exitstatus = waitforjob(jp, NULL);
                INTON;
@@ -412,12 +423,52 @@ evalsubshell(union node *n, int flags)
 }
 
 
+/*
+ * Evaluate a redirected compound command.
+ */
+
+static void
+evalredir(union node *n, int flags)
+{
+       struct jmploc jmploc;
+       struct jmploc *savehandler;
+       volatile int in_redirect = 1;
+
+       expredir(n->nredir.redirect);
+       savehandler = handler;
+       if (setjmp(jmploc.loc)) {
+               int e;
+
+               handler = savehandler;
+               e = exception;
+               if (e == EXERROR || e == EXEXEC) {
+                       popredir();
+                       if (in_redirect) {
+                               exitstatus = 2;
+                               return;
+                       }
+               }
+               longjmp(handler->loc, 1);
+       } else {
+               INTOFF;
+               handler = &jmploc;
+               redirect(n->nredir.redirect, REDIR_PUSH);
+               in_redirect = 0;
+               INTON;
+               evaltree(n->nredir.n, flags);
+       }
+       INTOFF;
+       handler = savehandler;
+       popredir();
+       INTON;
+}
+
 
 /*
  * Compute the names of the files in a redirection list.
  */
 
-STATIC void
+static void
 expredir(union node *n)
 {
        union node *redir;
@@ -438,7 +489,7 @@ expredir(union node *n)
                case NFROMFD:
                case NTOFD:
                        if (redir->ndup.vname) {
-                               expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+                               expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
                                fixredir(redir, fn.list->text, 1);
                        }
                        break;
@@ -455,7 +506,7 @@ expredir(union node *n)
  * of all the rest.)
  */
 
-STATIC void
+static void
 evalpipe(union node *n)
 {
        struct job *jp;
@@ -513,6 +564,19 @@ evalpipe(union node *n)
 
 
 
+static int
+is_valid_fast_cmdsubst(union node *n)
+{
+       union node *argp;
+
+       if (n->type != NCMD)
+               return 0;
+       for (argp = n->ncmd.args ; argp ; argp = argp->narg.next)
+               if (expandhassideeffects(argp->narg.text))
+                       return 0;
+       return 1;
+}
+
 /*
  * Execute a command inside back quotes.  If it's a builtin command, we
  * want to save its output in a block obtained from malloc.  Otherwise
@@ -526,6 +590,8 @@ evalbackcmd(union node *n, struct backcmd *result)
        int pip[2];
        struct job *jp;
        struct stackmark smark;         /* unnecessary */
+       struct jmploc jmploc;
+       struct jmploc *savehandler;
 
        setstackmark(&smark);
        result->fd = -1;
@@ -536,9 +602,21 @@ evalbackcmd(union node *n, struct backcmd *result)
                exitstatus = 0;
                goto out;
        }
-       if (n->type == NCMD) {
+       if (is_valid_fast_cmdsubst(n)) {
                exitstatus = oexitstatus;
-               evalcommand(n, EV_BACKCMD, result);
+               savehandler = handler;
+               if (setjmp(jmploc.loc)) {
+                       if (exception == EXERROR || exception == EXEXEC)
+                               exitstatus = 2;
+                       else if (exception != 0) {
+                               handler = savehandler;
+                               longjmp(handler->loc, 1);
+                       }
+               } else {
+                       handler = &jmploc;
+                       evalcommand(n, EV_BACKCMD, result);
+               }
+               handler = savehandler;
        } else {
                exitstatus = 0;
                if (pipe(pip) < 0)
@@ -563,13 +641,38 @@ out:
                result->fd, result->buf, result->nleft, result->jp));
 }
 
-
+/*
+ * Check if a builtin can safely be executed in the same process,
+ * even though it should be in a subshell (command substitution).
+ * Note that jobid, jobs, times and trap can show information not
+ * available in a child process; this is deliberate.
+ * The arguments should already have been expanded.
+ */
+static int
+safe_builtin(int idx, int argc, char **argv)
+{
+       if (idx == BLTINCMD || idx == COMMANDCMD || idx == ECHOCMD ||
+           idx == FALSECMD || idx == JOBIDCMD || idx == JOBSCMD ||
+           idx == KILLCMD || idx == PRINTFCMD || idx == PWDCMD ||
+           idx == TESTCMD || idx == TIMESCMD || idx == TRUECMD ||
+           idx == TYPECMD)
+               return (1);
+       if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD ||
+           idx == UMASKCMD)
+               return (argc <= 1 || (argc == 2 && argv[1][0] == '-'));
+       if (idx == SETCMD)
+               return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' ||
+                   argv[1][0] == '+') && argv[1][1] == 'o' &&
+                   argv[1][2] == '\0'));
+       return (0);
+}
 
 /*
  * Execute a simple command.
+ * Note: This may or may not return if (flags & EV_EXIT).
  */
 
-STATIC void
+static void
 evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
 {
        struct stackmark smark;
@@ -585,16 +688,18 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
        int mode;
        int pip[2];
        struct cmdentry cmdentry;
-       struct job *jp;
+       struct job *volatile jp;
        struct jmploc jmploc;
-       struct jmploc *volatile savehandler = NULL;
-       const char *volatile savecmdname;
-       volatile struct shparam saveparam;
-       struct localvar *volatile savelocalvars;
+       struct jmploc *savehandler;
+       const char *savecmdname;
+       struct shparam saveparam;
+       struct localvar *savelocalvars;
+       struct parsefile *savetopfile;
        volatile int e;
        char *volatile lastarg;
        int realstatus;
        volatile int do_clearcmdentry;
+       const char *path = pathval();
 
        /* First expand the arguments. */
        TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
@@ -602,6 +707,7 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
        arglist.lastp = &arglist.list;
        varlist.lastp = &varlist.list;
        varflag = 1;
+       jp = NULL;
        do_clearcmdentry = 0;
        oexitstatus = exitstatus;
        exitstatus = 0;
@@ -625,7 +731,9 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
        argc = 0;
        for (sp = arglist.list ; sp ; sp = sp->next)
                argc++;
-       argv = stalloc(sizeof (char *) * (argc + 1));
+       /* Add one slot at the beginning for tryexec(). */
+       argv = stalloc(sizeof (char *) * (argc + 2));
+       argv++;
 
        for (sp = arglist.list ; sp ; sp = sp->next) {
                TRACE(("evalcommand arg: %s\n", sp->text));
@@ -640,20 +748,35 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
        /* Print the command if xflag is set. */
        if (xflag) {
                char sep = 0;
+               const char *p;
                out2str(ps4val());
                for (sp = varlist.list ; sp ; sp = sp->next) {
                        if (sep != 0)
-                               outc(' ', &errout);
-                       out2str(sp->text);
+                               out2c(' ');
+                       p = strchr(sp->text, '=');
+                       if (p != NULL) {
+                               p++;
+                               outbin(sp->text, p - sp->text, out2);
+                               out2qstr(p);
+                       } else
+                               out2qstr(sp->text);
                        sep = ' ';
                }
                for (sp = arglist.list ; sp ; sp = sp->next) {
                        if (sep != 0)
-                               outc(' ', &errout);
-                       out2str(sp->text);
+                               out2c(' ');
+                       /* Disambiguate command looking like assignment. */
+                       if (sp == arglist.list &&
+                                       strchr(sp->text, '=') != NULL &&
+                                       strchr(sp->text, '\'') == NULL) {
+                               out2c('\'');
+                               out2str(sp->text);
+                               out2c('\'');
+                       } else
+                               out2qstr(sp->text);
                        sep = ' ';
                }
-               outc('\n', &errout);
+               out2c('\n');
                flushout(&errout);
        }
 
@@ -662,10 +785,10 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
                /* Variable assignment(s) without command */
                cmdentry.cmdtype = CMDBUILTIN;
                cmdentry.u.index = BLTINCMD;
-               cmdentry.special = 1;
+               cmdentry.special = 0;
        } else {
                static const char PATH[] = "PATH=";
-               const char *path = pathval();
+               int cmd_flags = 0, bltinonly = 0;
 
                /*
                 * Modify the command lookup path, if a PATH= assignment
@@ -692,46 +815,81 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
                                 * bookinging effort, since most such runs add
                                 * directories in front of the new PATH.
                                 */
-                               clearcmdentry(0);
+                               clearcmdentry();
                                do_clearcmdentry = 1;
                        }
 
-               find_command(argv[0], &cmdentry, 1, path);
-               if (cmdentry.cmdtype == CMDUNKNOWN) {   /* command not found */
-                       exitstatus = 127;
-                       flushout(&errout);
-                       return;
-               }
-               /* implement the bltin builtin here */
-               if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
-                       for (;;) {
-                               argv++;
-                               if (--argc == 0)
+               for (;;) {
+                       if (bltinonly) {
+                               cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
+                               if (cmdentry.u.index < 0) {
+                                       cmdentry.u.index = BLTINCMD;
+                                       argv--;
+                                       argc++;
                                        break;
-                               if ((cmdentry.u.index = find_builtin(*argv,
-                                   &cmdentry.special)) < 0) {
-                                       outfmt(&errout, "%s: not found\n", *argv);
-                                       exitstatus = 127;
-                                       flushout(&errout);
-                                       return;
                                }
-                               if (cmdentry.u.index != BLTINCMD)
+                       } else
+                               find_command(argv[0], &cmdentry, cmd_flags, path);
+                       /* implement the bltin and command builtins here */
+                       if (cmdentry.cmdtype != CMDBUILTIN)
+                               break;
+                       if (cmdentry.u.index == BLTINCMD) {
+                               if (argc == 1)
                                        break;
-                       }
+                               argv++;
+                               argc--;
+                               bltinonly = 1;
+                       } else if (cmdentry.u.index == COMMANDCMD) {
+                               if (argc == 1)
+                                       break;
+                               if (!strcmp(argv[1], "-p")) {
+                                       if (argc == 2)
+                                               break;
+                                       if (argv[2][0] == '-') {
+                                               if (strcmp(argv[2], "--"))
+                                                       break;
+                                               if (argc == 3)
+                                                       break;
+                                               argv += 3;
+                                               argc -= 3;
+                                       } else {
+                                               argv += 2;
+                                               argc -= 2;
+                                       }
+                                       path = _PATH_STDPATH;
+                                       clearcmdentry();
+                                       do_clearcmdentry = 1;
+                               } else if (!strcmp(argv[1], "--")) {
+                                       if (argc == 2)
+                                               break;
+                                       argv += 2;
+                                       argc -= 2;
+                               } else if (argv[1][0] == '-')
+                                       break;
+                               else {
+                                       argv++;
+                                       argc--;
+                               }
+                               cmd_flags |= DO_NOFUNC;
+                               bltinonly = 0;
+                       } else
+                               break;
                }
+               /*
+                * Special builtins lose their special properties when
+                * called via 'command'.
+                */
+               if (cmd_flags & DO_NOFUNC)
+                       cmdentry.special = 0;
        }
 
        /* Fork off a child process if necessary. */
        if (cmd->ncmd.backgnd
-        || (cmdentry.cmdtype == CMDNORMAL
-           && ((flags & EV_EXIT) == 0 || Tflag))
+        || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
+           && ((flags & EV_EXIT) == 0 || have_traps()))
         || ((flags & EV_BACKCMD) != 0
-           && (cmdentry.cmdtype != CMDBUILTIN
-                || cmdentry.u.index == CDCMD
-                || cmdentry.u.index == DOTCMD
-                || cmdentry.u.index == EVALCMD))
-        || (cmdentry.cmdtype == CMDBUILTIN &&
-           cmdentry.u.index == COMMANDCMD)) {
+           && (cmdentry.cmdtype != CMDBUILTIN ||
+                !safe_builtin(cmdentry.u.index, argc, argv)))) {
                jp = makejob(cmd, 1);
                mode = cmd->ncmd.backgnd;
                if (flags & EV_BACKCMD) {
@@ -758,7 +916,6 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
 #ifdef DEBUG
                trputs("Shell function:  ");  trargs(argv);
 #endif
-               redirect(cmd->ncmd.redirect, REDIR_PUSH);
                saveparam = shellparam;
                shellparam.malloc = 0;
                shellparam.reset = 1;
@@ -768,42 +925,46 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
                INTOFF;
                savelocalvars = localvars;
                localvars = NULL;
-               INTON;
+               reffunc(cmdentry.u.func);
+               savehandler = handler;
                if (setjmp(jmploc.loc)) {
-                       if (exception == EXSHELLPROC)
-                               freeparam(&saveparam);
-                       else {
-                               freeparam(&shellparam);
-                               shellparam = saveparam;
-                       }
+                       freeparam(&shellparam);
+                       shellparam = saveparam;
+                       if (exception == EXERROR || exception == EXEXEC)
+                               popredir();
+                       unreffunc(cmdentry.u.func);
                        poplocalvars();
                        localvars = savelocalvars;
+                       funcnest--;
                        handler = savehandler;
                        longjmp(handler->loc, 1);
                }
-               savehandler = handler;
                handler = &jmploc;
+               funcnest++;
+               redirect(cmd->ncmd.redirect, REDIR_PUSH);
+               INTON;
                for (sp = varlist.list ; sp ; sp = sp->next)
                        mklocal(sp->text);
-               funcnest++;
+               exitstatus = oexitstatus;
                if (flags & EV_TESTED)
-                       evaltree(cmdentry.u.func, EV_TESTED);
+                       evaltree(getfuncnode(cmdentry.u.func), EV_TESTED);
                else
-                       evaltree(cmdentry.u.func, 0);
-               funcnest--;
+                       evaltree(getfuncnode(cmdentry.u.func), 0);
                INTOFF;
+               unreffunc(cmdentry.u.func);
                poplocalvars();
                localvars = savelocalvars;
                freeparam(&shellparam);
                shellparam = saveparam;
                handler = savehandler;
+               funcnest--;
                popredir();
                INTON;
                if (evalskip == SKIPFUNC) {
                        evalskip = 0;
                        skipcount = 0;
                }
-               if (flags & EV_EXIT)
+               if (jp)
                        exitshell(exitstatus);
        } else if (cmdentry.cmdtype == CMDBUILTIN) {
 #ifdef DEBUG
@@ -815,68 +976,77 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
                        memout.nextc = memout.buf;
                        memout.bufsize = 64;
                        mode |= REDIR_BACKQ;
+                       cmdentry.special = 0;
                }
                savecmdname = commandname;
+               savetopfile = getcurrentfile();
                cmdenviron = varlist.list;
                e = -1;
+               savehandler = handler;
                if (setjmp(jmploc.loc)) {
                        e = exception;
                        exitstatus = (e == EXINT)? SIGINT+128 : 2;
                        goto cmddone;
                }
-               savehandler = handler;
                handler = &jmploc;
                redirect(cmd->ncmd.redirect, mode);
-               if (cmdentry.special)
-                       listsetvar(cmdenviron);
+               /*
+                * If there is no command word, redirection errors should
+                * not be fatal but assignment errors should.
+                */
+               if (argc == 0 && !(flags & EV_BACKCMD))
+                       cmdentry.special = 1;
+               listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET);
+               if (argc > 0)
+                       bltinsetlocale();
                commandname = argv[0];
                argptr = argv + 1;
-               optptr = NULL;                  /* initialize nextopt */
-               optind = 1;                     /* and getopt */
-               optreset = 1;
+               nextopt_optptr = NULL;          /* initialize nextopt */
+               builtin_flags = flags;
                exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
                flushall();
 cmddone:
+               if (argc > 0)
+                       bltinunsetlocale();
                cmdenviron = NULL;
                out1 = &output;
                out2 = &errout;
                freestdout();
-               if (e != EXSHELLPROC) {
-                       commandname = savecmdname;
-                       if (flags & EV_EXIT) {
-                               exitshell(exitstatus);
-                       }
-               }
                handler = savehandler;
-               if (e != -1) {
-                       if ((e != EXERROR && e != EXEXEC)
-                           || cmdentry.special)
-                               exraise(e);
-                       FORCEINTON;
-               }
-               if (cmdentry.u.index != EXECCMD)
-                       popredir();
+               commandname = savecmdname;
+               if (jp)
+                       exitshell(exitstatus);
                if (flags == EV_BACKCMD) {
                        backcmd->buf = memout.buf;
                        backcmd->nleft = memout.nextc - memout.buf;
                        memout.buf = NULL;
                }
+               if (cmdentry.u.index != EXECCMD &&
+                               (e == -1 || e == EXERROR || e == EXEXEC))
+                       popredir();
+               if (e != -1) {
+                       if ((e != EXERROR && e != EXEXEC)
+                           || cmdentry.special)
+                               exraise(e);
+                       popfilesupto(savetopfile);
+                       if (flags != EV_BACKCMD)
+                               FORCEINTON;
+               }
        } else {
 #ifdef DEBUG
                trputs("normal command:  ");  trargs(argv);
 #endif
-               clearredir();
                redirect(cmd->ncmd.redirect, 0);
                for (sp = varlist.list ; sp ; sp = sp->next)
                        setvareq(sp->text, VEXPORT|VSTACK);
                envp = environment();
-               shellexec(argv, envp, pathval(), cmdentry.u.index);
+               shellexec(argv, envp, path, cmdentry.u.index);
                /*NOTREACHED*/
        }
        goto out;
 
 parent:        /* parent process gets here (if we forked) */
-       if (mode == 0) {        /* argument to fork */
+       if (mode == FORK_FG) {  /* argument to fork */
                INTOFF;
                exitstatus = waitforjob(jp, &realstatus);
                INTON;
@@ -884,7 +1054,7 @@ parent:    /* parent process gets here (if we forked) */
                        evalskip = SKIPBREAK;
                        skipcount = loopnest;
                }
-       } else if (mode == 2) {
+       } else if (mode == FORK_NOJOB) {
                backcmd->fd = pip[0];
                close(pip[1]);
                backcmd->jp = jp;
@@ -894,7 +1064,7 @@ out:
        if (lastarg)
                setvar("_", lastarg, 0);
        if (do_clearcmdentry)
-               clearcmdentry(0);
+               clearcmdentry();
        popstackmark(&smark);
 }
 
@@ -907,7 +1077,7 @@ out:
  * check that the name will not be subject to expansion.
  */
 
-STATIC void
+static void
 prehash(union node *n)
 {
        struct cmdentry entry;
@@ -926,12 +1096,17 @@ prehash(union node *n)
  */
 
 /*
- * No command given, or a bltin command with no arguments.
+ * No command given, a bltin command with no arguments, or a bltin command
+ * with an invalid name.
  */
 
 int
-bltincmd(int argc __unused, char **argv __unused)
+bltincmd(int argc, char **argv)
 {
+       if (argc > 1) {
+               out2fmt_flush("%s: not found\n", argv[1]);
+               return 127;
+       }
        /*
         * Preserve exitstatus of a previous possible redirection
         * as POSIX mandates
@@ -971,23 +1146,18 @@ breakcmd(int argc, char **argv)
 int
 commandcmd(int argc, char **argv)
 {
-       static char stdpath[] = _PATH_STDPATH;
-       struct jmploc loc, *old;
-       struct strlist *sp;
-       const char *volatile path;
+       const char *path;
        int ch;
        int cmd = -1;
 
-       for (sp = cmdenviron; sp ; sp = sp->next)
-               setvareq(sp->text, VEXPORT|VSTACK);
-       path = pathval();
+       path = bltinlookup("PATH", 1);
 
        optind = optreset = 1;
        opterr = 0;
        while ((ch = getopt(argc, argv, "pvV")) != -1) {
                switch (ch) {
                case 'p':
-                       path = stdpath;
+                       path = _PATH_STDPATH;
                        break;
                case 'v':
                        cmd = TYPECMD_SMALLV;
@@ -1006,24 +1176,16 @@ commandcmd(int argc, char **argv)
        if (cmd != -1) {
                if (argc != 1)
                        error("wrong number of arguments");
-               return typecmd_impl(2, argv - 1, cmd);
-       }
-       if (argc != 0) {
-               old = handler;
-               handler = &loc;
-               if (setjmp(handler->loc) == 0)
-                       shellexec(argv, environment(), path, 0);
-               handler = old;
-               if (exception == EXEXEC)
-                       exit(exerrno);
-               exraise(exception);
+               return typecmd_impl(2, argv - 1, cmd, path);
        }
+       if (argc != 0)
+               error("commandcmd bad call");
 
        /*
         * Do nothing successfully if no command was specified;
         * ksh also does this.
         */
-       exit(0);
+       return(0);
 }
 
 
@@ -1065,6 +1227,12 @@ truecmd(int argc __unused, char **argv __unused)
 int
 execcmd(int argc, char **argv)
 {
+       /*
+        * Because we have historically not supported any options,
+        * only treat "--" specially.
+        */
+       if (argc > 1 && strcmp(argv[1], "--") == 0)
+               argc--, argv++;
        if (argc > 1) {
                struct strlist *sp;
 
index 04f0e2a..a3ec200 100644 (file)
  * SUCH DAMAGE.
  *
  *     @(#)eval.h      8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/eval.h,v 1.11 2005/12/04 18:44:21 stefanf Exp $
- * $DragonFly: src/bin/sh/eval.h,v 1.4 2007/01/07 01:14:53 pavalos Exp $
+ * $FreeBSD: src/bin/sh/eval.h,v 1.14 2009/12/27 18:04:05 jilles Exp $
  */
 
 extern const char *commandname;        /* currently executing command */
 extern int exitstatus;         /* exit status of last command */
+extern int oexitstatus;                /* saved exit status */
 extern struct strlist *cmdenviron;  /* environment for builtin command */
 
 
@@ -50,8 +50,13 @@ struct backcmd {             /* result of evalbackcmd */
        struct job *jp;         /* job structure for command */
 };
 
+/* flags in argument to evaltree/evalstring */
+#define EV_EXIT 01             /* exit after evaluating tree */
+#define EV_TESTED 02           /* exit status is checked; ignore -e flag */
+#define EV_BACKCMD 04          /* command executing within back quotes */
+
 int evalcmd(int, char **);
-void evalstring(char *);
+void evalstring(char *, int);
 union node;    /* BLETCH for ansi C */
 void evaltree(union node *, int);
 void evalbackcmd(union node *, struct backcmd *);
index 79b7a90..d9b32a3 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)exec.c  8.4 (Berkeley) 6/8/95
- * $FreeBSD: src/bin/sh/exec.c,v 1.30 2007/01/11 00:19:00 stefanf Exp $
- * $DragonFly: src/bin/sh/exec.c,v 1.11 2007/01/14 18:14:39 pavalos Exp $
+ * $FreeBSD: src/bin/sh/exec.c,v 1.52 2011/02/05 14:08:51 jilles Exp $
  */
 
 #include <sys/types.h>
@@ -43,6 +42,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <paths.h>
 #include <stdlib.h>
 
 /*
@@ -91,21 +91,20 @@ struct tblentry {
 };
 
 
-STATIC struct tblentry *cmdtable[CMDTABLESIZE];
-STATIC int builtinloc = -1;            /* index in path of %builtin, or -1 */
+static struct tblentry *cmdtable[CMDTABLESIZE];
 int exerrno = 0;                       /* Last exec error */
 
 
-STATIC void tryexec(char *, char **, char **);
-STATIC void printentry(struct tblentry *, int);
-STATIC struct tblentry *cmdlookup(char *, int);
-STATIC void delete_cmd_entry(void);
-
-extern const char *const parsekwd[];
+static void tryexec(char *, char **, char **);
+static void printentry(struct tblentry *, int);
+static struct tblentry *cmdlookup(const char *, int);
+static void delete_cmd_entry(void);
 
 /*
  * Exec a program.  Never returns.  If you change this routine, you may
  * have to change the find_command routine as well.
+ *
+ * The argv array may be changed and element argv[-1] should be writable.
  */
 
 void
@@ -124,43 +123,47 @@ shellexec(char **argv, char **envp, const char *path, int idx)
                                tryexec(cmdname, argv, envp);
                                if (errno != ENOENT && errno != ENOTDIR)
                                        e = errno;
+                               if (e == ENOEXEC)
+                                       break;
                        }
                        stunalloc(cmdname);
                }
        }
 
        /* Map to POSIX errors */
-       switch (e) {
-       case EACCES:
-               exerrno = 126;
-               break;
-       case ENOENT:
+       if (e == ENOENT || e == ENOTDIR) {
                exerrno = 127;
-               break;
-       default:
-               exerrno = 2;
-               break;
-       }
-       if (e == ENOENT || e == ENOTDIR)
                exerror(EXEXEC, "%s: not found", argv[0]);
-       exerror(EXEXEC, "%s: %s", argv[0], strerror(e));
+       } else {
+               exerrno = 126;
+               exerror(EXEXEC, "%s: %s", argv[0], strerror(e));
+       }
 }
 
 
-STATIC void
+static void
 tryexec(char *cmd, char **argv, char **envp)
 {
-       int e;
+       int e, in;
+       ssize_t n;
+       char buf[256];
 
        execve(cmd, argv, envp);
        e = errno;
        if (e == ENOEXEC) {
-               initshellproc();
-               setinputfile(cmd, 0);
-               commandname = arg0 = savestr(argv[0]);
-               setparam(argv + 1);
-               exraise(EXSHELLPROC);
-               /*NOTREACHED*/
+               INTOFF;
+               in = open(cmd, O_RDONLY | O_NONBLOCK);
+               if (in != -1) {
+                       n = pread(in, buf, sizeof buf, 0);
+                       close(in);
+                       if (n > 0 && memchr(buf, '\0', n) != NULL) {
+                               errno = ENOEXEC;
+                               return;
+                       }
+               }
+               *argv = cmd;
+               *--argv = __DECONST(char *, _PATH_BSHELL);
+               execve(_PATH_BSHELL, argv, envp);
        }
        errno = e;
 }
@@ -180,19 +183,18 @@ const char *pathopt;
 char *
 padvance(const char **path, const char *name)
 {
-       const char *p;
+       const char *p, *start;
        char *q;
-       const char *start;
        int len;
 
        if (*path == NULL)
                return NULL;
        start = *path;
-       for (p = start ; *p && *p != ':' && *p != '%' ; p++);
+       for (p = start; *p && *p != ':' && *p != '%'; p++)
+               ; /* nothing */
        len = p - start + strlen(name) + 2;     /* "2" is for '/' and '\0' */
-       while (stackblocksize() < len)
-               growstackblock();
-       q = stackblock();
+       STARTSTACKSTR(q);
+       CHECKSTRSPACE(len, q);
        if (p != start) {
                memcpy(q, start, p - start);
                q += p - start;
@@ -229,7 +231,7 @@ hashcmd(int argc __unused, char **argv __unused)
        verbose = 0;
        while ((c = nextopt("rv")) != '\0') {
                if (c == 'r') {
-                       clearcmdentry(0);
+                       clearcmdentry();
                } else if (c == 'v') {
                        verbose++;
                }
@@ -245,17 +247,16 @@ hashcmd(int argc __unused, char **argv __unused)
        }
        while ((name = *argptr) != NULL) {
                if ((cmdp = cmdlookup(name, 0)) != NULL
-                && (cmdp->cmdtype == CMDNORMAL
-                    || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
+                && cmdp->cmdtype == CMDNORMAL)
                        delete_cmd_entry();
-               find_command(name, &entry, 1, pathval());
+               find_command(name, &entry, DO_ERR, pathval());
                if (verbose) {
                        if (entry.cmdtype != CMDUNKNOWN) {      /* if no error msg */
                                cmdp = cmdlookup(name, 0);
                                if (cmdp != NULL)
                                        printentry(cmdp, verbose);
                                else
-                                       outfmt(&errout, "%s: not found\n", name);
+                                       outfmt(out2, "%s: not found\n", name);
                        }
                        flushall();
                }
@@ -265,7 +266,7 @@ hashcmd(int argc __unused, char **argv __unused)
 }
 
 
-STATIC void
+static void
 printentry(struct tblentry *cmdp, int verbose)
 {
        int idx;
@@ -286,7 +287,7 @@ printentry(struct tblentry *cmdp, int verbose)
                out1fmt("function %s", cmdp->cmdname);
                if (verbose) {
                        INTOFF;
-                       name = commandtext(cmdp->param.func);
+                       name = commandtext(getfuncnode(cmdp->param.func));
                        out1c(' ');
                        out1str(name);
                        ckfree(name);
@@ -310,9 +311,10 @@ printentry(struct tblentry *cmdp, int verbose)
  */
 
 void
-find_command(char *name, struct cmdentry *entry, int printerr, const char *path)
+find_command(const char *name, struct cmdentry *entry, int act,
+    const char *path)
 {
-       struct tblentry *cmdp;
+       struct tblentry *cmdp, loc_cmd;
        int idx;
        int prev;
        char *fullname;
@@ -329,13 +331,19 @@ find_command(char *name, struct cmdentry *entry, int printerr, const char *path)
        }
 
        /* If name is in the table, and not invalidated by cd, we're done */
-       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
-               goto success;
+       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
+               if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
+                       cmdp = NULL;
+               else
+                       goto success;
+       }
 
-       /* If %builtin not in path, check for builtin next */
-       if (builtinloc < 0 && (i = find_builtin(name, &spec)) >= 0) {
+       /* Check for builtin next */
+       if ((i = find_builtin(name, &spec)) >= 0) {
                INTOFF;
                cmdp = cmdlookup(name, 1);
+               if (cmdp->cmdtype == CMDFUNCTION)
+                       cmdp = &loc_cmd;
                cmdp->cmdtype = CMDBUILTIN;
                cmdp->param.index = i;
                cmdp->special = spec;
@@ -347,7 +355,7 @@ find_command(char *name, struct cmdentry *entry, int printerr, const char *path)
        prev = -1;              /* where to start */
        if (cmdp) {             /* doing a rehash */
                if (cmdp->cmdtype == CMDBUILTIN)
-                       prev = builtinloc;
+                       prev = -1;
                else
                        prev = cmdp->param.index;
        }
@@ -359,17 +367,7 @@ loop:
                stunalloc(fullname);
                idx++;
                if (pathopt) {
-                       if (prefix("builtin", pathopt)) {
-                               if ((i = find_builtin(name, &spec)) < 0)
-                                       goto loop;
-                               INTOFF;
-                               cmdp = cmdlookup(name, 1);
-                               cmdp->cmdtype = CMDBUILTIN;
-                               cmdp->param.index = i;
-                               cmdp->special = spec;
-                               INTON;
-                               goto success;
-                       } else if (prefix("func", pathopt)) {
+                       if (prefix("func", pathopt)) {
                                /* handled below */
                        } else {
                                goto loop;      /* ignore unimplemented options */
@@ -413,6 +411,8 @@ loop:
                TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
                INTOFF;
                cmdp = cmdlookup(name, 1);
+               if (cmdp->cmdtype == CMDFUNCTION)
+                       cmdp = &loc_cmd;
                cmdp->cmdtype = CMDNORMAL;
                cmdp->param.index = idx;
                INTON;
@@ -420,15 +420,16 @@ loop:
        }
 
        /* We failed.  If there was an entry for this command, delete it */
-       if (cmdp)
+       if (cmdp && cmdp->cmdtype != CMDFUNCTION)
                delete_cmd_entry();
-       if (printerr) {
+       if (act & DO_ERR) {
                if (e == ENOENT || e == ENOTDIR)
                        outfmt(out2, "%s: not found\n", name);
                else
                        outfmt(out2, "%s: %s\n", name, strerror(e));
        }
        entry->cmdtype = CMDUNKNOWN;
+       entry->u.index = 0;
        return;
 
 success:
@@ -448,7 +449,7 @@ success:
  */
 
 int
-find_builtin(char *name, int *special)
+find_builtin(const char *name, int *special)
 {
        const struct builtincmd *bp;
 
@@ -476,8 +477,7 @@ hashcd(void)
 
        for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
                for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
-                       if (cmdp->cmdtype == CMDNORMAL
-                        || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
+                       if (cmdp->cmdtype == CMDNORMAL)
                                cmdp->rehash = 1;
                }
        }
@@ -492,41 +492,9 @@ hashcd(void)
  */
 
 void
-changepath(const char *newval)
+changepath(const char *newval __unused)
 {
-       const char *old, *new;
-       int idx;
-       int firstchange;
-       int bltin;
-
-       old = pathval();
-       new = newval;
-       firstchange = 9999;     /* assume no change */
-       idx = 0;
-       bltin = -1;
-       for (;;) {
-               if (*old != *new) {
-                       firstchange = idx;
-                       if ((*old == '\0' && *new == ':')
-                        || (*old == ':' && *new == '\0'))
-                               firstchange++;
-                       old = new;      /* ignore subsequent differences */
-               }
-               if (*new == '\0')
-                       break;
-               if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
-                       bltin = idx;
-               if (*new == ':') {
-                       idx++;
-               }
-               new++, old++;
-       }
-       if (builtinloc < 0 && bltin >= 0)
-               builtinloc = bltin;             /* zap builtins */
-       if (builtinloc >= 0 && bltin < 0)
-               firstchange = 0;
-       clearcmdentry(firstchange);
-       builtinloc = bltin;
+       clearcmdentry();
 }
 
 
@@ -536,45 +504,7 @@ changepath(const char *newval)
  */
 
 void
-clearcmdentry(int firstchange)
-{
-       struct tblentry **tblp;
-       struct tblentry **pp;
-       struct tblentry *cmdp;
-
-       INTOFF;
-       for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
-               pp = tblp;
-               while ((cmdp = *pp) != NULL) {
-                       if ((cmdp->cmdtype == CMDNORMAL &&
-                            cmdp->param.index >= firstchange)
-                        || (cmdp->cmdtype == CMDBUILTIN &&
-                            builtinloc >= firstchange)) {
-                               *pp = cmdp->next;
-                               ckfree(cmdp);
-                       } else {
-                               pp = &cmdp->next;
-                       }
-               }
-       }
-       INTON;
-}
-
-
-/*
- * Delete all functions.
- */
-
-#ifdef mkinit
-MKINIT void deletefuncs(void);
-
-SHELLPROC {
-       deletefuncs();
-}
-#endif
-
-void
-deletefuncs(void)
+clearcmdentry(void)
 {
        struct tblentry **tblp;
        struct tblentry **pp;
@@ -584,9 +514,8 @@ deletefuncs(void)
        for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
                pp = tblp;
                while ((cmdp = *pp) != NULL) {
-                       if (cmdp->cmdtype == CMDFUNCTION) {
+                       if (cmdp->cmdtype == CMDNORMAL) {
                                *pp = cmdp->next;
-                               freefunc(cmdp->param.func);
                                ckfree(cmdp);
                        } else {
                                pp = &cmdp->next;
@@ -597,7 +526,6 @@ deletefuncs(void)
 }
 
 
-
 /*
  * Locate a command in the command hash table.  If "add" is nonzero,
  * add the command to the table if it is not already present.  The
@@ -606,14 +534,14 @@ deletefuncs(void)
  * entry.
  */
 
-STATIC struct tblentry **lastcmdentry;
+static struct tblentry **lastcmdentry;
 
 
-STATIC struct tblentry *
-cmdlookup(char *name, int add)
+static struct tblentry *
+cmdlookup(const char *name, int add)
 {
        int hashval;
-       char *p;
+       const char *p;
        struct tblentry *cmdp;
        struct tblentry **pp;
 
@@ -646,7 +574,7 @@ cmdlookup(char *name, int add)
  * Delete the command entry returned on the last lookup.
  */
 
-STATIC void
+static void
 delete_cmd_entry(void)
 {
        struct tblentry *cmdp;
@@ -666,14 +594,14 @@ delete_cmd_entry(void)
  */
 
 void
-addcmdentry(char *name, struct cmdentry *entry)
+addcmdentry(const char *name, struct cmdentry *entry)
 {
        struct tblentry *cmdp;
 
        INTOFF;
        cmdp = cmdlookup(name, 1);
        if (cmdp->cmdtype == CMDFUNCTION) {
-               freefunc(cmdp->param.func);
+               unreffunc(cmdp->param.func);
        }
        cmdp->cmdtype = entry->cmdtype;
        cmdp->param = entry->u;
@@ -686,7 +614,7 @@ addcmdentry(char *name, struct cmdentry *entry)
  */
 
 void
-defun(char *name, union node *func)
+defun(const char *name, union node *func)
 {
        struct cmdentry entry;
 
@@ -703,12 +631,12 @@ defun(char *name, union node *func)
  */
 
 int
-unsetfunc(char *name)
+unsetfunc(const char *name)
 {
        struct tblentry *cmdp;
 
        if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
-               freefunc(cmdp->param.func);
+               unreffunc(cmdp->param.func);
                delete_cmd_entry();
        }
        return (0);
@@ -720,7 +648,7 @@ unsetfunc(char *name)
  */
 
 int
-typecmd_impl(int argc, char **argv, int cmd)
+typecmd_impl(int argc, char **argv, int cmd, const char *path)
 {
        struct cmdentry entry;
        struct tblentry *cmdp;
@@ -729,10 +657,10 @@ typecmd_impl(int argc, char **argv, int cmd)
        int i;
        int err = 0;
 
-       for (i = 1; i < argc; i++) {
-               if (cmd != TYPECMD_SMALLV)
-                       out1str(argv[i]);
+       if (path != pathval())
+               clearcmdentry();
 
+       for (i = 1; i < argc; i++) {
                /* First look at the keywords */
                for (pp = parsekwd; *pp; pp++)
                        if (**pp == *argv[i] && equal(*pp, argv[i]))
@@ -742,7 +670,7 @@ typecmd_impl(int argc, char **argv, int cmd)
                        if (cmd == TYPECMD_SMALLV)
                                out1fmt("%s\n", argv[i]);
                        else
-                               out1str(" is a shell keyword\n");
+                               out1fmt("%s is a shell keyword\n", argv[i]);
                        continue;
                }
 
@@ -751,7 +679,8 @@ typecmd_impl(int argc, char **argv, int cmd)
                        if (cmd == TYPECMD_SMALLV)
                                out1fmt("alias %s='%s'\n", argv[i], ap->val);
                        else
-                               out1fmt(" is an alias for %s\n", ap->val);
+                               out1fmt("%s is an alias for %s\n", argv[i],
+                                   ap->val);
                        continue;
                }
 
@@ -759,26 +688,27 @@ typecmd_impl(int argc, char **argv, int cmd)
                if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
                        entry.cmdtype = cmdp->cmdtype;
                        entry.u = cmdp->param;
+                       entry.special = cmdp->special;
                }
                else {
                        /* Finally use brute force */
-                       find_command(argv[i], &entry, 0, pathval());
+                       find_command(argv[i], &entry, 0, path);
                }
 
                switch (entry.cmdtype) {
                case CMDNORMAL: {
                        if (strchr(argv[i], '/') == NULL) {
-                               const char *path = pathval();
+                               const char *path2 = path;
                                char *name;
                                int j = entry.u.index;
                                do {
-                                       name = padvance(&path, argv[i]);
+                                       name = padvance(&path2, argv[i]);
                                        stunalloc(name);
                                } while (--j >= 0);
                                if (cmd == TYPECMD_SMALLV)
                                        out1fmt("%s\n", name);
                                else
-                                       out1fmt(" is%s %s\n",
+                                       out1fmt("%s is%s %s\n", argv[i],
                                            (cmdp && cmd == TYPECMD_TYPE) ?
                                                " a tracked alias for" : "",
                                            name);
@@ -787,11 +717,12 @@ typecmd_impl(int argc, char **argv, int cmd)
                                        if (cmd == TYPECMD_SMALLV)
                                                out1fmt("%s\n", argv[i]);
                                        else
-                                               out1fmt(" is %s\n", argv[i]);
+                                               out1fmt("%s is %s\n", argv[i],
+                                                   argv[i]);
                                } else {
                                        if (cmd != TYPECMD_SMALLV)
-                                               out1fmt(": %s\n",
-                                                   strerror(errno));
+                                               outfmt(out2, "%s: %s\n",
+                                                   argv[i], strerror(errno));
                                        err |= 127;
                                }
                        }
@@ -801,23 +732,30 @@ typecmd_impl(int argc, char **argv, int cmd)
                        if (cmd == TYPECMD_SMALLV)
                                out1fmt("%s\n", argv[i]);
                        else
-                               out1str(" is a shell function\n");
+                               out1fmt("%s is a shell function\n", argv[i]);
                        break;
 
                case CMDBUILTIN:
                        if (cmd == TYPECMD_SMALLV)
                                out1fmt("%s\n", argv[i]);
+                       else if (entry.special)
+                               out1fmt("%s is a special shell builtin\n",
+                                   argv[i]);
                        else
-                               out1str(" is a shell builtin\n");
+                               out1fmt("%s is a shell builtin\n", argv[i]);
                        break;
 
                default:
                        if (cmd != TYPECMD_SMALLV)
-                               out1str(": not found\n");
+                               outfmt(out2, "%s: not found\n", argv[i]);
                        err |= 127;
                        break;
                }
        }
+
+       if (path != pathval())
+               clearcmdentry();
+
        return err;
 }
 
@@ -828,5 +766,5 @@ typecmd_impl(int argc, char **argv, int cmd)
 int
 typecmd(int argc, char **argv)
 {
-       return typecmd_impl(argc, argv, TYPECMD_TYPE);
+       return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
 }
index 3d83ffc..8f37365 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)exec.h      8.3 (Berkeley) 6/8/95
- * $FreeBSD: src/bin/sh/exec.h,v 1.15 2006/04/09 12:21:20 stefanf Exp $
- * $DragonFly: src/bin/sh/exec.h,v 1.5 2007/01/07 01:14:53 pavalos Exp $
+ * $FreeBSD: src/bin/sh/exec.h,v 1.22 2011/02/05 14:08:51 jilles Exp $
  */
 
 /* values of cmdtype */
@@ -51,30 +50,34 @@ enum {
        TYPECMD_TYPE            /* type */
 };
 
+union node;
 struct cmdentry {
        int cmdtype;
        union param {
                int index;
-               union node *func;
+               struct funcdef *func;
        } u;
        int special;
 };
 
 
-extern const char *pathopt;            /* set by padvance */
+/* action to find_command() */
+#define DO_ERR         0x01    /* prints errors */
+#define DO_NOFUNC      0x02    /* don't return shell functions, for command */
+
+extern const char *pathopt;    /* set by padvance */
 extern int exerrno;            /* last exec error */
 
-void shellexec(char **, char **, const char *, int);
+void shellexec(char **, char **, const char *, int) __dead2;
 char *padvance(const char **, const char *);
 int hashcmd(int, char **);
-void find_command(char *, struct cmdentry *, int, const char *);
-int find_builtin(char *, int *);
+void find_command(const char *, struct cmdentry *, int, const char *);
+int find_builtin(const char *, int *);
 void hashcd(void);
 void changepath(const char *);
-void deletefuncs(void);
-void addcmdentry(char *, struct cmdentry *);
-void defun(char *, union node *);
-int unsetfunc(char *);
-int typecmd_impl(int, char **, int);
+void addcmdentry(const char *, struct cmdentry *);
+void defun(const char *, union node *);
+int unsetfunc(const char *);
+int typecmd_impl(int, char **, int, const char *);
 int typecmd(int, char **);
-void clearcmdentry(int);
+void clearcmdentry(void);
index 4d3c56b..d687579 100644 (file)
@@ -1,6 +1,8 @@
 /*-
  * Copyright (c) 1991, 1993
  *     The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *     Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
  * SUCH DAMAGE.
  *
  * @(#)expand.c        8.5 (Berkeley) 5/15/95
- * $FreeBSD: src/bin/sh/expand.c,v 1.51 2006/11/07 22:46:13 stefanf Exp $
- * $DragonFly: src/bin/sh/expand.c,v 1.9 2007/01/07 16:58:30 pavalos Exp $
+ * $FreeBSD: src/bin/sh/expand.c,v 1.82 2011/02/02 21:48:53 jilles Exp $
  */
 
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/stat.h>
-#include <errno.h>
 #include <dirent.h>
-#include <unistd.h>
-#include <pwd.h>
-#include <stdlib.h>
+#include <errno.h>
+#include <inttypes.h>
 #include <limits.h>
+#include <pwd.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 /*
  * Routines to expand arguments to commands.  We have to deal with
@@ -82,36 +84,35 @@ struct ifsregion {
        struct ifsregion *next; /* next region in list */
        int begoff;             /* offset of start of region */
        int endoff;             /* offset of end of region */
-       int nulonly;            /* search for nul bytes only */
+       int inquotes;           /* search for nul bytes only */
 };
 
 
-STATIC char *expdest;                  /* output of current string */
-STATIC struct nodelist *argbackq;      /* list of back quote expressions */
-STATIC struct ifsregion ifsfirst;      /* first struct in list of ifs regions */
-STATIC struct ifsregion *ifslastp;     /* last struct in list */
-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 char *evalvar(char *, int);
-STATIC int varisset(char *, int);
-STATIC void varvalue(char *, int, int, int);
-STATIC void recordregion(int, int, int);
-STATIC void removerecordregions(int);
-STATIC void ifsbreakup(char *, struct arglist *);
-STATIC void expandmeta(struct strlist *, int);
-STATIC void expmeta(char *, char *);
-STATIC void addfname(char *);
-STATIC struct strlist *expsort(struct strlist *);
-STATIC struct strlist *msort(struct strlist *, int);
-STATIC int pmatch(char *, char *, int);
-STATIC char *cvtnum(int, char *);
-STATIC int collate_range_cmp(int, int);
-
-STATIC int
+static char *expdest;                  /* output of current string */
+static struct nodelist *argbackq;      /* list of back quote expressions */
+static struct ifsregion ifsfirst;      /* first struct in list of ifs regions */
+static struct ifsregion *ifslastp;     /* last struct in list */
+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, int);
+static char *evalvar(char *, int);
+static int varisset(char *, int);
+static void varvalue(char *, int, int, int);
+static void recordregion(int, int, int);
+static void removerecordregions(int);
+static void ifsbreakup(char *, struct arglist *);
+static void expandmeta(struct strlist *, int);
+static void expmeta(char *, char *);
+static void addfname(char *);
+static struct strlist *expsort(struct strlist *);
+static struct strlist *msort(struct strlist *, int);
+static char *cvtnum(int, char *);
+static int collate_range_cmp(int, int);
+
+static int
 collate_range_cmp(int c1, int c2)
 {
        static char s1[2], s2[2];
@@ -121,8 +122,6 @@ collate_range_cmp(int c1, int c2)
        return (strcoll(s1, s2));
 }
 
-extern int oexitstatus;
-
 /*
  * Expand shell variables and backquotes inside a here document.
  *     union node *arg         the document
@@ -132,19 +131,35 @@ extern int oexitstatus;
 void
 expandhere(union node *arg, int fd)
 {
-       herefd = fd;
        expandarg(arg, NULL, 0);
        xwrite(fd, stackblock(), expdest - stackblock());
 }
 
+static char *
+stputs_quotes(const char *data, const char *syntax, char *p)
+{
+       while (*data) {
+               CHECKSTRSPACE(2, p);
+               if (syntax[(int)*data] == CCTL)
+                       USTPUTC(CTLESC, p);
+               USTPUTC(*data++, p);
+       }
+       return (p);
+}
+#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
 
 /*
- * Perform variable substitution and command substitution on an argument,
- * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
- * perform splitting and file name expansion.  When arglist is NULL, perform
- * here document expansion.
+ * Perform expansions on an argument, placing the resulting list of arguments
+ * in arglist.  Parameter expansion, command substitution and arithmetic
+ * expansion are always performed; additional expansions can be requested
+ * via flag (EXP_*).
+ * The result is left in the stack string.
+ * When arglist is NULL, perform here document expansion.
+ *
+ * Caution: this function uses global state and is not reentrant.
+ * However, a new invocation after an interrupted invocation is safe
+ * and will reset the global state for the new call.
  */
-
 void
 expandarg(union node *arg, struct arglist *arglist, int flag)
 {
@@ -196,37 +211,54 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
 
 
 /*
- * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
- * characters to allow for further processing.  Otherwise treat
- * $@ like $* since no splitting will be performed.
+ * Perform parameter expansion, command substitution and arithmetic
+ * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
+ * Processing ends at a CTLENDVAR character as well as '\0'.
+ * This is used to expand word in ${var+word} etc.
+ * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC
+ * characters to allow for further processing.
+ * If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
  */
-
-STATIC void
+static void
 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 (;;) {
+               CHECKSTRSPACE(2, expdest);
                switch (c = *p++) {
                case '\0':
-               case CTLENDVAR: /* ??? */
+               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);
+                               USTPUTC(c, expdest);
+                       break;
+               case CTLQUOTEEND:
+                       lit_quoted = 0;
                        break;
                case CTLESC:
                        if (quotes)
-                               STPUTC(c, expdest);
+                               USTPUTC(c, expdest);
                        c = *p++;
-                       STPUTC(c, expdest);
+                       USTPUTC(c, expdest);
+                       if (split_lit && !lit_quoted)
+                               recordregion(expdest - stackblock() -
+                                   (quotes ? 2 : 1),
+                                   expdest - stackblock(), 0);
                        break;
                case CTLVAR:
                        p = evalvar(p, flag);
@@ -245,25 +277,32 @@ argstr(char *p, int flag)
                         * sort of a hack - expand tildes in variable
                         * assignments (after the first '=' and after ':'s).
                         */
-                       STPUTC(c, expdest);
-                       if (flag & EXP_VARTILDE && *p == '~') {
-                               if (c == '=') {
-                                       if (firsteq)
-                                               firsteq = 0;
-                                       else
-                                               break;
-                               }
+                       USTPUTC(c, expdest);
+                       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);
+                       USTPUTC(c, expdest);
+                       if (split_lit && !lit_quoted)
+                               recordregion(expdest - stackblock() - 1,
+                                   expdest - stackblock(), 0);
                }
        }
 breakloop:;
 }
 
-STATIC char *
+/*
+ * Perform tilde expansion, placing the result in the stack string and
+ * returning the next position in the input string to process.
+ */
+static char *
 exptilde(char *p, int flag)
 {
        char c, *startp = p;
@@ -273,8 +312,12 @@ exptilde(char *p, int flag)
 
        while ((c = *p) != '\0') {
                switch(c) {
-               case CTLESC:
-                       return (startp);
+               case CTLESC: /* This means CTL* are always considered quoted. */
+               case CTLVAR:
+               case CTLBACKQ:
+               case CTLBACKQ | CTLQUOTE:
+               case CTLARI:
+               case CTLENDARI:
                case CTLQUOTEMARK:
                        return (startp);
                case ':':
@@ -282,6 +325,7 @@ exptilde(char *p, int flag)
                                goto done;
                        break;
                case '/':
+               case CTLENDVAR:
                        goto done;
                }
                p++;
@@ -299,11 +343,10 @@ done:
        if (*home == '\0')
                goto lose;
        *p = c;
-       while ((c = *home++) != '\0') {
-               if (quotes && SQSYNTAX[(int)c] == CCTL)
-                       STPUTC(CTLESC, expdest);
-               STPUTC(c, expdest);
-       }
+       if (quotes)
+               STPUTS_QUOTES(home, SQSYNTAX, expdest);
+       else
+               STPUTS(home, expdest);
        return (p);
 lose:
        *p = c;
@@ -311,7 +354,7 @@ lose:
 }
 
 
-STATIC void
+static void
 removerecordregions(int endoff)
 {
        if (ifslastp == NULL)
@@ -357,27 +400,23 @@ removerecordregions(int endoff)
 void
 expari(int flag)
 {
-       char *p, *start;
-       int result;
+       char *p, *q, *start;
+       arith_t result;
        int begoff;
        int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
        int quoted;
 
-
        /*
         * This routine is slightly over-complicated for
         * efficiency.  First we make sure there is
         * enough space for the result, which may be bigger
-        * than the expression if we add exponentiation.  Next we
+        * than the expression.  Next we
         * scan backwards looking for the start of arithmetic.  If the
         * next previous character is a CTLESC character, then we
         * have to rescan starting from the beginning since CTLESC
         * characters have to be processed left to right.
         */
-#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
-#error "integers with more than 10 digits are not supported"
-#endif
-       CHECKSTRSPACE(12 - 2, expdest);
+       CHECKSTRSPACE(DIGITS(result) - 2, expdest);
        USTPUTC('\0', expdest);
        start = stackblock();
        p = expdest - 2;
@@ -398,8 +437,10 @@ expari(int flag)
        removerecordregions(begoff);
        if (quotes)
                rmescapes(p+2);
+       q = grabstackstr(expdest);
        result = arith(p+2);
-       fmtstr(p, 12, "%d", result);
+       ungrabstackstr(q, expdest);
+       fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result);
        while (*p++)
                ;
        if (quoted == 0)
@@ -410,10 +451,9 @@ expari(int flag)
 
 
 /*
- * Expand stuff in backwards quotes.
+ * Perform command substitution.
  */
-
-STATIC void
+static void
 expbackq(union node *cmd, int quoted, int flag)
 {
        struct backcmd in;
@@ -426,7 +466,6 @@ expbackq(union node *cmd, int quoted, int flag)
        char lastc;
        int startloc = dest - stackblock();
        char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
-       int saveherefd;
        int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
        int nnl;
 
@@ -434,15 +473,12 @@ expbackq(union node *cmd, int quoted, int flag)
        saveifs = ifsfirst;
        savelastp = ifslastp;
        saveargbackq = argbackq;
-       saveherefd = herefd;
-       herefd = -1;
        p = grabstackstr(dest);
        evalbackcmd(cmd, &in);
        ungrabstackstr(p, dest);
        ifsfirst = saveifs;
        ifslastp = savelastp;
        argbackq = saveargbackq;
-       herefd = saveherefd;
 
        p = in.buf;
        lastc = '\0';
@@ -461,16 +497,17 @@ expbackq(union node *cmd, int quoted, int flag)
                }
                lastc = *p++;
                if (lastc != '\0') {
-                       if (quotes && syntax[(int)lastc] == CCTL)
-                               STPUTC(CTLESC, dest);
                        if (lastc == '\n') {
                                nnl++;
                        } else {
+                               CHECKSTRSPACE(nnl + 2, dest);
                                while (nnl > 0) {
                                        nnl--;
-                                       STPUTC('\n', dest);
+                                       USTPUTC('\n', dest);
                                }
-                               STPUTC(lastc, dest);
+                               if (quotes && syntax[(int)lastc] == CCTL)
+                                       USTPUTC(CTLESC, dest);
+                               USTPUTC(lastc, dest);
                        }
                }
        }
@@ -483,9 +520,9 @@ expbackq(union node *cmd, int quoted, int flag)
                exitstatus = waitforjob(in.jp, NULL);
        if (quoted == 0)
                recordregion(startloc, dest - stackblock(), 0);
-       TRACE(("evalbackq: size=%d: \"%.*s\"\n",
-               (dest - stackblock()) - startloc,
-               (dest - stackblock()) - startloc,
+       TRACE(("expbackq: size=%td: \"%.*s\"\n",
+               ((dest - stackblock()) - startloc),
+               (int)((dest - stackblock()) - startloc),
                stackblock() + startloc));
        expdest = dest;
        INTON;
@@ -493,22 +530,21 @@ expbackq(union node *cmd, int quoted, int flag)
 
 
 
-STATIC int
+static int
 subevalvar(char *p, char *str, int strloc, int subtype, int startloc,
-  int varflags)
+  int varflags, int quotes)
 {
        char *startp;
        char *loc = NULL;
        char *q;
        int c = 0;
-       int saveherefd = herefd;
        struct nodelist *saveargbackq = argbackq;
        int amount;
 
-       herefd = -1;
-       argstr(p, 0);
+       argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
+           subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
+           EXP_CASE : 0) | EXP_TILDE);
        STACKSTRNUL(expdest);
-       herefd = saveherefd;
        argbackq = saveargbackq;
        startp = stackblock() + startloc;
        if (str == NULL)
@@ -520,13 +556,11 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc,
                amount = startp - expdest;
                STADJUST(amount, expdest);
                varflags &= ~VSNUL;
-               if (c != 0)
-                       *loc = c;
                return 1;
 
        case VSQUESTION:
                if (*p != CTLENDVAR) {
-                       outfmt(&errout, "%s\n", startp);
+                       outfmt(out2, "%s\n", startp);
                        error(NULL);
                }
                error("%.*s: parameter %snot set", (int)(p - str - 1),
@@ -538,12 +572,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;
@@ -552,14 +586,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++;
@@ -571,14 +604,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++;
@@ -590,12 +622,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;
@@ -619,7 +651,7 @@ recordleft:
  * input string.
  */
 
-STATIC char *
+static char *
 evalvar(char *p, int flag)
 {
        int subtype;
@@ -662,7 +694,7 @@ again: /* jump here after setting a variable with ${var=text} */
        }
        varlen = 0;
        startloc = expdest - stackblock();
-       if (!set && uflag) {
+       if (!set && uflag && *var != '@' && *var != '*') {
                switch (subtype) {
                case VSNORMAL:
                case VSTRIMLEFT:
@@ -691,12 +723,10 @@ again: /* jump here after setting a variable with ${var=text} */
                                        varlen++;
                        }
                        else {
-                               while (*val) {
-                                       if (quotes &&
-                                           syntax[(int)*val] == CCTL)
-                                               STPUTC(CTLESC, expdest);
-                                       STPUTC(*val++, expdest);
-                               }
+                               if (quotes)
+                                       STPUTS_QUOTES(val, syntax, expdest);
+                               else
+                                       STPUTS(val, expdest);
 
                        }
                }
@@ -725,7 +755,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)
@@ -745,7 +776,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);
                }
@@ -756,7 +787,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
@@ -807,12 +839,12 @@ record:
  * Test whether a specialized variable is set.
  */
 
-STATIC int
+static int
 varisset(char *name, int nulok)
 {
 
        if (*name == '!')
-               return backgndpid != -1;
+               return backgndpidset();
        else if (*name == '@' || *name == '*') {
                if (*shellparam.p == NULL)
                        return 0;
@@ -843,13 +875,20 @@ varisset(char *name, int nulok)
        return 1;
 }
 
-
+static void
+strtodest(const char *p, int flag, int subtype, int quoted)
+{
+       if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
+               STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
+       else
+               STPUTS(p, expdest);
+}
 
 /*
  * Add the value of a specialized variable to the stack string.
  */
 
-STATIC void
+static void
 varvalue(char *name, int quoted, int subtype, int flag)
 {
        int num;
@@ -857,22 +896,6 @@ varvalue(char *name, int quoted, int subtype, int flag)
        int i;
        char sep;
        char **ap;
-       char const *syntax;
-
-#define STRTODEST(p) \
-       do {\
-       if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \
-               syntax = quoted? DQSYNTAX : BASESYNTAX; \
-               while (*p) { \
-                       if (syntax[(int)*p] == CCTL) \
-                               STPUTC(CTLESC, expdest); \
-                       STPUTC(*p++, expdest); \
-               } \
-       } else \
-               while (*p) \
-                       STPUTC(*p++, expdest); \
-       } while (0)
-
 
        switch (*name) {
        case '$':
@@ -885,7 +908,7 @@ varvalue(char *name, int quoted, int subtype, int flag)
                num = shellparam.nparam;
                goto numvar;
        case '!':
-               num = backgndpid;
+               num = backgndpidval();
 numvar:
                expdest = cvtnum(num, expdest);
                break;
@@ -898,7 +921,7 @@ numvar:
        case '@':
                if (flag & EXP_FULL && quoted) {
                        for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
-                               STRTODEST(p);
+                               strtodest(p, flag, subtype, quoted);
                                if (*ap)
                                        STPUTC('\0', expdest);
                        }
@@ -911,21 +934,21 @@ numvar:
                else
                        sep = ' ';
                for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
-                       STRTODEST(p);
+                       strtodest(p, flag, subtype, quoted);
                        if (*ap && sep)
                                STPUTC(sep, expdest);
                }
                break;
        case '0':
                p = arg0;
-               STRTODEST(p);
+               strtodest(p, flag, subtype, quoted);
                break;
        default:
                if (is_digit(*name)) {
                        num = atoi(name);
                        if (num > 0 && num <= shellparam.nparam) {
                                p = shellparam.p[num - 1];
-                               STRTODEST(p);
+                               strtodest(p, flag, subtype, quoted);
                        }
                }
                break;
@@ -939,14 +962,20 @@ numvar:
  * string for IFS characters.
  */
 
-STATIC void
-recordregion(int start, int end, int nulonly)
+static void
+recordregion(int start, int end, int inquotes)
 {
        struct ifsregion *ifsp;
 
        if (ifslastp == NULL) {
                ifsp = &ifsfirst;
        } else {
+               if (ifslastp->endoff == start
+                   && ifslastp->inquotes == inquotes) {
+                       /* extend previous area */
+                       ifslastp->endoff = end;
+                       return;
+               }
                ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
                ifslastp->next = ifsp;
        }
@@ -954,7 +983,7 @@ recordregion(int start, int end, int nulonly)
        ifslastp->next = NULL;
        ifslastp->begoff = start;
        ifslastp->endoff = end;
-       ifslastp->nulonly = nulonly;
+       ifslastp->inquotes = inquotes;
 }
 
 
@@ -963,8 +992,14 @@ recordregion(int start, int end, int nulonly)
  * Break the argument string into pieces based upon IFS and add the
  * strings to the argument list.  The regions of the string to be
  * searched for IFS characters have been stored by recordregion.
+ * CTLESC characters are preserved but have little effect in this pass
+ * other than escaping CTL* characters.  In particular, they do not escape
+ * IFS characters: that should be done with the ifsregion mechanism.
+ * CTLQUOTEMARK characters are used to preserve empty quoted strings.
+ * This pass treats them as a regular character, making the string non-empty.
+ * Later, they are removed along with the other CTL* characters.
  */
-STATIC void
+static void
 ifsbreakup(char *string, struct arglist *arglist)
 {
        struct ifsregion *ifsp;
@@ -973,75 +1008,89 @@ ifsbreakup(char *string, struct arglist *arglist)
        char *p;
        char *q;
        const char *ifs;
-       int ifsspc;
-       int nulonly;
-
+       const char *ifsspc;
+       int had_param_ch = 0;
 
        start = string;
-       ifsspc = 0;
-       nulonly = 0;
-       if (ifslastp != NULL) {
-               ifsp = &ifsfirst;
-               do {
-                       p = string + ifsp->begoff;
-                       nulonly = ifsp->nulonly;
-                       ifs = nulonly ? nullstr :
-                               ( ifsset() ? ifsval() : " \t\n" );
-                       ifsspc = 0;
-                       while (p < string + ifsp->endoff) {
-                               q = p;
-                               if (*p == CTLESC)
+
+       if (ifslastp == NULL) {
+               /* Return entire argument, IFS doesn't apply to any of it */
+               sp = (struct strlist *)stalloc(sizeof *sp);
+               sp->text = start;
+               *arglist->lastp = sp;
+               arglist->lastp = &sp->next;
+               return;
+       }
+
+       ifs = ifsset() ? ifsval() : " \t\n";
+
+       for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
+               p = string + ifsp->begoff;
+               while (p < string + ifsp->endoff) {
+                       q = p;
+                       if (*p == CTLESC)
+                               p++;
+                       if (ifsp->inquotes) {
+                               /* Only NULs (should be from "$@") end args */
+                               had_param_ch = 1;
+                               if (*p != 0) {
                                        p++;
-                               if (strchr(ifs, *p)) {
-                                       if (!nulonly)
-                                               ifsspc = (strchr(" \t\n", *p) != NULL);
-                                       /* Ignore IFS whitespace at start */
-                                       if (q == start && ifsspc) {
-                                               p++;
-                                               start = p;
-                                               continue;
-                                       }
-                                       *q = '\0';
-                                       sp = (struct strlist *)stalloc(sizeof *sp);
-                                       sp->text = start;
-                                       *arglist->lastp = sp;
-                                       arglist->lastp = &sp->next;
+                                       continue;
+                               }
+                               ifsspc = NULL;
+                       } else {
+                               if (!strchr(ifs, *p)) {
+                                       had_param_ch = 1;
                                        p++;
-                                       if (!nulonly) {
-                                               for (;;) {
-                                                       if (p >= string + ifsp->endoff) {
-                                                               break;
-                                                       }
-                                                       q = p;
-                                                       if (*p == CTLESC)
-                                                               p++;
-                                                       if (strchr(ifs, *p) == NULL ) {
-                                                               p = q;
-                                                               break;
-                                                       } else if (strchr(" \t\n",*p) == NULL) {
-                                                               if (ifsspc) {
-                                                                       p++;
-                                                                       ifsspc = 0;
-                                                               } else {
-                                                                       p = q;
-                                                                       break;
-                                                               }
-                                                       } else
-                                                               p++;
-                                               }
-                                       }
-                                       start = p;
-                               } else
+                                       continue;
+                               }
+                               ifsspc = strchr(" \t\n", *p);
+
+                               /* Ignore IFS whitespace at start */
+                               if (q == start && ifsspc != NULL) {
                                        p++;
+                                       start = p;
+                                       continue;
+                               }
+                               had_param_ch = 0;
                        }
-               } while ((ifsp = ifsp->next) != NULL);
-               if (*start || (!ifsspc && start > string)) {
+
+                       /* Save this argument... */
+                       *q = '\0';
                        sp = (struct strlist *)stalloc(sizeof *sp);
                        sp->text = start;
                        *arglist->lastp = sp;
                        arglist->lastp = &sp->next;
+                       p++;
+
+                       if (ifsspc != NULL) {
+                               /* Ignore further trailing IFS whitespace */
+                               for (; p < string + ifsp->endoff; p++) {
+                                       q = p;
+                                       if (*p == CTLESC)
+                                               p++;
+                                       if (strchr(ifs, *p) == NULL) {
+                                               p = q;
+                                               break;
+                                       }
+                                       if (strchr(" \t\n", *p) == NULL) {
+                                               p++;
+                                               break;
+                                       }
+                               }
+                       }
+                       start = p;
                }
-       } else {
+       }
+
+       /*
+        * Save anything left as an argument.
+        * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
+        * generating 2 arguments, the second of which is empty.
+        * Some recent clarification of the Posix spec say that it
+        * should only generate one....
+        */
+       if (had_param_ch || *start != 0) {
                sp = (struct strlist *)stalloc(sizeof *sp);
                sp->text = start;
                *arglist->lastp = sp;
@@ -1050,16 +1099,15 @@ ifsbreakup(char *string, struct arglist *arglist)
 }
 
 
+static char expdir[PATH_MAX];
+#define expdir_end (expdir + sizeof(expdir))
 
 /*
- * Expand shell metacharacters.  At this point, the only control characters
- * should be escapes.  The results are stored in the list exparg.
+ * Perform pathname generation and remove control characters.
+ * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
+ * The results are stored in the list exparg.
  */
-
-STATIC char *expdir;
-
-
-STATIC void
+static void
 expandmeta(struct strlist *str, int flag __unused)
 {
        char *p;
@@ -1075,19 +1123,12 @@ expandmeta(struct strlist *str, int flag __unused)
                for (;;) {                      /* fast check for meta chars */
                        if ((c = *p++) == '\0')
                                goto nometa;
-                       if (c == '*' || c == '?' || c == '[' || c == '!')
+                       if (c == '*' || c == '?' || c == '[')
                                break;
                }
                savelastp = exparg.lastp;
                INTOFF;
-               if (expdir == NULL) {
-                       int i = strlen(str->text);
-                       expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
-               }
-
                expmeta(expdir, str->text);
-               ckfree(expdir);
-               expdir = NULL;
                INTON;
                if (exparg.lastp == savelastp) {
                        /*
@@ -1113,11 +1154,11 @@ nometa:
  * Do metacharacter (i.e. *, ?, [...]) expansion.
  */
 
-STATIC void
+static void
 expmeta(char *enddir, char *name)
 {
        char *p;
-       const char *q;
+       char *q;
        char *start;
        char *endname;
        int metaflag;
@@ -1126,10 +1167,11 @@ expmeta(char *enddir, char *name)
        struct dirent *dp;
        int atend;
        int matchdot;
+       int esc;
 
        metaflag = 0;
        start = name;
-       for (p = name ; ; p++) {
+       for (p = name; esc = 0, *p; p += esc + 1) {
                if (*p == '*' || *p == '?')
                        metaflag = 1;
                else if (*p == '[') {
@@ -1148,18 +1190,18 @@ expmeta(char *enddir, char *name)
                                        break;
                                }
                        }
-               } else if (*p == '!' && p[1] == '!'     && (p == name || p[-1] == '/')) {
-                       metaflag = 1;
                } else if (*p == '\0')
                        break;
                else if (*p == CTLQUOTEMARK)
                        continue;
-               else if (*p == CTLESC)
-                       p++;
-               if (*p == '/') {
-                       if (metaflag)
-                               break;
-                       start = p + 1;
+               else {
+                       if (*p == CTLESC)
+                               esc++;
+                       if (p[esc] == '/') {
+                               if (metaflag)
+                                       break;
+                               start = p + esc + 1;
+                       }
                }
        }
        if (metaflag == 0) {    /* we've reached the end of the file name */
@@ -1173,6 +1215,8 @@ expmeta(char *enddir, char *name)
                        *enddir++ = *p;
                        if (*p == '\0')
                                break;
+                       if (enddir == expdir_end)
+                               return;
                }
                if (metaflag == 0 || lstat(expdir, &statb) >= 0)
                        addfname(expdir);
@@ -1187,17 +1231,19 @@ expmeta(char *enddir, char *name)
                        if (*p == CTLESC)
                                p++;
                        *enddir++ = *p++;
+                       if (enddir == expdir_end)
+                               return;
                }
        }
        if (enddir == expdir) {
-               q = ".";
+               p = __DECONST(char *, ".");
        } else if (enddir == expdir + 1 && *expdir == '/') {
-               q = "/";
+               p = __DECONST(char *, "/");
        } else {
-               q = expdir;
+               p = expdir;
                enddir[-1] = '\0';
        }
-       if ((dirp = opendir(q)) == NULL)
+       if ((dirp = opendir(p)) == NULL)
                return;
        if (enddir != expdir)
                enddir[-1] = '/';
@@ -1205,7 +1251,8 @@ expmeta(char *enddir, char *name)
                atend = 1;
        } else {
                atend = 0;
-               *endname++ = '\0';
+               *endname = '\0';
+               endname += esc + 1;
        }
        matchdot = 0;
        p = start;
@@ -1219,22 +1266,23 @@ expmeta(char *enddir, char *name)
                if (dp->d_name[0] == '.' && ! matchdot)
                        continue;
                if (patmatch(start, dp->d_name, 0)) {
-                       if (atend) {
-                               scopy(dp->d_name, enddir);
+                       if (enddir + dp->d_namlen + 1 > expdir_end)
+                               continue;
+                       memcpy(enddir, dp->d_name, dp->d_namlen + 1);
+                       if (atend)
                                addfname(expdir);
-                       } else {
-                               char *t;
-                               for (t = enddir, q = dp->d_name;
-                                    (*t++ = *q++) != '\0';)
+                       else {
+                               if (enddir + dp->d_namlen + 2 > expdir_end)
                                        continue;
-                               t[-1] = '/';
-                               expmeta(t, endname);
+                               enddir[dp->d_namlen] = '/';
+                               enddir[dp->d_namlen + 1] = '\0';
+                               expmeta(enddir + dp->d_namlen + 1, endname);
                        }
                }
        }
        closedir(dirp);
        if (! atend)
-               endname[-1] = '/';
+               endname[-esc - 1] = esc ? CTLESC : '/';
 }
 
 
@@ -1242,7 +1290,7 @@ expmeta(char *enddir, char *name)
  * Add a file name to the list.
  */
 
-STATIC void
+static void
 addfname(char *name)
 {
        char *p;
@@ -1263,7 +1311,7 @@ addfname(char *name)
  * work.
  */
 
-STATIC struct strlist *
+static struct strlist *
 expsort(struct strlist *str)
 {
        int len;
@@ -1276,7 +1324,7 @@ expsort(struct strlist *str)
 }
 
 
-STATIC struct strlist *
+static struct strlist *
 msort(struct strlist *list, int len)
 {
        struct strlist *p, *q = NULL;
@@ -1323,21 +1371,9 @@ msort(struct strlist *list, int len)
  */
 
 int
-patmatch(char *pattern, char *string, int squoted)
-{
-#ifdef notdef
-       if (pattern[0] == '!' && pattern[1] == '!')
-               return 1 - pmatch(pattern + 2, string);
-       else
-#endif
-               return pmatch(pattern, string, squoted);
-}
-
-
-STATIC int
-pmatch(char *pattern, char *string, int squoted)
+patmatch(const char *pattern, const char *string, int squoted)
 {
-       char *p, *q;
+       const char *p, *q;
        char c;
 
        p = pattern;
@@ -1378,14 +1414,14 @@ pmatch(char *pattern, char *string, int squoted)
                                }
                        }
                        do {
-                               if (pmatch(p, q, squoted))
+                               if (patmatch(p, q, squoted))
                                        return 1;
                                if (squoted && *q == CTLESC)
                                        q++;
                        } while (*q++ != '\0');
                        return 0;
                case '[': {
-                       char *endp;
+                       const char *endp;
                        int invert, found;
                        char chr;
 
@@ -1456,7 +1492,7 @@ breakloop:
 
 
 /*
- * Remove any CTLESC characters from a string.
+ * Remove any CTLESC and CTLQUOTEMARK characters from a string.
  */
 
 void
@@ -1465,13 +1501,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;
                }
@@ -1489,7 +1525,7 @@ rmescapes(char *str)
  */
 
 int
-casematch(union node *pattern, char *val)
+casematch(union node *pattern, const char *val)
 {
        struct stackmark smark;
        int result;
@@ -1511,7 +1547,7 @@ casematch(union node *pattern, char *val)
  * Our own itoa().
  */
 
-STATIC char *
+static char *
 cvtnum(int num, char *buf)
 {
        char temp[32];
@@ -1527,12 +1563,83 @@ cvtnum(int num, char *buf)
        if (neg)
                *--p = '-';
 
-       while (*p)
-               STPUTC(*p++, buf);
+       STPUTS(p, buf);
        return buf;
 }
 
 /*
+ * Check statically if expanding a string may have side effects.
+ */
+int
+expandhassideeffects(const char *p)
+{
+       int c;
+       int arinest;
+
+       arinest = 0;
+       while ((c = *p++) != '\0') {
+               switch (c) {
+               case CTLESC:
+                       p++;
+                       break;
+               case CTLVAR:
+                       c = *p++;
+                       /* Expanding $! sets the job to remembered. */
+                       if (*p == '!')
+                               return 1;
+                       if ((c & VSTYPE) == VSASSIGN)
+                               return 1;
+                       /*
+                        * If we are in arithmetic, the parameter may contain
+                        * '=' which may cause side effects. Exceptions are
+                        * the length of a parameter and $$, $# and $? which
+                        * are always numeric.
+                        */
+                       if ((c & VSTYPE) == VSLENGTH) {
+                               while (*p != '=')
+                                       p++;
+                               p++;
+                               break;
+                       }
+                       if ((*p == '$' || *p == '#' || *p == '?') &&
+                           p[1] == '=') {
+                               p += 2;
+                               break;
+                       }
+                       if (arinest > 0)
+                               return 1;
+                       break;
+               case CTLBACKQ:
+               case CTLBACKQ | CTLQUOTE:
+                       if (arinest > 0)
+                               return 1;
+                       break;
+               case CTLARI:
+                       arinest++;
+                       break;
+               case CTLENDARI:
+                       arinest--;
+                       break;
+               case '=':
+                       if (*p == '=') {
+                               /* Allow '==' operator. */
+                               p++;
+                               continue;
+                       }
+                       if (arinest > 0)
+                               return 1;
+                       break;
+               case '!': case '<': case '>':
+                       /* Allow '!=', '<=', '>=' operators. */
+                       if (*p == '=')
+                               p++;
+                       break;
+               }
+       }
+       return 0;
+}
+
+/*
  * Do most of the work for wordexp(3).
  */
 
@@ -1546,9 +1653,7 @@ wordexpcmd(int argc, char **argv)
        for (i = 1, len = 0; i < argc; i++)
                len += strlen(argv[i]);
        out1fmt("%08x", (int)len);
-       for (i = 1; i < argc; i++) {
-               out1str(argv[i]);
-               out1c('\0');
-       }
+       for (i = 1; i < argc; i++)
+               outbin(argv[i], strlen(argv[i]) + 1, out1);
         return (0);
 }
index 1f5f28b..3de59b5 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)expand.h    8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/expand.h,v 1.12 2004/04/06 20:06:51 markm Exp $
- * $DragonFly: src/bin/sh/expand.h,v 1.3 2007/01/07 08:26:55 pavalos Exp $
+ * $FreeBSD: src/bin/sh/expand.h,v 1.15 2010/12/28 21:27:08 jilles Exp $
  */
 
 struct strlist {
@@ -57,13 +56,16 @@ 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;
 void expandhere(union node *, int);
 void expandarg(union node *, struct arglist *, int);
 void expari(int);
-int patmatch(char *, char *, int);
+int patmatch(const char *, const char *, int);
 void rmescapes(char *);
-int casematch(union node *, char *);
+int casematch(union node *, const char *);
+int expandhassideeffects(const char *);
 int wordexpcmd(int, char **);
index b90a07c..8876fd2 100644 (file)
@@ -1,3 +1,6 @@
+#!/bin/sh
+
+#-
 # Copyright (c) 1991, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -33,8 +36,7 @@
 # SUCH DAMAGE.
 #
 #      @(#)cmv 8.2 (Berkeley) 5/4/95
-# $FreeBSD: src/bin/sh/funcs/cmv,v 1.6 1999/08/27 23:15:33 peter Exp $
-# $DragonFly: src/bin/sh/funcs/cmv,v 1.2 2003/06/17 04:22:50 dillon Exp $
+# $FreeBSD: src/bin/sh/funcs/cmv,v 1.8 2005/01/10 08:39:26 imp Exp $
 
 # Conditional move--don't replace an existing file.
 
index 48ee798..2b7188e 100644 (file)
@@ -1,3 +1,6 @@
+#!/bin/sh
+
+#-
 # Copyright (c) 1991, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -33,8 +36,7 @@
 # SUCH DAMAGE.
 #
 #      @(#)dirs        8.2 (Berkeley) 5/4/95
-# $FreeBSD: src/bin/sh/funcs/dirs,v 1.6 1999/08/27 23:15:34 peter Exp $
-# $DragonFly: src/bin/sh/funcs/dirs,v 1.2 2003/06/17 04:22:50 dillon Exp $
+# $FreeBSD: src/bin/sh/funcs/dirs,v 1.8 2005/01/10 08:39:26 imp Exp $
 
 # pushd, popd, and dirs --- written by Chris Bertin
 # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
index 3d0be99..56232f7 100644 (file)
@@ -1,3 +1,6 @@
+#!/bin/sh
+
+#-
 # Copyright (c) 1991, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -33,8 +36,7 @@
 # SUCH DAMAGE.
 #
 #      @(#)kill        8.2 (Berkeley) 5/4/95
-# $FreeBSD: src/bin/sh/funcs/kill,v 1.6 1999/08/27 23:15:34 peter Exp $
-# $DragonFly: src/bin/sh/funcs/kill,v 1.2 2003/06/17 04:22:50 dillon Exp $
+# $FreeBSD: src/bin/sh/funcs/kill,v 1.8 2005/01/10 08:39:26 imp Exp $
 
 # Convert job names to process ids and then run /bin/kill.
 
index a5f31a2..bf1a182 100644 (file)
@@ -1,3 +1,6 @@
+#!/bin/sh
+
+#-
 # Copyright (c) 1991, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -33,8 +36,7 @@
 # SUCH DAMAGE.
 #
 #      @(#)login       8.2 (Berkeley) 5/4/95
-# $FreeBSD: src/bin/sh/funcs/login,v 1.6 1999/08/27 23:15:34 peter Exp $
-# $DragonFly: src/bin/sh/funcs/login,v 1.2 2003/06/17 04:22:50 dillon Exp $
+# $FreeBSD: src/bin/sh/funcs/login,v 1.8 2005/01/10 08:39:26 imp Exp $
 
 # replaces the login builtin in the BSD shell
 login () exec login "$@"
index d4da8d9..1f80c02 100644 (file)
@@ -1,3 +1,6 @@
+#!/bin/sh
+
+#-
 # Copyright (c) 1991, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -33,7 +36,6 @@
 # SUCH DAMAGE.
 #
 #      @(#)newgrp      8.2 (Berkeley) 5/4/95
-# $FreeBSD: src/bin/sh/funcs/newgrp,v 1.6 1999/08/27 23:15:34 peter Exp $
-# $DragonFly: src/bin/sh/funcs/newgrp,v 1.2 2003/06/17 04:22:50 dillon Exp $
+# $FreeBSD: src/bin/sh/funcs/newgrp,v 1.8 2005/01/10 08:39:26 imp Exp $
 
 newgrp() exec newgrp "$@"
index 945c713..08f4a28 100644 (file)
@@ -1,3 +1,6 @@
+#!/bin/sh
+
+#-
 # Copyright (c) 1991, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -33,8 +36,7 @@
 # SUCH DAMAGE.
 #
 #      @(#)popd        8.2 (Berkeley) 5/4/95
-# $FreeBSD: src/bin/sh/funcs/popd,v 1.6 1999/08/27 23:15:35 peter Exp $
-# $DragonFly: src/bin/sh/funcs/popd,v 1.2 2003/06/17 04:22:50 dillon Exp $
+# $FreeBSD: src/bin/sh/funcs/popd,v 1.8 2005/01/10 08:39:26 imp Exp $
 
 # pushd, popd, and dirs --- written by Chris Bertin
 # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
index 75d1f33..3df956e 100644 (file)
@@ -1,3 +1,6 @@
+#!/bin/sh
+
+#-
 # Copyright (c) 1991, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -33,8 +36,7 @@
 # SUCH DAMAGE.
 #
 #      @(#)pushd       8.2 (Berkeley) 5/4/95
-# $FreeBSD: src/bin/sh/funcs/pushd,v 1.6 1999/08/27 23:15:35 peter Exp $
-# $DragonFly: src/bin/sh/funcs/pushd,v 1.2 2003/06/17 04:22:50 dillon Exp $
+# $FreeBSD: src/bin/sh/funcs/pushd,v 1.8 2005/01/10 08:39:26 imp Exp $
 
 # pushd, popd, and dirs --- written by Chris Bertin
 # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
index a6b7a9c..f023ada 100644 (file)
@@ -1,3 +1,4 @@
+#-
 # Copyright (c) 1991, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
 # SUCH DAMAGE.
 #
 #      @(#)suspend     8.2 (Berkeley) 5/4/95
-# $FreeBSD: src/bin/sh/funcs/suspend,v 1.6 1999/08/27 23:15:35 peter Exp $
-# $DragonFly: src/bin/sh/funcs/suspend,v 1.2 2003/06/17 04:22:50 dillon Exp $
+# $FreeBSD: src/bin/sh/funcs/suspend,v 1.9 2010/11/13 22:20:46 jilles Exp $
 
 suspend() {
        local -
-       set +j
+       set +m
        kill -TSTP 0
 }
index f7dc3c6..f51e91a 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)histedit.c      8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/histedit.c,v 1.29 2006/08/04 07:56:31 yar Exp $
- * $DragonFly: src/bin/sh/histedit.c,v 1.12 2008/02/13 15:13:37 matthias Exp $
+ * $FreeBSD: src/bin/sh/histedit.c,v 1.42 2010/12/29 19:39:51 jilles Exp $
  */
 
 #include <sys/param.h>
@@ -68,7 +67,7 @@ EditLine *el; /* editline cookie */
 int displayhist;
 static FILE *el_in, *el_out, *el_err;
 
-STATIC char *fc_replace(const char *, char *, char *);
+static char *fc_replace(const char *, char *, char *);
 
 /*
  * Set history and editing status.  Called whenever the status may
@@ -92,13 +91,13 @@ histedit(void)
                        if (hist != NULL)
                                sethistsize(histsizeval());
                        else
-                               out2str("sh: can't initialize history\n");
+                               out2fmt_flush("sh: can't initialize history\n");
                }
                if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
                        /*
                         * turn editing on
                         */
-                       char *term, *shname;
+                       char *term;
 
                        INTOFF;
                        if (el_in == NULL)
@@ -116,9 +115,6 @@ histedit(void)
                        }
                        else
                                unsetenv("TERM");
-                       shname = arg0;
-                       if (shname[0] == '-')
-                               shname++;
                        el = el_init(arg0, el_in, el_out, el_err);
                        if (el != NULL) {
                                if (hist)
@@ -129,7 +125,7 @@ histedit(void)
                                    _el_fn_complete);
                        } else {
 bad:
-                               out2str("sh: can't initialize editing\n");
+                               out2fmt_flush("sh: can't initialize editing\n");
                        }
                        INTON;
                } else if (!editing && el) {
@@ -177,6 +173,13 @@ sethistsize(const char *hs)
        }
 }
 
+void
+setterm(const char *term)
+{
+       if (rootshell && el != NULL && term != NULL)
+               el_set(el, EL_TERMINAL, term);
+}
+
 int
 histcmd(int argc, char **argv)
 {
@@ -190,8 +193,9 @@ histcmd(int argc, char **argv)
        char *pat = NULL, *repl = NULL;
        static int active = 0;
        struct jmploc jmploc;
-       struct jmploc *volatile savehandler;
-       char editfile[PATH_MAX];
+       struct jmploc *savehandler;
+       char editfilestr[PATH_MAX];
+       char *volatile editfile;
        FILE *efp = NULL;
        int oldhistnum;
 
@@ -229,24 +233,24 @@ histcmd(int argc, char **argv)
                }
        argc -= optind, argv += optind;
 
+       savehandler = handler;
        /*
         * If executing...
         */
        if (lflg == 0 || editor || sflg) {
                lflg = 0;       /* ignore */
-               editfile[0] = '\0';
+               editfile = NULL;
                /*
                 * Catch interrupts to reset active counter and
                 * cleanup temp files.
                 */
                if (setjmp(jmploc.loc)) {
                        active = 0;
-                       if (*editfile)
+                       if (editfile)
                                unlink(editfile);
                        handler = savehandler;
                        longjmp(handler->loc, 1);
                }
-               savehandler = handler;
                handler = &jmploc;
                if (++active > MAXHISTLOOPS) {
                        active = 0;
@@ -294,7 +298,7 @@ histcmd(int argc, char **argv)
                laststr = argv[1];
                break;
        default:
-               error("too many args");
+               error("too many arguments");
        }
        /*
         * Turn into event numbers.
@@ -320,12 +324,13 @@ histcmd(int argc, char **argv)
        if (editor) {
                int fd;
                INTOFF;         /* easier */
-               sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP);
-               if ((fd = mkstemp(editfile)) < 0)
+               sprintf(editfilestr, "%s/_shXXXXXX", _PATH_TMP);
+               if ((fd = mkstemp(editfilestr)) < 0)
                        error("can't create temporary file %s", editfile);
+               editfile = editfilestr;
                if ((efp = fdopen(fd, "w")) == NULL) {
                        close(fd);
-                       error("can't allocate stdio buffer for temp");
+                       error("Out of space");
                }
        }
 
@@ -345,14 +350,16 @@ histcmd(int argc, char **argv)
                                out1fmt("%5d ", he.num);
                        out1str(he.str);
                } else {
-                       const char *s = pat ?
-                          fc_replace(he.str, pat, repl) : he.str;
+                       char *s = pat ?
+                          fc_replace(he.str, pat, repl) :
+                          __DECONST(char *, he.str);
 
                        if (sflg) {
                                if (displayhist) {
                                        out2str(s);
+                                       flushout(out2);
                                }
-                               evalstring(strcpy(stalloc(strlen(s) + 1), s));
+                               evalstring(s, 0);
                                if (displayhist && hist) {
                                        /*
                                         *  XXX what about recursive and
@@ -384,7 +391,7 @@ histcmd(int argc, char **argv)
                fclose(efp);
                editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
                sprintf(editcmd, "%s %s", editor, editfile);
-               evalstring(editcmd);    /* XXX - should use no JC command */
+               evalstring(editcmd, 0); /* XXX - should use no JC command */
                INTON;
                readcmdfile(editfile);  /* XXX - should read back - quick tst */
                unlink(editfile);
@@ -394,10 +401,11 @@ histcmd(int argc, char **argv)
                --active;
        if (displayhist)
                displayhist = 0;
+       handler = savehandler;
        return 0;
 }
 
-STATIC char *
+static char *
 fc_replace(const char *s, char *p, char *r)
 {
        char *dest;
@@ -406,21 +414,20 @@ fc_replace(const char *s, char *p, char *r)
        STARTSTACKSTR(dest);
        while (*s) {
                if (*s == *p && strncmp(s, p, plen) == 0) {
-                       while (*r)
-                               STPUTC(*r++, dest);
+                       STPUTS(r, dest);
                        s += plen;
                        *p = '\0';      /* so no more matches */
                } else
                        STPUTC(*s++, dest);
        }
-       STACKSTRNUL(dest);
+       STPUTC('\0', dest);
        dest = grabstackstr(dest);
 
        return (dest);
 }
 
 int
-not_fcnumber(char *s)
+not_fcnumber(const char *s)
 {
        if (s == NULL)
                return (0);
index 318dafc..e9f058c 100644 (file)
  * SUCH DAMAGE.
  *
  *     @(#)init.h      8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/init.h,v 1.6.2.1 2002/07/19 04:38:51 tjr Exp $
- * $DragonFly: src/bin/sh/init.h,v 1.2 2003/06/17 04:22:50 dillon Exp $
+ * $FreeBSD: src/bin/sh/init.h,v 1.9 2011/02/04 22:47:55 jilles Exp $
  */
 
-void init(void);
-void reset(void);
-void initshellproc(void);
+void   init(void);
+void   reset(void);
index 7b67a61..656bc1f 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)input.c 8.3 (Berkeley) 6/9/95
- * $FreeBSD: src/bin/sh/input.c,v 1.23 2006/04/29 10:29:10 stefanf Exp $
- * $DragonFly: src/bin/sh/input.c,v 1.8 2007/01/13 20:10:26 pavalos Exp $
+ * $FreeBSD: src/bin/sh/input.c,v 1.29 2009/12/27 18:04:05 jilles Exp $
  */
 
 #include <stdio.h>     /* defines BUFSIZ */
@@ -98,13 +97,13 @@ MKINIT int parselleft;              /* copy of parsefile->lleft */
 char *parsenextc;              /* copy of parsefile->nextc */
 MKINIT struct parsefile basepf;        /* top level input file */
 MKINIT char basebuf[BUFSIZ];   /* buffer for top level input file */
-STATIC struct parsefile *parsefile = &basepf;  /* current input file */
+static struct parsefile *parsefile = &basepf;  /* current input file */
 int init_editline = 0;         /* editline library initialized? */
 int whichprompt;               /* 1 == PS1, 2 == PS2 */
 
 EditLine *el;                  /* cookie for editline package */
 
-STATIC void pushfile(void);
+static void pushfile(void);
 static int preadfd(void);
 
 #ifdef mkinit
@@ -117,13 +116,8 @@ INIT {
 }
 
 RESET {
-       if (exception != EXSHELLPROC)
-               parselleft = parsenleft = 0;    /* clear input buffer */
-       popallfiles();
-}
-
-SHELLPROC {
        popallfiles();
+       parselleft = parsenleft = 0;    /* clear input buffer */
 }
 #endif
 
@@ -214,7 +208,7 @@ retry:
                                 if (flags >= 0 && flags & O_NONBLOCK) {
                                         flags &=~ O_NONBLOCK;
                                         if (fcntl(0, F_SETFL, flags) >= 0) {
-                                               out2str("sh: turning off NDELAY mode\n");
+                                               out2fmt_flush("sh: turning off NDELAY mode\n");
                                                 goto retry;
                                         }
                                 }
@@ -303,7 +297,8 @@ check:
        if (parsefile->fd == 0 && hist && something) {
                HistEvent he;
                INTOFF;
-               history(hist, &he, whichprompt == 1 ? H_ENTER : H_APPEND, parsenextc);
+               history(hist, &he, whichprompt == 1 ? H_ENTER : H_ADD,
+                   parsenextc);
                INTON;
        }
 #endif
@@ -319,6 +314,23 @@ check:
 }
 
 /*
+ * Returns if we are certain we are at EOF. Does not cause any more input
+ * to be read from the outside world.
+ */
+
+int
+preadateof(void)
+{
+       if (parsenleft > 0)
+               return 0;
+       if (parsefile->strpush)
+               return 0;
+       if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+               return 1;
+       return 0;
+}
+
+/*
  * Undo the last call to pgetc.  Only one character may be pushed back.
  * PEOF may be pushed back.
  */
@@ -340,7 +352,7 @@ pushstring(char *s, int len, void *ap)
        struct strpush *sp;
 
        INTOFF;
-/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
+/*out2fmt_flush("*** calling pushstring: %s, %d\n", s, len);*/
        if (parsefile->strpush) {
                sp = ckmalloc(sizeof (struct strpush));
                sp->prev = parsefile->strpush;
@@ -367,7 +379,7 @@ popstring(void)
        parsenextc = sp->prevstring;
        parsenleft = sp->prevnleft;
        parselleft = sp->prevlleft;
-/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
+/*out2fmt_flush("*** calling popstring: restoring to '%s'\n", parsenextc);*/
        if (sp->ap)
                sp->ap->flag &= ~ALIASINUSE;
        parsefile->strpush = sp->prev;
@@ -449,7 +461,7 @@ setinputstring(char *string, int push)
  * adds a new entry to the stack and popfile restores the previous level.
  */
 
-STATIC void
+static void
 pushfile(void)
 {
        struct parsefile *pf;
@@ -490,6 +502,32 @@ popfile(void)
 
 
 /*
+ * Return current file (to go back to it later using popfilesupto()).
+ */
+
+struct parsefile *
+getcurrentfile(void)
+{
+       return parsefile;
+}
+
+
+/*
+ * Pop files until the given file is on top again. Useful for regular
+ * builtins that read shell commands from files or strings.
+ * If the given file is not an active file, an error is raised.
+ */
+
+void
+popfilesupto(struct parsefile *file)
+{
+       while (parsefile != file && parsefile != &basepf)
+               popfile();
+       if (parsefile != file)
+               error("popfilesupto() misused");
+}
+
+/*
  * Return to top level.
  */
 
index 948eec8..d0deb33 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)input.h     8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/input.h,v 1.7.2.1 2002/07/19 04:38:51 tjr Exp $
- * $DragonFly: src/bin/sh/input.h,v 1.3 2004/03/19 18:39:41 cpressey Exp $
+ * $FreeBSD: src/bin/sh/input.h,v 1.12 2009/12/24 18:41:14 jilles Exp $
  */
 
 /* PEOF (the end of file marker) is defined in syntax.h */
@@ -50,9 +49,12 @@ extern int parsenleft;               /* number of characters left in input buffer */
 extern char *parsenextc;       /* next character in input buffer */
 extern int init_editline;      /* 0 == not setup, 1 == OK, -1 == failed */
 
+struct parsefile;
+
 char *pfgets(char *, int);
 int pgetc(void);
 int preadbuffer(void);
+int preadateof(void);
 void pungetc(void);
 void pushstring(char *, int, void *);
 void popstring(void);
@@ -60,6 +62,8 @@ void setinputfile(const char *, int);
 void setinputfd(int, int);
 void setinputstring(char *, int);
 void popfile(void);
+struct parsefile *getcurrentfile(void);
+void popfilesupto(struct parsefile *);
 void popallfiles(void);
 void closescript(void);
 
index 939ece5..b679bac 100644 (file)
  * SUCH DAMAGE.
  *
  * @(#)jobs.c  8.5 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/jobs.c,v 1.72 2006/10/07 16:51:16 stefanf Exp $
- * $DragonFly: src/bin/sh/jobs.c,v 1.9 2007/01/13 21:27:45 pavalos Exp $
+ * $FreeBSD: src/bin/sh/jobs.c,v 1.92 2011/02/04 22:47:55 jilles Exp $
  */
 
-#include <fcntl.h>
-#include <signal.h>
-#include <errno.h>
-#include <paths.h>
-#include <unistd.h>
-#include <stdlib.h>
+#include <sys/ioctl.h>
 #include <sys/param.h>
-#include <sys/wait.h>
-#include <sys/time.h>
 #include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <paths.h>
-#include <sys/ioctl.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
 
 #include "shell.h"
 #if JOBS
 #include "mystring.h"
 
 
-STATIC struct job *jobtab;     /* array of jobs */
-STATIC int njobs;              /* size of array */
+static struct job *jobtab;     /* array of jobs */
+static int njobs;              /* size of array */
 MKINIT pid_t backgndpid = -1;  /* pid of last background process */
+MKINIT struct job *bgjob = NULL; /* last background process */
 #if JOBS
-STATIC struct job *jobmru;     /* most recently used job list */
-STATIC pid_t initialpgrp;      /* pgrp of shell on invocation */
+static struct job *jobmru;     /* most recently used job list */
+static pid_t initialpgrp;      /* pgrp of shell on invocation */
 #endif
 int in_waitcmd = 0;            /* are we in waitcmd()? */
 int in_dowait = 0;             /* are we in dowait()? */
@@ -85,20 +85,22 @@ volatile sig_atomic_t breakwaitcmd = 0;     /* should wait be terminated? */
 static int ttyfd = -1;
 
 #if JOBS
-STATIC void restartjob(struct job *);
+static void restartjob(struct job *);
 #endif
-STATIC void freejob(struct job *);
-STATIC struct job *getjob(char *);
-STATIC pid_t dowait(int, struct job *);
-STATIC pid_t waitproc(int, int *);
-STATIC void cmdtxt(union node *);
-STATIC void cmdputs(const char *);
+static void freejob(struct job *);
+static struct job *getjob(char *);
+static pid_t dowait(int, struct job *);
+static pid_t waitproc(int, int *);
+static void checkzombies(void);
+static void cmdtxt(union node *);
+static void cmdputs(const char *);
 #if JOBS
-STATIC void setcurjob(struct job *);
-STATIC void deljob(struct job *);
-STATIC struct job *getcurjob(struct job *);
+static void setcurjob(struct job *);
+static void deljob(struct job *);
+static struct job *getcurjob(struct job *);
 #endif
-STATIC void showjob(struct job *, pid_t, int);
+static void printjobcmd(struct job *);
+static void showjob(struct job *, int);
 
 
 /*
@@ -146,14 +148,12 @@ setjobctl(int on)
                do { /* while we are in the background */
                        initialpgrp = tcgetpgrp(ttyfd);
                        if (initialpgrp < 0) {
-out:                           out2str("sh: can't access tty; job control turned off\n");
+out:                           out2fmt_flush("sh: can't access tty; job control turned off\n");
                                mflag = 0;
                                return;
                        }
-                       if (initialpgrp == -1)
-                               initialpgrp = getpgrp();
-                       else if (initialpgrp != getpgrp()) {
-                               killpg(0, SIGTTIN);
+                       if (initialpgrp != getpgrp()) {
+                               kill(0, SIGTTIN);
                                continue;
                        }
                } while (0);
@@ -176,21 +176,6 @@ out:                               out2str("sh: can't access tty; job control turned off\n");
 #endif
 
 
-#ifdef mkinit
-INCLUDE <sys/types.h>
-INCLUDE <stdlib.h>
-
-SHELLPROC {
-       backgndpid = -1;
-#if JOBS
-       jobctl = 0;
-#endif
-}
-
-#endif
-
-
-
 #if JOBS
 int
 fgcmd(int argc __unused, char **argv)
@@ -202,8 +187,7 @@ fgcmd(int argc __unused, char **argv)
        jp = getjob(argv[1]);
        if (jp->jobctl == 0)
                error("job not created under job control");
-       out1str(jp->ps[0].cmd);
-       out1c('\n');
+       printjobcmd(jp);
        flushout(&output);
        pgrp = jp->ps[0].pid;
        tcsetpgrp(ttyfd, pgrp);
@@ -219,7 +203,6 @@ fgcmd(int argc __unused, char **argv)
 int
 bgcmd(int argc, char **argv)
 {
-       char s[64];
        struct job *jp;
 
        do {
@@ -230,16 +213,14 @@ bgcmd(int argc, char **argv)
                        continue;
                restartjob(jp);
                jp->foreground = 0;
-               fmtstr(s, 64, "[%td] ", jp - jobtab + 1);
-               out1str(s);
-               out1str(jp->ps[0].cmd);
-               out1c('\n');
+               out1fmt("[%td] ", jp - jobtab + 1);
+               printjobcmd(jp);
        } while (--argc > 1);
        return 0;
 }
 
 
-STATIC void
+static void
 restartjob(struct job *jp)
 {
        struct procstat *ps;
@@ -249,7 +230,7 @@ restartjob(struct job *jp)
                return;
        setcurjob(jp);
        INTOFF;
-       killpg(jp->ps[0].pid, SIGCONT);
+       kill(-jp->ps[0].pid, SIGCONT);
        for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
                if (WIFSTOPPED(ps->status)) {
                        ps->status = -1;
@@ -293,19 +274,33 @@ jobscmd(int argc, char *argv[])
                showjobs(0, mode);
        else
                while ((id = *argv++) != NULL)
-                       showjob(getjob(id), 0, mode);
+                       showjob(getjob(id), mode);
 
        return (0);
 }
 
-STATIC void
-showjob(struct job *jp, pid_t pid, int mode)
+static void
+printjobcmd(struct job *jp)
+{
+       struct procstat *ps;
+       int i;
+
+       for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
+               out1str(ps->cmd);
+               if (i > 0)
+                       out1str(" | ");
+       }
+       out1c('\n');
+}
+
+static void
+showjob(struct job *jp, int mode)
 {
        char s[64];
+       char statestr[64];
        struct procstat *ps;
        struct job *j;
        int col, curr, i, jobno, prev, procno;
-       pid_t ppid;
        char c;
 
        procno = (mode == SHOWJOBS_PGIDS) ? 1 : jp->nprocs;
@@ -318,16 +313,44 @@ showjob(struct job *jp, pid_t pid, int mode)
                        prev = j - jobtab + 1;
        }
 #endif
+       ps = jp->ps + jp->nprocs - 1;
+       if (jp->state == 0) {
+               strcpy(statestr, "Running");
+#if JOBS
+       } else if (jp->state == JOBSTOPPED) {
+               while (!WIFSTOPPED(ps->status) && ps > jp->ps)
+                       ps--;
+               if (WIFSTOPPED(ps->status))
+                       i = WSTOPSIG(ps->status);
+               else
+                       i = -1;
+               if (i > 0 && i < sys_nsig && sys_siglist[i])
+                       strcpy(statestr, sys_siglist[i]);
+               else
+                       strcpy(statestr, "Suspended");
+#endif
+       } else if (WIFEXITED(ps->status)) {
+               if (WEXITSTATUS(ps->status) == 0)
+                       strcpy(statestr, "Done");
+               else
+                       fmtstr(statestr, 64, "Done(%d)",
+                           WEXITSTATUS(ps->status));
+       } else {
+               i = WTERMSIG(ps->status);
+               if (i > 0 && i < sys_nsig && sys_siglist[i])
+                       strcpy(statestr, sys_siglist[i]);
+               else
+                       fmtstr(statestr, 64, "Signal %d", i);
+               if (WCOREDUMP(ps->status))
+                       strcat(statestr, " (core dumped)");
+       }
+
        for (ps = jp->ps ; ; ps++) {    /* for each process */
                if (mode == SHOWJOBS_PIDS || mode == SHOWJOBS_PGIDS) {
-                       ppid = (mode == SHOWJOBS_PIDS) ? ps->pid :
-                           getpgid(ps->pid);
-                       out1fmt("%d\n", (int)ppid);
+                       out1fmt("%d\n", (int)ps->pid);
                        goto skip;
                }
-               if (mode != SHOWJOBS_VERBOSE && ps != jp->ps && pid == 0)
-                       goto skip;
-               if (pid != 0 && pid != ps->pid)
+               if (mode != SHOWJOBS_VERBOSE && ps != jp->ps)
                        goto skip;
                if (jobno == curr && ps == jp->ps)
                        c = '+';
@@ -346,39 +369,19 @@ showjob(struct job *jp, pid_t pid, int mode)
                        out1str(s);
                        col += strlen(s);
                }
-               s[0] = '\0';
-               if (ps != jp->ps) {
-                       *s = '\0';
-               } else if (ps->status == -1) {
-                       strcpy(s, "Running");
-               } else if (WIFEXITED(ps->status)) {
-                       if (WEXITSTATUS(ps->status) == 0)
-                               strcpy(s, "Done");
-                       else
-                               fmtstr(s, 64, "Done (%d)",
-                                   WEXITSTATUS(ps->status));
-               } else {
-#if JOBS
-                       if (WIFSTOPPED(ps->status))
-                               i = WSTOPSIG(ps->status);
-                       else
-#endif
-                               i = WTERMSIG(ps->status);
-                       if ((i & 0x7F) < sys_nsig && sys_siglist[i & 0x7F])
-                               scopy(sys_siglist[i & 0x7F], s);
-                       else
-                               fmtstr(s, 64, "Signal %d", i & 0x7F);
-                       if (WCOREDUMP(ps->status))
-                               strcat(s, " (core dumped)");
+               if (ps == jp->ps) {
+                       out1str(statestr);
+                       col += strlen(statestr);
                }
-               out1str(s);
-               col += strlen(s);
                do {
                        out1c(' ');
                        col++;
                } while (col < 30);
-               out1str(ps->cmd);
-               out1c('\n');
+               if (mode == SHOWJOBS_VERBOSE) {
+                       out1str(ps->cmd);
+                       out1c('\n');
+               } else
+                       printjobcmd(jp);
 skip:          if (--procno <= 0)
                        break;
        }
@@ -400,7 +403,7 @@ showjobs(int change, int mode)
        struct job *jp;
 
        TRACE(("showjobs(%d) called\n", change));
-       while (dowait(0, NULL) > 0);
+       checkzombies();
        for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
                if (! jp->used)
                        continue;
@@ -410,9 +413,13 @@ showjobs(int change, int mode)
                }
                if (change && ! jp->changed)
                        continue;
-               showjob(jp, 0, mode);
+               showjob(jp, mode);
                jp->changed = 0;
-               if (jp->state == JOBDONE) {
+               /* Hack: discard jobs for which $! has not been referenced
+                * in interactive mode when they terminate.
+                */
+               if (jp->state == JOBDONE && !jp->remembered &&
+                               (iflag || jp != bgjob)) {
                        freejob(jp);
                }
        }
@@ -423,13 +430,15 @@ showjobs(int change, int mode)
  * Mark a job structure as unused.
  */
 
-STATIC void
+static void
 freejob(struct job *jp)
 {
        struct procstat *ps;
        int i;
 
        INTOFF;
+       if (bgjob == jp)
+               bgjob = NULL;
        for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
                if (ps->cmd != nullstr)
                        ckfree(ps->cmd);
@@ -476,12 +485,27 @@ waitcmd(int argc, char **argv)
 #endif
                                else
                                        retval = WTERMSIG(status) + 128;
-                               if (! iflag)
+                               if (! iflag || ! job->changed)
                                        freejob(job);
+                               else {
+                                       job->remembered = 0;
+                                       if (job == bgjob)
+                                               bgjob = NULL;
+                               }
                                in_waitcmd--;
                                return retval;
                        }
                } else {
+                       for (jp = jobtab ; jp < jobtab + njobs; jp++)
+                               if (jp->used && jp->state == JOBDONE) {
+                                       if (! iflag || ! jp->changed)
+                                               freejob(jp);
+                                       else {
+                                               jp->remembered = 0;
+                                               if (jp == bgjob)
+                                                       bgjob = NULL;
+                                       }
+                               }
                        for (jp = jobtab ; ; jp++) {
                                if (jp >= jobtab + njobs) {     /* no running procs */
                                        in_waitcmd--;
@@ -519,7 +543,7 @@ jobidcmd(int argc __unused, char **argv)
  * Convert a job name to a job structure.
  */
 
-STATIC struct job *
+static struct job *
 getjob(char *name)
 {
        int jobno;
@@ -591,6 +615,14 @@ currentjob:        if ((jp = getcurjob(NULL)) == NULL)
 }
 
 
+pid_t
+getjobpgrp(char *name)
+{
+       struct job *jp;
+
+       jp = getjob(name);
+       return -jp->ps[0].pid;
+}
 
 /*
  * Return a new job structure,
@@ -622,6 +654,8 @@ makejob(union node *node __unused, int nprocs)
                                                jp[i].next = &jp[jp[i].next -
                                                    jobtab];
 #endif
+                               if (bgjob != NULL)
+                                       bgjob = &jp[bgjob - jobtab];
                                /* Relocate `ps' pointers */
                                for (i = 0; i < njobs; i++)
                                        if (jp[i].ps == &jobtab[i].ps0)
@@ -643,6 +677,7 @@ makejob(union node *node __unused, int nprocs)
        jp->changed = 0;
        jp->nprocs = 0;
        jp->foreground = 0;
+       jp->remembered = 0;
 #if JOBS
        jp->jobctl = jobctl;
        jp->next = NULL;
@@ -653,13 +688,13 @@ makejob(union node *node __unused, int nprocs)
                jp->ps = &jp->ps0;
        }
        INTON;
-       TRACE(("makejob(%p, %d) returns %%%d\n", (void *)node, nprocs,
+       TRACE(("makejob(%p, %d) returns %%%td\n", (void *)node, nprocs,
            jp - jobtab + 1));
        return jp;
 }
 
 #if JOBS
-STATIC void
+static void
 setcurjob(struct job *cj)
 {
        struct job *jp, *prev;
@@ -679,7 +714,7 @@ setcurjob(struct job *cj)
        jobmru = cj;
 }
 
-STATIC void
+static void
 deljob(struct job *j)
 {
        struct job *jp, *prev;
@@ -699,7 +734,7 @@ deljob(struct job *j)
  * Return the most recently used job that isn't `nj', and preferably one
  * that is stopped.
  */
-STATIC struct job *
+static struct job *
 getcurjob(struct job *nj)
 {
        struct job *jp;
@@ -739,9 +774,11 @@ forkshell(struct job *jp, union node *n, int mode)
        pid_t pid;
        pid_t pgrp;
 
-       TRACE(("forkshell(%%%d, %p, %d) called\n", jp - jobtab, (void *)n,
+       TRACE(("forkshell(%%%td, %p, %d) called\n", jp - jobtab, (void *)n,
            mode));
        INTOFF;
+       if (mode == FORK_BG && (jp == NULL || jp->nprocs == 0))
+               checkzombies();
        flushall();
        pid = fork();
        if (pid == -1) {
@@ -757,6 +794,7 @@ forkshell(struct job *jp, union node *n, int mode)
                TRACE(("Child shell %d\n", (int)getpid()));
                wasroot = rootshell;
                rootshell = 0;
+               handler = &main_handler;
                closescript();
                INTON;
                clear_traps();
@@ -817,8 +855,13 @@ forkshell(struct job *jp, union node *n, int mode)
                        pgrp = jp->ps[0].pid;
                setpgid(pid, pgrp);
        }
-       if (mode == FORK_BG)
+       if (mode == FORK_BG) {
+               if (bgjob != NULL && bgjob->state == JOBDONE &&
+                   !bgjob->remembered && !iflag)
+                       freejob(bgjob);
                backgndpid = pid;               /* set $! */
+               bgjob = jp;
+       }
        if (jp) {
                struct procstat *ps = &jp->ps[jp->nprocs++];
                ps->pid = pid;
@@ -862,12 +905,13 @@ waitforjob(struct job *jp, int *origstatus)
 {
 #if JOBS
        pid_t mypgrp = getpgrp();
+       int propagate_int = jp->jobctl && jp->foreground;
 #endif
        int status;
        int st;
 
        INTOFF;
-       TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
+       TRACE(("waitforjob(%%%td) called\n", jp - jobtab + 1));
        while (jp->state == 0)
                if (dowait(1, jp) == -1)
                        dotrap();
@@ -894,11 +938,14 @@ waitforjob(struct job *jp, int *origstatus)
        if (! JOBS || jp->state == JOBDONE)
                freejob(jp);
        if (int_pending()) {
-               if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
-                       kill(getpid(), SIGINT);
-               else
+               if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGINT)
                        CLEAR_PENDING_INT;
        }
+#if JOBS
+       else if (rootshell && iflag && propagate_int &&
+                       WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
+               kill(getpid(), SIGINT);
+#endif
        INTON;
        return st;
 }
@@ -909,7 +956,7 @@ waitforjob(struct job *jp, int *origstatus)
  * Wait for a process to terminate.
  */
 
-STATIC pid_t
+static pid_t
 dowait(int block, struct job *job)
 {
        pid_t pid;
@@ -920,7 +967,7 @@ dowait(int block, struct job *job)
        int done;
        int stopped;
        int sig;
-       int i;
+       int coredump;
 
        in_dowait++;
        TRACE(("dowait(%d) called\n", block));
@@ -946,7 +993,7 @@ dowait(int block, struct job *job)
        INTOFF;
        thisjob = NULL;
        for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
-               if (jp->used) {
+               if (jp->used && jp->nprocs > 0) {
                        done = 1;
                        stopped = 1;
                        for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
@@ -967,47 +1014,46 @@ dowait(int block, struct job *job)
                        if (stopped) {          /* stopped or done */
                                int state = done? JOBDONE : JOBSTOPPED;
                                if (jp->state != state) {
-                                       TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
+                                       TRACE(("Job %td: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
                                        jp->state = state;
+                                       if (jp != job) {
+                                               if (done && !jp->remembered &&
+                                                   !iflag && jp != bgjob)
+                                                       freejob(jp);
 #if JOBS
-                                       if (done)
-                                               deljob(jp);
+                                               else if (done)
+                                                       deljob(jp);
 #endif
+                                       }
                                }
                        }
                }
        }
        INTON;
-       if (! rootshell || ! iflag || (job && thisjob == job)) {
-#if JOBS
-               if (WIFSTOPPED(status))
-                       sig = WSTOPSIG(status);
-               else
-#endif
-               {
-                       if (WIFEXITED(status))
-                               sig = 0;
+       if (!thisjob || thisjob->state == 0)
+               ;
+       else if ((!rootshell || !iflag || thisjob == job) &&
+           thisjob->foreground && thisjob->state != JOBSTOPPED) {
+               sig = 0;
+               coredump = 0;
+               for (sp = thisjob->ps; sp < thisjob->ps + thisjob->nprocs; sp++)
+                       if (WIFSIGNALED(sp->status)) {
+                               sig = WTERMSIG(sp->status);
+                               coredump = WCOREDUMP(sp->status);
+                       }
+               if (sig > 0 && sig != SIGINT && sig != SIGPIPE) {
+                       if (sig < sys_nsig && sys_siglist[sig])
+                               out2str(sys_siglist[sig]);
                        else
-                               sig = WTERMSIG(status);
-               }
-               if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
-                       if (!mflag ||
-                           (thisjob->foreground && !WIFSTOPPED(status))) {
-                               i = WTERMSIG(status);
-                               if ((i & 0x7F) < sys_nsig && sys_siglist[i & 0x7F])
-                                       out1str(sys_siglist[i & 0x7F]);
-                               else
-                                       out1fmt("Signal %d", i & 0x7F);
-                               if (WCOREDUMP(status))
-                                       out1str(" (core dumped)");
-                               out1c('\n');
-                       } else
-                               showjob(thisjob, pid, SHOWJOBS_DEFAULT);
+                               outfmt(out2, "Signal %d", sig);
+                       if (coredump)
+                               out2str(" (core dumped)");
+                       out2c('\n');
+                       flushout(out2);
                }
        } else {
                TRACE(("Not printing status, rootshell=%d, job=%p\n", rootshell, job));
-               if (thisjob)
-                       thisjob->changed = 1;
+               thisjob->changed = 1;
        }
        return pid;
 }
@@ -1019,7 +1065,7 @@ dowait(int block, struct job *job)
  * stopped processes.  If block is zero, we return a value of zero
  * rather than blocking.
  */
-STATIC pid_t
+static pid_t
 waitproc(int block, int *status)
 {
        int flags;
@@ -1050,7 +1096,7 @@ stoppedjobs(void)
                if (jp->used == 0)
                        continue;
                if (jp->state == JOBSTOPPED) {
-                       out2str("You have stopped jobs.\n");
+                       out2fmt_flush("You have stopped jobs.\n");
                        job_warning = 2;
                        return (1);
                }
@@ -1059,13 +1105,37 @@ stoppedjobs(void)
        return (0);
 }
 
+
+static void
+checkzombies(void)
+{
+       while (njobs > 0 && dowait(0, NULL) > 0)
+               ;
+}
+
+
+int
+backgndpidset(void)
+{
+       return backgndpid != -1;
+}
+
+
+pid_t
+backgndpidval(void)
+{
+       if (bgjob != NULL)
+               bgjob->remembered = 1;
+       return backgndpid;
+}
+
 /*
  * Return a string identifying a command (to be printed by the
  * jobs command.
  */
 
-STATIC char *cmdnextc;
-STATIC int cmdnleft;
+static char *cmdnextc;
+static int cmdnleft;
 #define MAXCMDTEXT     200
 
 char *
@@ -1081,7 +1151,7 @@ commandtext(union node *n)
 }
 
 
-STATIC void
+static void
 cmdtxt(union node *n)
 {
        union node *np;
@@ -1214,7 +1284,7 @@ redir:
 
 
 
-STATIC void
+static void
 cmdputs(const char *s)
 {
        const char *p;
@@ -1234,13 +1304,46 @@ cmdputs(const char *s)
                        if (--cmdnleft > 0)
                                *q++ = '{';
                        subtype = *p++;
+                       if ((subtype & VSTYPE) == VSLENGTH && --cmdnleft > 0)
+                               *q++ = '#';
                } else if (c == '=' && subtype != 0) {
-                       *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
+                       *q = "}-+?=##%%\0X"[(subtype & VSTYPE) - VSNORMAL];
+                       if (*q)
+                               q++;
+                       else
+                               cmdnleft++;
+                       if (((subtype & VSTYPE) == VSTRIMLEFTMAX ||
+                           (subtype & VSTYPE) == VSTRIMRIGHTMAX) &&
+                           --cmdnleft > 0)
+                               *q = q[-1], q++;
                        subtype = 0;
                } else if (c == CTLENDVAR) {
                        *q++ = '}';
-               } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE)
-                       cmdnleft++;             /* ignore it */
+               } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE) {
+                       cmdnleft -= 5;
+                       if (cmdnleft > 0) {
+                               *q++ = '$';
+                               *q++ = '(';
+                               *q++ = '.';
+                               *q++ = '.';
+                               *q++ = '.';
+                               *q++ = ')';
+                       }
+               } else if (c == CTLARI) {
+                       cmdnleft -= 2;
+                       if (cmdnleft > 0) {
+                               *q++ = '$';
+                               *q++ = '(';
+                               *q++ = '(';
+                       }
+                       p++;
+               } else if (c == CTLENDARI) {
+                       if (--cmdnleft > 0) {
+                               *q++ = ')';
+                               *q++ = ')';
+                       }
+               } else if (c == CTLQUOTEMARK || c == CTLQUOTEEND)
+                       cmdnleft++; /* ignore */
                else
                        *q++ = c;
                if (--cmdnleft <= 0) {
index fd45933..9b812f8 100644 (file)
@@ -34,8 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)jobs.h      8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/jobs.h,v 1.19 2006/10/07 16:51:16 stefanf Exp $
- * $DragonFly: src/bin/sh/jobs.h,v 1.3 2007/01/13 20:33:47 pavalos Exp $
+ * $FreeBSD: src/bin/sh/jobs.h,v 1.20 2010/06/29 22:37:45 jilles Exp $
  */
 
 /* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
@@ -73,6 +72,7 @@ struct job {
        char used;              /* true if this entry is in used */
        char changed;           /* true if status has changed */
        char foreground;        /* true if running in the foreground */
+       char remembered;        /* true if $! referenced */
 #if JOBS
        char jobctl;            /* job running under job control */
        struct job *next;       /* job used after this one */
@@ -86,7 +86,6 @@ enum {
        SHOWJOBS_PGIDS          /* PID of the group leader only */
 };
 
-extern pid_t backgndpid;       /* pid of last background process */
 extern int job_warning;                /* user was warned about stopped jobs */
 extern int in_waitcmd;         /* are we in waitcmd()? */
 extern int in_dowait;          /* are we in dowait()? */
@@ -99,10 +98,13 @@ int jobscmd(int, char **);
 void showjobs(int, int);
 int waitcmd(int, char **);
 int jobidcmd(int, char **);
+pid_t getjobpgrp(char *);
 struct job *makejob(union node *, int);
 pid_t forkshell(struct job *, union node *, int);
 int waitforjob(struct job *, int *);
 int stoppedjobs(void);
+int backgndpidset(void);
+pid_t backgndpidval(void);
 char *commandtext(union node *);
 
 #if ! JOBS
diff --git a/bin/sh/mail.c