.Xr csh 1 ,
.Xr killall 1 ,
.Xr ps 1 ,
+.Xr sh 1 ,
.Xr kill 2 ,
.Xr sigaction 2
.Sh STANDARDS
#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 *);
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) {
} 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
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
{
warnx("unknown signal %s; valid signals:", name);
printsignals(stderr);
- exit(1);
+#ifdef SHELL
+ error(NULL);
+#else
+ exit(2);
+#endif
}
static void
" kill -l [exit_status]",
" kill -signal_name pid ...",
" kill -signal_number pid ...");
- exit(1);
+#ifdef SHELL
+ error(NULL);
+#else
+ exit(2);
+#endif
}
# @(#)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
# @(#)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
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
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:
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
* 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;
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 */
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;
ckfree(ap);
INTON;
}
+ aliases--;
return (0);
}
}
return (1);
}
-#ifdef mkinit
-MKINIT void rmaliases(void);
-
-SHELLPROC {
- rmaliases();
-}
-#endif
-
-void
+static void
rmaliases(void)
{
struct alias *ap, *tmp;
ckfree(tmp);
}
}
+ aliases = 0;
INTON;
}
struct alias *
-lookupalias(char *name, int check)
+lookupalias(const char *name, int check)
{
struct alias *ap = *hashalias(name);
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)
{
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);
return (i);
}
-STATIC struct alias **
-hashalias(char *p)
+static struct alias **
+hashalias(const char *p)
{
unsigned int hashval;
* 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
int flag;
};
-struct alias *lookupalias(char *, int);
+struct alias *lookupalias(const char *, int);
int aliascmd(int, char **);
int unaliascmd(int, char **);
-void rmaliases(void);
* 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 **);
+++ /dev/null
-%{
-/*-
- * 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
+++ /dev/null
-%{
-/*-
- * 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;
-}
--- /dev/null
+/*-
+ * 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;
+}
+
/*-
* 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);
--- /dev/null
+/*-
+ * 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;
+}
* 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 $
*/
/*
#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 *[]);
#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;
+++ /dev/null
-.\"-
-.\" 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.
* 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 $
*/
/*
# 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
histcmd -h fc
jobidcmd jobid
jobscmd jobs
+killcmd kill
localcmd local
printfcmd printf
pwdcmd pwd
* 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)
* 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)
{
return 0;
}
-STATIC int
+static int
cdlogical(char *dest)
{
char *p;
STPUTC('/', p);
first = 0;
component = q;
- while (*q)
- STPUTC(*q++, p);
+ STPUTS(q, p);
if (equal(component, ".."))
continue;
STACKSTRNUL(p);
}
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);
}
* 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;
}
-/*
- * 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) {
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 */
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');
}
}
/*
- * 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);
+}
* 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 **);
* 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 $
*/
/*
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;
void
onint(void)
{
- sigset_t sigset;
+ sigset_t sigs;
/*
* The !in_dotrap here is safe. The only way we can arrive here
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.
}
+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
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)
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);
}
* 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 $
*/
/*
/* 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 */
/*
#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;
/*
* 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;
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 *);
/*
loopnest = 0;
funcnest = 0;
}
-
-SHELLPROC {
- exitstatus = 0;
-}
#endif
STARTSTACKSTR(concat);
ap = argv + 2;
for (;;) {
- while (*p)
- STPUTC(*p++, concat);
+ STPUTS(p, concat);
if ((p = *ap++) == NULL)
break;
STPUTC(' ', concat);
STPUTC('\0', concat);
p = grabstackstr(concat);
}
- evalstring(p);
- }
+ evalstring(p, builtin_flags & EV_TESTED);
+ } else
+ exitstatus = 0;
return exitstatus;
}
*/
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.
evaltree(union node *n, int flags)
{
int do_etest;
+ union node *next;
do_etest = 0;
if (n == NULL) {
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();
}
-STATIC void
+static void
evalloop(union node *n, int flags)
{
int status;
}
if (evalskip == SKIPBREAK && --skipcount <= 0)
evalskip = 0;
+ if (evalskip == SKIPFUNC || evalskip == SKIPFILE)
+ status = exitstatus;
break;
}
if (n->type == NWHILE) {
-STATIC void
+static void
evalfor(union node *n, int flags)
{
struct arglist arglist;
-STATIC void
+static void
evalcase(union node *n, int flags)
{
union node *cp;
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) {
* 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;
}
+/*
+ * 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;
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;
* of all the rest.)
*/
-STATIC void
+static void
evalpipe(union node *n)
{
struct job *jp;
+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
int pip[2];
struct job *jp;
struct stackmark smark; /* unnecessary */
+ struct jmploc jmploc;
+ struct jmploc *savehandler;
setstackmark(&smark);
result->fd = -1;
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)
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;
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));
arglist.lastp = &arglist.list;
varlist.lastp = &varlist.list;
varflag = 1;
+ jp = NULL;
do_clearcmdentry = 0;
oexitstatus = exitstatus;
exitstatus = 0;
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));
/* 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);
}
/* 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
* 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) {
#ifdef DEBUG
trputs("Shell function: "); trargs(argv);
#endif
- redirect(cmd->ncmd.redirect, REDIR_PUSH);
saveparam = shellparam;
shellparam.malloc = 0;
shellparam.reset = 1;
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
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;
evalskip = SKIPBREAK;
skipcount = loopnest;
}
- } else if (mode == 2) {
+ } else if (mode == FORK_NOJOB) {
backcmd->fd = pip[0];
close(pip[1]);
backcmd->jp = jp;
if (lastarg)
setvar("_", lastarg, 0);
if (do_clearcmdentry)
- clearcmdentry(0);
+ clearcmdentry();
popstackmark(&smark);
}
* check that the name will not be subject to expansion.
*/
-STATIC void
+static void
prehash(union node *n)
{
struct cmdentry entry;
*/
/*
- * 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
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;
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);
}
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;
* 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 */
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 *);
* 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>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <paths.h>
#include <stdlib.h>
/*
};
-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
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;
}
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;
verbose = 0;
while ((c = nextopt("rv")) != '\0') {
if (c == 'r') {
- clearcmdentry(0);
+ clearcmdentry();
} else if (c == 'v') {
verbose++;
}
}
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();
}
}
-STATIC void
+static void
printentry(struct tblentry *cmdp, int verbose)
{
int idx;
out1fmt("function %s", cmdp->cmdname);
if (verbose) {
INTOFF;
- name = commandtext(cmdp->param.func);
+ name = commandtext(getfuncnode(cmdp->param.func));
out1c(' ');
out1str(name);
ckfree(name);
*/
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;
}
/* 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;
prev = -1; /* where to start */
if (cmdp) { /* doing a rehash */
if (cmdp->cmdtype == CMDBUILTIN)
- prev = builtinloc;
+ prev = -1;
else
prev = cmdp->param.index;
}
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 */
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;
}
/* 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:
*/
int
-find_builtin(char *name, int *special)
+find_builtin(const char *name, int *special)
{
const struct builtincmd *bp;
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;
}
}
*/
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();
}
*/
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;
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;
}
-
/*
* 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
* 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;
* Delete the command entry returned on the last lookup.
*/
-STATIC void
+static void
delete_cmd_entry(void)
{
struct tblentry *cmdp;
*/
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;
*/
void
-defun(char *name, union node *func)
+defun(const char *name, union node *func)
{
struct cmdentry entry;
*/
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);
*/
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;
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]))
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;
}
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;
}
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);
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;
}
}
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;
}
int
typecmd(int argc, char **argv)
{
- return typecmd_impl(argc, argv, TYPECMD_TYPE);
+ return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
}
* 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 */
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);
/*-
* 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
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];
return (strcoll(s1, s2));
}
-extern int oexitstatus;
-
/*
* Expand shell variables and backquotes inside a here document.
* union node *arg the document
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)
{
/*
- * 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);
* 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;
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 ':':
goto done;
break;
case '/':
+ case CTLENDVAR:
goto done;
}
p++;
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;
}
-STATIC void
+static void
removerecordregions(int endoff)
{
if (ifslastp == NULL)
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;
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)
/*
- * Expand stuff in backwards quotes.
+ * Perform command substitution.
*/
-
-STATIC void
+static void
expbackq(union node *cmd, int quoted, int flag)
{
struct backcmd in;
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;
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';
}
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);
}
}
}
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;
-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)
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),
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;
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++;
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++;
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;
* input string.
*/
-STATIC char *
+static char *
evalvar(char *p, int flag)
{
int subtype;
}
varlen = 0;
startloc = expdest - stackblock();
- if (!set && uflag) {
+ if (!set && uflag && *var != '@' && *var != '*') {
switch (subtype) {
case VSNORMAL:
case VSTRIMLEFT:
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);
}
}
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)
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);
}
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
* 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;
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;
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 '$':
num = shellparam.nparam;
goto numvar;
case '!':
- num = backgndpid;
+ num = backgndpidval();
numvar:
expdest = cvtnum(num, expdest);
break;
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);
}
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;
* 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;
}
ifslastp->next = NULL;
ifslastp->begoff = start;
ifslastp->endoff = end;
- ifslastp->nulonly = nulonly;
+ ifslastp->inquotes = inquotes;
}
* 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;
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;
}
+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;
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) {
/*
* 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;
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 == '[') {
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 */
*enddir++ = *p;
if (*p == '\0')
break;
+ if (enddir == expdir_end)
+ return;
}
if (metaflag == 0 || lstat(expdir, &statb) >= 0)
addfname(expdir);
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] = '/';
atend = 1;
} else {
atend = 0;
- *endname++ = '\0';
+ *endname = '\0';
+ endname += esc + 1;
}
matchdot = 0;
p = start;
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 : '/';
}
* Add a file name to the list.
*/
-STATIC void
+static void
addfname(char *name)
{
char *p;
* work.
*/
-STATIC struct strlist *
+static struct strlist *
expsort(struct strlist *str)
{
int len;
}
-STATIC struct strlist *
+static struct strlist *
msort(struct strlist *list, int len)
{
struct strlist *p, *q = NULL;
*/
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;
}
}
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;
/*
- * Remove any CTLESC characters from a string.
+ * Remove any CTLESC and CTLQUOTEMARK characters from a string.
*/
void
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;
}
*/
int
-casematch(union node *pattern, char *val)
+casematch(union node *pattern, const char *val)
{
struct stackmark smark;
int result;
* Our own itoa().
*/
-STATIC char *
+static char *
cvtnum(int num, char *buf)
{
char temp[32];
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).
*/
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);
}
* 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 {
#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 **);
+#!/bin/sh
+
+#-
# Copyright (c) 1991, 1993
# The Regents of the University of California. All rights reserved.
#
# 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.
+#!/bin/sh
+
+#-
# Copyright (c) 1991, 1993
# The Regents of the University of California. All rights reserved.
#
# 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
+#!/bin/sh
+
+#-
# Copyright (c) 1991, 1993
# The Regents of the University of California. All rights reserved.
#
# 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.
+#!/bin/sh
+
+#-
# Copyright (c) 1991, 1993
# The Regents of the University of California. All rights reserved.
#
# 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 "$@"
+#!/bin/sh
+
+#-
# Copyright (c) 1991, 1993
# The Regents of the University of California. All rights reserved.
#
# 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 "$@"
+#!/bin/sh
+
+#-
# Copyright (c) 1991, 1993
# The Regents of the University of California. All rights reserved.
#
# 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
+#!/bin/sh
+
+#-
# Copyright (c) 1991, 1993
# The Regents of the University of California. All rights reserved.
#
# 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
+#-
# 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
}
* 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>
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
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)
}
else
unsetenv("TERM");
- shname = arg0;
- if (shname[0] == '-')
- shname++;
el = el_init(arg0, el_in, el_out, el_err);
if (el != NULL) {
if (hist)
_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) {
}
}
+void
+setterm(const char *term)
+{
+ if (rootshell && el != NULL && term != NULL)
+ el_set(el, EL_TERMINAL, term);
+}
+
int
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;
}
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;
laststr = argv[1];
break;
default:
- error("too many args");
+ error("too many arguments");
}
/*
* Turn into event numbers.
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");
}
}
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
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);
--active;
if (displayhist)
displayhist = 0;
+ handler = savehandler;
return 0;
}
-STATIC char *
+static char *
fc_replace(const char *s, char *p, char *r)
{
char *dest;
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);
* 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);
* 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 */
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
}
RESET {
- if (exception != EXSHELLPROC)
- parselleft = parsenleft = 0; /* clear input buffer */
- popallfiles();
-}
-
-SHELLPROC {
popallfiles();
+ parselleft = parsenleft = 0; /* clear input buffer */
}
#endif
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;
}
}
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
}
/*
+ * 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.
*/
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;
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;
* adds a new entry to the stack and popfile restores the previous level.
*/
-STATIC void
+static void
pushfile(void)
{
struct parsefile *pf;
/*
+ * 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.
*/
* 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 */
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);
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);
* 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()? */
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);
/*
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);
#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)
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);
int
bgcmd(int argc, char **argv)
{
- char s[64];
struct job *jp;
do {
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;
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;
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;
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 = '+';
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;
}
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;
}
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);
}
}
* 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);
#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--;
* Convert a job name to a job structure.
*/
-STATIC struct job *
+static struct job *
getjob(char *name)
{
int jobno;
}
+pid_t
+getjobpgrp(char *name)
+{
+ struct job *jp;
+
+ jp = getjob(name);
+ return -jp->ps[0].pid;
+}
/*
* Return a new job structure,
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)
jp->changed = 0;
jp->nprocs = 0;
jp->foreground = 0;
+ jp->remembered = 0;
#if JOBS
jp->jobctl = jobctl;
jp->next = NULL;
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;
jobmru = cj;
}
-STATIC void
+static void
deljob(struct job *j)
{
struct job *jp, *prev;
* 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;
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) {
TRACE(("Child shell %d\n", (int)getpid()));
wasroot = rootshell;
rootshell = 0;
+ handler = &main_handler;
closescript();
INTON;
clear_traps();
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;
{
#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();
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;
}
* Wait for a process to terminate.
*/
-STATIC pid_t
+static pid_t
dowait(int block, struct job *job)
{
pid_t pid;
int done;
int stopped;
int sig;
- int i;
+ int coredump;
in_dowait++;
TRACE(("dowait(%d) called\n", block));
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++) {
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;
}
* 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;
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);
}
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 *
}
-STATIC void
+static void
cmdtxt(union node *n)
{
union node *np;
-STATIC void
+static void
cmdputs(const char *s)
{
const char *p;
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) {
* 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. */
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 */
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()? */
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
* SUCH DAMAGE.
*
* @(#)mail.c 8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/mail.c,v 1.9.2.2 2002/07/19 04:38:51 tjr Exp $
- * $DragonFly: src/bin/sh/mail.c,v 1.3 2004/03/19 18:39:41 cpressey Exp $
+ * $FreeBSD: src/bin/sh/mail.c,v 1.16 2010/10/13 04:01:01 obrien Exp $
*/
/*
#include "shell.h"
#include "exec.h" /* defines padvance() */
+#include "mail.h"
#include "var.h"
#include "output.h"
#include "memalloc.h"
#include <sys/stat.h>
#include <stdlib.h>
-#include "mail.h"
#define MAXMBOXES 10
-STATIC int nmboxes; /* number of mailboxes */
-STATIC time_t mailtime[MAXMBOXES]; /* times of mailboxes */
+static int nmboxes; /* number of mailboxes */
+static time_t mailtime[MAXMBOXES]; /* times of mailboxes */
* SUCH DAMAGE.
*
* @(#)mail.h 8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/mail.h,v 1.6.2.1 2002/07/19 04:38:51 tjr Exp $
- * $DragonFly: src/bin/sh/mail.h,v 1.2 2003/06/17 04:22:50 dillon Exp $
+ * $FreeBSD: src/bin/sh/mail.h,v 1.8 2004/04/06 20:06:51 markm Exp $
*/
-void chkmail(int);
+void chkmail(int);
*
* @(#) Copyright (c) 1991, 1993 The Regents of the University of California. All rights reserved.
* @(#)main.c 8.6 (Berkeley) 5/28/95
- * $FreeBSD: src/bin/sh/main.c,v 1.29 2006/10/07 16:51:16 stefanf Exp $
- * $DragonFly: src/bin/sh/main.c,v 1.8 2007/01/13 23:36:14 pavalos Exp $
+ * $FreeBSD: src/bin/sh/main.c,v 1.44 2011/02/04 22:47:55 jilles Exp $
*/
#include <stdio.h>
int rootpid;
int rootshell;
+struct jmploc main_handler;
-STATIC void read_profile(const char *);
-STATIC const char *find_dot_file(const char *);
-
-extern int oexitstatus;
+static void read_profile(const char *);
+static const char *find_dot_file(const char *);
/*
* Main routine. We initialize things, parse the arguments, execute
int
main(int argc, char *argv[])
{
- struct jmploc jmploc;
struct stackmark smark;
volatile int state;
char *shinit;
setlocale(LC_ALL, "");
state = 0;
- if (setjmp(jmploc.loc)) {
- /*
- * When a shell procedure is executed, we raise the
- * exception EXSHELLPROC to clean up before executing
- * the shell procedure.
- */
+ if (setjmp(main_handler.loc)) {
switch (exception) {
- case EXSHELLPROC:
- rootpid = getpid();
- rootshell = 1;
- minusc = NULL;
- state = 3;
- break;
-
case EXEXEC:
exitstatus = exerrno;
break;
break;
}
- if (exception != EXSHELLPROC) {
- if (state == 0 || iflag == 0 || ! rootshell)
- exitshell(exitstatus);
- }
+ if (state == 0 || iflag == 0 || ! rootshell)
+ exitshell(exitstatus);
reset();
- if (exception == EXINT) {
- out2c('\n');
- flushout(&errout);
- }
+ if (exception == EXINT)
+ out2fmt_flush("\n");
popstackmark(&smark);
FORCEINTON; /* enable interrupts */
if (state == 1)
else
goto state4;
}
- handler = &jmploc;
+ handler = &main_handler;
#ifdef DEBUG
opentrace();