* Copyright (c) 1989, 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
- * Copyright (c) 1997-2003 Herbert Xu <herbert@debian.org>
+ * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
* was re-ported from NetBSD and debianized.
*
*
* This code is derived from software contributed to Berkeley by
* Kenneth Almquist.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*
* Original BSD copyright notice is retained at the end of this file.
*/
* rewrite arith.y to micro stack based cryptic algorithm by
* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
*
- * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2003 to be
+ * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
+ * dynamic variables.
+ *
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
* used in busybox and size optimizations,
- * support locale, rewrited arith (see notes to this)
+ * rewrote arith (see notes to this), added locale support,
+ * rewrote dynamic variables.
*
*/
#endif
#include <sys/types.h>
-#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/stat.h>
-#include <sys/time.h>
#include <sys/wait.h>
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <stdint.h>
-#include <sysexits.h>
-
+#include <time.h>
#include <fnmatch.h>
#undef JOBS
#endif
-#if JOBS
+#if JOBS || defined(CONFIG_ASH_READ_NCHARS)
#include <termios.h>
#endif
#ifdef CONFIG_ASH_ALIAS
-/* $NetBSD: alias.h,v 1.5 2002/11/24 22:35:38 christos Exp $ */
+/* alias.h */
#define ALIASINUSE 1
#define ALIASDEAD 2
static void printalias(const struct alias *);
#endif
-/* $NetBSD: cd.h,v 1.3 2002/11/24 22:35:39 christos Exp $ */
+/* cd.h */
static void setpwd(const char *, int);
-/* $NetBSD: error.h,v 1.15 2002/11/24 22:35:39 christos Exp $ */
+/* error.h */
/*
* We enclose jmp_buf in a structure so that we can declare pointers to
* jump locations. The global variable handler contains the location to
* jump to when an exception occurs, and the global variable exception
- * contains a code identifying the exeception. To implement nested
+ * contains a code identifying the exception. To implement nested
* exception handlers, the user should save the value of handler on entry
* to an inner scope, set handler to point to a jmploc structure for the
* inner scope, and restore handler on exit from the scope.
static volatile int suppressint;
static volatile sig_atomic_t intpending;
-static int exerrno; /* Last exec error, error for EXEXEC */
-
/* exceptions */
#define EXINT 0 /* SIGINT received */
#define EXERROR 1 /* a generic error */
* more fun than worrying about efficiency and portability. :-))
*/
-#define barrier() ({ __asm__ __volatile__ ("": : :"memory"); })
+#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); })
#define INTOFF \
({ \
suppressint++; \
- barrier(); \
+ xbarrier(); \
0; \
})
#define SAVEINT(v) ((v) = suppressint)
#define RESTOREINT(v) \
({ \
- barrier(); \
+ xbarrier(); \
if ((suppressint = (v)) == 0 && intpending) onint(); \
0; \
})
#define EXSIGON() \
({ \
exsig++; \
- barrier(); \
+ xbarrier(); \
if (pendingsigs) \
exraise(EXSIG); \
0; \
/* EXSIG is turned off by evalbltin(). */
-static void exraise(int) __attribute__((__noreturn__));
-static void onint(void) __attribute__((__noreturn__));
+static void exraise(int) ATTRIBUTE_NORETURN;
+static void onint(void) ATTRIBUTE_NORETURN;
-static void error(const char *, ...) __attribute__((__noreturn__));
-static void exerror(int, const char *, ...) __attribute__((__noreturn__));
+static void sh_error(const char *, ...) ATTRIBUTE_NORETURN;
+static void exerror(int, const char *, ...) ATTRIBUTE_NORETURN;
static void sh_warnx(const char *, ...);
#else
#define INTON \
({ \
- barrier(); \
+ xbarrier(); \
if (--suppressint == 0 && intpending) onint(); \
0; \
})
#define FORCEINTON \
({ \
- barrier(); \
+ xbarrier(); \
suppressint = 0; \
if (intpending) onint(); \
0; \
})
#endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */
-/*
- * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
- * so we use _setjmp instead.
- */
-
-#if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__)
-#define setjmp(jmploc) _setjmp(jmploc)
-#define longjmp(jmploc, val) _longjmp(jmploc, val)
-#endif
-
-/* $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $ */
+/* expand.h */
struct strlist {
struct strlist *next;
static void expari(int);
#endif
-/* $NetBSD: eval.h,v 1.13 2002/11/24 22:35:39 christos Exp $ */
+/* eval.h */
static char *commandname; /* currently executing command */
static struct strlist *cmdenviron; /* environment for builtin command */
static void freefunc(struct funcnode *);
-/* $NetBSD: parser.h,v 1.15 2002/11/24 22:35:42 christos Exp $ */
+/* parser.h */
/* control characters in argument strings */
#define CTL_FIRST '\201' /* first 'special' character */
/* next character in input buffer */
static char *parsenextc; /* copy of parsefile->nextc */
+
+struct strpush {
+ struct strpush *prev; /* preceding string on stack */
+ char *prevstring;
+ int prevnleft;
+#ifdef CONFIG_ASH_ALIAS
+ struct alias *ap; /* if push was associated with an alias */
+#endif
+ char *string; /* remember the string since it may change */
+};
+
+struct parsefile {
+ struct parsefile *prev; /* preceding file on stack */
+ int linno; /* current line */
+ int fd; /* file descriptor (or -1 if string) */
+ int nleft; /* number of chars left in this line */
+ int lleft; /* number of chars left in this buffer */
+ char *nextc; /* next char in buffer */
+ char *buf; /* input buffer */
+ struct strpush *strpush; /* for pushing strings at this level */
+ struct strpush basestrpush; /* so pushing one is fast */
+};
+
static struct parsefile basepf; /* top level input file */
-static char basebuf[IBUFSIZ]; /* buffer for top level input file */
+#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
static struct parsefile *parsefile = &basepf; /* current input file */
static const char *const *findkwd(const char *);
static char *endofname(const char *);
-/* $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
+/* shell.h */
typedef void *pointer;
#define __builtin_expect(x, expected_value) (x)
#endif
-#define likely(x) __builtin_expect((x),1)
+#define xlikely(x) __builtin_expect((x),1)
#define TEOF 0
return buf;
}
-/* $NetBSD: machdep.h,v 1.10 2002/10/07 14:26:08 christos Exp $ */
+/* machdep.h */
/*
* Most machines require the value returned from malloc to be aligned
#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
+/* C99 say: "char" declaration may be signed or unsigned default */
+#define SC2INT(chr2may_be_negative_int) (int)((signed char)chr2may_be_negative_int)
+
/*
* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
* (assuming ascii char codes, as the original implementation did)
return S_I_T[indx][syntax];
}
-#else /* USE_SIT_FUNCTION */
+#else /* USE_SIT_FUNCTION */
#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
/* 257 127 */ CWORD_CWORD_CWORD_CWORD,
};
-#endif /* USE_SIT_FUNCTION */
+#endif /* USE_SIT_FUNCTION */
-/* $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $ */
+/* alias.c */
#define ATABSIZE 39
static char *nodesavestr(char *);
-
-static void evalstring(char *, int);
+static int evalstring(char *, int mask);
union node; /* BLETCH for ansi C */
static void evaltree(union node *, int);
static void evalbackcmd(union node *, struct backcmd *);
-/* in_function returns nonzero if we are currently evaluating a function */
-#define in_function() funcnest
static int evalskip; /* set if we are skipping commands */
static int skipcount; /* number of levels to skip */
static int funcnest; /* depth of function calls */
/* reasons for skipping commands (see comment on breakcmd routine) */
-#define SKIPBREAK 1
-#define SKIPCONT 2
-#define SKIPFUNC 3
-#define SKIPFILE 4
+#define SKIPBREAK (1 << 0)
+#define SKIPCONT (1 << 1)
+#define SKIPFUNC (1 << 2)
+#define SKIPFILE (1 << 3)
+#define SKIPEVAL (1 << 4)
/*
* This file was generated by the mkbuiltins program.
*/
-#ifdef JOBS
+#if JOBS
static int bgcmd(int, char **);
#endif
static int breakcmd(int, char **);
#endif
static int dotcmd(int, char **);
static int evalcmd(int, char **);
+#ifdef CONFIG_ASH_BUILTIN_ECHO
+static int echocmd(int, char **);
+#endif
static int execcmd(int, char **);
static int exitcmd(int, char **);
static int exportcmd(int, char **);
static int falsecmd(int, char **);
-#ifdef JOBS
+#if JOBS
static int fgcmd(int, char **);
#endif
#ifdef CONFIG_ASH_GETOPTS
#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
static int helpcmd(int argc, char **argv);
#endif
-#ifdef JOBS
+#if JOBS
static int jobscmd(int, char **);
#endif
#ifdef CONFIG_ASH_MATH_SUPPORT
static int unsetcmd(int, char **);
static int waitcmd(int, char **);
static int ulimitcmd(int, char **);
-#ifdef JOBS
+#if JOBS
static int killcmd(int, char **);
#endif
-/* $NetBSD: mail.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */
+/* mail.h */
#ifdef CONFIG_ASH_MAIL
static void chkmail(void);
static void changemail(const char *);
#endif
-/* $NetBSD: exec.h,v 1.20 2003/01/22 20:36:04 dsl Exp $ */
+/* exec.h */
/* values of cmdtype */
#define CMDUNKNOWN -1 /* no entry in table for command */
/* unsigned flags; */
};
-#ifdef CONFIG_ASH_CMDCMD
-# ifdef JOBS
-# ifdef CONFIG_ASH_ALIAS
-# define COMMANDCMD (builtincmd + 7)
-# define EXECCMD (builtincmd + 10)
-# else
-# define COMMANDCMD (builtincmd + 6)
-# define EXECCMD (builtincmd + 9)
-# endif
-# else /* ! JOBS */
-# ifdef CONFIG_ASH_ALIAS
-# define COMMANDCMD (builtincmd + 6)
-# define EXECCMD (builtincmd + 9)
-# else
-# define COMMANDCMD (builtincmd + 5)
-# define EXECCMD (builtincmd + 8)
-# endif
-# endif /* JOBS */
-#else /* ! CONFIG_ASH_CMDCMD */
-# ifdef JOBS
-# ifdef CONFIG_ASH_ALIAS
-# define EXECCMD (builtincmd + 9)
-# else
-# define EXECCMD (builtincmd + 8)
-# endif
-# else /* ! JOBS */
-# ifdef CONFIG_ASH_ALIAS
-# define EXECCMD (builtincmd + 8)
-# else
-# define EXECCMD (builtincmd + 7)
-# endif
-# endif /* JOBS */
-#endif /* CONFIG_ASH_CMDCMD */
+
+#define COMMANDCMD (builtincmd + 5 + \
+ ENABLE_ASH_ALIAS + ENABLE_ASH_JOB_CONTROL)
+#define EXECCMD (builtincmd + 7 + \
+ ENABLE_ASH_CMDCMD + ENABLE_ASH_ALIAS + \
+ ENABLE_ASH_BUILTIN_ECHO + ENABLE_ASH_JOB_CONTROL)
#define BUILTIN_NOSPEC "0"
#define BUILTIN_SPECIAL "1"
#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
+#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4)
static const struct builtincmd builtincmd[] = {
{ BUILTIN_SPEC_REG ".", dotcmd },
#ifdef CONFIG_ASH_ALIAS
{ BUILTIN_REG_ASSG "alias", aliascmd },
#endif
-#ifdef JOBS
+#if JOBS
{ BUILTIN_REGULAR "bg", bgcmd },
#endif
{ BUILTIN_SPEC_REG "break", breakcmd },
{ BUILTIN_REGULAR "command", commandcmd },
#endif
{ BUILTIN_SPEC_REG "continue", breakcmd },
+#ifdef CONFIG_ASH_BUILTIN_ECHO
+ { BUILTIN_REGULAR "echo", echocmd },
+#endif
{ BUILTIN_SPEC_REG "eval", evalcmd },
{ BUILTIN_SPEC_REG "exec", execcmd },
{ BUILTIN_SPEC_REG "exit", exitcmd },
{ BUILTIN_SPEC_REG_ASSG "export", exportcmd },
{ BUILTIN_REGULAR "false", falsecmd },
-#ifdef JOBS
+#if JOBS
{ BUILTIN_REGULAR "fg", fgcmd },
#endif
#ifdef CONFIG_ASH_GETOPTS
#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
{ BUILTIN_NOSPEC "help", helpcmd },
#endif
-#ifdef JOBS
+#if JOBS
{ BUILTIN_REGULAR "jobs", jobscmd },
{ BUILTIN_REGULAR "kill", killcmd },
#endif
{ BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
{ BUILTIN_SPEC_REG "return", returncmd },
{ BUILTIN_SPEC_REG "set", setcmd },
+ { BUILTIN_SPEC_REG "source", dotcmd },
{ BUILTIN_SPEC_REG "shift", shiftcmd },
{ BUILTIN_SPEC_REG "times", timescmd },
{ BUILTIN_SPEC_REG "trap", trapcmd },
static const char *pathopt; /* set by padvance */
static void shellexec(char **, const char *, int)
- __attribute__((__noreturn__));
+ ATTRIBUTE_NORETURN;
static char *padvance(const char **, const char *);
static void find_command(char *, struct cmdentry *, int, const char *);
static struct builtincmd *find_builtin(const char *);
static void defun(char *, union node *);
static void unsetfunc(const char *);
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+typedef int64_t arith_t;
+#define arith_t_type (long long)
+#else
+typedef long arith_t;
+#define arith_t_type (long)
+#endif
+
#ifdef CONFIG_ASH_MATH_SUPPORT
-static int dash_arith(const char *);
+static arith_t dash_arith(const char *);
+static arith_t arith(const char *expr, int *perrcode);
+#endif
+
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+static unsigned long rseed;
+static void change_random(const char *);
+# ifndef DYNAMIC_VAR
+# define DYNAMIC_VAR
+# endif
#endif
-/* $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */
+/* init.h */
static void reset(void);
-/* $NetBSD: var.h,v 1.21 2003/01/22 20:36:04 dsl Exp $ */
+/* var.h */
/*
* Shell variables.
#define VNOFUNC 0x40 /* don't call the callback function */
#define VNOSET 0x80 /* do not set variable - just readonly test */
#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
-
+#ifdef DYNAMIC_VAR
+# define VDYNAMIC 0x200 /* dynamic variable */
+# else
+# define VDYNAMIC 0
+#endif
struct var {
struct var *next; /* next entry in hash list */
int flags; /* flags are defined above */
const char *text; /* name=value */
- void (*func)(const char *);
- /* function to be called when */
+ void (*func)(const char *); /* function to be called when */
/* the variable gets set/unset */
};
static void change_lc_ctype(const char *value);
#endif
+
#define VTABSIZE 39
static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
#endif
{ 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
- { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
- { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
- { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
+ { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
+ { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
+ { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
#ifdef CONFIG_ASH_GETOPTS
{ 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
#endif
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+ {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
+#endif
#ifdef CONFIG_LOCALE_SUPPORT
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL=", change_lc_all},
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE=", change_lc_ctype},
+ {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
+ {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
#endif
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE=", NULL},
+ {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
#endif
};
#define vps2 (&vps1)[1]
#define vps4 (&vps2)[1]
#define voptind (&vps4)[1]
-
+#ifdef CONFIG_ASH_GETOPTS
+#define vrandom (&voptind)[1]
+#else
+#define vrandom (&vps4)[1]
+#endif
#define defpath (defpathvar + 5)
/*
static int loopnest; /* current loop nesting level */
-struct strpush {
- struct strpush *prev; /* preceding string on stack */
- char *prevstring;
- int prevnleft;
-#ifdef CONFIG_ASH_ALIAS
- struct alias *ap; /* if push was associated with an alias */
-#endif
- char *string; /* remember the string since it may change */
-};
-
-struct parsefile {
- struct parsefile *prev; /* preceding file on stack */
- int linno; /* current line */
- int fd; /* file descriptor (or -1 if string) */
- int nleft; /* number of chars left in this line */
- int lleft; /* number of chars left in this buffer */
- char *nextc; /* next char in buffer */
- char *buf; /* input buffer */
- struct strpush *strpush; /* for pushing strings at this level */
- struct strpush basestrpush; /* so pushing one is fast */
-};
-
/*
* The parsefile structure pointed to by the global variable parsefile
* contains information about the current file being read.
extern char **environ;
-/* $NetBSD: output.h,v 1.16 2002/11/24 22:35:42 christos Exp $ */
+/* output.h */
static void outstr(const char *, FILE *);
static void outcslow(int, FILE *);
static void flushall(void);
-static void flushout(FILE *);
+static void flusherr(void);
static int out1fmt(const char *, ...)
__attribute__((__format__(__printf__,1,2)));
static int fmtstr(char *, size_t, const char *, ...)
__attribute__((__format__(__printf__,3,4)));
-static void xwrite(int, const void *, size_t);
static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
static void out2str(const char *p)
{
outstr(p, stderr);
- flushout(stderr);
+ flusherr();
}
/*
{
char **envp;
char ppid[32];
+ const char *p;
+ struct stat st1, st2;
initvar();
- for (envp = environ ; *envp ; envp++) {
+ for (envp = environ ; envp && *envp ; envp++) {
if (strchr(*envp, '=')) {
setvareq(*envp, VEXPORT|VTEXTFIXED);
}
snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
setvar("PPID", ppid, 0);
- setpwd(0, 0);
+
+ p = lookupvar("PWD");
+ if (p)
+ if (*p != '/' || stat(p, &st1) || stat(".", &st2) ||
+ st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
+ p = 0;
+ setpwd(p, 0);
}
}
/* PEOF (the end of file marker) */
+enum {
+ INPUT_PUSH_FILE = 1,
+ INPUT_NOFILE_OK = 2,
+};
+
/*
* The input line number. Input.c just defines this variable, and saves
* and restores it when files are pushed and popped. The user of this
static void pungetc(void);
static void pushstring(char *, void *);
static void popstring(void);
-static void setinputfile(const char *, int);
static void setinputfd(int, int);
static void setinputstring(char *);
static void popfile(void);
static void closescript(void);
-/* $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl Exp $ */
+/* jobs.h */
/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
static void showjobs(FILE *, int);
#endif
-/* $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $ */
+/* main.h */
/* pid of main shell */
static int rootpid;
-/* true if we aren't a child of the main shell */
-static int rootshell;
+/* shell level: 0 for the main shell, 1 for its children, and so on */
+static int shlvl;
+#define rootshell (!shlvl)
static void readcmdfile(char *);
-static void cmdloop(int);
+static int cmdloop(int);
-/* $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $ */
+/* memalloc.h */
struct stackmark {
static char *stputs(const char *, char *);
-static inline char *_STPUTC(char c, char *p) {
+static inline char *_STPUTC(int c, char *p) {
if (p == sstrend)
p = growstackstr();
*p++ = c;
#define ckfree(p) free((pointer)(p))
-/* $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $ */
+/* mystring.h */
#define DOLATSTRLEN 4
#define equal(s1, s2) (strcmp(s1, s2) == 0)
#define scopy(s1, s2) ((void)strcpy(s2, s1))
-/* $NetBSD: options.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
+/* options.h */
struct shparam {
int nparam; /* # of positional parameters (without $0) */
#define aflag optlist[10]
#define bflag optlist[11]
#define uflag optlist[12]
-#define qflag optlist[13]
+#define viflag optlist[13]
#ifdef DEBUG
#define nolog optlist[14]
#define debug optlist[15]
-#define NOPTS 16
-#else
-#define NOPTS 14
#endif
-/* $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */
+#ifndef CONFIG_FEATURE_COMMAND_EDITING_VI
+#define setvimode(on) viflag = 0 /* forcibly keep the option off */
+#endif
+/* options.c */
-static const char *const optletters_optnames[NOPTS] = {
+
+static const char *const optletters_optnames[] = {
"e" "errexit",
"f" "noglob",
"I" "ignoreeof",
"a" "allexport",
"b" "notify",
"u" "nounset",
- "q" "quietprofile",
+ "\0" "vi",
#ifdef DEBUG
"\0" "nolog",
"\0" "debug",
#define optletters(n) optletters_optnames[(n)][0]
#define optnames(n) (&optletters_optnames[(n)][1])
+#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
static char optlist[NOPTS];
static int setcmd(int, char **);
static int nextopt(const char *);
-/* $NetBSD: redir.h,v 1.14 2002/11/24 22:35:43 christos Exp $ */
+/* redir.h */
/* flags passed to redirect */
#define REDIR_PUSH 01 /* save previous values of file descriptors */
static int copyfd(int, int);
static int redirectsafe(union node *, int);
-/* $NetBSD: show.h,v 1.6 2003/01/22 20:36:04 dsl Exp $ */
+/* show.h */
#ifdef DEBUG
static void opentrace(void);
#endif
-/* $NetBSD: trap.h,v 1.16 2002/11/24 22:35:43 christos Exp $ */
+/* trap.h */
/* trap handler commands */
static void setsignal(int);
static void ignoresig(int);
static void onsig(int);
-static void dotrap(void);
+static int dotrap(void);
static void setinteractive(int);
-static void exitshell(void) __attribute__((__noreturn__));
+static void exitshell(void) ATTRIBUTE_NORETURN;
static int decode_signal(const char *, int);
/*
{
evalskip = 0;
loopnest = 0;
- funcnest = 0;
}
/* from input.c: */
#endif /* CONFIG_ASH_ALIAS */
-/* $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
+/* cd.c */
/*
* The cd and pwd commands.
else if (dest[0] == '-' && dest[1] == '\0') {
dest = bltinlookup("OLDPWD");
flags |= CD_PRINT;
- goto step7;
}
if (!dest)
dest = nullstr;
break;
}
} while (path);
- error("can't cd to %s", dest);
+ sh_error("can't cd to %s", dest);
/* NOTREACHED */
out:
if (flags & CD_PRINT)
setvar("PWD", dir, VEXPORT);
}
-/* $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
+/* error.c */
/*
* Errors and exceptions.
static void exverror(int, const char *, va_list)
- __attribute__((__noreturn__));
+ ATTRIBUTE_NORETURN;
/*
* Called to raise an exception. Since C doesn't include exceptions, we
int i;
intpending = 0;
+#if 0
+ /* comment by vodz: its strange for me, this programm don`t use other
+ signal block */
sigsetmask(0);
+#endif
i = EXSIG;
if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
if (!(rootshell && iflag)) {
static void
exvwarning(const char *msg, va_list ap)
{
- FILE *errs;
- const char *name;
- const char *fmt;
+ FILE *errs;
- errs = stderr;
- name = arg0;
- fmt = "%s: ";
- if (commandname) {
- name = commandname;
- fmt = "%s: %d: ";
- }
- fprintf(errs, fmt, name, startlinno);
- vfprintf(errs, msg, ap);
- outcslow('\n', errs);
+ errs = stderr;
+ fprintf(errs, "%s: ", arg0);
+ if (commandname) {
+ const char *fmt = (!iflag || parsefile->fd) ?
+ "%s: %d: " : "%s: ";
+ fprintf(errs, fmt, commandname, startlinno);
+ }
+ vfprintf(errs, msg, ap);
+ outcslow('\n', errs);
}
/*
static void
-error(const char *msg, ...)
+sh_error(const char *msg, ...)
{
va_list ap;
}
-/* $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $ */
+/* eval.c */
/*
* Evaluate a command.
*/
/*
- * The eval commmand.
+ * The eval command.
*/
static int
STPUTC('\0', concat);
p = grabstackstr(concat);
}
- evalstring(p, EV_TESTED);
+ evalstring(p, ~SKIPEVAL);
+
}
return exitstatus;
}
* Execute a command or commands contained in a string.
*/
-static void
-evalstring(char *s, int flag)
+static int
+evalstring(char *s, int mask)
{
union node *n;
struct stackmark smark;
+ int skip;
- setstackmark(&smark);
setinputstring(s);
+ setstackmark(&smark);
+ skip = 0;
while ((n = parsecmd(0)) != NEOF) {
- evaltree(n, flag);
+ evaltree(n, 0);
popstackmark(&smark);
- if (evalskip)
+ skip = evalskip;
+ if (skip)
break;
}
popfile();
- popstackmark(&smark);
+
+ skip &= mask;
+ evalskip = skip;
+ return skip;
}
break;
}
out:
- if (pendingsigs)
- dotrap();
- if (flags & EV_EXIT || checkexit & exitstatus)
+ if ((checkexit & exitstatus))
+ evalskip |= SKIPEVAL;
+ else if (pendingsigs && dotrap())
+ goto exexit;
+
+ if (flags & EV_EXIT) {
+exexit:
exraise(EXEXIT);
+ }
}
if (lp->next) {
if (pipe(pip) < 0) {
close(prevfd);
- error("Pipe call failed");
+ sh_error("Pipe call failed");
}
}
if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
struct job *jp;
if (pipe(pip) < 0)
- error("Pipe call failed");
+ sh_error("Pipe call failed");
jp = makejob(n, 1);
if (forkshell(jp, n, FORK_NOJOB) == 0) {
FORCEINTON;
}
#endif
+static inline int
+isassignment(const char *p)
+{
+ const char *q = endofname(p);
+ if (p == q)
+ return 0;
+ return *q == '=';
+}
+#ifdef CONFIG_ASH_EXPAND_PRMT
+static const char *expandstr(const char *ps);
+#else
+#define expandstr(s) s
+#endif
/*
* Execute a simple command.
int cmd_is_exec;
int status;
char **nargv;
+ struct builtincmd *bcmd;
+ int pseudovarflag = 0;
/* First expand the arguments. */
TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
*arglist.lastp = NULL;
argc = 0;
+ if (cmd->ncmd.args)
+ {
+ bcmd = find_builtin(cmd->ncmd.args->narg.text);
+ pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
+ }
+
for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
struct strlist **spp;
spp = arglist.lastp;
- expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+ if (pseudovarflag && isassignment(argp->narg.text))
+ expandarg(argp, &arglist, EXP_VARTILDE);
+ else
+ expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+
for (sp = *spp; sp; sp = sp->next)
argc++;
}
const char *p = " %s";
p++;
- dprintf(preverrout_fd, p, ps4val());
+ dprintf(preverrout_fd, p, expandstr(ps4val()));
sp = varlist.list;
for(n = 0; n < 2; n++) {
}
sp = arglist.list;
}
- xwrite(preverrout_fd, "\n", 1);
+ bb_full_write(preverrout_fd, "\n", 1);
}
cmd_is_exec = 0;
find_command(argv[0], &cmdentry, cmd_flag, path);
if (cmdentry.cmdtype == CMDUNKNOWN) {
status = 127;
- flushout(stderr);
+ flusherr();
goto bail;
}
localvars = NULL;
shellparam.malloc = 0;
func->count++;
+ funcnest++;
INTON;
shellparam.nparam = argc - 1;
shellparam.p = argv + 1;
shellparam.optind = 1;
shellparam.optoff = -1;
#endif
- funcnest++;
evaltree(&func->n, flags & EV_TESTED);
- funcnest--;
funcdone:
INTOFF;
+ funcnest--;
freefunc(func);
poplocalvars();
localvars = savelocalvars;
shellparam = saveparam;
handler = savehandler;
INTON;
- if (evalskip == SKIPFUNC) {
- evalskip = 0;
- skipcount = 0;
- }
+ evalskip &= ~SKIPFUNC;
return e;
}
+static inline int
+goodname(const char *p)
+{
+ return !*endofname(p);
+}
+
/*
* Search for a command. This is called before we fork so that the
* location of the command will be available in the parent as well as
- * the child.
+ * the child. The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
*/
static void
struct cmdentry entry;
if (n->type == NCMD && n->ncmd.args)
- find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
+ if (goodname(n->ncmd.args->narg.text))
+ find_command(n->ncmd.args->narg.text, &entry, 0,
+ pathval());
}
int n = argc > 1 ? number(argv[1]) : 1;
if (n <= 0)
- error(illnum, argv[1]);
+ sh_error(illnum, argv[1]);
if (n > loopnest)
n = loopnest;
if (n > 0) {
static int
returncmd(int argc, char **argv)
{
- int ret = argc > 1 ? number(argv[1]) : exitstatus;
-
- if (funcnest) {
- evalskip = SKIPFUNC;
- skipcount = 1;
- return ret;
- }
- else {
- /* Do what ksh does; skip the rest of the file */
- evalskip = SKIPFILE;
- skipcount = 1;
- return ret;
- }
+ /*
+ * If called outside a function, do what ksh does;
+ * skip the rest of the file.
+ */
+ evalskip = funcnest ? SKIPFUNC : SKIPFILE;
+ return argv[1] ? number(argv[1]) : exitstatus;
}
}
-/* $NetBSD: exec.c,v 1.35 2003/01/22 20:36:04 dsl Exp $ */
+/* exec.c */
/*
* When commands are first encountered, they are entered in a hash table.
char *cmdname;
int e;
char **envp;
+ int exerrno;
clearredir(1);
envp = environment();
exerrno = 2;
break;
}
+ exitstatus = exerrno;
TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
argv[0], e, suppressint ));
exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
{
int repeated = 0;
#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
- int flg_bb = 0;
- char *name = cmd;
-
-#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
- name = bb_get_last_path_component(name);
- if(find_applet_by_name(name) != NULL)
- flg_bb = 1;
-#else
- if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
- flg_bb = 1;
- }
-#endif
- if(flg_bb) {
- char **ap;
- char **new;
-
- *argv = name;
- if(strcmp(name, "busybox")) {
- for (ap = argv; *ap; ap++);
- ap = new = xmalloc((ap - argv + 2) * sizeof(char *));
- *ap++ = cmd = "/bin/busybox";
- while ((*ap++ = *argv++));
- argv = new;
- repeated++;
- } else {
- cmd = "/bin/busybox";
- }
+ if(find_applet_by_name(cmd) != NULL) {
+ /* re-exec ourselves with the new arguments */
+ execve("/proc/self/exe",argv,envp);
+ /* If proc isn't mounted, try hardcoded path to busybox binary*/
+ execve("/bin/busybox",argv,envp);
+ /* If they called chroot or otherwise made the binary no longer
+ * executable, fall through */
}
#endif
for (ap = argv; *ap; ap++)
;
ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
- *ap++ = cmd = "/bin/sh";
+ ap[1] = cmd;
+ *ap = cmd = (char *)DEFAULT_SHELL;
+ ap += 2;
+ argv++;
while ((*ap++ = *argv++))
;
argv = new;
readcmdfile(fullname);
if ((cmdp = cmdlookup(name, 0)) == NULL ||
cmdp->cmdtype != CMDFUNCTION)
- error("%s not defined in %s", name, fullname);
+ sh_error("%s not defined in %s", name, fullname);
stunalloc(fullname);
goto success;
}
commandcmd(int argc, char **argv)
{
int c;
- int default_path = 0;
- int verify_only = 0;
- int verbose_verify_only = 0;
+ enum {
+ VERIFY_BRIEF = 1,
+ VERIFY_VERBOSE = 2,
+ } verify = 0;
while ((c = nextopt("pvV")) != '\0')
- switch (c) {
- default:
+ if (c == 'V')
+ verify |= VERIFY_VERBOSE;
+ else if (c == 'v')
+ verify |= VERIFY_BRIEF;
#ifdef DEBUG
- fprintf(stderr,
-"command: nextopt returned character code 0%o\n", c);
- return EX_SOFTWARE;
+ else if (c != 'p')
+ abort();
#endif
- case 'p':
- default_path = 1;
- break;
- case 'v':
- verify_only = 1;
- break;
- case 'V':
- verbose_verify_only = 1;
- break;
- }
-
- if (default_path + verify_only + verbose_verify_only > 1 ||
- !*argptr) {
- fprintf(stderr,
- "command [-p] command [arg ...]\n"
- "command {-v|-V} command\n");
- return EX_USAGE;
- }
-
- if (verify_only || verbose_verify_only) {
- return describe_command(*argptr, verbose_verify_only);
- }
+ if (verify)
+ return describe_command(*argptr, verify - VERIFY_BRIEF);
return 0;
}
#endif
-/* $NetBSD: expand.c,v 1.56 2002/11/24 22:35:39 christos Exp $ */
+/* expand.c */
/*
* Routines to expand arguments to commands. We have to deal with
static void expbackq(union node *, int, int);
static const char *subevalvar(char *, char *, int, int, int, int, int);
static char *evalvar(char *, int);
-static int varisset(char *, int);
static void strtodest(const char *, int, int);
static void memtodest(const char *p, size_t len, int syntax, int quotes);
-static void varvalue(char *, int, int);
+static ssize_t varvalue(char *, 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 int patmatch(char *, const char *);
-static int cvtnum(long);
+static int cvtnum(arith_t);
static size_t esclen(const char *, const char *);
static char *scanleft(char *, char *, char *, char *, int, int);
static char *scanright(char *, char *, char *, char *, int, int);
static void varunset(const char *, const char *, const char *, int)
- __attribute__((__noreturn__));
+ ATTRIBUTE_NORETURN;
#define pmatch(a, b) !fnmatch((a), (b), 0)
{
herefd = fd;
expandarg(arg, (struct arglist *)NULL, 0);
- xwrite(fd, stackblock(), expdest - (char *)stackblock());
+ bb_full_write(fd, stackblock(), expdest - (char *)stackblock());
}
ifsfirst.next = NULL;
ifslastp = NULL;
argstr(arg->narg.text, flag);
+ p = _STPUTC('\0', expdest);
+ expdest = p - 1;
if (arglist == NULL) {
return; /* here document expanded */
}
- STPUTC('\0', expdest);
- p = grabstackstr(expdest);
+ p = grabstackstr(p);
exparg.lastp = &exparg.list;
/*
* TODO - EXP_REDIR
done:
*p = '\0';
if (*name == '\0') {
- if ((home = lookupvar(homestr)) == NULL)
- goto lose;
+ home = lookupvar(homestr);
} else {
if ((pw = getpwnam(name)) == NULL)
goto lose;
home = pw->pw_dir;
}
- if (*home == '\0')
+ if (!home || !*home)
goto lose;
*p = c;
startloc = expdest - (char *)stackblock();
p--;
#ifdef DEBUG
if (p < start) {
- error("missing CTLARI (shouldn't happen)");
+ sh_error("missing CTLARI (shouldn't happen)");
}
#endif
}
char *var;
int patloc;
int c;
- int set;
int startloc;
- size_t varlen;
+ ssize_t varlen;
int easy;
int quotes;
int quoted;
quoted = varflags & VSQUOTE;
var = p;
easy = (!quoted || (*var == '@' && shellparam.nparam));
- varlen = 0;
startloc = expdest - (char *)stackblock();
p = strchr(p, '=') + 1;
- if (!is_name(*var)) {
- set = varisset(var, varflags & VSNUL);
- set--;
- if (subtype == VSPLUS)
- goto vsplus;
- if (++set) {
- varvalue(var, quoted, flag);
- if (subtype == VSLENGTH) {
- varlen =
- expdest - (char *)stackblock() -
- startloc;
- STADJUST(-varlen, expdest);
- goto vslen;
- }
- }
- } else {
- const char *val;
again:
- /* jump here after setting a variable with ${var=text} */
- val = lookupvar(var);
- set = !val || ((varflags & VSNUL) && !*val);
- if (subtype == VSPLUS)
- goto vsplus;
- if (--set) {
- varlen = strlen(val);
- if (subtype == VSLENGTH)
- goto vslen;
- memtodest(
- val, varlen, quoted ? DQSYNTAX : BASESYNTAX,
- quotes
- );
- }
- }
+ varlen = varvalue(var, varflags, flag);
+ if (varflags & VSNUL)
+ varlen--;
+ if (subtype == VSPLUS) {
+ varlen = -1 - varlen;
+ goto vsplus;
+ }
if (subtype == VSMINUS) {
vsplus:
- if (!set) {
+ if (varlen < 0) {
argstr(
p, flag | EXP_TILDE |
(quoted ? EXP_QWORD : EXP_WORD)
}
if (subtype == VSASSIGN || subtype == VSQUESTION) {
- if (!set) {
+ if (varlen < 0) {
if (subevalvar(p, var, 0, subtype, startloc,
varflags, 0)) {
varflags &= ~VSNUL;
goto end;
}
- if (!set && uflag)
+ if (varlen < 0 && uflag)
varunset(p, var, 0, 0);
if (subtype == VSLENGTH) {
-vslen:
- cvtnum(varlen);
+ cvtnum(varlen > 0 ? varlen : 0);
goto record;
}
}
#endif
- if (set) {
+ if (varlen >= 0) {
/*
* Terminate the string and start recording the pattern
* right after it
if ((c = *p++) == CTLESC)
p++;
else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
- if (set)
+ if (varlen >= 0)
argbackq = argbackq->next;
} else if (c == CTLVAR) {
if ((*p++ & VSTYPE) != VSNORMAL)
}
-
-/*
- * Test whether a specialized variable is set.
- */
-
-static int
-varisset(char *name, int nulok)
-{
- if (*name == '!')
- return backgndpid != 0;
- else if (*name == '@' || *name == '*') {
- if (*shellparam.p == NULL)
- return 0;
-
- if (nulok) {
- char **av;
-
- for (av = shellparam.p; *av; av++)
- if (**av != '\0')
- return 1;
- return 0;
- }
- } else if (is_digit(*name)) {
- char *ap;
- int num = atoi(name);
-
- if (num > shellparam.nparam)
- return 0;
-
- if (num == 0)
- ap = arg0;
- else
- ap = shellparam.p[num - 1];
-
- if (nulok && (ap == NULL || *ap == '\0'))
- return 0;
- }
- return 1;
-}
-
-
/*
* Put a string on the stack.
*/
q = makestrspace(len * 2, q);
while (len--) {
- int c = *p++;
+ int c = SC2INT(*p++);
if (!c)
continue;
if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
* Add the value of a specialized variable to the stack string.
*/
-static void
-varvalue(char *name, int quoted, int flags)
+static ssize_t
+varvalue(char *name, int varflags, int flags)
{
int num;
char *p;
int i;
- int sep;
+ int sep = 0;
int sepq = 0;
+ ssize_t len = 0;
char **ap;
int syntax;
- int allow_split = flags & EXP_FULL;
+ int quoted = varflags & VSQUOTE;
+ int subtype = varflags & VSTYPE;
int quotes = flags & (EXP_FULL | EXP_CASE);
+ if (quoted && (flags & EXP_FULL))
+ sep = 1 << CHAR_BIT;
+
syntax = quoted ? DQSYNTAX : BASESYNTAX;
switch (*name) {
case '$':
goto numvar;
case '!':
num = backgndpid;
+ if (num == 0)
+ return -1;
numvar:
- cvtnum(num);
+ len = cvtnum(num);
break;
case '-':
- for (i = 0 ; i < NOPTS ; i++) {
- if (optlist[i])
- STPUTC(optletters(i), expdest);
+ p = makestrspace(NOPTS, expdest);
+ for (i = NOPTS - 1; i >= 0; i--) {
+ if (optlist[i]) {
+ USTPUTC(optletters(i), p);
+ len++;
+ }
}
+ expdest = p;
break;
case '@':
- if (allow_split && quoted) {
- sep = 1 << CHAR_BIT;
+ if (sep)
goto param;
- }
/* fall through */
case '*':
- sep = ifsset() ? ifsval()[0] : ' ';
- if (quotes) {
- sepq = (SIT(sep, syntax) == CCTL) || (SIT(sep, syntax) == CBACK);
- }
+ sep = ifsset() ? SC2INT(ifsval()[0]) : ' ';
+ if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
+ sepq = 1;
param:
- for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
- strtodest(p, syntax, quotes);
+ if (!(ap = shellparam.p))
+ return -1;
+ while ((p = *ap++)) {
+ size_t partlen;
+
+ partlen = strlen(p);
+ len += partlen;
+
+ if (!(subtype == VSPLUS || subtype == VSLENGTH))
+ memtodest(p, partlen, syntax, quotes);
+
if (*ap && sep) {
- p = expdest;
+ char *q;
+
+ len++;
+ if (subtype == VSPLUS || subtype == VSLENGTH) {
+ continue;
+ }
+ q = expdest;
if (sepq)
- STPUTC(CTLESC, p);
- STPUTC(sep, p);
- expdest = p;
+ STPUTC(CTLESC, q);
+ STPUTC(sep, q);
+ expdest = q;
}
}
- break;
+ return len;
case '0':
- strtodest(arg0, syntax, quotes);
- break;
- default:
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
num = atoi(name);
- if (num > 0 && num <= shellparam.nparam) {
- strtodest(shellparam.p[num - 1], syntax, quotes);
- }
- break;
+ if (num < 0 || num > shellparam.nparam)
+ return -1;
+ p = num ? shellparam.p[num - 1] : arg0;
+ goto value;
+ default:
+ p = lookupvar(name);
+value:
+ if (!p)
+ return -1;
+
+ len = strlen(p);
+ if (!(subtype == VSPLUS || subtype == VSLENGTH))
+ memtodest(p, len, syntax, quotes);
+ return len;
}
+
+ if (subtype == VSPLUS || subtype == VSLENGTH)
+ STADJUST(-len, expdest);
+ return len;
}
char *start;
char *endname;
int metaflag;
- struct stat64 statb;
+ struct stat statb;
DIR *dirp;
struct dirent *dp;
int atend;
p++;
*enddir++ = *p;
} while (*p++);
- if (metaflag == 0 || lstat64(expdir, &statb) >= 0)
+ if (metaflag == 0 || lstat(expdir, &statb) >= 0)
addfname(expdir);
return;
}
*/
static int
-cvtnum(long num)
+cvtnum(arith_t num)
{
int len;
expdest = makestrspace(32, expdest);
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+ len = fmtstr(expdest, 32, "%lld", (long long) num);
+#else
len = fmtstr(expdest, 32, "%ld", num);
+#endif
STADJUST(len, expdest);
return len;
}
} else
msg = umsg;
}
- error("%.*s: %s%s", end - var - 1, var, msg, tail);
+ sh_error("%.*s: %s%s", end - var - 1, var, msg, tail);
}
-/* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */
+/* input.c */
/*
* This implements the input routines used by the parser.
*/
#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
-#define IBUFSIZ (BUFSIZ + 1)
static void pushfile(void);
-/*
- * Read a line from the script.
- */
-
-static inline char *
-pfgets(char *line, int len)
-{
- char *p = line;
- int nleft = len;
- int c;
-
- while (--nleft > 0) {
- c = pgetc2();
- if (c == PEOF) {
- if (p == line)
- return NULL;
- break;
- }
- *p++ = c;
- if (c == '\n')
- break;
- }
- *p = '\0';
- return line;
-}
-
-
/*
* Read a character from the script, returning PEOF on end of file.
* Nul characters in the input are silently discarded.
*/
-#define pgetc_as_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
+
+#define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer())
#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
#define pgetc_macro() pgetc()
}
#endif
+/*
+ * Read a line from the script.
+ */
+
+static inline char *
+pfgets(char *line, int len)
+{
+ char *p = line;
+ int nleft = len;
+ int c;
+
+ while (--nleft > 0) {
+ c = pgetc2();
+ if (c == PEOF) {
+ if (p == line)
+ return NULL;
+ break;
+ }
+ *p++ = c;
+ if (c == '\n')
+ break;
+ }
+ *p = '\0';
+ return line;
+}
+
+
#ifdef CONFIG_FEATURE_COMMAND_EDITING
+#ifdef CONFIG_ASH_EXPAND_PRMT
+static char *cmdedit_prompt;
+#else
static const char *cmdedit_prompt;
+#endif
static inline void putprompt(const char *s)
{
+#ifdef CONFIG_ASH_EXPAND_PRMT
+ free(cmdedit_prompt);
+ cmdedit_prompt = bb_xstrdup(s);
+#else
cmdedit_prompt = s;
+#endif
}
#else
static inline void putprompt(const char *s)
if (!iflag || parsefile->fd)
nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
else {
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+ cmdedit_path_lookup = pathval();
+#endif
nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
if(nr == 0) {
/* Ctrl+C presend */
- raise(SIGINT);
+ if(trap[SIGINT]) {
+ buf[0] = '\n';
+ buf[1] = 0;
+ raise(SIGINT);
+ return 1;
+ }
goto retry;
}
- if(nr < 0) {
+ if(nr < 0 && errno == 0) {
/* Ctrl+D presend */
nr = 0;
}
int
preadbuffer(void)
{
- char *p, *q;
+ char *q;
int more;
char savec;
#endif
popstring();
if (--parsenleft >= 0)
- return (*parsenextc++);
+ return SC2INT(*parsenextc++);
}
if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
return PEOF;
flushall();
+ more = parselleft;
+ if (more <= 0) {
again:
- if (parselleft <= 0) {
- if ((parselleft = preadfd()) <= 0) {
+ if ((more = preadfd()) <= 0) {
parselleft = parsenleft = EOF_NLEFT;
return PEOF;
}
}
- q = p = parsenextc;
+ q = parsenextc;
/* delete nul characters */
- for (more = 1; more;) {
- switch (*p) {
- case '\0':
- p++; /* Skip nul */
- goto check;
+ for (;;) {
+ int c;
- case '\n':
- parsenleft = q - parsenextc;
- more = 0; /* Stop processing here */
- break;
+ more--;
+ c = *q;
+ if (!c)
+ memmove(q, q + 1, more);
+ else {
+ q++;
+ if (c == '\n') {
+ parsenleft = q - parsenextc - 1;
+ break;
+ }
}
- *q++ = *p++;
-check:
- if (--parselleft <= 0 && more) {
+ if (more <= 0) {
parsenleft = q - parsenextc - 1;
if (parsenleft < 0)
goto again;
- more = 0;
+ break;
}
}
+ parselleft = more;
savec = *q;
*q = '\0';
*q = savec;
- return *parsenextc++;
+ return SC2INT(*parsenextc++);
}
/*
* old input onto the stack first.
*/
-void
-setinputfile(const char *fname, int push)
+static int
+setinputfile(const char *fname, int flags)
{
int fd;
int fd2;
INTOFF;
- if ((fd = open(fname, O_RDONLY)) < 0)
- error("Can't open %s", fname);
+ if ((fd = open(fname, O_RDONLY)) < 0) {
+ if (flags & INPUT_NOFILE_OK)
+ goto out;
+ sh_error("Can't open %s", fname);
+ }
if (fd < 10) {
fd2 = copyfd(fd, 10);
close(fd);
if (fd2 < 0)
- error("Out of file descriptors");
+ sh_error("Out of file descriptors");
fd = fd2;
}
- setinputfd(fd, push);
+ setinputfd(fd, flags & INPUT_PUSH_FILE);
+out:
INTON;
+ return fd;
}
}
}
-/* $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $ */
+/* jobs.c */
/* mode flags for set_curjob */
#define CUR_DELETE 2
put after all stopped jobs. */
do {
jp1 = *jpp;
-#ifdef JOBS
+#if JOBS
if (!jp1 || jp1->state != JOBSTOPPED)
#endif
break;
jpp = &jp1->prev_job;
} while (1);
/* FALLTHROUGH */
-#ifdef JOBS
+#if JOBS
case CUR_STOPPED:
#endif
/* newly stopped job - becomes curjob */
if (argc <= 1) {
usage:
- error(
+ sh_error(
"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
"kill -l [exitstatus]"
);
case 's':
signo = decode_signal(optionarg, 1);
if (signo < 0) {
- error(
+ sh_error(
"invalid signal number or name: %s",
optionarg
);
if (name)
out1fmt(snlfmt, name);
else
- error("invalid signal number or exit status: %s", *argptr);
+ sh_error("invalid signal number or exit status: %s", *argptr);
return 0;
}
if (**argv == '%') {
jp = getjob(*argv, 0);
pid = -jp->ps[0].pid;
- } else
- pid = number(*argv);
+ } else {
+ pid = **argv == '-' ?
+ -number(*argv + 1) : number(*argv);
+ }
if (kill(pid, signo) != 0) {
- sh_warnx("%m\n");
+ sh_warnx("(%d) - %m", pid);
i = 1;
}
} while (*++argv);
}
#endif
-#ifdef JOBS
+#if JOBS
static int
fgcmd(int argc, char **argv)
{
int st;
col = 0;
- st = WEXITSTATUS(status);
if (!WIFEXITED(status)) {
- st = WSTOPSIG(status);
#if JOBS
- if (!WIFSTOPPED(status))
- st = WTERMSIG(status);
+ if (WIFSTOPPED(status))
+ st = WSTOPSIG(status);
+ else
#endif
+ st = WTERMSIG(status);
if (sigonly) {
if (st == SIGINT || st == SIGPIPE)
goto out;
+#if JOBS
if (WIFSTOPPED(status))
goto out;
+#endif
}
st &= 0x7f;
- col = fmtstr(s, 32, u_signal_names(NULL, &st, 0));
+ col = fmtstr(s, 32, strsignal(st));
if (WCOREDUMP(status)) {
col += fmtstr(s + col, 16, " (core dumped)");
}
} else if (!sigonly) {
+ st = WEXITSTATUS(status);
if (st)
col = fmtstr(s, 16, "Done(%d)", st);
else
#endif
return jp;
err:
- error(err_msg, name);
+ sh_error(err_msg, name);
}
jq--;
#define joff(p) ((struct job *)((char *)(p) + l))
#define jmove(p) (p) = (void *)((char *)(p) + offset)
- if (likely(joff(jp)->ps == &jq->ps0))
+ if (xlikely(joff(jp)->ps == &jq->ps0))
jmove(joff(jp)->ps);
if (joff(jp)->prev_job)
jmove(joff(jp)->prev_job);
static inline void
forkchild(struct job *jp, union node *n, int mode)
{
- int wasroot;
+ int oldlvl;
TRACE(("Child shell %d\n", getpid()));
- wasroot = rootshell;
- rootshell = 0;
+ oldlvl = shlvl;
+ shlvl++;
closescript();
clear_traps();
#if JOBS
/* do job control only in root shell */
jobctl = 0;
- if (mode != FORK_NOJOB && jp->jobctl && wasroot) {
+ if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
pid_t pgrp;
if (jp->nprocs == 0)
ignoresig(SIGQUIT);
if (jp->nprocs == 0) {
close(0);
- if (open(_PATH_DEVNULL, O_RDONLY) != 0)
- error("Can't open %s", _PATH_DEVNULL);
+ if (open(bb_dev_null, O_RDONLY) != 0)
+ sh_error("Can't open %s", bb_dev_null);
}
}
- if (wasroot && iflag) {
+ if (!oldlvl && iflag) {
setsignal(SIGINT);
setsignal(SIGQUIT);
setsignal(SIGTERM);
TRACE(("Fork failed, errno=%d", errno));
if (jp)
freejob(jp);
- error("Cannot fork");
+ sh_error("Cannot fork");
}
if (pid == 0)
forkchild(jp, n, mode);
* the interactive program catches interrupts, the user doesn't want
* these interrupts to also abort the loop. The approach we take here
* is to have the shell ignore interrupt signals while waiting for a
- * forground process to terminate, and then send itself an interrupt
+ * foreground process to terminate, and then send itself an interrupt
* signal if the child process was terminated by an interrupt signal.
* Unfortunately, some programs want to do a bit of cleanup and then
* exit on interrupt; unless these processes terminate themselves by
}
if (sp->status == -1)
state = JOBRUNNING;
-#ifdef JOBS
+#if JOBS
if (state == JOBRUNNING)
continue;
if (WIFSTOPPED(sp->status)) {
if (thisjob)
goto gotjob;
}
-#ifdef JOBS
+#if JOBS
if (!WIFSTOPPED(status))
#endif
if (thisjob->state != state) {
TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state));
thisjob->state = state;
-#ifdef JOBS
+#if JOBS
if (state == JOBSTOPPED) {
set_curjob(thisjob, CUR_STOPPED);
}
const char *p;
char s[2];
+ if (!n)
+ return;
switch (n->type) {
default:
#if DEBUG
char *nextc;
int subtype = 0;
int quoted = 0;
- static const char *const vstype[16] = {
- nullstr, "}", "-", "+", "?", "=",
- "#", "##", "%", "%%"
+ static const char vstype[VSTYPE + 1][4] = {
+ "", "}", "-", "+", "?", "=",
+ "%", "%%", "#", "##"
};
-
nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
p = s;
while ((c = *p++) != 0) {
goto dostr;
break;
case CTLENDVAR:
+ str = "\"}" + !(quoted & 1);
quoted >>= 1;
subtype = 0;
- if (quoted & 1) {
- str = "\"}";
- goto dostr;
- }
- c = '}';
- break;
+ goto dostr;
case CTLBACKQ:
str = "$(...)";
goto dostr;
case '=':
if (subtype == 0)
break;
+ if ((subtype & VSTYPE) != VSNORMAL)
+ quoted <<= 1;
str = vstype[subtype & VSTYPE];
if (subtype & VSNUL)
c = ':';
else
- c = *str++;
- if (c != '}')
- quoted <<= 1;
+ goto checkstr;
break;
case '\'':
case '\\':
break;
}
USTPUTC(c, nextc);
+checkstr:
if (!str)
continue;
dostr:
xtcsetpgrp(int fd, pid_t pgrp)
{
if (tcsetpgrp(fd, pgrp))
- error("Cannot set tty process group (%m)");
+ sh_error("Cannot set tty process group (%m)");
}
#endif /* JOBS */
}
#ifdef CONFIG_ASH_MAIL
-/* $NetBSD: mail.c,v 1.15 2002/11/24 22:35:40 christos Exp $ */
+/* mail.c */
/*
* Routines to check for mail. (Perhaps make part of main.c?)
#endif /* CONFIG_ASH_MAIL */
-/* $NetBSD: main.c,v 1.46 2002/12/11 19:12:18 christos Exp $ */
+/* main.c */
#if PROFILE
#endif
state = 0;
if (setjmp(jmploc.loc)) {
- int status;
int e;
+ int s;
reset();
e = exception;
- switch (exception) {
- case EXEXEC:
- status = exerrno;
- break;
-
- case EXERROR:
- status = 2;
- break;
-
- default:
- status = exitstatus;
- break;
- }
- exitstatus = status;
-
- if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
+ if (e == EXERROR)
+ exitstatus = 2;
+ s = state;
+ if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
exitshell();
if (e == EXINT) {
}
popstackmark(&smark);
FORCEINTON; /* enable interrupts */
- if (state == 1)
+ if (s == 1)
goto state1;
- else if (state == 2)
+ else if (s == 2)
goto state2;
- else if (state == 3)
+ else if (s == 3)
goto state3;
else
goto state4;
trputs("Shell args: "); trargs(argv);
#endif
rootpid = getpid();
- rootshell = 1;
+
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+ rseed = rootpid + ((time_t)time((time_t *)0));
+#endif
init();
setstackmark(&smark);
procargs(argc, argv);
* loop; it turns on prompting if the shell is interactive.
*/
-static void
+static int
cmdloop(int top)
{
union node *n;
int numeof = 0;
TRACE(("cmdloop(%d) called\n", top));
- setstackmark(&smark);
for (;;) {
- if (pendingsigs)
- dotrap();
+ int skip;
+
+ setstackmark(&smark);
#if JOBS
if (jobctl)
showjobs(stderr, SHOW_CHANGED);
out2str("\nUse \"exit\" to leave shell.\n");
}
numeof++;
- } else if (n != NULL && nflag == 0) {
+ } else if (nflag == 0) {
job_warning = (job_warning == 2) ? 1 : 0;
numeof = 0;
evaltree(n, 0);
}
popstackmark(&smark);
- setstackmark(&smark);
- if (evalskip == SKIPFILE) {
+ skip = evalskip;
+
+ if (skip) {
evalskip = 0;
- break;
+ return skip & SKIPEVAL;
}
}
- popstackmark(&smark);
+
+ return 0;
}
static void
read_profile(const char *name)
{
- int fd;
- int xflag_set = 0;
- int vflag_set = 0;
+ int skip;
- INTOFF;
- if ((fd = open(name, O_RDONLY)) >= 0)
- setinputfd(fd, 1);
- INTON;
- if (fd < 0)
+ if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
return;
- /* -q turns off -x and -v just when executing init files */
- if (qflag) {
- if (xflag)
- xflag = 0, xflag_set = 1;
- if (vflag)
- vflag = 0, vflag_set = 1;
- }
- cmdloop(0);
- if (qflag) {
- if (xflag_set)
- xflag = 1;
- if (vflag_set)
- vflag = 1;
- }
+
+ skip = cmdloop(0);
popfile();
+
+ if (skip)
+ exitshell();
}
static void
readcmdfile(char *name)
{
- int fd;
-
- INTOFF;
- if ((fd = open(name, O_RDONLY)) >= 0)
- setinputfd(fd, 1);
- else
- error("Can't open %s", name);
- INTON;
+ setinputfile(name, INPUT_PUSH_FILE);
cmdloop(0);
popfile();
}
}
/* not found in the PATH */
- error(not_found_msg, name);
+ sh_error(not_found_msg, name);
/* NOTREACHED */
}
-int
-dotcmd(int argc, char **argv)
+static int dotcmd(int argc, char **argv)
{
- exitstatus = 0;
+ struct strlist *sp;
+ volatile struct shparam saveparam;
+ int status = 0;
+
+ for (sp = cmdenviron; sp; sp = sp->next)
+ setvareq(bb_xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
- if (argc >= 2) { /* That's what SVR2 does */
+ if (argc >= 2) { /* That's what SVR2 does */
char *fullname;
- struct stackmark smark;
- setstackmark(&smark);
fullname = find_dot_file(argv[1]);
- setinputfile(fullname, 1);
+
+ if (argc > 2) {
+ saveparam = shellparam;
+ shellparam.malloc = 0;
+ shellparam.nparam = argc - 2;
+ shellparam.p = argv + 2;
+ };
+
+ setinputfile(fullname, INPUT_PUSH_FILE);
commandname = fullname;
cmdloop(0);
popfile();
- popstackmark(&smark);
+
+ if (argc > 2) {
+ freeparam(&shellparam);
+ shellparam = saveparam;
+ };
+ status = exitstatus;
}
- return exitstatus;
+ return status;
}
/* NOTREACHED */
}
-/* $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $ */
+#ifdef CONFIG_ASH_BUILTIN_ECHO
+static int
+echocmd(int argc, char **argv)
+{
+ return bb_echo(argc, argv);
+}
+#endif
+/* memalloc.c */
/*
* Same for malloc, realloc, but returns an error when out of space.
{
p = realloc(p, nbytes);
if (p == NULL)
- error(bb_msg_memory_exhausted);
+ sh_error(bb_msg_memory_exhausted);
return p;
}
{
char *p = strdup(s);
if (!p)
- error(bb_msg_memory_exhausted);
+ sh_error(bb_msg_memory_exhausted);
return p;
}
blocksize = MINSIZE;
len = sizeof(struct stack_block) - MINSIZE + blocksize;
if (len < blocksize)
- error(bb_msg_memory_exhausted);
+ sh_error(bb_msg_memory_exhausted);
INTOFF;
sp = ckmalloc(len);
sp->prev = stackp;
newlen = stacknleft * 2;
if (newlen < stacknleft)
- error(bb_msg_memory_exhausted);
+ sh_error(bb_msg_memory_exhausted);
if (newlen < 128)
newlen += 128;
{
size_t len = stackblocksize();
if (herefd >= 0 && len >= 1024) {
- xwrite(herefd, stackblock(), len);
+ bb_full_write(herefd, stackblock(), len);
return stackblock();
}
growstackblock();
return stnputs(s, strlen(s), p);
}
-/* $NetBSD: mystring.c,v 1.15 2002/11/24 22:35:42 christos Exp $ */
+/* mystring.c */
/*
* String functions.
{
if (! is_number(s))
- error(illnum, s);
+ sh_error(illnum, s);
return atoi(s);
}
xminusc = minusc;
if (*xargv == NULL) {
if (xminusc)
- error("-c requires an argument");
+ sh_error("-c requires an argument");
sflag = 1;
}
if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
#endif
setinteractive(iflag);
setjobctl(mflag);
+ setvimode(viflag);
}
static inline void
optlist[i] = val;
return;
}
- error("Illegal option -o %s", name);
+ sh_error("Illegal option -o %s", name);
}
}
optlist[i] = val;
return;
}
- error("Illegal option -%c", flag);
+ sh_error("Illegal option -%c", flag);
/* NOTREACHED */
}
if (argc > 1)
n = number(argv[1]);
if (n > shellparam.nparam)
- error("can't shift that many");
+ sh_error("can't shift that many");
INTOFF;
shellparam.nparam -= n;
for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
#endif
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+/* Roughly copied from bash.. */
+static void change_random(const char *value)
+{
+ if(value == NULL) {
+ /* "get", generate */
+ char buf[16];
+
+ rseed = rseed * 1103515245 + 12345;
+ sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
+ /* set without recursion */
+ setvar(vrandom.text, buf, VNOFUNC);
+ vrandom.flags &= ~VNOFUNC;
+ } else {
+ /* set/reset */
+ rseed = strtoul(value, (char **)NULL, 10);
+ }
+}
+#endif
+
+
#ifdef CONFIG_ASH_GETOPTS
static int
getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
char c = '?';
int done = 0;
int err = 0;
- char s[10];
- char **optnext = optfirst + *param_optind - 1;
+ char s[12];
+ char **optnext;
- if (*param_optind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
- strlen(*(optnext - 1)) < *optoff)
+ if(*param_optind < 1)
+ return 1;
+ optnext = optfirst + *param_optind - 1;
+
+ if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
p = NULL;
else
- p = *(optnext - 1) + *optoff;
+ p = optnext[-1] + *optoff;
if (p == NULL || *p == '\0') {
/* Current word is done, advance */
- if (optnext == NULL)
- return 1;
p = *optnext;
if (p == NULL || *p != '-' || *++p == '\0') {
atend:
char **optbase;
if (argc < 3)
- error("Usage: getopts optstring var [arg]");
+ sh_error("Usage: getopts optstring var [arg]");
else if (argc == 3) {
optbase = shellparam.p;
if (shellparam.optind > shellparam.nparam + 1) {
c = *p++;
for (q = optstring ; *q != c ; ) {
if (*q == '\0')
- error("Illegal option -%c", c);
+ sh_error("Illegal option -%c", c);
if (*++q == ':')
q++;
}
if (*++q == ':') {
if (*p == '\0' && (p = *argptr++) == NULL)
- error("No arg for -%c option", c);
+ sh_error("No arg for -%c option", c);
optionarg = p;
p = NULL;
}
}
-/* $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
+/* output.c */
void
outstr(const char *p, FILE *file)
}
void
-flushout(FILE *dest)
+flusherr(void)
{
INTOFF;
- fflush(dest);
+ fflush(stderr);
INTON;
}
}
-/*
- * Version of write which resumes after a signal is caught.
- */
-
-static void
-xwrite(int fd, const void *p, size_t n)
-{
- ssize_t i;
-
- do {
- i = bb_full_write(fd, p, n);
- } while (i < 0 && errno == EINTR);
-}
-
-/* $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $ */
+/* parser.c */
/*
static int xxreadtoken(void);
static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs);
static int noexpand(char *);
-static void synexpect(int) __attribute__((__noreturn__));
-static void synerror(const char *) __attribute__((__noreturn__));
+static void synexpect(int) ATTRIBUTE_NORETURN;
+static void synerror(const char *) ATTRIBUTE_NORETURN;
static void setprompt(int);
-static inline int
-goodname(const char *p)
-{
- return !*endofname(p);
-}
-
-static inline int
-isassignment(const char *p)
-{
- const char *q = endofname(p);
- if (p == q)
- return 0;
- return *q == '=';
-}
/*
while (here) {
if (needprompt) {
setprompt(2);
- needprompt = 0;
}
readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
here->eofmark, here->striptabs);
}
if (needprompt) {
setprompt(2);
- needprompt = 0;
}
startlinno = plinno;
for (;;) { /* until token or start of word found */
}
if (needprompt) {
setprompt(2);
- needprompt = 0;
}
startlinno = plinno;
for (;;) { /* until token or start of word found */
char *out;
int len;
char line[EOFMARKLEN + 1];
- struct nodelist *bqlist;
- int quotef;
- int dblquote;
- int varnest; /* levels of variables expansion */
- int arinest; /* levels of arithmetic expansion */
- int parenlevel; /* levels of parens in arithmetic */
- int dqvarnest; /* levels of variables expansion within double quotes */
- int oldstyle;
- int prevsyntax; /* syntax before arithmetic */
+ struct nodelist *bqlist = 0;
+ int quotef = 0;
+ int dblquote = 0;
+ int varnest = 0; /* levels of variables expansion */
+ int arinest = 0; /* levels of arithmetic expansion */
+ int parenlevel = 0; /* levels of parens in arithmetic */
+ int dqvarnest = 0; /* levels of variables expansion within double quotes */
+ int oldstyle = 0;
+ int prevsyntax = 0; /* syntax before arithmetic */
#if __GNUC__
/* Avoid longjmp clobbering */
(void) &out;
if (doprompt)
setprompt(2);
} else {
- if (
- dblquote &&
+ if (dblquote &&
c != '\\' && c != '`' &&
c != '$' && (
c != '"' ||
- eofmark != NULL
- )
+ eofmark != NULL)
) {
USTPUTC(CTLESC, out);
USTPUTC('\\', out);
struct jmploc jmploc;
struct jmploc *volatile savehandler;
size_t savelen;
- int saveprompt;
+ int saveprompt = 0;
#ifdef __GNUC__
(void) &saveprompt;
#endif
for (;;) {
if (needprompt) {
setprompt(2);
- needprompt = 0;
}
switch (pc = pgetc()) {
case '`':
* more letters, underscores, and digits).
*/
-char *
+static char *
endofname(const char *name)
{
char *p;
static void
synerror(const char *msg)
{
- error("Syntax error: %s", msg);
+ sh_error("Syntax error: %s", msg);
/* NOTREACHED */
}
* should be added here.
*/
+#ifdef CONFIG_ASH_EXPAND_PRMT
+static const char *
+expandstr(const char *ps)
+{
+ union node n;
+
+ /* XXX Fix (char *) cast. */
+ setinputstring((char *)ps);
+ readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
+ popfile();
+
+ n.narg.type = NARG;
+ n.narg.next = NULL;
+ n.narg.text = wordtext;
+ n.narg.backquote = backquotelist;
+
+ expandarg(&n, NULL, 0);
+ return stackblock();
+}
+#endif
+
static void setprompt(int whichprompt)
{
const char *prompt;
+#ifdef CONFIG_ASH_EXPAND_PRMT
+ struct stackmark smark;
+#endif
+
+ needprompt = 0;
switch (whichprompt) {
case 1:
default: /* 0 */
prompt = nullstr;
}
- putprompt(prompt);
+#ifdef CONFIG_ASH_EXPAND_PRMT
+ setstackmark(&smark);
+ stalloc(stackblocksize());
+#endif
+ putprompt(expandstr(prompt));
+#ifdef CONFIG_ASH_EXPAND_PRMT
+ popstackmark(&smark);
+#endif
}
sizeof(const char *), pstrcmp);
}
-/* $NetBSD: redir.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
+/* redir.c */
/*
* Code for dealing with input/output redirection.
size_t len = 0;
if (pipe(pip) < 0)
- error("Pipe call failed");
+ sh_error("Pipe call failed");
if (redir->type == NHERE) {
len = strlen(redir->nhere.doc->narg.text);
if (len <= PIPESIZE) {
- xwrite(pip[1], redir->nhere.doc->narg.text, len);
+ bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
goto out;
}
}
#endif
signal(SIGPIPE, SIG_DFL);
if (redir->type == NHERE)
- xwrite(pip[1], redir->nhere.doc->narg.text, len);
+ bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
else
expandhere(redir->nhere.doc, pip[1]);
_exit(0);
return f;
ecreate:
- error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+ sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
eopen:
- error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
+ sh_error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
}
static inline void
if (i != EBADF) {
close(newfd);
errno = i;
- error("%d: %m", fd);
+ sh_error("%d: %m", fd);
/* NOTREACHED */
}
} else {
if (errno == EMFILE)
return EMPTY;
else
- error("%d: %m", from);
+ sh_error("%d: %m", from);
}
return newfd;
}
return err;
}
-/* $NetBSD: show.c,v 1.24 2003/01/22 20:36:04 dsl Exp $ */
+/* show.c */
#ifdef DEBUG
static void shtree(union node *, int, char *, FILE*);
#endif /* DEBUG */
-/* $NetBSD: trap.c,v 1.28 2002/11/24 22:35:43 christos Exp $ */
+/* trap.c */
/*
* Sigmode records the current value of the signal handlers for the various
action = *ap++;
while (*ap) {
if ((signo = decode_signal(*ap, 0)) < 0)
- error("%s: bad trap", *ap);
+ sh_error("%s: bad trap", *ap);
INTOFF;
if (action) {
if (action[0] == '-' && action[1] == '\0')
* handlers while we are executing a trap handler.
*/
-void
+int
dotrap(void)
{
char *p;
char *q;
+ int i;
int savestatus;
+ int skip = 0;
savestatus = exitstatus;
- q = gotsig;
- while (pendingsigs = 0, barrier(), (p = memchr(q, 1, NSIG - 1))) {
- *p = 0;
- p = trap[p - q + 1];
+ pendingsigs = 0;
+ xbarrier();
+
+ for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
+ if (!*q)
+ continue;
+ *q = 0;
+
+ p = trap[i + 1];
if (!p)
continue;
- evalstring(p, 0);
+ skip = evalstring(p, SKIPEVAL);
exitstatus = savestatus;
+ if (skip)
+ break;
}
+
+ return skip;
}
if(!do_banner) {
out1fmt(
- "\n\n" BB_BANNER " Built-in shell (ash)\n"
- "Enter 'help' for a list of built-in commands.\n\n");
+ "\n\n%s Built-in shell (ash)\n"
+ "Enter 'help' for a list of built-in commands.\n\n",
+ BB_BANNER);
do_banner++;
}
}
status = exitstatus;
TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
if (setjmp(loc.loc)) {
+ if (exception == EXEXIT)
+ _exit(exitstatus);
goto out;
}
handler = &loc;
- if ((p = trap[0]) != NULL && *p != '\0') {
+ if ((p = trap[0])) {
trap[0] = NULL;
evalstring(p, 0);
}
flushall();
+ setjobctl(0);
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
if (iflag && rootshell) {
const char *hp = lookupvar("HISTFILE");
return name ? signo : -1;
}
-/* $NetBSD: var.c,v 1.32 2003/01/22 20:36:04 dsl Exp $ */
+/* var.c */
static struct var *vartab[VTABSIZE];
static struct var **findvar(struct var **, const char *);
/*
- * Initialize the varable symbol tables and import the environment
+ * Initialize the variable symbol tables and import the environment
*/
p = strchrnul(q, '=');
namelen = p - name;
if (!namelen || p != q)
- error("%.*s: bad variable name", namelen, name);
+ sh_error("%.*s: bad variable name", namelen, name);
vallen = 0;
if (val == NULL) {
flags |= VUNSET;
}
INTOFF;
p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
- *p++ = '\0';
- if (vallen) {
- p[-1] = '=';
+ if (val) {
+ *p++ = '=';
p = mempcpy(p, val, vallen);
}
*p = '\0';
flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
vp = *findvar(vpp, s);
if (vp) {
- if (vp->flags & VREADONLY) {
+ if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
+ const char *n;
+
if (flags & VNOSAVE)
free(s);
- error("%.*s: is read only", strchrnul(s, '=') - s, s);
+ n = vp->text;
+ sh_error("%.*s: is read only", strchrnul(n, '=') - n, n);
}
if (flags & VNOSET)
{
struct var *v;
- if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
- return strchrnul(v->text, '=') + 1;
+ if ((v = *findvar(hashvar(name), name))) {
+#ifdef DYNAMIC_VAR
+ /*
+ * Dynamic variables are implemented roughly the same way they are
+ * in bash. Namely, they're "special" so long as they aren't unset.
+ * As soon as they're unset, they're no longer dynamic, and dynamic
+ * lookup will no longer happen at that point. -- PFM.
+ */
+ if((v->flags & VDYNAMIC))
+ (*v->func)(NULL);
+#endif
+ if(!(v->flags & VUNSET))
+ return strchrnul(v->text, '=') + 1;
}
+
return NULL;
}
retval = 1;
if (flags & VREADONLY)
goto out;
+#ifdef DYNAMIC_VAR
+ vp->flags &= ~VDYNAMIC;
+#endif
if (flags & VUNSET)
goto ok;
if ((flags & VSTRFIXED) == 0) {
}
return vpp;
}
-/* $NetBSD: setmode.c,v 1.29 2003/01/15 23:58:03 kleink Exp $ */
+/* setmode.c */
#include <sys/times.h>
}
#ifdef CONFIG_ASH_MATH_SUPPORT
-static int
+static arith_t
dash_arith(const char *s)
{
- long result;
+ arith_t result;
int errcode = 0;
INTOFF;
result = arith(s, &errcode);
if (errcode < 0) {
if (errcode == -3)
- error("exponent less than 0");
+ sh_error("exponent less than 0");
else if (errcode == -2)
- error("divide by zero");
+ sh_error("divide by zero");
else if (errcode == -5)
- error("expression recursion loop detected");
+ sh_error("expression recursion loop detected");
else
synerror(s);
}
letcmd(int argc, char **argv)
{
char **ap;
- long i;
+ arith_t i;
ap = argv + 1;
if(!*ap)
- error("expression expected");
+ sh_error("expression expected");
for (ap = argv + 1; *ap; ap++) {
i = dash_arith(*ap);
}
}
#endif /* CONFIG_ASH_MATH_SUPPORT */
-/* $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $ */
+/* miscbltin.c */
/*
- * Miscelaneous builtins.
+ * Miscellaneous builtins.
*/
#undef rflag
#ifdef __GLIBC__
-#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
+#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
typedef enum __rlimit_resource rlim_t;
#endif
#endif
int startword;
int status;
int i;
+#if defined(CONFIG_ASH_READ_NCHARS)
+ int nch_flag = 0;
+ int nchars = 0;
+ int silent = 0;
+ struct termios tty, old_tty;
+#endif
+#if defined(CONFIG_ASH_READ_TIMEOUT)
+ fd_set set;
+ struct timeval ts;
+
+ ts.tv_sec = ts.tv_usec = 0;
+#endif
rflag = 0;
prompt = NULL;
- while ((i = nextopt("p:r")) != '\0') {
- if (i == 'p')
+#if defined(CONFIG_ASH_READ_NCHARS) && defined(CONFIG_ASH_READ_TIMEOUT)
+ while ((i = nextopt("p:rt:n:s")) != '\0')
+#elif defined(CONFIG_ASH_READ_NCHARS)
+ while ((i = nextopt("p:rn:s")) != '\0')
+#elif defined(CONFIG_ASH_READ_TIMEOUT)
+ while ((i = nextopt("p:rt:")) != '\0')
+#else
+ while ((i = nextopt("p:r")) != '\0')
+#endif
+ {
+ switch(i) {
+ case 'p':
prompt = optionarg;
- else
+ break;
+#if defined(CONFIG_ASH_READ_NCHARS)
+ case 'n':
+ nchars = strtol(optionarg, &p, 10);
+ if (*p)
+ sh_error("invalid count");
+ nch_flag = (nchars > 0);
+ break;
+ case 's':
+ silent = 1;
+ break;
+#endif
+#if defined(CONFIG_ASH_READ_TIMEOUT)
+ case 't':
+ ts.tv_sec = strtol(optionarg, &p, 10);
+ ts.tv_usec = 0;
+ if (*p == '.') {
+ char *p2;
+ if (*++p) {
+ int scale;
+ ts.tv_usec = strtol(p, &p2, 10);
+ if (*p2)
+ sh_error("invalid timeout");
+ scale = p2 - p;
+ /* normalize to usec */
+ if (scale > 6)
+ sh_error("invalid timeout");
+ while (scale++ < 6)
+ ts.tv_usec *= 10;
+ }
+ } else if (*p) {
+ sh_error("invalid timeout");
+ }
+ if ( ! ts.tv_sec && ! ts.tv_usec)
+ sh_error("invalid timeout");
+ break;
+#endif
+ case 'r':
rflag = 1;
+ break;
+ default:
+ break;
+ }
}
if (prompt && isatty(0)) {
out2str(prompt);
}
if (*(ap = argptr) == NULL)
- error("arg count");
+ sh_error("arg count");
if ((ifs = bltinlookup("IFS")) == NULL)
ifs = defifs;
+#if defined(CONFIG_ASH_READ_NCHARS)
+ if (nch_flag || silent) {
+ tcgetattr(0, &tty);
+ old_tty = tty;
+ if (nch_flag) {
+ tty.c_lflag &= ~ICANON;
+ tty.c_cc[VMIN] = nchars;
+ }
+ if (silent) {
+ tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
+
+ }
+ tcsetattr(0, TCSANOW, &tty);
+ }
+#endif
+#if defined(CONFIG_ASH_READ_TIMEOUT)
+ if (ts.tv_sec || ts.tv_usec) {
+ FD_ZERO (&set);
+ FD_SET (0, &set);
+
+ i = select (FD_SETSIZE, &set, NULL, NULL, &ts);
+ if (!i) {
+#if defined(CONFIG_ASH_READ_NCHARS)
+ if (nch_flag)
+ tcsetattr(0, TCSANOW, &old_tty);
+#endif
+ return 1;
+ }
+ }
+#endif
status = 0;
startword = 1;
backslash = 0;
STARTSTACKSTR(p);
- for (;;) {
+#if defined(CONFIG_ASH_READ_NCHARS)
+ while (!nch_flag || nchars--)
+#else
+ for (;;)
+#endif
+ {
if (read(0, &c, 1) != 1) {
status = 1;
break;
STPUTC(c, p);
}
}
+#if defined(CONFIG_ASH_READ_NCHARS)
+ if (nch_flag || silent)
+ tcsetattr(0, TCSANOW, &old_tty);
+#endif
+
STACKSTRNUL(p);
/* Remove trailing blanks */
while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
mask = 0;
do {
if (*ap >= '8' || *ap < '0')
- error(illnum, argv[1]);
+ sh_error(illnum, argv[1]);
mask = (mask << 3) + (*ap - '0');
} while (*++ap != '\0');
umask(mask);
} else {
mask = ~mask & 0777;
if (!bb_parse_mode(ap, &mask)) {
- error("Illegal mode: %s", ap);
+ sh_error("Illegal mode: %s", ap);
}
umask(~mask & 0777);
}
{ "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
#endif
#ifdef RLIMIT_NPROC
- { "process(processes)", RLIMIT_NPROC, 1, 'p' },
+ { "process", RLIMIT_NPROC, 1, 'p' },
#endif
#ifdef RLIMIT_NOFILE
- { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' },
+ { "nofiles", RLIMIT_NOFILE, 1, 'n' },
#endif
-#ifdef RLIMIT_VMEM
- { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' },
+#ifdef RLIMIT_AS
+ { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
#endif
-#ifdef RLIMIT_SWAP
- { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' },
+#ifdef RLIMIT_LOCKS
+ { "locks", RLIMIT_LOCKS, 1, 'w' },
#endif
{ (char *) 0, 0, 0, '\0' }
};
+enum limtype { SOFT = 0x1, HARD = 0x2 };
+
+static void printlim(enum limtype how, const struct rlimit *limit,
+ const struct limits *l)
+{
+ rlim_t val;
+
+ val = limit->rlim_max;
+ if (how & SOFT)
+ val = limit->rlim_cur;
+
+ if (val == RLIM_INFINITY)
+ out1fmt("unlimited\n");
+ else {
+ val /= l->factor;
+ out1fmt("%lld\n", (long long) val);
+ }
+}
+
int
ulimitcmd(int argc, char **argv)
{
int c;
rlim_t val = 0;
- enum { SOFT = 0x1, HARD = 0x2 }
- how = SOFT | HARD;
+ enum limtype how = SOFT | HARD;
const struct limits *l;
int set, all = 0;
int optc, what;
struct rlimit limit;
what = 'f';
- while ((optc = nextopt("HSatfdsmcnpl")) != '\0')
+ while ((optc = nextopt("HSa"
+#ifdef RLIMIT_CPU
+ "t"
+#endif
+#ifdef RLIMIT_FSIZE
+ "f"
+#endif
+#ifdef RLIMIT_DATA
+ "d"
+#endif
+#ifdef RLIMIT_STACK
+ "s"
+#endif
+#ifdef RLIMIT_CORE
+ "c"
+#endif
+#ifdef RLIMIT_RSS
+ "m"
+#endif
+#ifdef RLIMIT_MEMLOCK
+ "l"
+#endif
+#ifdef RLIMIT_NPROC
+ "p"
+#endif
+#ifdef RLIMIT_NOFILE
+ "n"
+#endif
+#ifdef RLIMIT_AS
+ "v"
+#endif
+#ifdef RLIMIT_LOCKS
+ "w"
+#endif
+ )) != '\0')
switch (optc) {
case 'H':
how = HARD;
what = optc;
}
- for (l = limits; l->name && l->option != what; l++)
+ for (l = limits; l->option != what; l++)
;
- if (!l->name)
- error("internal error (%c)", what);
set = *argptr ? 1 : 0;
if (set) {
char *p = *argptr;
if (all || argptr[1])
- error("too many arguments");
+ sh_error("too many arguments");
if (strncmp(p, "unlimited\n", 9) == 0)
val = RLIM_INFINITY;
else {
break;
}
if (c)
- error("bad number");
+ sh_error("bad number");
val *= l->factor;
}
}
if (all) {
for (l = limits; l->name; l++) {
getrlimit(l->cmd, &limit);
- if (how & SOFT)
- val = limit.rlim_cur;
- else if (how & HARD)
- val = limit.rlim_max;
-
out1fmt("%-20s ", l->name);
- if (val == RLIM_INFINITY)
- out1fmt("unlimited\n");
- else
- {
- val /= l->factor;
- out1fmt("%lld\n", (long long) val);
- }
+ printlim(how, &limit, l);
}
return 0;
}
if (how & SOFT)
limit.rlim_cur = val;
if (setrlimit(l->cmd, &limit) < 0)
- error("error setting limit (%m)");
+ sh_error("error setting limit (%m)");
} else {
- if (how & SOFT)
- val = limit.rlim_cur;
- else if (how & HARD)
- val = limit.rlim_max;
-
- if (val == RLIM_INFINITY)
- out1fmt("unlimited\n");
- else
- {
- val /= l->factor;
- out1fmt("%lld\n", (long long) val);
- }
+ printlim(how, &limit, l);
}
return 0;
}
/* This is my infix parser/evaluator. It is optimized for size, intended
* as a replacement for yacc-based parsers. However, it may well be faster
- * than a comparable parser writen in yacc. The supported operators are
+ * than a comparable parser written in yacc. The supported operators are
* listed in #defines below. Parens, order of operations, and error handling
- * are supported. This code is threadsafe. The exact expression format should
+ * are supported. This code is thread safe. The exact expression format should
* be that which POSIX specifies for shells. */
/* The code uses a simple two-stack algorithm. See
* http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
- * for a detailed explaination of the infix-to-postfix algorithm on which
+ * for a detailed explanation of the infix-to-postfix algorithm on which
* this is based (this code differs in that it applies operators immediately
* to the stack instead of adding them to a queue to end up with an
* expression). */
* parens and then checking that all binary ops and right parens are
* preceded by a valid expression (NUM_TOKEN).
*
- * Note: It may be desireable to replace Aaron's test for whitespace with
+ * Note: It may be desirable to replace Aaron's test for whitespace with
* ctype's isspace() if it is used by another busybox applet or if additional
* whitespace chars should be considered. Look below the "#include"s for a
* precompiler test.
* - realize comma separated - expr, expr
* - realise ++expr --expr expr++ expr--
* - realise expr ? expr : expr (but, second expr calculate always)
- * - allow hexdecimal and octal numbers
+ * - allow hexadecimal and octal numbers
* - was restored loses XOR operator
* - remove one goto label, added three ;-)
* - protect $((num num)) as true zero expr (Manuel`s error)
typedef unsigned char operator;
/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
- * precedence, and 3 high bits are an ID unique accross operators of that
+ * precedence, and 3 high bits are an ID unique across operators of that
* precedence. The ID portion is so that multiple operators can have the
* same precedence, ensuring that the leftmost one is evaluated first.
* Consider * and /. */
typedef struct ARITCH_VAR_NUM {
- long val;
- long contidional_second_val;
+ arith_t val;
+ arith_t contidional_second_val;
char contidional_second_val_initialized;
char *var; /* if NULL then is regular number,
- else is varable name */
+ else is variable name */
} v_n_t;
static inline int
arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
{
- long numptr_val;
v_n_t *numptr_m1;
- long rez;
+ arith_t numptr_val, rez;
int ret_arith_lookup_val;
if (NUMPTR == numstack) goto err; /* There is no operator that can work
if(numptr_val < 0)
return -3; /* exponent less than 0 */
else {
- long c = 1;
+ arith_t c = 1;
if(numptr_val)
while(numptr_val--)
goto err;
}
/* save to shell variable */
- sprintf(buf, "%ld", rez);
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+ snprintf(buf, sizeof(buf), "%lld", arith_t_type rez);
+#else
+ snprintf(buf, sizeof(buf), "%ld", arith_t_type rez);
+#endif
setvar(numptr_m1->var, buf, 0);
/* after saving, make previous value for v++ or v-- */
if(op == TOK_POST_INC)
#define endexpression &op_tokens[sizeof(op_tokens)-7]
-extern long arith (const char *expr, int *perrcode)
+static arith_t arith (const char *expr, int *perrcode)
{
register char arithval; /* Current character under analysis */
operator lasttok, op;
/* Stack of integers */
/* The proof that there can be no more than strlen(startbuf)/2+1 integers
- * in any given correct or incorrect expression is left as an excersize to
+ * in any given correct or incorrect expression is left as an exercise to
* the reader. */
v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
*numstackptr = numstack;
goto prologue;
}
if((p = endofname(expr)) != expr) {
- int var_name_size = (p-expr) + 1; /* trailing zero */
+ size_t var_name_size = (p-expr) + 1; /* trailing zero */
numstackptr->var = alloca(var_name_size);
safe_strncpy(numstackptr->var, expr, var_name_size);
continue;
} else if (is_digit(arithval)) {
numstackptr->var = NULL;
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+ numstackptr->val = strtoll(expr, (char **) &expr, 0);
+#else
numstackptr->val = strtol(expr, (char **) &expr, 0);
+#endif
goto num;
}
for(p = op_tokens; ; p++) {
* 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. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
- * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
- *
- * 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.
*