* package.
*
* Modified by Erik Andersen <andersee@debian.org> and
- * Vladimir Oleynik <vodz@usa.net> to be used in busybox
+ * Vladimir Oleynik <dzo@simtreas.ru> to be used in busybox
*
*
* Original copyright notice is retained at the end of this file.
/* These defines allow you to adjust the feature set to be compiled
* into the ash shell. As a rule, enabling these options will make
* ash get bigger... With all of these options off, ash adds about
- * 62k to busybox on an x86 system.*/
-
+ * 60k to busybox on an x86 system.*/
/* Enable job control. This allows you to run jobs in the background,
* which is great when ash is being used as an interactive shell, but
* it completely useless for is all you are doing is running scripts.
* This adds about 2.5k on an x86 system. */
-#define JOBS
+#undef JOBS
/* This enables alias support in ash. If you want to support things
* like "alias ls='ls -l'" with ash, enable this. This is only useful
#define ASH_ALIAS
/* If you need ash to act as a full Posix shell, with full math
- * support, enable this. This option needs some work, since it
- * doesn't compile right now... */
-#undef ASH_MATH_SUPPORT
-
-/* This shell builtin is used to indicate how the shell would interpret
- * what you give it. This command is only useful when debugging, and
- * is obsolete anyways. Adds about 670 bytes... You probably want to
- * leave this disabled. */
-#undef ASH_TYPE
+ * support, enable this. This adds a bit over 2k an x86 system. */
+//#undef ASH_MATH_SUPPORT
+#define ASH_MATH_SUPPORT
/* Getopts is used by shell procedures to parse positional parameters.
* You probably want to leave this disabled, and use the busybox getopt
/* This allows you to override shell builtins and use whatever is on
* the filesystem. This is most useful when ash is acting as a
- * standalone shell. Adds about 320 bytes. */
+ * standalone shell. Adds about 272 bytes. */
#undef ASH_CMDCMD
-/* This makes a few common apps that are usually part of busybox
- * anyways to be used as builtins. This may cause these builtins to be
- * a little bit faster, but leaving this disabled will save you 2k. */
-#undef ASH_BBAPPS_AS_BUILTINS
/* Optimize size vs speed as size */
#define ASH_OPTIMIZE_FOR_SIZE
* will generate a core dump. */
#undef DEBUG
-
-
/* These are here to work with glibc -- Don't change these... */
#undef FNMATCH_BROKEN
#undef GLOB_BROKEN
-#undef _GNU_SOURCE
#include <assert.h>
+#include <stddef.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CSPCL 13 /* these terminate a word */
#define CIGN 14 /* character should be ignored */
-/* Syntax classes for is_ functions */
-#define ISDIGIT 01 /* a digit */
-#define ISUPPER 02 /* an upper case letter */
-#define ISLOWER 04 /* a lower case letter */
-#define ISUNDER 010 /* an underscore */
-#define ISSPECL 020 /* the name of a special parameter */
-
#define SYNBASE 130
#define PEOF -130
#define TEOF 0
#define TNL 1
-#define TSEMI 2
-#define TBACKGND 3
-#define TAND 4
-#define TOR 5
-#define TPIPE 6
-#define TLP 7
-#define TRP 8
-#define TENDCASE 9
-#define TENDBQUOTE 10
-#define TREDIR 11
-#define TWORD 12
-#define TASSIGN 13
+#define TREDIR 2
+#define TWORD 3
+#define TASSIGN 4
+#define TSEMI 5
+#define TBACKGND 6
+#define TAND 7
+#define TOR 8
+#define TPIPE 9
+#define TLP 10
+#define TRP 11
+#define TENDCASE 12
+#define TENDBQUOTE 13
#define TNOT 14
#define TCASE 15
#define TDO 16
#define TEND 29
-#define BASESYNTAX (basesyntax + SYNBASE)
-#define DQSYNTAX (dqsyntax + SYNBASE)
-#define SQSYNTAX (sqsyntax + SYNBASE)
-#define ARISYNTAX (arisyntax + SYNBASE)
/* control characters in argument strings */
#define CTLESC '\201'
#define CTLENDARI '\207'
#define CTLQUOTEMARK '\210'
-#define is_digit(c) ((((unsigned char)(c)) - '0') <= 9)
-#define is_alpha(c) (((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))
+
+#define is_digit(c) ((c)>='0' && (c)<='9')
#define is_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))
#define is_in_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))
-#define is_special(c) ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))
+
+/*
+ * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
+ * (assuming ascii char codes, as the original implementation did)
+ */
+#define is_special(c) \
+ ( (((unsigned int)c) - 33 < 32) \
+ && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
+
#define digit_val(c) ((c) - '0')
* so we use _setjmp instead.
*/
-#if !defined(__GLIBC__)
+#if defined(BSD)
#define setjmp(jmploc) _setjmp(jmploc)
#define longjmp(jmploc, val) _longjmp(jmploc, val)
#endif
int optoff; /* used by getopts */
};
+/*
+ * When commands are first encountered, they are entered in a hash table.
+ * This ensures that a full path search will not have to be done for them
+ * on each invocation.
+ *
+ * We should investigate converting to a linear search, even though that
+ * would make the command name "hash" a misnomer.
+ */
+#define CMDTABLESIZE 31 /* should be prime */
+#define ARB 1 /* actual size determined at run time */
+
+
+
+struct tblentry {
+ struct tblentry *next; /* next entry in hash chain */
+ union param param; /* definition of builtin function */
+ short cmdtype; /* index identifying command */
+ char rehash; /* if set, cd done since entry created */
+ char cmdname[ARB]; /* name of command */
+};
+
+
+static struct tblentry *cmdtable[CMDTABLESIZE];
+static int builtinloc = -1; /* index in path of %builtin, or -1 */
+static int exerrno = 0; /* Last exec error */
+
+
+static void tryexec (char *, char **, char **);
+static void printentry (struct tblentry *, int);
+static void clearcmdentry (int);
+static struct tblentry *cmdlookup (const char *, int);
+static void delete_cmd_entry (void);
+static int path_change (const char *, int *);
+
+
static void flushall (void);
static void out2fmt (const char *, ...)
__attribute__((__format__(__printf__,1,2)));
-static void out1fmt (const char *, ...)
- __attribute__((__format__(__printf__,1,2)));
static int xwrite (int, const char *, int);
-static void outstr (const char *p, FILE *file) { fputs(p, file); }
+static inline void outstr (const char *p, FILE *file) { fputs(p, file); }
static void out1str(const char *p) { outstr(p, stdout); }
static void out2str(const char *p) { outstr(p, stderr); }
+#ifndef ASH_OPTIMIZE_FOR_SIZE
#define out2c(c) putc((c), stderr)
+#else
+static void out2c(int c) { putc(c, stderr); }
+#endif
-/* syntax table used when not in quotes */
-static const char basesyntax[257] = {
- CENDFILE, CSPCL, CWORD, CCTL,
- CCTL, CCTL, CCTL, CCTL,
- CCTL, CCTL, CCTL, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CSPCL,
- CNL, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CSPCL, CWORD,
- CDQUOTE, CWORD, CVAR, CWORD,
- CSPCL, CSQUOTE, CSPCL, CSPCL,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CSPCL, CSPCL, CWORD,
- CSPCL, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CBACK, CWORD,
- CWORD, CWORD, CBQUOTE, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CSPCL, CENDVAR,
- CWORD
-};
-
-/* syntax table used when in double quotes */
-static const char dqsyntax[257] = {
- CENDFILE, CIGN, CWORD, CCTL,
- CCTL, CCTL, CCTL, CCTL,
- CCTL, CCTL, CCTL, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CNL, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CCTL,
- CENDQUOTE,CWORD, CVAR, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CCTL, CWORD, CWORD, CCTL,
- CWORD, CCTL, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CCTL, CWORD, CWORD, CCTL,
- CWORD, CCTL, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CCTL, CBACK, CCTL,
- CWORD, CWORD, CBQUOTE, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CENDVAR,
- CCTL
-};
-
-/* syntax table used when in single quotes */
-static const char sqsyntax[257] = {
- CENDFILE, CIGN, CWORD, CCTL,
- CCTL, CCTL, CCTL, CCTL,
- CCTL, CCTL, CCTL, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CNL, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CCTL,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CENDQUOTE,CWORD, CWORD,
- CCTL, CWORD, CWORD, CCTL,
- CWORD, CCTL, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CCTL, CWORD, CWORD, CCTL,
- CWORD, CCTL, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CCTL, CCTL, CCTL,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CCTL
-};
-/* syntax table used when in arithmetic */
-static const char arisyntax[257] = {
- CENDFILE, CIGN, CWORD, CCTL,
- CCTL, CCTL, CCTL, CCTL,
- CCTL, CCTL, CCTL, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CNL, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CDQUOTE, CWORD, CVAR, CWORD,
- CWORD, CSQUOTE, CLP, CRP,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CBACK, CWORD,
- CWORD, CWORD, CBQUOTE, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CWORD,
- CWORD, CWORD, CWORD, CENDVAR,
- CWORD
+#ifdef ASH_OPTIMIZE_FOR_SIZE
+#define USE_SIT_FUNCTION
+#endif
+
+/* number syntax index */
+#define BASESYNTAX 0 /* not in quotes */
+#define DQSYNTAX 1 /* in double quotes */
+#define SQSYNTAX 2 /* in single quotes */
+#define ARISYNTAX 3 /* in arithmetic */
+
+static const char S_I_T[][4] = {
+ /* 0 */ { CSPCL, CIGN, CIGN, CIGN }, /* PEOA */
+ /* 1 */ { CSPCL, CWORD, CWORD, CWORD }, /* ' ' */
+ /* 2 */ { CNL, CNL, CNL, CNL }, /* \n */
+ /* 3 */ { CWORD, CCTL, CCTL, CWORD }, /* !*-/:=?[]~ */
+ /* 4 */ { CDQUOTE, CENDQUOTE, CWORD, CDQUOTE }, /* '"' */
+ /* 5 */ { CVAR, CVAR, CWORD, CVAR }, /* $ */
+ /* 6 */ { CSQUOTE, CWORD, CENDQUOTE, CSQUOTE }, /* "'" */
+ /* 7 */ { CSPCL, CWORD, CWORD, CLP }, /* ( */
+ /* 8 */ { CSPCL, CWORD, CWORD, CRP }, /* ) */
+ /* 9 */ { CBACK, CBACK, CCTL, CBACK }, /* \ */
+ /* 10 */ { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* ` */
+ /* 11 */ { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* } */
+#ifndef USE_SIT_FUNCTION
+ /* 12 */ { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* PEOF */
+ /* 13 */ { CWORD, CWORD, CWORD, CWORD }, /* 0-9A-Za-z */
+ /* 14 */ { CCTL, CCTL, CCTL, CCTL } /* CTLESC ... */
+#endif
};
-/* character classification table */
-static const char is_type[257] = {
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, ISSPECL,
- 0, ISSPECL, ISSPECL, 0,
- 0, 0, 0, 0,
- ISSPECL, 0, 0, ISSPECL,
- 0, 0, ISDIGIT, ISDIGIT,
- ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
- ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
- 0, 0, 0, 0,
- 0, ISSPECL, ISSPECL, ISUPPER,
- ISUPPER, ISUPPER, ISUPPER, ISUPPER,
- ISUPPER, ISUPPER, ISUPPER, ISUPPER,
- ISUPPER, ISUPPER, ISUPPER, ISUPPER,
- ISUPPER, ISUPPER, ISUPPER, ISUPPER,
- ISUPPER, ISUPPER, ISUPPER, ISUPPER,
- ISUPPER, ISUPPER, ISUPPER, ISUPPER,
- ISUPPER, 0, 0, 0,
- 0, ISUNDER, 0, ISLOWER,
- ISLOWER, ISLOWER, ISLOWER, ISLOWER,
- ISLOWER, ISLOWER, ISLOWER, ISLOWER,
- ISLOWER, ISLOWER, ISLOWER, ISLOWER,
- ISLOWER, ISLOWER, ISLOWER, ISLOWER,
- ISLOWER, ISLOWER, ISLOWER, ISLOWER,
- ISLOWER, ISLOWER, ISLOWER, ISLOWER,
- ISLOWER, 0, 0, 0,
- 0
-};
+#ifdef USE_SIT_FUNCTION
-/* Array indicating which tokens mark the end of a list */
-static const char tokendlist[] = {
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 1,
-};
+#define U_C(c) ((unsigned char)(c))
-static const char *const tokname[] = {
- "end of file",
- "newline",
- "\";\"",
- "\"&\"",
- "\"&&\"",
- "\"||\"",
- "\"|\"",
- "\"(\"",
- "\")\"",
- "\";;\"",
- "\"`\"",
- "redirection",
- "word",
- "assignment",
- "\"!\"",
- "\"case\"",
- "\"do\"",
- "\"done\"",
- "\"elif\"",
- "\"else\"",
- "\"esac\"",
- "\"fi\"",
- "\"for\"",
- "\"if\"",
- "\"in\"",
- "\"then\"",
- "\"until\"",
- "\"while\"",
- "\"{\"",
- "\"}\"",
+static int SIT(int c, int syntax)
+{
+ static const char spec_symbls[]="\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
+ static const char syntax_index_table [] = {
+ 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
+ 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
+ 3, 1, 3, 3, 9, 3,10, 1, /* "=>?[\\]`|" */
+ 11,3 }; /* "}~" */
+ const char *s;
+ int indx;
+
+ if(c==PEOF) /* 2^8+2 */
+ return CENDFILE;
+ if(c==PEOA) /* 2^8+1 */
+ indx = 0;
+ else if(U_C(c)>=U_C(CTLESC) && U_C(c)<=U_C(CTLQUOTEMARK))
+ return CCTL;
+ else {
+ s = strchr(spec_symbls, c);
+ if(s==0)
+ return CWORD;
+ indx = syntax_index_table[(s-spec_symbls)];
+ }
+ return S_I_T[indx][syntax];
+}
+
+#else /* USE_SIT_FUNCTION */
+
+#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
+
+#define CSPCL_CIGN_CIGN_CIGN 0
+#define CSPCL_CWORD_CWORD_CWORD 1
+#define CNL_CNL_CNL_CNL 2
+#define CWORD_CCTL_CCTL_CWORD 3
+#define CDQUOTE_CENDQUOTE_CWORD_CDQUOTE 4
+#define CVAR_CVAR_CWORD_CVAR 5
+#define CSQUOTE_CWORD_CENDQUOTE_CSQUOTE 6
+#define CSPCL_CWORD_CWORD_CLP 7
+#define CSPCL_CWORD_CWORD_CRP 8
+#define CBACK_CBACK_CCTL_CBACK 9
+#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
+#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
+#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
+#define CWORD_CWORD_CWORD_CWORD 13
+#define CCTL_CCTL_CCTL_CCTL 14
+
+static const char syntax_index_table[258] = {
+ /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
+ /* 0 -130 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
+ /* 1 -129 PEOA */ CSPCL_CIGN_CIGN_CIGN,
+ /* 2 -128 0xff */ CWORD_CWORD_CWORD_CWORD,
+ /* 3 -127 */ CCTL_CCTL_CCTL_CCTL, /* CTLQUOTEMARK */
+ /* 4 -126 */ CCTL_CCTL_CCTL_CCTL,
+ /* 5 -125 */ CCTL_CCTL_CCTL_CCTL,
+ /* 6 -124 */ CCTL_CCTL_CCTL_CCTL,
+ /* 7 -123 */ CCTL_CCTL_CCTL_CCTL,
+ /* 8 -122 */ CCTL_CCTL_CCTL_CCTL,
+ /* 9 -121 */ CCTL_CCTL_CCTL_CCTL,
+ /* 10 -120 */ CCTL_CCTL_CCTL_CCTL, /* CTLESC */
+ /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
+ /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
+ /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
+ /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
+ /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
+ /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
+ /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
+ /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
+ /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
+ /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
+ /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
+ /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
+ /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
+ /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
+ /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
+ /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
+ /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
+ /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
+ /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
+ /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
+ /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
+ /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
+ /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
+ /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
+ /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
+ /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
+ /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
+ /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
+ /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
+ /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
+ /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
+ /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
+ /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
+ /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
+ /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
+ /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
+ /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
+ /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
+ /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
+ /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
+ /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
+ /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
+ /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
+ /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
+ /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
+ /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
+ /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
+ /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
+ /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
+ /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
+ /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
+ /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
+ /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
+ /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
+ /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
+ /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
+ /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
+ /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
+ /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
+ /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
+ /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
+ /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
+ /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
+ /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
+ /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
+ /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
+ /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
+ /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
+ /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
+ /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
+ /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
+ /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
+ /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
+ /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
+ /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
+ /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
+ /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
+ /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
+ /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
+ /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
+ /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
+ /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
+ /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
+ /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
+ /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
+ /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
+ /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
+ /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
+ /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
+ /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
+ /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
+ /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
+ /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
+ /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
+ /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
+ /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
+ /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
+ /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
+ /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
+ /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
+ /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
+ /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
+ /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
+ /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
+ /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
+ /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
+ /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
+ /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
+ /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
+ /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
+ /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
+ /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
+ /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
+ /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
+ /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
+ /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
+ /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
+ /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
+ /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
+ /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
+ /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
+ /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
+ /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
+ /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
+ /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
+ /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
+ /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
+ /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
+ /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
+ /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
+ /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
+ /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
+ /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
+ /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
+ /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
+ /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
+ /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
+ /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
+ /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
+ /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
+ /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
+ /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
+ /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
+ /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
+ /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
+ /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
+ /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
+ /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
+ /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
+ /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
+ /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
+ /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
+ /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CDQUOTE,
+ /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
+ /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
+ /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
+ /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CSQUOTE,
+ /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
+ /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
+ /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
+ /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
+ /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
+ /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
+ /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
+ /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
+ /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
+ /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
+ /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
+ /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
+ /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
+ /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
+ /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
+ /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
+ /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
+ /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
+ /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
+ /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
+ /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
+ /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
+ /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
+ /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
+ /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
+ /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
+ /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
+ /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
+ /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
+ /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
+ /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
+ /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
+ /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
+ /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
+ /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
+ /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
+ /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
+ /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
+ /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
+ /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
+ /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
+ /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
+ /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
+ /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
+ /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
+ /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
+ /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
+ /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
+ /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
+ /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
+ /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
+ /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
+ /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
+ /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
+ /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
+ /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
+ /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
+ /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
+ /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
+ /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
+ /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
+ /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
+ /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
+ /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
+ /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
+ /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
+ /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
+ /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
+ /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
+ /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
+ /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
+ /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
+ /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
+ /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
+ /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
+ /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
+ /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
+ /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
+ /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
+ /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
+ /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
+ /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
+ /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
+ /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
};
+#endif /* USE_SIT_FUNCTION */
+
+
+/* first char is indicating which tokens mark the end of a list */
+static const char *const tokname_array[] = {
+ "\1end of file",
+ "\0newline",
+ "\0redirection",
+ "\0word",
+ "\0assignment",
+ "\0;",
+ "\0&",
+ "\0&&",
+ "\0||",
+ "\0|",
+ "\0(",
+ "\1)",
+ "\1;;",
+ "\1`",
#define KWDOFFSET 14
-
-static const char *const parsekwd[] = {
- "!",
- "case",
- "do",
- "done",
- "elif",
- "else",
- "esac",
- "fi",
- "for",
- "if",
- "in",
- "then",
- "until",
- "while",
- "{",
- "}"
+ /* the following are keywords */
+ "\0!",
+ "\0case",
+ "\1do",
+ "\1done",
+ "\1elif",
+ "\1else",
+ "\1esac",
+ "\1fi",
+ "\0for",
+ "\0if",
+ "\0in",
+ "\1then",
+ "\0until",
+ "\0while",
+ "\0{",
+ "\1}",
};
+static const char *tokname(int tok)
+{
+ static char buf[16];
+
+ if(tok>=TSEMI)
+ buf[0] = '"';
+ sprintf(buf+(tok>=TSEMI), "%s%c",
+ tokname_array[tok]+1, (tok>=TSEMI ? '"' : 0));
+ return buf;
+}
static int plinno = 1; /* input line number */
};
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
#define rmescapes(p) _rmescapes((p), 0)
static char *_rmescapes (char *, int);
#else
static void setvar (const char *, const char *, int);
static void setvareq (char *, int);
static void listsetvar (struct strlist *);
-static char *lookupvar (const char *);
-static char *bltinlookup (const char *);
+static const char *lookupvar (const char *);
+static const char *bltinlookup (const char *);
static char **environment (void);
static int showvarscmd (int, char **);
static void mklocal (char *);
char *p;
p = single_quote(ap->val);
- out1fmt("alias %s=%s\n", ap->name, p);
+ printf("alias %s=%s\n", ap->name, p);
stunalloc(p);
}
#endif
#ifdef ASH_MATH_SUPPORT
-/* The generated file arith.c has been snipped. If you want this
- * stuff back in, feel free to add it to your own copy. */
+/* The generated file arith.c has been replaced with a custom hand
+ * written implementation written by Aaron Lehmann <aaronl@vitelus.com>.
+ * This is now part of libbb, so that it can be used by all the shells
+ * in busybox. */
#define ARITH_NUM 257
#define ARITH_LPAREN 258
#define ARITH_RPAREN 259
#define ARITH_BNOT 281
static void expari (int);
-/* From arith.y */
-static int arith (const char *);
-static int expcmd (int , char **);
-static void arith_lex_reset (void);
-static int yylex (void);
-
#endif
static char *trap[NSIG]; /* trap handler commands */
static int exportcmd (int, char **);
static int histcmd (int, char **);
static int hashcmd (int, char **);
+static int helpcmd (int, char **);
static int jobscmd (int, char **);
static int localcmd (int, char **);
#ifndef BB_PWD
static int ulimitcmd (int, char **);
static int timescmd (int, char **);
#ifdef ASH_MATH_SUPPORT
-static int expcmd (int, char **);
+static int letcmd (int, char **);
#endif
-#ifdef ASH_TYPE
static int typecmd (int, char **);
-#endif
#ifdef ASH_GETOPTS
static int getoptscmd (int, char **);
#endif
#ifndef BB_TRUE_FALSE
-# ifdef ASH_BBAPPS_AS_BUILTINS
static int true_main (int, char **);
static int false_main (int, char **);
-# endif
#endif
static void setpwd (const char *, int);
* have been warned.
*/
static const struct builtincmd builtincmds[] = {
- { BUILTIN_SPECIAL ".", dotcmd },
+ { BUILTIN_SPECIAL ".", dotcmd }, /* first, see declare DOTCMD */
{ BUILTIN_SPECIAL ":", true_main },
#ifdef ASH_ALIAS
{ BUILTIN_REG_ASSG "alias", aliascmd },
{ BUILTIN_SPECIAL "break", breakcmd },
{ BUILTIN_SPECIAL "builtin", bltincmd },
{ BUILTIN_REGULAR "cd", cdcmd },
-#ifdef ASH_BBAPPS_AS_BUILTINS
{ BUILTIN_NOSPEC "chdir", cdcmd },
-#endif
#ifdef ASH_CMDCMD
{ BUILTIN_REGULAR "command", commandcmd },
#endif
{ BUILTIN_SPECIAL "eval", evalcmd },
{ BUILTIN_SPECIAL "exec", execcmd },
{ BUILTIN_SPECIAL "exit", exitcmd },
-#ifdef ASH_MATH_SUPPORT
- { BUILTIN_NOSPEC "exp", expcmd },
-#endif
{ BUILTIN_SPEC_ASSG "export", exportcmd },
-#ifdef ASH_BBAPPS_AS_BUILTINS
{ BUILTIN_REGULAR "false", false_main },
-#endif
{ BUILTIN_REGULAR "fc", histcmd },
#ifdef JOBS
{ BUILTIN_REGULAR "fg", fgcmd },
{ BUILTIN_REGULAR "getopts", getoptscmd },
#endif
{ BUILTIN_NOSPEC "hash", hashcmd },
+ { BUILTIN_NOSPEC "help", helpcmd },
{ BUILTIN_REGULAR "jobs", jobscmd },
#ifdef JOBS
{ BUILTIN_REGULAR "kill", killcmd },
#endif
#ifdef ASH_MATH_SUPPORT
- { BUILTIN_NOSPEC "let", expcmd },
+ { BUILTIN_REGULAR "let", letcmd },
#endif
{ BUILTIN_ASSIGN "local", localcmd },
#ifndef BB_PWD
{ BUILTIN_SPECIAL "shift", shiftcmd },
{ BUILTIN_SPECIAL "times", timescmd },
{ BUILTIN_SPECIAL "trap", trapcmd },
-#ifdef ASH_BBAPPS_AS_BUILTINS
{ BUILTIN_REGULAR "true", true_main },
-#endif
-#ifdef ASH_TYPE
{ BUILTIN_NOSPEC "type", typecmd },
-#endif
{ BUILTIN_NOSPEC "ulimit", ulimitcmd },
{ BUILTIN_REGULAR "umask", umaskcmd },
#ifdef ASH_ALIAS
};
#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) )
-static const struct builtincmd *DOTCMD = &builtincmds[0];
+#define DOTCMD &builtincmds[0]
static struct builtincmd *BLTINCMD;
static struct builtincmd *EXECCMD;
static struct builtincmd *EVALCMD;
#endif
static int intreceived;
-static struct job *makejob (union node *, int);
-static int forkshell (struct job *, union node *, int);
+static struct job *makejob (const union node *, int);
+static int forkshell (struct job *, const union node *, int);
static int waitforjob (struct job *);
static int docd (char *, int);
updatepwd(badstat ? NULL : dest);
INTON;
if (print && iflag)
- out1fmt(snlfmt, curdir);
+ printf(snlfmt, curdir);
return 0;
}
int argc;
char **argv;
{
- out1fmt(snlfmt, curdir);
+ printf(snlfmt, curdir);
return 0;
}
#endif
if (handler == NULL)
abort();
#endif
+ flushall();
exception = e;
longjmp(handler->loc, 1);
}
vfprintf(stderr, msg, ap);
out2c('\n');
}
- flushall();
exraise(cond);
/* NOTREACHED */
}
-#ifdef __STDC__
static void
error(const char *msg, ...)
-#else
-static void
-error(va_alist)
- va_dcl
-#endif
{
-#ifndef __STDC__
- const char *msg;
-#endif
va_list ap;
-#ifdef __STDC__
va_start(ap, msg);
-#else
- va_start(ap);
- msg = va_arg(ap, const char *);
-#endif
exverror(EXERROR, msg, ap);
/* NOTREACHED */
va_end(ap);
}
-#ifdef __STDC__
static void
exerror(int cond, const char *msg, ...)
-#else
-static void
-exerror(va_alist)
- va_dcl
-#endif
{
-#ifndef __STDC__
- int cond;
- const char *msg;
-#endif
va_list ap;
-#ifdef __STDC__
va_start(ap, msg);
-#else
- va_start(ap);
- cond = va_arg(ap, int);
- msg = va_arg(ap, const char *);
-#endif
exverror(cond, msg, ap);
/* NOTREACHED */
va_end(ap);
struct errname {
short errcode; /* error number */
- short action; /* operation which encountered the error */
+ char action; /* operation which encountered the error */
};
/*
static int funcnest; /* depth of function calls */
-
static struct strlist *cmdenviron; /* environment for builtin command */
static int exitstatus; /* exit status of last command */
static 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 evalsubshell (const union node *, int);
static void expredir (union node *);
-static void evalpipe (union node *);
-static void evalcommand (union node *, int);
static void prehash (union node *);
static void eprintlist (struct strlist *);
popstackmark(&smark);
}
+static struct builtincmd *find_builtin (const char *);
+static void expandarg (union node *, struct arglist *, int);
+static void calcsize (const union node *);
+static union node *copynode (const union node *);
+
/*
- * Evaluate a parse tree. The value is left in the global variable
- * exitstatus.
+ * Make a copy of a parse tree.
+ */
+
+static int funcblocksize; /* size of structures in function */
+static int funcstringsize; /* size of strings in node */
+static pointer funcblock; /* block to allocate function from */
+static char *funcstring; /* block to allocate strings from */
+
+
+static inline union node *
+copyfunc(union node *n)
+{
+ if (n == NULL)
+ return NULL;
+ funcblocksize = 0;
+ funcstringsize = 0;
+ calcsize(n);
+ funcblock = ckmalloc(funcblocksize + funcstringsize);
+ funcstring = (char *) funcblock + funcblocksize;
+ return copynode(n);
+}
+
+/*
+ * Free a parse tree.
*/
-static struct builtincmd *find_builtin (const char *);
-static void defun (char *, union node *);
static void
-evaltree(n, flags)
- union node *n;
- int flags;
+freefunc(union node *n)
{
- int checkexit = 0;
- if (n == NULL) {
- TRACE(("evaltree(NULL) called\n"));
- goto out;
- }
- TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
- switch (n->type) {
- case NSEMI:
- evaltree(n->nbinary.ch1, flags & EV_TESTED);
- if (evalskip)
- goto out;
- evaltree(n->nbinary.ch2, flags);
- break;
- case NAND:
- evaltree(n->nbinary.ch1, EV_TESTED);
- if (evalskip || exitstatus != 0)
- goto out;
- 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);
- 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
- exitstatus = 0;
- break;
- }
- case NWHILE:
- case NUNTIL:
- evalloop(n, flags);
- break;
- case NFOR:
- evalfor(n, flags);
- break;
- case NCASE:
- evalcase(n, flags);
- break;
- case NDEFUN: {
- struct builtincmd *bcmd;
- if (
- (bcmd = find_builtin(n->narg.text)) &&
- IS_BUILTIN_SPECIAL(bcmd)
- ) {
- out2fmt("%s is a special built-in\n", n->narg.text);
- exitstatus = 1;
- break;
- }
- defun(n->narg.text, n->narg.next);
- exitstatus = 0;
- break;
- }
- case NNOT:
- evaltree(n->nnot.com, EV_TESTED);
- exitstatus = !exitstatus;
- break;
+ if (n)
+ ckfree(n);
+}
- case NPIPE:
- evalpipe(n);
- checkexit = 1;
- break;
- case NCMD:
- evalcommand(n, flags);
- checkexit = 1;
- break;
-#ifdef DEBUG
- default:
- out1fmt("Node type = %d\n", n->type);
- break;
-#endif
+
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name.
+ */
+
+static inline void
+addcmdentry(char *name, struct cmdentry *entry)
+{
+ struct tblentry *cmdp;
+
+ INTOFF;
+ cmdp = cmdlookup(name, 1);
+ if (cmdp->cmdtype == CMDFUNCTION) {
+ freefunc(cmdp->param.func);
}
-out:
- if (pendingsigs)
- dotrap();
- if (
- flags & EV_EXIT ||
- (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
- )
- exitshell(exitstatus);
+ cmdp->cmdtype = entry->cmdtype;
+ cmdp->param = entry->u;
+ INTON;
}
-
-static void
-evalloop(n, flags)
- union node *n;
- int flags;
+static inline void
+evalloop(const union node *n, int flags)
{
int status;
exitstatus = status;
}
-static void expandarg (union node *, struct arglist *, int);
-static void fixredir(union node *n, const char *text, int err);
-
static void
-evalfor(n, flags)
- union node *n;
- int flags;
+evalfor(const union node *n, int flags)
{
struct arglist arglist;
union node *argp;
popstackmark(&smark);
}
-
-static void
-evalcase(n, flags)
- union node *n;
- int flags;
+static inline void
+evalcase(const union node *n, int flags)
{
union node *cp;
union node *patp;
popstackmark(&smark);
}
-/*
- * Kick off a subshell to evaluate a tree.
- */
-
-static void
-evalsubshell(n, flags)
- 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_TESTED;
- redirect(n->nredir.redirect, 0);
- evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
- }
- if (! backgnd) {
- INTOFF;
- exitstatus = waitforjob(jp);
- INTON;
- }
-}
-
-
-/*
- * Compute the names of the files in a redirection list.
- */
-
-static void
-expredir(n)
- union node *n;
-{
- union node *redir;
-
- for (redir = n ; redir ; redir = redir->nfile.next) {
- struct arglist fn;
- fn.lastp = &fn.list;
- oexitstatus = exitstatus;
- switch (redir->type) {
- case NFROMTO:
- case NFROM:
- case NTO:
- case NAPPEND:
- case NTOOV:
- expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
- redir->nfile.expfname = fn.list->text;
- break;
- case NFROMFD:
- case NTOFD:
- if (redir->ndup.vname) {
- expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
- fixredir(redir, fn.list->text, 1);
- }
- break;
- }
- }
-}
-
/*
* Evaluate a pipeline. All the processes in the pipeline are children
* of the process creating the pipeline. (This differs from some versions
* of all the rest.)
*/
-static void
+static inline void
evalpipe(n)
union node *n;
{
}
}
-
-
-/*
- * 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
- * we fork off a subprocess and get the output of the command via a pipe.
- * Should be called with interrupts off.
- */
-
-static void
-evalbackcmd(union node *n, struct backcmd *result)
-{
- int pip[2];
- struct job *jp;
- struct stackmark smark; /* unnecessary */
-
- setstackmark(&smark);
- result->fd = -1;
- result->buf = NULL;
- result->nleft = 0;
- result->jp = NULL;
- if (n == NULL) {
- exitstatus = 0;
- goto out;
- }
- exitstatus = 0;
- if (pipe(pip) < 0)
- error("Pipe call failed");
- jp = makejob(n, 1);
- if (forkshell(jp, n, FORK_NOJOB) == 0) {
- FORCEINTON;
- close(pip[0]);
- if (pip[1] != 1) {
- close(1);
- dup_as_newfd(pip[1], 1);
- close(pip[1]);
- }
- eflag = 0;
- evaltree(n, EV_EXIT);
- }
- close(pip[1]);
- result->fd = pip[0];
- result->jp = jp;
-out:
- popstackmark(&smark);
- TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
- result->fd, result->buf, result->nleft, result->jp));
-}
-
-
-
-/*
- * Execute a simple command.
- */
-
static void find_command (const char *, struct cmdentry *, int, const char *);
static int
return *word == '=';
}
+
static void
evalcommand(union node *cmd, int flags)
{
}
if (argp) {
struct builtincmd *bcmd;
- bool pseudovarflag;
+ int pseudovarflag;
bcmd = find_builtin(arglist.list->text);
pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
for (; argp; argp = argp->narg.next) {
popstackmark(&smark);
}
-
-
/*
- * 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 check for "goodname" is an overly conservative
- * check that the name will not be subject to expansion.
+ * Evaluate a parse tree. The value is left in the global variable
+ * exitstatus.
*/
-
static void
-prehash(n)
+evaltree(n, flags)
union node *n;
+ int flags;
{
- struct cmdentry entry;
+ int checkexit = 0;
+ if (n == NULL) {
+ TRACE(("evaltree(NULL) called\n"));
+ goto out;
+ }
+ TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
+ switch (n->type) {
+ case NSEMI:
+ evaltree(n->nbinary.ch1, flags & EV_TESTED);
+ if (evalskip)
+ goto out;
+ evaltree(n->nbinary.ch2, flags);
+ break;
+ case NAND:
+ evaltree(n->nbinary.ch1, EV_TESTED);
+ if (evalskip || exitstatus != 0)
+ goto out;
+ 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);
+ 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
+ exitstatus = 0;
+ break;
+ }
+ case NWHILE:
+ case NUNTIL:
+ evalloop(n, flags);
+ break;
+ case NFOR:
+ evalfor(n, flags);
+ break;
+ case NCASE:
+ evalcase(n, flags);
+ break;
+ case NDEFUN: {
+ struct builtincmd *bcmd;
+ struct cmdentry entry;
+ if (
+ (bcmd = find_builtin(n->narg.text)) &&
+ IS_BUILTIN_SPECIAL(bcmd)
+ ) {
+ out2fmt("%s is a special built-in\n", n->narg.text);
+ exitstatus = 1;
+ break;
+ }
+ entry.cmdtype = CMDFUNCTION;
+ entry.u.func = copyfunc(n->narg.next);
+ addcmdentry(n->narg.text, &entry);
+ exitstatus = 0;
+ break;
+ }
+ case NNOT:
+ evaltree(n->nnot.com, EV_TESTED);
+ exitstatus = !exitstatus;
+ break;
- if (n->type == NCMD && n->ncmd.args)
- if (goodname(n->ncmd.args->narg.text))
- find_command(n->ncmd.args->narg.text, &entry, 0,
- pathval());
+ case NPIPE:
+ evalpipe(n);
+ checkexit = 1;
+ break;
+ case NCMD:
+ evalcommand(n, flags);
+ checkexit = 1;
+ break;
+#ifdef DEBUG
+ default:
+ printf("Node type = %d\n", n->type);
+ break;
+#endif
+ }
+out:
+ if (pendingsigs)
+ dotrap();
+ if (
+ flags & EV_EXIT ||
+ (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
+ )
+ exitshell(exitstatus);
}
-
-
/*
- * Builtin commands. Builtin commands whose functions are closely
- * tied to evaluation are implemented here.
+ * Kick off a subshell to evaluate a tree.
*/
+static void
+evalsubshell(const 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_TESTED;
+ redirect(n->nredir.redirect, 0);
+ evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
+ }
+ if (! backgnd) {
+ INTOFF;
+ exitstatus = waitforjob(jp);
+ INTON;
+ }
+}
+
/*
- * No command given, or a bltin command with no arguments. Set the
- * specified variables.
+ * Compute the names of the files in a redirection list.
+ */
+
+static void fixredir(union node *n, const char *text, int err);
+
+static void
+expredir(union node *n)
+{
+ union node *redir;
+
+ for (redir = n ; redir ; redir = redir->nfile.next) {
+ struct arglist fn;
+ fn.lastp = &fn.list;
+ oexitstatus = exitstatus;
+ switch (redir->type) {
+ case NFROMTO:
+ case NFROM:
+ case NTO:
+ case NAPPEND:
+ case NTOOV:
+ expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+ redir->nfile.expfname = fn.list->text;
+ break;
+ case NFROMFD:
+ case NTOFD:
+ if (redir->ndup.vname) {
+ expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+ fixredir(redir, fn.list->text, 1);
+ }
+ break;
+ }
+ }
+}
+
+
+/*
+ * 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
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+
+static void
+evalbackcmd(union node *n, struct backcmd *result)
+{
+ int pip[2];
+ struct job *jp;
+ struct stackmark smark; /* unnecessary */
+
+ setstackmark(&smark);
+ result->fd = -1;
+ result->buf = NULL;
+ result->nleft = 0;
+ result->jp = NULL;
+ if (n == NULL) {
+ exitstatus = 0;
+ goto out;
+ }
+ exitstatus = 0;
+ if (pipe(pip) < 0)
+ error("Pipe call failed");
+ jp = makejob(n, 1);
+ if (forkshell(jp, n, FORK_NOJOB) == 0) {
+ FORCEINTON;
+ close(pip[0]);
+ if (pip[1] != 1) {
+ close(1);
+ dup_as_newfd(pip[1], 1);
+ close(pip[1]);
+ }
+ eflag = 0;
+ evaltree(n, EV_EXIT);
+ }
+ close(pip[1]);
+ result->fd = pip[0];
+ result->jp = jp;
+out:
+ popstackmark(&smark);
+ TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+ result->fd, result->buf, result->nleft, result->jp));
+}
+
+
+/*
+ * Execute a simple command.
+ */
+
+/*
+ * 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 check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+
+static void
+prehash(n)
+ union node *n;
+{
+ struct cmdentry entry;
+
+ if (n->type == NCMD && n->ncmd.args)
+ if (goodname(n->ncmd.args->narg.text))
+ find_command(n->ncmd.args->narg.text, &entry, 0,
+ pathval());
+}
+
+
+/*
+ * Builtin commands. Builtin commands whose functions are closely
+ * tied to evaluation are implemented here.
+ */
+
+/*
+ * No command given, or a bltin command with no arguments. Set the
+ * specified variables.
*/
int
#ifndef BB_TRUE_FALSE
-#ifdef ASH_BBAPPS_AS_BUILTINS
static int
false_main(argc, argv)
int argc;
return 0;
}
#endif
-#endif
/*
* Controls whether the shell is interactive or not.
setinteractive(int on)
{
static int is_interactive;
+ static int do_banner=0;
if (on == is_interactive)
return;
setsignal(SIGTERM);
chkmail(1);
is_interactive = on;
+ if (do_banner==0 && is_interactive) {
+ /* Looks like they want an interactive shell */
+ printf( "\n\n" BB_BANNER " Built-in shell (ash)\n");
+ printf( "Enter 'help' for a list of built-in commands.\n\n");
+ do_banner=1;
+ }
}
static void
out2fmt(" %s",sp->text);
}
}
-/*
- * When commands are first encountered, they are entered in a hash table.
- * This ensures that a full path search will not have to be done for them
- * on each invocation.
- *
- * We should investigate converting to a linear search, even though that
- * would make the command name "hash" a misnomer.
- */
-#define CMDTABLESIZE 31 /* should be prime */
-#define ARB 1 /* actual size determined at run time */
-
-
-
-struct tblentry {
- struct tblentry *next; /* next entry in hash chain */
- union param param; /* definition of builtin function */
- short cmdtype; /* index identifying command */
- char rehash; /* if set, cd done since entry created */
- char cmdname[ARB]; /* name of command */
-};
-
-
-static struct tblentry *cmdtable[CMDTABLESIZE];
-static int builtinloc = -1; /* index in path of %builtin, or -1 */
-static int exerrno = 0; /* Last exec error */
-
-
-static void tryexec (char *, char **, char **);
-static void printentry (struct tblentry *, int);
-static void clearcmdentry (int);
-static struct tblentry *cmdlookup (const char *, int);
-static void delete_cmd_entry (void);
-#ifdef ASH_TYPE
-static int describe_command (char *, int);
-#endif
-static int path_change (const char *, int *);
-
/*
* Exec a program. Never returns. If you change this routine, you may
static int preadbuffer(void);
static void pushfile (void);
-static int preadfd (void);
/*
* Read a character from the script, returning PEOF on end of file.
static void
-tryexec(cmd, argv, envp)
- char *cmd;
- char **argv;
- char **envp;
- {
+tryexec(char *cmd, char **argv, char **envp)
+{
int e;
#ifdef BB_FEATURE_SH_STANDALONE_SHELL
for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
putenv(*argv_l);
argv_l=argv;
- for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++);
+ for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
optind = 1;
run_applet_by_name(name, argc_l, argv);
#endif
return stalloc(len);
}
+/*
+ * Wrapper around strcmp for qsort/bsearch/...
+ */
+static int
+pstrcmp(const void *a, const void *b)
+{
+ return strcmp((const char *) a, (*(const char *const *) b) + 1);
+}
+
+/*
+ * Find a keyword is in a sorted array.
+ */
+
+static const char *const *
+findkwd(const char *s)
+{
+ return bsearch(s, tokname_array + KWDOFFSET,
+ (sizeof(tokname_array)/sizeof(const char *)) - KWDOFFSET,
+ sizeof(const char *), pstrcmp);
+}
/*** Command hashing code ***/
int verbose;
struct cmdentry entry;
char *name;
+#ifdef ASH_ALIAS
+ const struct alias *ap;
+#endif
verbose = 0;
- while ((c = nextopt("rv")) != '\0') {
+ while ((c = nextopt("rvV")) != '\0') {
if (c == 'r') {
clearcmdentry(0);
return 0;
- } else if (c == 'v') {
- verbose++;
+ } else if (c == 'v' || c == 'V') {
+ verbose = c;
}
}
if (*argptr == NULL) {
return 0;
}
c = 0;
- while ((name = *argptr) != NULL) {
+ while ((name = *argptr++) != NULL) {
if ((cmdp = cmdlookup(name, 0)) != NULL
&& (cmdp->cmdtype == CMDNORMAL
|| (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
delete_cmd_entry();
+#ifdef ASH_ALIAS
+ /* Then look at the aliases */
+ if ((ap = lookupalias(name, 0)) != NULL) {
+ if (verbose=='v')
+ printf("%s is an alias for %s\n", name, ap->val);
+ else
+ printalias(ap);
+ continue;
+ }
+#endif
+ /* First look at the keywords */
+ if (findkwd(name)!=0) {
+ if (verbose=='v')
+ printf("%s is a shell keyword\n", name);
+ else
+ printf(snlfmt, name);
+ continue;
+ }
+
find_command(name, &entry, DO_ERR, pathval());
if (entry.cmdtype == CMDUNKNOWN) c = 1;
else if (verbose) {
cmdp = cmdlookup(name, 0);
- if (cmdp) printentry(cmdp, verbose);
+ if (cmdp) printentry(cmdp, verbose=='v');
flushall();
}
- argptr++;
}
return c;
}
-
static void
printentry(cmdp, verbose)
struct tblentry *cmdp;
const char *path;
char *name;
+ printf("%s%s", cmdp->cmdname, (verbose ? " is " : ""));
if (cmdp->cmdtype == CMDNORMAL) {
idx = cmdp->param.index;
path = pathval();
name = padvance(&path, cmdp->cmdname);
stunalloc(name);
} while (--idx >= 0);
- out1str(name);
+ if(verbose)
+ out1str(name);
} else if (cmdp->cmdtype == CMDBUILTIN) {
- out1fmt("builtin %s", cmdp->cmdname);
+ if(verbose)
+ out1str("a shell builtin");
} else if (cmdp->cmdtype == CMDFUNCTION) {
- out1fmt("function %s", cmdp->cmdname);
if (verbose) {
INTOFF;
+ out1str("a function\n");
name = commandtext(cmdp->param.func);
- out1fmt(" %s", name);
+ printf("%s() {\n %s\n}", cmdp->cmdname, name);
ckfree(name);
INTON;
}
error("internal error: cmdtype %d", cmdp->cmdtype);
#endif
}
- out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
+ printf(snlfmt, cmdp->rehash ? "*" : nullstr);
}
+/*** List the available builtins ***/
+
+
+static int helpcmd(int argc, char** argv)
+{
+ int col, i;
+
+ printf("\nBuilt-in commands:\n-------------------\n");
+ for (col=0, i=0; i < NUMBUILTINS; i++) {
+ col += printf("%c%s", ((col == 0) ? '\t' : ' '),
+ builtincmds[i].name+1);
+ if (col > 60) {
+ printf("\n");
+ col = 0;
+ }
+ }
+#ifdef BB_FEATURE_SH_STANDALONE_SHELL
+ {
+ extern const struct BB_applet applets[];
+ extern const size_t NUM_APPLETS;
+
+ for (i=0; i < NUM_APPLETS; i++) {
+
+ col += printf("%c%s", ((col == 0) ? '\t' : ' '),
+ applets[i].name);
+ if (col > 60) {
+ printf("\n");
+ col = 0;
+ }
+ }
+ }
+#endif
+ printf("\n\n");
+ return EXIT_SUCCESS;
+}
+
/*
* Resolve a command name. If you change this routine, you may have to
* change the shellexec routine as well.
int bltin;
int firstchange;
int updatetbl;
- bool regular;
+ int regular;
struct builtincmd *bcmd;
/* If name contains a slash, don't use the hash table */
INTON;
}
-/*
- * Free a parse tree.
- */
-
-static void
-freefunc(union node *n)
-{
- if (n)
- ckfree(n);
-}
-
/*
* Delete all functions.
+
+
+static const unsigned char nodesize[26] = {
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct ncmd)),
+ ALIGN(sizeof (struct npipe)),
+ ALIGN(sizeof (struct nredir)),
+ ALIGN(sizeof (struct nredir)),
+ ALIGN(sizeof (struct nredir)),
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct nif)),
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct nfor)),
+ ALIGN(sizeof (struct ncase)),
+ ALIGN(sizeof (struct nclist)),
+ ALIGN(sizeof (struct narg)),
+ ALIGN(sizeof (struct narg)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct ndup)),
+ ALIGN(sizeof (struct ndup)),
+ ALIGN(sizeof (struct nhere)),
+ ALIGN(sizeof (struct nhere)),
+ ALIGN(sizeof (struct nnot)),
+};
+
+
+
/*
- * Add a new command entry, replacing any existing command entry for
- * the same name.
+ * Delete a function if it exists.
*/
static void
-addcmdentry(char *name, struct cmdentry *entry)
+unsetfunc(char *name)
{
struct tblentry *cmdp;
- INTOFF;
- cmdp = cmdlookup(name, 1);
- if (cmdp->cmdtype == CMDFUNCTION) {
- freefunc(cmdp->param.func);
- }
- cmdp->cmdtype = entry->cmdtype;
- cmdp->param = entry->u;
- INTON;
-}
-
-
-/*
- * Define a shell function.
- */
-
-static union node *copyfunc(union node *);
-
-static void
-defun(char *name, union node *func)
-{
- struct cmdentry entry;
-
- entry.cmdtype = CMDFUNCTION;
- entry.u.func = copyfunc(func);
- addcmdentry(name, &entry);
-}
-
-
-/*
- * Delete a function if it exists.
- */
-
-static void
-unsetfunc(char *name)
-{
- struct tblentry *cmdp;
-
- if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
+ if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
freefunc(cmdp->param.func);
delete_cmd_entry();
}
}
-/*
- * Wrapper around strcmp for qsort/bsearch/...
- */
-static int
-pstrcmp(const void *a, const void *b)
-{
- return strcmp((const char *) a, *(const char *const *) b);
-}
-
-/*
- * Find a keyword is in a sorted array.
- */
-
-static const char *const *
-findkwd(const char *s)
-{
- return bsearch(s, parsekwd, sizeof(parsekwd) / sizeof(const char *),
- sizeof(const char *), pstrcmp);
-}
-#ifdef ASH_TYPE
/*
* Locate and print what a word is...
*/
static int
-typecmd(argc, argv)
- int argc;
- char **argv;
+typecmd(int argc, char **argv)
{
int i;
int err = 0;
+ char *argv_a[2];
+
+ argv_a[1] = 0;
for (i = 1; i < argc; i++) {
- err |= describe_command(argv[i], 1);
+ argv_a[0] = argv[i];
+ argptr = argv_a;
+ optptr = "v";
+ err |= hashcmd(argc, argv);
}
return err;
}
-static int
-describe_command(char *command, int verbose)
-{
- struct cmdentry entry;
- struct tblentry *cmdp;
-#ifdef ASH_ALIAS
- const struct alias *ap;
-#endif
- const char *path = pathval();
-
- if (verbose) {
- out1str(command);
- }
-
- /* First look at the keywords */
- if (findkwd(command)) {
- out1str(verbose ? " is a shell keyword" : command);
- goto out;
- }
-
-#ifdef ASH_ALIAS
- /* Then look at the aliases */
- if ((ap = lookupalias(command, 0)) != NULL) {
- if (verbose) {
- out1fmt(" is an alias for %s", ap->val);
- } else {
- printalias(ap);
- }
- goto out;
- }
-#endif
- /* Then check if it is a tracked alias */
- if ((cmdp = cmdlookup(command, 0)) != NULL) {
- entry.cmdtype = cmdp->cmdtype;
- entry.u = cmdp->param;
- } else {
- /* Finally use brute force */
- find_command(command, &entry, DO_ABS, path);
- }
-
- switch (entry.cmdtype) {
- case CMDNORMAL: {
- int j = entry.u.index;
- char *p;
- if (j == -1) {
- p = command;
- } else {
- do {
- p = padvance(&path, command);
- stunalloc(p);
- } while (--j >= 0);
- }
- if (verbose) {
- out1fmt(
- " is%s %s",
- cmdp ? " a tracked alias for" : nullstr, p
- );
- } else {
- out1str(p);
- }
- break;
- }
-
- case CMDFUNCTION:
- if (verbose) {
- out1str(" is a shell function");
- } else {
- out1str(command);
- }
- break;
-
- case CMDBUILTIN:
- if (verbose) {
- out1fmt(
- " is a %sshell builtin",
- IS_BUILTIN_SPECIAL(entry.u.cmd) ?
- "special " : nullstr
- );
- } else {
- out1str(command);
- }
- break;
-
- default:
- if (verbose) {
- out1str(": not found\n");
- }
- return 127;
- }
-
-out:
- putchar('\n');
- return 0;
-}
-#endif
-
#ifdef ASH_CMDCMD
static int
commandcmd(argc, argv)
case 'V':
verbose_verify_only = 1;
break;
- default:
- out2fmt(
-"command: nextopt returned character code 0%o\n", c);
- return EX_SOFTWARE;
}
if (default_path + verify_only + verbose_verify_only > 1 ||
!*argptr) {
- out2fmt(
-"command [-p] command [arg ...]\n");
- out2fmt(
-"command {-v|-V} command\n");
+ out2str(
+ "command [-p] command [arg ...]\n"
+ "command {-v|-V} command\n");
return EX_USAGE;
}
-#ifdef ASH_TYPE
if (verify_only || verbose_verify_only) {
- return describe_command(*argptr, verbose_verify_only);
+ char *argv_a[2];
+
+ argv_a[1] = 0;
+ argv_a[0] = *argptr;
+ argptr = argv_a;
+ optptr = verbose_verify_only ? "v" : "V"; /* reverse special */
+ return hashcmd(argc, argv);
}
-#endif
return 0;
}
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 strtodest (const char *, const char *, int);
+static void strtodest (const char *, int, int);
static void varvalue (char *, int, int);
static void recordregion (int, int, int);
static void removerecordregions (int);
static void ifsbreakup (char *, struct arglist *);
static void ifsfree (void);
static void expandmeta (struct strlist *, int);
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB)
#if !defined(GLOB_BROKEN)
static void addglob (const glob_t *);
#endif
#endif
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
static void expmeta (char *, char *);
#endif
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
static struct strlist *expsort (struct strlist *);
static struct strlist *msort (struct strlist *, int);
#endif
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
static int patmatch (char *, char *, int);
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
static int patmatch2 (char *, char *, int);
#else
static int pmatch (char *, char *, int);
*/
/* arg: the document, fd: where to write the expanded version */
-static void
+static inline void
expandhere(union node *arg, int fd)
{
herefd = fd;
}
-
/*
- * 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.
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
*/
-static void
-argstr(p, flag)
+static inline char *
+evalvar(p, flag)
char *p;
int flag;
{
- char c;
- int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
- int firsteq = 1;
+ int subtype;
+ int varflags;
+ char *var;
+ const char *val;
+ int patloc;
+ int c;
+ int set;
+ int special;
+ int startloc;
+ int varlen;
+ int easy;
+ int quotes = flag & (EXP_FULL | EXP_CASE);
- if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
- p = exptilde(p, flag);
- for (;;) {
- switch (c = *p++) {
- case '\0':
- case CTLENDVAR: /* ??? */
- goto breakloop;
- case CTLQUOTEMARK:
- /* "$@" syntax adherence hack */
- if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
- break;
- if ((flag & EXP_FULL) != 0)
- STPUTC(c, expdest);
- break;
- case CTLESC:
- if (quotes)
- STPUTC(c, expdest);
- c = *p++;
- STPUTC(c, expdest);
- break;
- case CTLVAR:
- p = evalvar(p, flag);
- break;
- case CTLBACKQ:
- case CTLBACKQ|CTLQUOTE:
- expbackq(argbackq->n, c & CTLQUOTE, flag);
- argbackq = argbackq->next;
- break;
-#ifdef ASH_MATH_SUPPORT
- case CTLENDARI:
- expari(flag);
- break;
-#endif
- case ':':
- case '=':
- /*
- * 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;
- }
- p = exptilde(p, flag);
+ varflags = *p++;
+ subtype = varflags & VSTYPE;
+ var = p;
+ special = 0;
+ if (! is_name(*p))
+ special = 1;
+ p = strchr(p, '=') + 1;
+again: /* jump here after setting a variable with ${var=text} */
+ if (special) {
+ set = varisset(var, varflags & VSNUL);
+ val = NULL;
+ } else {
+ val = lookupvar(var);
+ if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
+ val = NULL;
+ set = 0;
+ } else
+ set = 1;
+ }
+ varlen = 0;
+ startloc = expdest - stackblock();
+ if (set && subtype != VSPLUS) {
+ /* insert the value of the variable */
+ if (special) {
+ varvalue(var, varflags & VSQUOTE, flag);
+ if (subtype == VSLENGTH) {
+ varlen = expdest - stackblock() - startloc;
+ STADJUST(-varlen, expdest);
+ }
+ } else {
+ if (subtype == VSLENGTH) {
+ varlen = strlen(val);
+ } else {
+ strtodest(
+ val,
+ varflags & VSQUOTE ?
+ DQSYNTAX : BASESYNTAX,
+ quotes
+ );
}
- break;
- default:
- STPUTC(c, expdest);
}
}
-breakloop:;
- return;
-}
-static char *
-exptilde(p, flag)
- char *p;
- int flag;
-{
- char c, *startp = p;
- struct passwd *pw;
- const char *home;
- int quotes = flag & (EXP_FULL | EXP_CASE);
+ if (subtype == VSPLUS)
+ set = ! set;
- while ((c = *p) != '\0') {
- switch(c) {
- case CTLESC:
- return (startp);
- case CTLQUOTEMARK:
- return (startp);
- case ':':
- if (flag & EXP_VARTILDE)
- goto done;
- break;
- case '/':
- goto done;
- }
- p++;
- }
-done:
- *p = '\0';
- if (*(startp+1) == '\0') {
+ easy = ((varflags & VSQUOTE) == 0 ||
+ (*var == '@' && shellparam.nparam != 1));
+
+
+ switch (subtype) {
+ case VSLENGTH:
+ expdest = cvtnum(varlen, expdest);
+ goto record;
+
+ case VSNORMAL:
+ if (!easy)
+ break;
+record:
+ recordregion(startloc, expdest - stackblock(),
+ varflags & VSQUOTE);
+ break;
+
+ case VSPLUS:
+ case VSMINUS:
+ if (!set) {
+ argstr(p, flag);
+ break;
+ }
+ if (easy)
+ goto record;
+ break;
+
+ case VSTRIMLEFT:
+ case VSTRIMLEFTMAX:
+ case VSTRIMRIGHT:
+ case VSTRIMRIGHTMAX:
+ if (!set)
+ break;
+ /*
+ * Terminate the string and start recording the pattern
+ * right after it
+ */
+ STPUTC('\0', expdest);
+ patloc = expdest - stackblock();
+ if (subevalvar(p, NULL, patloc, subtype,
+ startloc, varflags, quotes) == 0) {
+ int amount = (expdest - stackblock() - patloc) + 1;
+ STADJUST(-amount, expdest);
+ }
+ /* Remove any recorded regions beyond start of variable */
+ removerecordregions(startloc);
+ goto record;
+
+ case VSASSIGN:
+ case VSQUESTION:
+ if (!set) {
+ if (subevalvar(p, var, 0, subtype, startloc,
+ varflags, quotes)) {
+ varflags &= ~VSNUL;
+ /*
+ * Remove any recorded regions beyond
+ * start of variable
+ */
+ removerecordregions(startloc);
+ goto again;
+ }
+ break;
+ }
+ if (easy)
+ goto record;
+ break;
+
+#ifdef DEBUG
+ default:
+ abort();
+#endif
+ }
+
+ if (subtype != VSNORMAL) { /* skip to end of alternative */
+ int nesting = 1;
+ for (;;) {
+ if ((c = *p++) == CTLESC)
+ p++;
+ else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+ if (set)
+ argbackq = argbackq->next;
+ } else if (c == CTLVAR) {
+ if ((*p++ & VSTYPE) != VSNORMAL)
+ nesting++;
+ } else if (c == CTLENDVAR) {
+ if (--nesting == 0)
+ break;
+ }
+ }
+ }
+ return p;
+}
+
+
+/*
+ * 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.
+ */
+
+static void
+argstr(p, flag)
+ char *p;
+ int flag;
+{
+ char c;
+ int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
+ int firsteq = 1;
+
+ if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
+ p = exptilde(p, flag);
+ for (;;) {
+ switch (c = *p++) {
+ case '\0':
+ case CTLENDVAR: /* ??? */
+ goto breakloop;
+ case CTLQUOTEMARK:
+ /* "$@" syntax adherence hack */
+ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
+ break;
+ if ((flag & EXP_FULL) != 0)
+ STPUTC(c, expdest);
+ break;
+ case CTLESC:
+ if (quotes)
+ STPUTC(c, expdest);
+ c = *p++;
+ STPUTC(c, expdest);
+ break;
+ case CTLVAR:
+ p = evalvar(p, flag);
+ break;
+ case CTLBACKQ:
+ case CTLBACKQ|CTLQUOTE:
+ expbackq(argbackq->n, c & CTLQUOTE, flag);
+ argbackq = argbackq->next;
+ break;
+#ifdef ASH_MATH_SUPPORT
+ case CTLENDARI:
+ expari(flag);
+ break;
+#endif
+ case ':':
+ case '=':
+ /*
+ * 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;
+ }
+ p = exptilde(p, flag);
+ }
+ break;
+ default:
+ STPUTC(c, expdest);
+ }
+ }
+breakloop:;
+ return;
+}
+
+static char *
+exptilde(p, flag)
+ char *p;
+ int flag;
+{
+ char c, *startp = p;
+ struct passwd *pw;
+ const char *home;
+ int quotes = flag & (EXP_FULL | EXP_CASE);
+
+ while ((c = *p) != '\0') {
+ switch(c) {
+ case CTLESC:
+ return (startp);
+ case CTLQUOTEMARK:
+ return (startp);
+ case ':':
+ if (flag & EXP_VARTILDE)
+ goto done;
+ break;
+ case '/':
+ goto done;
+ }
+ p++;
+ }
+done:
+ *p = '\0';
+ if (*(startp+1) == '\0') {
if ((home = lookupvar("HOME")) == NULL)
goto lose;
} else {
expari(int flag)
{
char *p, *start;
+ int errcode;
int result;
int begoff;
int quotes = flag & (EXP_FULL | EXP_CASE);
removerecordregions(begoff);
if (quotes)
rmescapes(p+2);
- result = arith(p+2);
+ result = arith(p+2, &errcode);
+ if (errcode < 0) {
+ if(errcode == -2)
+ error("divide by zero");
+ else
+ error("syntax error: \"%s\"\n", p+2);
+ }
snprintf(p, 12, "%d", result);
while (*p++)
struct nodelist *volatile saveargbackq;
char lastc;
int startloc = dest - stackblock();
- char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
+ int syntax = quoted ? DQSYNTAX : BASESYNTAX;
volatile int saveherefd;
int quotes = flag & (EXP_FULL | EXP_CASE);
struct jmploc jmploc;
if (--in.nleft < 0) {
if (in.fd < 0)
break;
- while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
+ i = safe_read(in.fd, buf, sizeof buf);
TRACE(("expbackq: read returns %d\n", i));
if (i <= 0)
break;
}
lastc = *p++;
if (lastc != '\0') {
- if (quotes && syntax[(int)lastc] == CCTL)
+ if (quotes && SIT(lastc, syntax) == CCTL)
STPUTC(CTLESC, dest);
STPUTC(lastc, dest);
}
/*
- * Expand a variable, and return a pointer to the next character in the
- * input string.
+ * Test whether a specialized variable is set.
*/
-static char *
-evalvar(p, flag)
- char *p;
- int flag;
+static int
+varisset(name, nulok)
+ char *name;
+ int nulok;
{
- int subtype;
- int varflags;
- char *var;
- char *val;
- int patloc;
- int c;
- int set;
- int special;
- int startloc;
- int varlen;
- int easy;
- int quotes = flag & (EXP_FULL | EXP_CASE);
-
- varflags = *p++;
- subtype = varflags & VSTYPE;
- var = p;
- special = 0;
- if (! is_name(*p))
- special = 1;
- p = strchr(p, '=') + 1;
-again: /* jump here after setting a variable with ${var=text} */
- if (special) {
- set = varisset(var, varflags & VSNUL);
- val = NULL;
- } else {
- val = lookupvar(var);
- if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
- val = NULL;
- set = 0;
- } else
- set = 1;
- }
- varlen = 0;
- startloc = expdest - stackblock();
- if (set && subtype != VSPLUS) {
- /* insert the value of the variable */
- if (special) {
- varvalue(var, varflags & VSQUOTE, flag);
- if (subtype == VSLENGTH) {
- varlen = expdest - stackblock() - startloc;
- STADJUST(-varlen, expdest);
- }
- } else {
- if (subtype == VSLENGTH) {
- varlen = strlen(val);
- } else {
- strtodest(
- val,
- varflags & VSQUOTE ?
- DQSYNTAX : BASESYNTAX,
- quotes
- );
- }
- }
- }
-
- if (subtype == VSPLUS)
- set = ! set;
-
- easy = ((varflags & VSQUOTE) == 0 ||
- (*var == '@' && shellparam.nparam != 1));
-
-
- switch (subtype) {
- case VSLENGTH:
- expdest = cvtnum(varlen, expdest);
- goto record;
-
- case VSNORMAL:
- if (!easy)
- break;
-record:
- recordregion(startloc, expdest - stackblock(),
- varflags & VSQUOTE);
- break;
-
- case VSPLUS:
- case VSMINUS:
- if (!set) {
- argstr(p, flag);
- break;
- }
- if (easy)
- goto record;
- break;
-
- case VSTRIMLEFT:
- case VSTRIMLEFTMAX:
- case VSTRIMRIGHT:
- case VSTRIMRIGHTMAX:
- if (!set)
- break;
- /*
- * Terminate the string and start recording the pattern
- * right after it
- */
- STPUTC('\0', expdest);
- patloc = expdest - stackblock();
- if (subevalvar(p, NULL, patloc, subtype,
- startloc, varflags, quotes) == 0) {
- int amount = (expdest - stackblock() - patloc) + 1;
- STADJUST(-amount, expdest);
- }
- /* Remove any recorded regions beyond start of variable */
- removerecordregions(startloc);
- goto record;
-
- case VSASSIGN:
- case VSQUESTION:
- if (!set) {
- if (subevalvar(p, var, 0, subtype, startloc,
- varflags, quotes)) {
- varflags &= ~VSNUL;
- /*
- * Remove any recorded regions beyond
- * start of variable
- */
- removerecordregions(startloc);
- goto again;
- }
- break;
- }
- if (easy)
- goto record;
- break;
-
-#ifdef DEBUG
- default:
- abort();
-#endif
- }
-
- if (subtype != VSNORMAL) { /* skip to end of alternative */
- int nesting = 1;
- for (;;) {
- if ((c = *p++) == CTLESC)
- p++;
- else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
- if (set)
- argbackq = argbackq->next;
- } else if (c == CTLVAR) {
- if ((*p++ & VSTYPE) != VSNORMAL)
- nesting++;
- } else if (c == CTLENDVAR) {
- if (--nesting == 0)
- break;
- }
- }
- }
- return p;
-}
-
-/*
- * Test whether a specialized variable is set.
- */
-
-static int
-varisset(name, nulok)
- char *name;
- int nulok;
-{
- if (*name == '!')
- return backgndpid != -1;
- else if (*name == '@' || *name == '*') {
- if (*shellparam.p == NULL)
- return 0;
+ if (*name == '!')
+ return backgndpid != -1;
+ else if (*name == '@' || *name == '*') {
+ if (*shellparam.p == NULL)
+ return 0;
if (nulok) {
char **av;
*/
static void
-strtodest(p, syntax, quotes)
- const char *p;
- const char *syntax;
- int quotes;
+strtodest(const char *p, int syntax, int quotes)
{
while (*p) {
- if (quotes && syntax[(int) *p] == CCTL)
+ if (quotes && SIT(*p,syntax) == CCTL)
STPUTC(CTLESC, expdest);
STPUTC(*p++, expdest);
}
*/
static void
-varvalue(name, quoted, flags)
- char *name;
- int quoted;
- int flags;
+varvalue(char *name, int quoted, int flags)
{
int num;
char *p;
int sep;
int sepq = 0;
char **ap;
- char const *syntax;
+ int syntax;
int allow_split = flags & EXP_FULL;
int quotes = flags & (EXP_FULL | EXP_CASE);
case '*':
sep = ifsset() ? ifsval()[0] : ' ';
if (quotes) {
- sepq = syntax[(int) sep] == CCTL;
+ sepq = SIT(sep,syntax) == CCTL;
}
param:
for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
* should be escapes. The results are stored in the list exparg.
*/
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
static void
expandmeta(str, flag)
struct strlist *str;
goto nometa;
p = preglob(str->text);
INTOFF;
- switch (glob(p, GLOB_NOMAGIC, 0, &pglob)) {
+ switch (glob(p, 0, 0, &pglob)) {
case 0:
- if (!(pglob.gl_flags & GLOB_MAGCHAR))
+ if(pglob.gl_pathv[1]==0 && !strcmp(p, pglob.gl_pathv[0]))
goto nometa2;
addglob(&pglob);
globfree(&pglob);
break;
}
}
- } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) {
+ } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) {
metaflag = 1;
} else if (*p == '\0')
break;
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
/*
* Sort the results of file name expansion. It calculates the number of
* strings to sort and then calls msort (short for merge sort) to do the
* Returns true if the pattern matches the string.
*/
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
/* squoted: string might have quote chars */
static int
patmatch(char *pattern, char *string, int squoted)
* Remove any CTLESC characters from a string.
*/
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
static char *
_rmescapes(char *str, int flag)
{
size_t len = p - str;
q = r = stalloc(strlen(p) + len + 1);
if (len > 0) {
-#ifdef _GNU_SOURCE
- q = mempcpy(q, str, len);
-#else
memcpy(q, str, len);
q += len;
-#endif
}
}
while (*p) {
}
-static int whichprompt; /* 1 == PS1, 2 == PS2 */
-
-
struct redirtab {
struct redirtab *next;
- short renamed[10];
+ short renamed[10]; /* Current ash support only 0-9 descriptors */
+ /* char on arm (and others) can't be negative */
};
static struct redirtab *redirlist;
* interactive shell and control is returned to the main command loop.
*/
-#ifdef ASH_ALIAS
/* 1 == check for aliases, 2 == also check for assignments */
-static int checkalias;
-#endif
+static int checkalias; /* also used in no alias mode for check assignments */
static void
reset(void) {
{
tokpushback = 0;
checkkwd = 0;
-#ifdef ASH_ALIAS
checkalias = 0;
-#endif
}
/* from redir.c: */
*/
#ifdef BB_FEATURE_COMMAND_EDITING
-unsigned int shell_context;
static const char * cmdedit_prompt;
static inline void putprompt(const char *s) {
cmdedit_prompt = s;
* Read a line from the script.
*/
-static char *
+static inline char *
pfgets(char *line, int len)
{
char *p = line;
return line;
}
-static int
+static inline int
preadfd(void)
{
int nr;
retry:
#ifdef BB_FEATURE_COMMAND_EDITING
{
- if (parsefile->fd)
- nr = read(parsefile->fd, buf, BUFSIZ - 1);
+ if (!iflag || parsefile->fd)
+ nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
else {
- do {
- cmdedit_read_input((char*)cmdedit_prompt, buf);
- nr = strlen(buf);
- } while (nr <=0 || shell_context);
- cmdedit_terminate();
+ nr = cmdedit_read_input((char*)cmdedit_prompt, buf);
}
}
#else
- nr = read(parsefile->fd, buf, BUFSIZ - 1);
+ nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
#endif
if (nr < 0) {
- if (errno == EINTR)
- goto retry;
if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
int flags = fcntl(0, F_GETFL, 0);
if (flags >= 0 && flags & O_NONBLOCK) {
}
-
-
/*
* Like setinputfile, but takes input from a string.
*/
static void
-setinputstring(string)
- char *string;
- {
+setinputstring(char *string)
+{
INTOFF;
pushfile();
parsenextc = string;
static void freejob (struct job *);
static struct job *getjob (const char *);
static int dowait (int, struct job *);
-static int waitproc (int, int *);
static void waitonint(int);
return fd0_redirected != 0;
}
-static int openredirect (union node *);
-static void dupredirect (union node *, int, char[10 ]);
-static int openhere (union node *);
-static int noclobberopen (const char *);
-
-
+static void dupredirect (const union node *, int, int fd1dup);
#ifdef JOBS
/*
initialpgrp = tcgetpgrp(2);
if (initialpgrp < 0) {
#endif
- out2str("sh: can't access tty; job cenabletrol turned off\n");
+ out2str("sh: can't access tty; job control turned off\n");
mflag = 0;
return;
}
} while (0);
#ifdef OLD_TTY_DRIVER
if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
- out2str("sh: need new tty driver to run job cenabletrol; job cenabletrol turned off\n");
+ out2str("sh: need new tty driver to run job control; job control turned off\n");
mflag = 0;
return;
}
#else
tcsetpgrp(2, rootpid);
#endif
- } else { /* turning job cenabletrol off */
+ } else { /* turning job control off */
setpgid(0, initialpgrp);
#ifdef OLD_TTY_DRIVER
ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
#endif
-/* A translation list so we can be polite to our users. */
-static char *signal_names[NSIG + 2] = {
- "EXIT",
- "SIGHUP",
- "SIGINT",
- "SIGQUIT",
- "SIGILL",
- "SIGTRAP",
- "SIGABRT",
- "SIGBUS",
- "SIGFPE",
- "SIGKILL",
- "SIGUSR1",
- "SIGSEGV",
- "SIGUSR2",
- "SIGPIPE",
- "SIGALRM",
- "SIGTERM",
- "SIGJUNK(16)",
- "SIGCHLD",
- "SIGCONT",
- "SIGSTOP",
- "SIGTSTP",
- "SIGTTIN",
- "SIGTTOU",
- "SIGURG",
- "SIGXCPU",
- "SIGXFSZ",
- "SIGVTALRM",
- "SIGPROF",
- "SIGWINCH",
- "SIGIO",
- "SIGPWR",
- "SIGSYS",
- "SIGRTMIN",
- "SIGRTMIN+1",
- "SIGRTMIN+2",
- "SIGRTMIN+3",
- "SIGRTMIN+4",
- "SIGRTMIN+5",
- "SIGRTMIN+6",
- "SIGRTMIN+7",
- "SIGRTMIN+8",
- "SIGRTMIN+9",
- "SIGRTMIN+10",
- "SIGRTMIN+11",
- "SIGRTMIN+12",
- "SIGRTMIN+13",
- "SIGRTMIN+14",
- "SIGRTMIN+15",
- "SIGRTMAX-15",
- "SIGRTMAX-14",
- "SIGRTMAX-13",
- "SIGRTMAX-12",
- "SIGRTMAX-11",
- "SIGRTMAX-10",
- "SIGRTMAX-9",
- "SIGRTMAX-8",
- "SIGRTMAX-7",
- "SIGRTMAX-6",
- "SIGRTMAX-5",
- "SIGRTMAX-4",
- "SIGRTMAX-3",
- "SIGRTMAX-2",
- "SIGRTMAX-1",
- "SIGRTMAX",
- "DEBUG",
- (char *)0x0,
-};
-
-
-
#ifdef JOBS
static int
killcmd(argc, argv)
}
if (list) {
+ const char *name;
+
if (!*argptr) {
out1str("0\n");
for (i = 1; i < NSIG; i++) {
- out1fmt(snlfmt, signal_names[i] + 3);
+ name = u_signal_names(0, &i, 1);
+ if(name)
+ printf(snlfmt, name);
}
return 0;
}
- signo = atoi(*argptr);
- if (signo > 128)
- signo -= 128;
- if (0 < signo && signo < NSIG)
- out1fmt(snlfmt, signal_names[signo] + 3);
+ name = u_signal_names(*argptr, &signo, -1);
+ if (name)
+ printf(snlfmt, name);
else
error("invalid signal number or exit status: %s",
*argptr);
}
out1str(s);
col += strlen(s);
- out1fmt(
+ printf(
"%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ',
ps->cmd
);
*/
static void
-freejob(jp)
- struct job *jp;
- {
- struct procstat *ps;
+freejob(struct job *jp)
+{
+ const struct procstat *ps;
int i;
INTOFF;
*/
static struct job *
-makejob(node, nprocs)
- union node *node;
- int nprocs;
+makejob(const union node *node, int nprocs)
{
int i;
struct job *jp;
static int
-forkshell(struct job *jp, union node *n, int mode)
+forkshell(struct job *jp, const union node *n, int mode)
{
int pid;
+#ifdef JOBS
int pgrp;
+#endif
const char *devnull = _PATH_DEVNULL;
const char *nullerr = "Can't open %s";
}
return pid;
}
+#ifdef JOBS
if (rootshell && mode != FORK_NOJOB && mflag) {
if (jp == NULL || jp->nprocs == 0)
pgrp = pid;
pgrp = jp->ps[0].pid;
setpgid(pid, pgrp);
}
+#endif
if (mode == FORK_BG)
backgndpid = pid; /* set $! */
if (jp) {
*/
static int
-waitforjob(jp)
- struct job *jp;
- {
+waitforjob(struct job *jp)
+{
#ifdef JOBS
int mypgrp = getpgrp();
#endif
* Wait for a process to terminate.
*/
+/*
+ * Do a wait system call. If job control is compiled in, we accept
+ * stopped processes. If block is zero, we return a value of zero
+ * rather than blocking.
+ *
+ * System V doesn't have a non-blocking wait system call. It does
+ * have a SIGCLD signal that is sent to a process when one of it's
+ * children dies. The obvious way to use SIGCLD would be to install
+ * a handler for SIGCLD which simply bumped a counter when a SIGCLD
+ * was received, and have waitproc bump another counter when it got
+ * the status of a process. Waitproc would then know that a wait
+ * system call would not block if the two counters were different.
+ * This approach doesn't work because if a process has children that
+ * have not been waited for, System V will send it a SIGCLD when it
+ * installs a signal handler for SIGCLD. What this means is that when
+ * a child exits, the shell will be sent SIGCLD signals continuously
+ * until is runs out of stack space, unless it does a wait call before
+ * restoring the signal handler. The code below takes advantage of
+ * this (mis)feature by installing a signal handler for SIGCLD and
+ * then checking to see whether it was called. If there are any
+ * children to be waited for, it will be.
+ *
+ */
+
+static inline int
+waitproc(int block, int *status)
+{
+ int flags;
+
+ flags = 0;
+#ifdef JOBS
+ if (jobctl)
+ flags |= WUNTRACED;
+#endif
+ if (block == 0)
+ flags |= WNOHANG;
+ return wait3(status, flags, (struct rusage *)NULL);
+}
+
static int
-dowait(block, job)
- int block;
- struct job *job;
+dowait(int block, struct job *job)
{
int pid;
int status;
-/*
- * Do a wait system call. If job control is compiled in, we accept
- * stopped processes. If block is zero, we return a value of zero
- * rather than blocking.
- *
- * System V doesn't have a non-blocking wait system call. It does
- * have a SIGCLD signal that is sent to a process when one of it's
- * children dies. The obvious way to use SIGCLD would be to install
- * a handler for SIGCLD which simply bumped a counter when a SIGCLD
- * was received, and have waitproc bump another counter when it got
- * the status of a process. Waitproc would then know that a wait
- * system call would not block if the two counters were different.
- * This approach doesn't work because if a process has children that
- * have not been waited for, System V will send it a SIGCLD when it
- * installs a signal handler for SIGCLD. What this means is that when
- * a child exits, the shell will be sent SIGCLD signals continuously
- * until is runs out of stack space, unless it does a wait call before
- * restoring the signal handler. The code below takes advantage of
- * this (mis)feature by installing a signal handler for SIGCLD and
- * then checking to see whether it was called. If there are any
- * children to be waited for, it will be.
- *
- */
-
-static int
-waitproc(block, status)
- int block;
- int *status;
-{
- int flags;
-
- flags = 0;
-#ifdef JOBS
- if (jobctl)
- flags |= WUNTRACED;
-#endif
- if (block == 0)
- flags |= WNOHANG;
- return wait3(status, flags, (struct rusage *)NULL);
-}
/*
* return 1 if there are stopped jobs, otherwise 0
cmdnextc = q;
}
+//#define CMDTXT_TABLE
+#ifdef CMDTXT_TABLE
+/*
+ * To collect a lot of redundant code in cmdtxt() case statements, we
+ * implement a mini language here. Each type of node struct has an
+ * associated instruction sequence that operates on its members via
+ * their offsets. The instruction are pack in unsigned chars with
+ * format IIDDDDDE where the bits are
+ * I : part of the instruction opcode, which are
+ * 00 : member is a pointer to another node -- process it recursively
+ * 40 : member is a pointer to a char string -- output it
+ * 80 : output the string whose index is stored in the data field
+ * CC : flag signaling that this case needs external processing
+ * D : data - either the (shifted) index of a fixed string to output or
+ * the actual offset of the member to operate on in the struct
+ * (since we assume bit 0 is set, the offset is not shifted)
+ * E : flag signaling end of instruction sequence
+ *
+ * WARNING: In order to handle larger offsets for 64bit archs, this code
+ * assumes that no offset can be an odd number and stores the
+ * end-of-instructions flag in bit 0.
+ */
+
+#define CMDTXT_NOMORE 0x01 /* NOTE: no offset should be odd */
+#define CMDTXT_CHARPTR 0x40
+#define CMDTXT_STRING 0x80
+#define CMDTXT_SPECIAL 0xC0
+#define CMDTXT_OFFSETMASK 0x3E
+
+static const char * const cmdtxt_strings[] = {
+ /* 0 1 2 3 4 5 6 7 */
+ "; ", "(", ")", " && ", " || ", "if ", "; then ", "...",
+ /* 8 9 10 11 12 13 */
+ "while ", "; do ", "; done", "until ", "for ", " in ...",
+ /* 14 15 16 17 */
+ "case ", "???", "() ...", "<<..."
+};
+
+static const char * const redir_strings[] = {
+ ">", "<", "<>", ">>", ">|", ">&", "<&"
+};
+
+static const unsigned char cmdtxt_ops[] = {
+#define CMDTXT_NSEMI 0
+ offsetof(union node, nbinary.ch1),
+ 0|CMDTXT_STRING,
+ offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
+#define CMDTXT_NCMD (CMDTXT_NSEMI + 3)
+#define CMDTXT_NPIPE (CMDTXT_NCMD)
+#define CMDTXT_NCASE (CMDTXT_NCMD)
+#define CMDTXT_NTO (CMDTXT_NCMD)
+#define CMDTXT_NFROM (CMDTXT_NCMD)
+#define CMDTXT_NFROMTO (CMDTXT_NCMD)
+#define CMDTXT_NAPPEND (CMDTXT_NCMD)
+#define CMDTXT_NTOOV (CMDTXT_NCMD)
+#define CMDTXT_NTOFD (CMDTXT_NCMD)
+#define CMDTXT_NFROMFD (CMDTXT_NCMD)
+ CMDTXT_SPECIAL,
+#define CMDTXT_NREDIR (CMDTXT_NPIPE + 1)
+#define CMDTXT_NBACKGND (CMDTXT_NREDIR)
+ offsetof(union node, nredir.n)|CMDTXT_NOMORE,
+#define CMDTXT_NSUBSHELL (CMDTXT_NBACKGND + 1)
+ (1*2)|CMDTXT_STRING,
+ offsetof(union node, nredir.n),
+ (2*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NAND (CMDTXT_NSUBSHELL + 3)
+ offsetof(union node, nbinary.ch1),
+ (3*2)|CMDTXT_STRING,
+ offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
+#define CMDTXT_NOR (CMDTXT_NAND + 3)
+ offsetof(union node, nbinary.ch1),
+ (4*2)|CMDTXT_STRING,
+ offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
+#define CMDTXT_NIF (CMDTXT_NOR + 3)
+ (5*2)|CMDTXT_STRING,
+ offsetof(union node, nif.test),
+ (6*2)|CMDTXT_STRING,
+ offsetof(union node, nif.ifpart),
+ (7*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NWHILE (CMDTXT_NIF + 5)
+ (8*2)|CMDTXT_STRING,
+ offsetof(union node, nbinary.ch1),
+ (9*2)|CMDTXT_STRING,
+ offsetof(union node, nbinary.ch2),
+ (10*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NUNTIL (CMDTXT_NWHILE + 5)
+ (11*2)|CMDTXT_STRING,
+ offsetof(union node, nbinary.ch1),
+ (9*2)|CMDTXT_STRING,
+ offsetof(union node, nbinary.ch2),
+ (10*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NFOR (CMDTXT_NUNTIL + 5)
+ (12*2)|CMDTXT_STRING,
+ offsetof(union node, nfor.var)|CMDTXT_CHARPTR,
+ (13*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NCLIST (CMDTXT_NFOR + 3) /* TODO: IS THIS CORRECT??? */
+#define CMDTXT_NNOT (CMDTXT_NCLIST) /* TODO: IS THIS CORRECT??? */
+ (15*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NDEFUN (CMDTXT_NCLIST + 1)
+ offsetof(union node, narg.text)|CMDTXT_CHARPTR,
+ (16*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NARG (CMDTXT_NDEFUN + 2)
+ offsetof(union node, narg.text)|CMDTXT_CHARPTR|CMDTXT_NOMORE,
+#define CMDTXT_NHERE (CMDTXT_NARG + 1)
+#define CMDTXT_NXHERE (CMDTXT_NHERE)
+ (17*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+};
+
+#if CMDTXT_NXHERE != 36
+#error CMDTXT_NXHERE
+#endif
+
+static const unsigned char cmdtxt_ops_index[26] = {
+ CMDTXT_NSEMI,
+ CMDTXT_NCMD,
+ CMDTXT_NPIPE,
+ CMDTXT_NREDIR,
+ CMDTXT_NBACKGND,
+ CMDTXT_NSUBSHELL,
+ CMDTXT_NAND,
+ CMDTXT_NOR,
+ CMDTXT_NIF,
+ CMDTXT_NWHILE,
+ CMDTXT_NUNTIL,
+ CMDTXT_NFOR,
+ CMDTXT_NCASE,
+ CMDTXT_NCLIST,
+ CMDTXT_NDEFUN,
+ CMDTXT_NARG,
+ CMDTXT_NTO,
+ CMDTXT_NFROM,
+ CMDTXT_NFROMTO,
+ CMDTXT_NAPPEND,
+ CMDTXT_NTOOV,
+ CMDTXT_NTOFD,
+ CMDTXT_NFROMFD,
+ CMDTXT_NHERE,
+ CMDTXT_NXHERE,
+ CMDTXT_NNOT,
+};
static void
cmdtxt(const union node *n)
{
- union node *np;
- struct nodelist *lp;
const char *p;
- int i;
- char s[2];
if (n == NULL)
return;
- switch (n->type) {
- case NSEMI:
- cmdtxt(n->nbinary.ch1);
- cmdputs("; ");
- cmdtxt(n->nbinary.ch2);
+
+ p = cmdtxt_ops + (int) cmdtxt_ops_index[n->type];
+ if ((*p & CMDTXT_SPECIAL) != CMDTXT_SPECIAL) { /* normal case */
+ do {
+ if (*p & CMDTXT_STRING) { /* output fixed string */
+ cmdputs(cmdtxt_strings[((int)(*p & CMDTXT_OFFSETMASK) >> 1)]);
+ } else {
+ const char *pf = ((const char *) n)
+ + ((int)(*p & CMDTXT_OFFSETMASK));
+ if (*p & CMDTXT_CHARPTR) { /* output dynamic string */
+ cmdputs(*((const char **) pf));
+ } else { /* output field */
+ cmdtxt(*((const union node **) pf));
+ }
+ }
+ } while (!(*p++ & CMDTXT_NOMORE));
+ } else if (n->type == NCMD) {
+ union node *np;
+ for (np = n->ncmd.args ; np ; np = np->narg.next) {
+ cmdtxt(np);
+ if (np->narg.next)
+ cmdputs(spcstr);
+ }
+ for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
+ cmdputs(spcstr);
+ cmdtxt(np);
+ }
+ } else if (n->type == NPIPE) {
+ struct nodelist *lp;
+ for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+ cmdtxt(lp->n);
+ if (lp->next)
+ cmdputs(" | ");
+ }
+ } else if (n->type == NCASE) {
+ cmdputs(cmdtxt_strings[14]);
+ cmdputs(n->ncase.expr->narg.text);
+ cmdputs(cmdtxt_strings[13]);
+ } else {
+#if (NTO != 16) || (NFROM != 17) || (NFROMTO != 18) || (NAPPEND != 19) || (NTOOV != 20) || (NTOFD != 21) || (NFROMFD != 22)
+#error Assumption violated regarding range and ordering of NTO ... NFROMFD!
+#endif
+ char s[2];
+
+#ifdef DEBUG
+ assert((n->type >= NTO) && (n->type <= NFROMFD));
+#endif
+
+ p = redir_strings[n->type - NTO];
+ if (n->nfile.fd != ('>' == *p)) {
+ s[0] = n->nfile.fd + '0';
+ s[1] = '\0';
+ cmdputs(s);
+ }
+ cmdputs(p);
+ if (n->type >= NTOFD) {
+ s[0] = n->ndup.dupfd + '0';
+ s[1] = '\0';
+ cmdputs(s);
+ } else {
+ cmdtxt(n->nfile.fname);
+ }
+ }
+}
+#else /* CMDTXT_TABLE */
+static void
+cmdtxt(const union node *n)
+{
+ union node *np;
+ struct nodelist *lp;
+ const char *p;
+ int i;
+ char s[2];
+
+ if (n == NULL)
+ return;
+ switch (n->type) {
+ case NSEMI:
+ cmdtxt(n->nbinary.ch1);
+ cmdputs("; ");
+ cmdtxt(n->nbinary.ch2);
break;
case NAND:
cmdtxt(n->nbinary.ch1);
break;
}
}
-
+#endif /* CMDTXT_TABLE */
static char *
commandtext(const union node *n)
#endif
static void read_profile (const char *);
-static char *find_dot_file (char *);
static void cmdloop (int);
static void options (int);
-static void minus_o (char *, int);
static void setoption (int, int);
static void procargs (int, char **);
*/
int
-shell_main(argc, argv)
+ash_main(argc, argv)
int argc;
char **argv;
{
struct jmploc jmploc;
struct stackmark smark;
volatile int state;
- char *shinit;
+ const char *shinit;
- DOTCMD = find_builtin(".");
BLTINCMD = find_builtin("builtin");
EXECCMD = find_builtin("exec");
EVALCMD = find_builtin("eval");
+#ifndef BB_FEATURE_SH_FANCY_PROMPT
+ unsetenv("PS1");
+ unsetenv("PS2");
+#endif
+
#if PROFILE
monitor(4, etext, profile_buf, sizeof profile_buf, 50);
#endif
* exception EXSHELLPROC to clean up before executing
* the shell procedure.
*/
- switch (exception) {
- case EXSHELLPROC:
+ if (exception == EXSHELLPROC) {
rootpid = getpid();
rootshell = 1;
minusc = NULL;
state = 3;
- break;
-
- case EXEXEC:
- exitstatus = exerrno;
- break;
-
- case EXERROR:
- exitstatus = 2;
- break;
-
- default:
- break;
- }
-
- if (exception != EXSHELLPROC) {
+ } else {
+ if (exception == EXEXEC) {
+ exitstatus = exerrno;
+ } else if (exception == EXERROR) {
+ exitstatus = 2;
+ }
if (state == 0 || iflag == 0 || ! rootshell)
exitshell(exitstatus);
}
state3:
state = 4;
if (sflag == 0 || minusc) {
- static int sigs[] = {
+ static const char sigs[] = {
SIGINT, SIGQUIT, SIGHUP,
#ifdef SIGTSTP
SIGTSTP,
#endif
SIGPIPE
};
-#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0]))
+#define SIGSSIZE ((sizeof(sigs)/sizeof(sigs[0])) - 1) /* trailing nul */
int i;
for (i = 0; i < SIGSSIZE; i++)
const char *name;
{
int fd;
- int xflag_set = 0;
- int vflag_set = 0;
+ int xflag_save;
+ int vflag_save;
INTOFF;
if ((fd = open(name, O_RDONLY)) >= 0)
if (fd < 0)
return;
/* -q turns off -x and -v just when executing init files */
+ /* Note: Might do a little redundant work, but reduces code size. */
+ xflag_save = xflag;
+ vflag_save = vflag;
if (qflag) {
- if (xflag)
- xflag = 0, xflag_set = 1;
- if (vflag)
- vflag = 0, vflag_set = 1;
+ vflag = xflag = 0;
}
cmdloop(0);
- if (qflag) {
- if (xflag_set)
- xflag = 1;
- if (vflag_set)
- vflag = 1;
- }
+ xflag = xflag_save;
+ vflag = vflag_save;
popfile();
}
*/
-static char *
+static inline char *
find_dot_file(mybasename)
char *mybasename;
{
exitshell(exitstatus);
/* NOTREACHED */
}
+
static pointer
stalloc(int nbytes)
{
#undef rflag
-#ifdef __GLIBC__
-static mode_t getmode(const void *, mode_t);
-static void *setmode(const char *);
-
#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
-typedef enum __rlimit_resource rlim_t;
-#endif
+typedef long rlim_t;
#endif
*/
static int
-readcmd(argc, argv)
- int argc;
- char **argv;
+readcmd(int argc, char **argv)
{
char **ap;
int backslash;
rflag = 1;
}
if (prompt && isatty(0)) {
- putprompt(prompt);
+ out2str(prompt); /* read without cmdedit */
flushall();
}
if (*(ap = argptr) == NULL)
int argc;
char **argv;
{
+ static const char permuser[3] = "ugo";
+ static const char permmode[3] = "rwx";
+ static const short int permmask[] = {
+ S_IRUSR, S_IWUSR, S_IXUSR,
+ S_IRGRP, S_IWGRP, S_IXGRP,
+ S_IROTH, S_IWOTH, S_IXOTH
+ };
+
char *ap;
- int mask;
+ mode_t mask;
int i;
int symbolic_mode = 0;
- while ((i = nextopt("S")) != '\0') {
+ while (nextopt("S") != '\0') {
symbolic_mode = 1;
}
if ((ap = *argptr) == NULL) {
if (symbolic_mode) {
- char u[4], g[4], o[4];
-
- i = 0;
- if ((mask & S_IRUSR) == 0)
- u[i++] = 'r';
- if ((mask & S_IWUSR) == 0)
- u[i++] = 'w';
- if ((mask & S_IXUSR) == 0)
- u[i++] = 'x';
- u[i] = '\0';
-
- i = 0;
- if ((mask & S_IRGRP) == 0)
- g[i++] = 'r';
- if ((mask & S_IWGRP) == 0)
- g[i++] = 'w';
- if ((mask & S_IXGRP) == 0)
- g[i++] = 'x';
- g[i] = '\0';
-
- i = 0;
- if ((mask & S_IROTH) == 0)
- o[i++] = 'r';
- if ((mask & S_IWOTH) == 0)
- o[i++] = 'w';
- if ((mask & S_IXOTH) == 0)
- o[i++] = 'x';
- o[i] = '\0';
-
- out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
+ char buf[18];
+ char *p = buf;
+ for (i=0 ; i<3 ; i++) {
+ int j;
+ *p++ = permuser[i];
+ *p++ = '=';
+ for (j=0 ; j<3 ; j++) {
+ if ((mask & permmask[3*i+j]) == 0) {
+ *p++ = permmode[j];
+ }
+ }
+ *p++ = ',';
+ }
+ *--p = 0;
+ puts(buf);
} else {
- out1fmt("%.4o\n", mask);
+ printf("%.4o\n", mask);
}
} else {
- if (isdigit((unsigned char)*ap)) {
+ if (is_digit((unsigned char)*ap)) {
mask = 0;
do {
if (*ap >= '8' || *ap < '0')
} while (*++ap != '\0');
umask(mask);
} else {
- void *set;
-
- INTOFF;
- if ((set = setmode(ap)) != 0) {
- mask = getmode(set, ~mask & 0777);
- ckfree(set);
- }
- INTON;
- if (!set)
+ mask = ~mask & 0777;
+ if (parse_mode(ap, &mask) == FALSE) {
error("Illegal mode: %s", ap);
-
+ }
umask(~mask & 0777);
}
}
struct limits {
const char *name;
- int cmd;
- int factor; /* multiply by to get rlim_{cur,max} values */
- char option;
+ short cmd;
+ short factor; /* multiply by to get rlim_{cur,max} values */
};
static const struct limits limits[] = {
#ifdef RLIMIT_CPU
- { "time(seconds)", RLIMIT_CPU, 1, 't' },
+ { "time(seconds)", RLIMIT_CPU, 1 },
#endif
#ifdef RLIMIT_FSIZE
- { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
+ { "file(blocks)", RLIMIT_FSIZE, 512 },
#endif
#ifdef RLIMIT_DATA
- { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
+ { "data(kbytes)", RLIMIT_DATA, 1024 },
#endif
#ifdef RLIMIT_STACK
- { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
+ { "stack(kbytes)", RLIMIT_STACK, 1024 },
#endif
#ifdef RLIMIT_CORE
- { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
+ { "coredump(blocks)", RLIMIT_CORE, 512 },
#endif
#ifdef RLIMIT_RSS
- { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
+ { "memory(kbytes)", RLIMIT_RSS, 1024 },
#endif
#ifdef RLIMIT_MEMLOCK
- { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
+ { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024 },
#endif
#ifdef RLIMIT_NPROC
- { "process(processes)", RLIMIT_NPROC, 1, 'p' },
+ { "process(processes)", RLIMIT_NPROC, 1 },
#endif
#ifdef RLIMIT_NOFILE
- { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' },
+ { "nofiles(descriptors)", RLIMIT_NOFILE, 1 },
#endif
#ifdef RLIMIT_VMEM
- { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' },
+ { "vmemory(kbytes)", RLIMIT_VMEM, 1024 },
#endif
#ifdef RLIMIT_SWAP
- { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' },
+ { "swap(kbytes)", RLIMIT_SWAP, 1024 },
#endif
- { (char *) 0, 0, 0, '\0' }
+ { NULL, 0, 0 }
};
static int
int argc;
char **argv;
{
+ static const char unlimited_string[] = "unlimited";
int c;
rlim_t val = 0;
enum { SOFT = 0x1, HARD = 0x2 }
struct rlimit limit;
what = 'f';
- while ((optc = nextopt("HSatfdsmcnpl")) != '\0')
- switch (optc) {
- case 'H':
+
+ 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_VMEM
+ "v"
+#endif
+#ifdef RLIMIT_SWAP
+ "w"
+#endif
+ )) != '\0') {
+ if (optc == 'H') {
how = HARD;
- break;
- case 'S':
+ } else if (optc == 'S') {
how = SOFT;
- break;
- case 'a':
+ } else if (optc == 'a') {
all = 1;
- break;
- default:
+ } else {
what = optc;
}
+ }
- for (l = limits; l->name && l->option != what; l++)
- ;
- if (!l->name)
- error("internal error (%c)", what);
+ for (l = limits; l->name; l++) {
+ if(l->name[0] == what)
+ break;
+ if(l->name[1]=='w' && what=='w')
+ break;
+ }
set = *argptr ? 1 : 0;
if (set) {
if (all || argptr[1])
error("too many arguments");
- if (strcmp(p, "unlimited") == 0)
+ if (strcmp(p, unlimited_string) == 0)
val = RLIM_INFINITY;
else {
val = (rlim_t) 0;
val *= l->factor;
}
}
+
if (all) {
for (l = limits; l->name; l++) {
+ printf("%-20s ", l->name);
getrlimit(l->cmd, &limit);
+ OUTPUT_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");
+ puts(unlimited_string);
else
{
val /= l->factor;
- out1fmt("%lld\n", (long long) val);
+ printf("%lld\n", (long long) val);
+ }
+ if (!all) {
+ break;
}
}
return 0;
}
- getrlimit(l->cmd, &limit);
- if (set) {
- if (how & HARD)
- limit.rlim_max = val;
- if (how & SOFT)
- limit.rlim_cur = val;
- if (setrlimit(l->cmd, &limit) < 0)
- 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);
- }
+ if (!set) {
+ goto OUTPUT_LIMIT;
}
+
+ getrlimit(l->cmd, &limit);
+ if (how & HARD)
+ limit.rlim_max = val;
+ if (how & SOFT)
+ limit.rlim_cur = val;
+ if (setrlimit(l->cmd, &limit) < 0)
+ error("error setting limit (%m)");
return 0;
}
/*
*/
static int
-prefix(pfx, string)
- char const *pfx;
- char const *string;
- {
+prefix(char const *pfx, char const *string)
+{
while (*pfx) {
if (*pfx++ != *string++)
return 0;
len2 = strspn(s + len1, "'");
len1p = len1 ? len1 + 2 : len1;
- switch (len2) {
- case 0:
- len2p = 0;
- break;
- case 1:
- len2p = 2;
- break;
- default:
- len2p = len2 + 2;
- }
+ len2p = len2 + ((len2 < 2) ? len2 : 2);
CHECKSTRSPACE(len1p + len2p + 1, p);
if (len1) {
*p = '\'';
-#ifdef _GNU_SOURCE
- q = mempcpy(p + 1, s, len1);
-#else
q = p + 1 + len1;
memcpy(p + 1, s, len1);
-#endif
*q++ = '\'';
s += len1;
}
- switch (len2) {
- case 0:
- break;
- case 1:
- *q++ = '\\';
- *q = '\'';
- s++;
- break;
- default:
+ if (len2 > 1) {
*q = '"';
-#ifdef _GNU_SOURCE
- *(char *) mempcpy(q + 1, s, len2) = '"';
-#else
q += 1 + len2;
memcpy(q + 1, s, len2);
*q = '"';
-#endif
s += len2;
+ } else if (len2 == 1) {
+ *q++ = '\\';
+ *q = '\'';
+ s++;
}
STADJUST(len1p + len2p, p);
}
-/*
- * This file was generated by the mknodes program.
- */
-
/*
* Routine for dealing with parsed shell commands.
*/
-static int funcblocksize; /* size of structures in function */
-static int funcstringsize; /* size of strings in node */
-static pointer funcblock; /* block to allocate function from */
-static char *funcstring; /* block to allocate strings from */
-
-static const short nodesize[26] = {
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct ncmd)),
- ALIGN(sizeof (struct npipe)),
- ALIGN(sizeof (struct nredir)),
- ALIGN(sizeof (struct nredir)),
- ALIGN(sizeof (struct nredir)),
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct nif)),
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct nfor)),
- ALIGN(sizeof (struct ncase)),
- ALIGN(sizeof (struct nclist)),
- ALIGN(sizeof (struct narg)),
- ALIGN(sizeof (struct narg)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct ndup)),
- ALIGN(sizeof (struct ndup)),
- ALIGN(sizeof (struct nhere)),
- ALIGN(sizeof (struct nhere)),
- ALIGN(sizeof (struct nnot)),
+static void sizenodelist (const struct nodelist *);
+static struct nodelist *copynodelist (const struct nodelist *);
+static char *nodesavestr (const char *);
+
+//#define CALCSIZE_TABLE
+//#define COPYNODE_TABLE
+#if defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE)
+/*
+ * To collect a lot of redundant code in case statements for copynode()
+ * and calcsize(), we implement a mini language here. Each type of node
+ * struct has an associated instruction sequence that operates on its
+ * members via their offsets. The instruction are pack in unsigned chars
+ * with format IIDDDDDE where the bits are
+ * I : part of the instruction opcode, which are
+ * 00 : member is a pointer to another node
+ * 40 : member is an integer
+ * 80 : member is a pointer to a nodelist
+ * CC : member is a pointer to a char string
+ * D : data - the actual offset of the member to operate on in the struct
+ * (since we assume bit 0 is set, it is not shifted)
+ * E : flag signaling end of instruction sequence
+ *
+ * WARNING: In order to handle larger offsets for 64bit archs, this code
+ * assumes that no offset can be an odd number and stores the
+ * end-of-instructions flag in bit 0.
+ */
+
+#define NODE_INTEGER 0x40
+#define NODE_NODELIST 0x80
+#define NODE_CHARPTR 0xC0
+#define NODE_NOMORE 0x01 /* Note: no offset should be odd (aligned)*/
+#define NODE_MBRMASK 0xC0
+#define NODE_OFFSETMASK 0x3E
+
+static const unsigned char copynode_ops[35] = {
+#define COPYNODE_OPS0 0
+ offsetof(union node, nbinary.ch2),
+ offsetof(union node, nbinary.ch1)|NODE_NOMORE,
+#define COPYNODE_OPS1 (COPYNODE_OPS0 + 2)
+ offsetof(union node, ncmd.redirect),
+ offsetof(union node, ncmd.args),
+ offsetof(union node, ncmd.assign),
+ offsetof(union node, ncmd.backgnd)|NODE_INTEGER|NODE_NOMORE,
+#define COPYNODE_OPS2 (COPYNODE_OPS1 + 4)
+ offsetof(union node, npipe.cmdlist)|NODE_NODELIST,
+ offsetof(union node, npipe.backgnd)|NODE_INTEGER|NODE_NOMORE,
+#define COPYNODE_OPS3 (COPYNODE_OPS2 + 2)
+ offsetof(union node, nredir.redirect),
+ offsetof(union node, nredir.n)|NODE_NOMORE,
+#define COPYNODE_OPS4 (COPYNODE_OPS3 + 2)
+ offsetof(union node, nif.elsepart),
+ offsetof(union node, nif.ifpart),
+ offsetof(union node, nif.test)|NODE_NOMORE,
+#define COPYNODE_OPS5 (COPYNODE_OPS4 + 3)
+ offsetof(union node, nfor.var)|NODE_CHARPTR,
+ offsetof(union node, nfor.body),
+ offsetof(union node, nfor.args)|NODE_NOMORE,
+#define COPYNODE_OPS6 (COPYNODE_OPS5 + 3)
+ offsetof(union node, ncase.cases),
+ offsetof(union node, ncase.expr)|NODE_NOMORE,
+#define COPYNODE_OPS7 (COPYNODE_OPS6 + 2)
+ offsetof(union node, nclist.body),
+ offsetof(union node, nclist.pattern),
+ offsetof(union node, nclist.next)|NODE_NOMORE,
+#define COPYNODE_OPS8 (COPYNODE_OPS7 + 3)
+ offsetof(union node, narg.backquote)|NODE_NODELIST,
+ offsetof(union node, narg.text)|NODE_CHARPTR,
+ offsetof(union node, narg.next)|NODE_NOMORE,
+#define COPYNODE_OPS9 (COPYNODE_OPS8 + 3)
+ offsetof(union node, nfile.fname),
+ offsetof(union node, nfile.fd)|NODE_INTEGER,
+ offsetof(union node, nfile.next)|NODE_NOMORE,
+#define COPYNODE_OPS10 (COPYNODE_OPS9 + 3)
+ offsetof(union node, ndup.vname),
+ offsetof(union node, ndup.dupfd)|NODE_INTEGER,
+ offsetof(union node, ndup.fd)|NODE_INTEGER,
+ offsetof(union node, ndup.next)|NODE_NOMORE,
+#define COPYNODE_OPS11 (COPYNODE_OPS10 + 4)
+ offsetof(union node, nhere.doc),
+ offsetof(union node, nhere.fd)|NODE_INTEGER,
+ offsetof(union node, nhere.next)|NODE_NOMORE,
+#define COPYNODE_OPS12 (COPYNODE_OPS11 + 3)
+ offsetof(union node, nnot.com)|NODE_NOMORE,
};
+#if COPYNODE_OPS12 != 34
+#error COPYNODE_OPS12 is incorrect
+#endif
+
+static const unsigned char copynode_ops_index[26] = {
+ COPYNODE_OPS0, /* NSEMI */
+ COPYNODE_OPS1, /* NCMD */
+ COPYNODE_OPS2, /* NPIPE */
+ COPYNODE_OPS3, /* NREDIR */
+ COPYNODE_OPS3, /* NBACKGND */
+ COPYNODE_OPS3, /* NSUBSHELL */
+ COPYNODE_OPS0, /* NAND */
+ COPYNODE_OPS0, /* NOR */
+ COPYNODE_OPS4, /* NIF */
+ COPYNODE_OPS0, /* NWHILE */
+ COPYNODE_OPS0, /* NUNTIL */
+ COPYNODE_OPS5, /* NFOR */
+ COPYNODE_OPS6, /* NCASE */
+ COPYNODE_OPS7, /* NCLIST */
+ COPYNODE_OPS8, /* NDEFUN */
+ COPYNODE_OPS8, /* NARG */
+ COPYNODE_OPS9, /* NTO */
+ COPYNODE_OPS9, /* NFROM */
+ COPYNODE_OPS9, /* NFROMTO */
+ COPYNODE_OPS9, /* NAPPEND */
+ COPYNODE_OPS9, /* NTOOV */
+ COPYNODE_OPS10, /* NTOFD */
+ COPYNODE_OPS10, /* NFROMFD */
+ COPYNODE_OPS11, /* NHERE */
+ COPYNODE_OPS11, /* NXHERE */
+ COPYNODE_OPS12, /* NNOT */
+};
-static void calcsize (union node *);
-static void sizenodelist (struct nodelist *);
-static union node *copynode (union node *);
-static struct nodelist *copynodelist (struct nodelist *);
-static char *nodesavestr (char *);
-
-
-
-/*
- * Make a copy of a parse tree.
- */
+#if NODE_CHARPTR != NODE_MBRMASK
+#error NODE_CHARPTR != NODE_MBRMASK!!!
+#endif
+#endif /* defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE) */
+#ifdef COPYNODE_TABLE
static union node *
-copyfunc(union node *n)
+copynode(const union node *n)
{
- if (n == NULL)
- return NULL;
- funcblocksize = 0;
- funcstringsize = 0;
- calcsize(n);
- funcblock = ckmalloc(funcblocksize + funcstringsize);
- funcstring = (char *) funcblock + funcblocksize;
- return copynode(n);
-}
+ union node *new;
+ const unsigned char *p;
-
-
-static void
-calcsize(n)
- union node *n;
+ if (n == NULL) {
+ return NULL;
+ }
+ new = funcblock;
+ new->type = n->type;
+ funcblock = (char *) funcblock + (int) nodesize[n->type];
+ p = copynode_ops + (int) copynode_ops_index[n->type];
+ do {
+ char *nn = ((char *) new) + ((int)(*p & NODE_OFFSETMASK));
+ const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK));
+
+ if (!(*p & NODE_MBRMASK)) { /* standard node */
+ *((union node **)nn) = copynode(*((const union node **) no));
+ } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
+ *((const char **)nn) = nodesavestr(*((const char **)no));
+ } else if (*p & NODE_NODELIST) { /* nodelist */
+ *((struct nodelist **)nn)
+ = copynodelist(*((const struct nodelist **) no));
+ } else { /* integer */
+ *((int *) nn) = *((int *) no);
+ }
+ } while (!(*p++ & NODE_NOMORE));
+ return new;
+}
+#else /* COPYNODE_TABLE */
+static union node *
+copynode(const union node *n)
{
+ union node *new;
+
if (n == NULL)
- return;
- funcblocksize += nodesize[n->type];
+ return NULL;
+ new = funcblock;
+ funcblock = (char *) funcblock + nodesize[n->type];
switch (n->type) {
case NSEMI:
case NAND:
case NOR:
case NWHILE:
case NUNTIL:
- calcsize(n->nbinary.ch2);
- calcsize(n->nbinary.ch1);
+ new->nbinary.ch2 = copynode(n->nbinary.ch2);
+ new->nbinary.ch1 = copynode(n->nbinary.ch1);
break;
case NCMD:
- calcsize(n->ncmd.redirect);
- calcsize(n->ncmd.args);
- calcsize(n->ncmd.assign);
+ new->ncmd.redirect = copynode(n->ncmd.redirect);
+ new->ncmd.args = copynode(n->ncmd.args);
+ new->ncmd.assign = copynode(n->ncmd.assign);
+ new->ncmd.backgnd = n->ncmd.backgnd;
break;
case NPIPE:
- sizenodelist(n->npipe.cmdlist);
+ new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
+ new->npipe.backgnd = n->npipe.backgnd;
break;
case NREDIR:
case NBACKGND:
case NSUBSHELL:
- calcsize(n->nredir.redirect);
- calcsize(n->nredir.n);
+ new->nredir.redirect = copynode(n->nredir.redirect);
+ new->nredir.n = copynode(n->nredir.n);
break;
case NIF:
- calcsize(n->nif.elsepart);
- calcsize(n->nif.ifpart);
- calcsize(n->nif.test);
+ new->nif.elsepart = copynode(n->nif.elsepart);
+ new->nif.ifpart = copynode(n->nif.ifpart);
+ new->nif.test = copynode(n->nif.test);
break;
case NFOR:
- funcstringsize += strlen(n->nfor.var) + 1;
- calcsize(n->nfor.body);
- calcsize(n->nfor.args);
+ new->nfor.var = nodesavestr(n->nfor.var);
+ new->nfor.body = copynode(n->nfor.body);
+ new->nfor.args = copynode(n->nfor.args);
break;
case NCASE:
- calcsize(n->ncase.cases);
- calcsize(n->ncase.expr);
+ new->ncase.cases = copynode(n->ncase.cases);
+ new->ncase.expr = copynode(n->ncase.expr);
break;
case NCLIST:
- calcsize(n->nclist.body);
- calcsize(n->nclist.pattern);
- calcsize(n->nclist.next);
+ new->nclist.body = copynode(n->nclist.body);
+ new->nclist.pattern = copynode(n->nclist.pattern);
+ new->nclist.next = copynode(n->nclist.next);
break;
case NDEFUN:
case NARG:
- sizenodelist(n->narg.backquote);
- funcstringsize += strlen(n->narg.text) + 1;
- calcsize(n->narg.next);
+ new->narg.backquote = copynodelist(n->narg.backquote);
+ new->narg.text = nodesavestr(n->narg.text);
+ new->narg.next = copynode(n->narg.next);
break;
case NTO:
case NFROM:
case NFROMTO:
case NAPPEND:
case NTOOV:
- calcsize(n->nfile.fname);
- calcsize(n->nfile.next);
+ new->nfile.fname = copynode(n->nfile.fname);
+ new->nfile.fd = n->nfile.fd;
+ new->nfile.next = copynode(n->nfile.next);
break;
case NTOFD:
case NFROMFD:
- calcsize(n->ndup.vname);
- calcsize(n->ndup.next);
+ new->ndup.vname = copynode(n->ndup.vname);
+ new->ndup.dupfd = n->ndup.dupfd;
+ new->ndup.fd = n->ndup.fd;
+ new->ndup.next = copynode(n->ndup.next);
break;
case NHERE:
case NXHERE:
- calcsize(n->nhere.doc);
- calcsize(n->nhere.next);
+ new->nhere.doc = copynode(n->nhere.doc);
+ new->nhere.fd = n->nhere.fd;
+ new->nhere.next = copynode(n->nhere.next);
break;
case NNOT:
- calcsize(n->nnot.com);
+ new->nnot.com = copynode(n->nnot.com);
break;
};
+ new->type = n->type;
+ return new;
}
+#endif /* COPYNODE_TABLE */
-
-
+#ifdef CALCSIZE_TABLE
static void
-sizenodelist(lp)
- struct nodelist *lp;
+calcsize(const union node *n)
{
- while (lp) {
- funcblocksize += ALIGN(sizeof(struct nodelist));
- calcsize(lp->n);
- lp = lp->next;
- }
-}
+ const unsigned char *p;
+ if (n == NULL)
+ return;
+ funcblocksize += (int) nodesize[n->type];
+ p = copynode_ops + (int) copynode_ops_index[n->type];
+ do {
+ const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK));
-static union node *
-copynode(n)
- union node *n;
+ if (!(*p & NODE_MBRMASK)) { /* standard node */
+ calcsize(*((const union node **) no));
+ } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
+ funcstringsize += strlen(*((const char **)no)) + 1;
+ } else if (*p & NODE_NODELIST) { /* nodelist */
+ sizenodelist(*((const struct nodelist **) no));
+ } /* else integer -- ignore */
+ } while (!(*p++ & NODE_NOMORE));
+}
+#else /* CALCSIZE_TABLE */
+static void
+calcsize(const union node *n)
{
- union node *new;
-
if (n == NULL)
- return NULL;
- new = funcblock;
- funcblock = (char *) funcblock + nodesize[n->type];
+ return;
+ funcblocksize += nodesize[n->type];
switch (n->type) {
case NSEMI:
case NAND:
case NOR:
case NWHILE:
case NUNTIL:
- new->nbinary.ch2 = copynode(n->nbinary.ch2);
- new->nbinary.ch1 = copynode(n->nbinary.ch1);
+ calcsize(n->nbinary.ch2);
+ calcsize(n->nbinary.ch1);
break;
case NCMD:
- new->ncmd.redirect = copynode(n->ncmd.redirect);
- new->ncmd.args = copynode(n->ncmd.args);
- new->ncmd.assign = copynode(n->ncmd.assign);
- new->ncmd.backgnd = n->ncmd.backgnd;
+ calcsize(n->ncmd.redirect);
+ calcsize(n->ncmd.args);
+ calcsize(n->ncmd.assign);
break;
case NPIPE:
- new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
- new->npipe.backgnd = n->npipe.backgnd;
+ sizenodelist(n->npipe.cmdlist);
break;
case NREDIR:
case NBACKGND:
case NSUBSHELL:
- new->nredir.redirect = copynode(n->nredir.redirect);
- new->nredir.n = copynode(n->nredir.n);
+ calcsize(n->nredir.redirect);
+ calcsize(n->nredir.n);
break;
case NIF:
- new->nif.elsepart = copynode(n->nif.elsepart);
- new->nif.ifpart = copynode(n->nif.ifpart);
- new->nif.test = copynode(n->nif.test);
+ calcsize(n->nif.elsepart);
+ calcsize(n->nif.ifpart);
+ calcsize(n->nif.test);
break;
case NFOR:
- new->nfor.var = nodesavestr(n->nfor.var);
- new->nfor.body = copynode(n->nfor.body);
- new->nfor.args = copynode(n->nfor.args);
+ funcstringsize += strlen(n->nfor.var) + 1;
+ calcsize(n->nfor.body);
+ calcsize(n->nfor.args);
break;
case NCASE:
- new->ncase.cases = copynode(n->ncase.cases);
- new->ncase.expr = copynode(n->ncase.expr);
+ calcsize(n->ncase.cases);
+ calcsize(n->ncase.expr);
break;
case NCLIST:
- new->nclist.body = copynode(n->nclist.body);
- new->nclist.pattern = copynode(n->nclist.pattern);
- new->nclist.next = copynode(n->nclist.next);
+ calcsize(n->nclist.body);
+ calcsize(n->nclist.pattern);
+ calcsize(n->nclist.next);
break;
case NDEFUN:
case NARG:
- new->narg.backquote = copynodelist(n->narg.backquote);
- new->narg.text = nodesavestr(n->narg.text);
- new->narg.next = copynode(n->narg.next);
+ sizenodelist(n->narg.backquote);
+ funcstringsize += strlen(n->narg.text) + 1;
+ calcsize(n->narg.next);
break;
case NTO:
case NFROM:
case NFROMTO:
case NAPPEND:
case NTOOV:
- new->nfile.fname = copynode(n->nfile.fname);
- new->nfile.fd = n->nfile.fd;
- new->nfile.next = copynode(n->nfile.next);
+ calcsize(n->nfile.fname);
+ calcsize(n->nfile.next);
break;
case NTOFD:
case NFROMFD:
- new->ndup.vname = copynode(n->ndup.vname);
- new->ndup.dupfd = n->ndup.dupfd;
- new->ndup.fd = n->ndup.fd;
- new->ndup.next = copynode(n->ndup.next);
+ calcsize(n->ndup.vname);
+ calcsize(n->ndup.next);
break;
case NHERE:
case NXHERE:
- new->nhere.doc = copynode(n->nhere.doc);
- new->nhere.fd = n->nhere.fd;
- new->nhere.next = copynode(n->nhere.next);
+ calcsize(n->nhere.doc);
+ calcsize(n->nhere.next);
break;
case NNOT:
- new->nnot.com = copynode(n->nnot.com);
+ calcsize(n->nnot.com);
break;
};
- new->type = n->type;
- return new;
+}
+#endif /* CALCSIZE_TABLE */
+
+static void
+sizenodelist(const struct nodelist *lp)
+{
+ while (lp) {
+ funcblocksize += ALIGN(sizeof(struct nodelist));
+ calcsize(lp->n);
+ lp = lp->next;
+ }
}
static struct nodelist *
-copynodelist(lp)
- struct nodelist *lp;
+copynodelist(const struct nodelist *lp)
{
struct nodelist *start;
struct nodelist **lpp;
}
-
static char *
-nodesavestr(s)
- char *s;
+nodesavestr(const char *s)
{
-#ifdef _GNU_SOURCE
- char *rtn = funcstring;
-
- funcstring = stpcpy(funcstring, s) + 1;
- return rtn;
-#else
- register char *p = s;
- register char *q = funcstring;
+ const char *p = s;
+ char *q = funcstring;
char *rtn = funcstring;
while ((*q++ = *p++) != '\0')
continue;
funcstring = q;
return rtn;
-#endif
}
#ifdef ASH_GETOPTS
* to the argument list; we advance it past the options.
*/
+static inline void
+minus_o(const char *name, int val)
+{
+ int i;
+
+ if (name == NULL) {
+ out1str("Current option settings\n");
+ for (i = 0; i < NOPTS; i++)
+ printf("%-16s%s\n", optent_name(optlist[i]),
+ optent_val(i) ? "on" : "off");
+ } else {
+ for (i = 0; i < NOPTS; i++)
+ if (equal(name, optent_name(optlist[i]))) {
+ setoption(optent_letter(optlist[i]), val);
+ return;
+ }
+ error("Illegal option -o %s", name);
+ }
+}
+
+
static void
-options(cmdline)
- int cmdline;
+options(int cmdline)
{
char *p;
int val;
}
}
-static void
-minus_o(name, val)
- char *name;
- int val;
-{
- int i;
-
- if (name == NULL) {
- out1str("Current option settings\n");
- for (i = 0; i < NOPTS; i++)
- out1fmt("%-16s%s\n", optent_name(optlist[i]),
- optent_val(i) ? "on" : "off");
- } else {
- for (i = 0; i < NOPTS; i++)
- if (equal(name, optent_name(optlist[i]))) {
- setoption(optent_letter(optlist[i]), val);
- return;
- }
- error("Illegal option -o %s", name);
- }
-}
-
static void
setoption(int flag, int val)
if (err) {
*myoptind = 1;
*optoff = -1;
- flushall();
exraise(EXERROR);
}
return done;
*/
static int
-nextopt(optstring)
- const char *optstring;
- {
+nextopt(const char *optstring)
+{
char *p;
const char *q;
char c;
va_end(ap);
}
-
-static void
-out1fmt(const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stdout, fmt, ap);
- va_end(ap);
-}
-
/*
* Version of write which resumes after a signal is caught.
*/
static struct nodelist *backquotelist;
static union node *redirnode;
-struct heredoc *heredoc;
+static struct heredoc *heredoc;
static int quoteflag; /* set if (part of) last token was quoted */
static int startlinno; /* line # where last token started */
static union node *simplecmd (void);
static void parsefname (void);
static void parseheredoc (void);
-static int peektoken (void);
+static char peektoken (void);
static int readtoken (void);
static int xxreadtoken (void);
-static int readtoken1 (int, char const *, char *, int);
+static int readtoken1 (int, int, const char *, int);
static int noexpand (char *);
static void synexpect (int) __attribute__((noreturn));
static void synerror (const char *) __attribute__((noreturn));
int tok;
checkkwd = 2;
- if (nlflag == 0 && tokendlist[peektoken()])
+ if (nlflag == 0 && peektoken())
return NULL;
n1 = NULL;
for (;;) {
tokpushback++;
}
checkkwd = 2;
- if (tokendlist[peektoken()])
+ if (peektoken())
return n1;
break;
case TEOF:
n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
n1->nbinary.ch1 = list(0);
if ((got=readtoken()) != TDO) {
-TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
+TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : ""));
synexpect(TDO);
}
n1->nbinary.ch2 = list(0);
redir = NULL;
rpp = &redir;
-#ifdef ASH_ALIAS
checkalias = 2;
-#endif
for (;;) {
switch (readtoken()) {
case TWORD:
}
}
-static int
+static char
peektoken() {
int t;
t = readtoken();
tokpushback++;
- return (t);
+ return tokname_array[t][0];
}
static int
readtoken() {
int t;
+
#ifdef ASH_ALIAS
- int savecheckkwd = checkkwd;
int savecheckalias = checkalias;
+ int savecheckkwd = checkkwd;
struct alias *ap;
#endif
const char *const *pp;
if ((pp = findkwd(wordtext))) {
- lasttoken = t = pp - parsekwd + KWDOFFSET;
- TRACE(("keyword %s recognized\n", tokname[t]));
+ lasttoken = t = pp - tokname_array;
+ TRACE(("keyword %s recognized\n", tokname(t)));
goto out;
}
}
}
-#ifdef ASH_ALIAS
+
if (t != TWORD) {
if (t != TREDIR) {
checkalias = 0;
}
} else if (checkalias == 2 && isassignment(wordtext)) {
lasttoken = t = TASSIGN;
+#ifdef ASH_ALIAS
} else if (checkalias) {
if (!quoteflag && (ap = lookupalias(wordtext, 1)) != NULL) {
if (*ap->val) {
goto top;
}
checkalias = 0;
- }
#endif
+ }
out:
#ifdef DEBUG
if (!alreadyseen)
- TRACE(("token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : ""));
+ TRACE(("token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : ""));
else
- TRACE(("reread token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : ""));
+ TRACE(("reread token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : ""));
#endif
return (t);
}
#define PARSEARITH() {goto parsearith; parsearith_return:;}
static int
-readtoken1(firstc, syntax, eofmark, striptabs)
- int firstc;
- char const *syntax;
- char *eofmark;
- int striptabs;
- {
+readtoken1(int firstc, int syntax, const char *eofmark, int striptabs)
+{
int c = firstc;
char *out;
int len;
int parenlevel; /* levels of parens in arithmetic */
int dqvarnest; /* levels of variables expansion within double quotes */
int oldstyle;
- char const *prevsyntax; /* syntax before arithmetic */
+ int prevsyntax; /* syntax before arithmetic */
#if __GNUC__
/* Avoid longjmp clobbering */
(void) &out;
CHECKEND(); /* set c to PEOF if at end of here document */
for (;;) { /* until end of line or end of word */
CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */
- switch(syntax[c]) {
+ switch(SIT(c,syntax)) {
case CNL: /* '\n' */
if (syntax == BASESYNTAX)
goto endword; /* exit outer loop */
if (dblquote && c != '\\' && c != '`' && c != '$'
&& (c != '"' || eofmark != NULL))
USTPUTC('\\', out);
- if (SQSYNTAX[c] == CCTL)
+ if (SIT(c,SQSYNTAX) == CCTL)
USTPUTC(CTLESC, out);
else if (eofmark == NULL)
USTPUTC(CTLQUOTEMARK, out);
}
if (c == *eofmark) {
if (pfgets(line, sizeof line) != NULL) {
- char *p, *q;
+ const char *p, *q;
p = line;
for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
continue;
if (c == CTLESC)
p++;
- else if (BASESYNTAX[(int)c] == CCTL)
+ else if (SIT(c,BASESYNTAX) == CCTL)
return 0;
}
return 1;
int token;
{
char msg[64];
+ int l;
- if (token >= 0) {
- snprintf(msg, 64, "%s unexpected (expecting %s)",
- tokname[lasttoken], tokname[token]);
- } else {
- snprintf(msg, 64, "%s unexpected", tokname[lasttoken]);
- }
+ l = sprintf(msg, "%s unexpected", tokname(lasttoken));
+ if (token >= 0)
+ sprintf(msg+l, " (expecting %s)", tokname(token));
synerror(msg);
/* NOTREACHED */
}
* called by editline -- any expansions to the prompt
* should be added here.
*/
-static inline const char *
-getprompt(void *unused)
+static void
+setprompt(int whichprompt)
{
- switch (whichprompt) {
- case 0:
- return "";
+ char *prompt;
+ switch (whichprompt) {
case 1:
- return ps1val();
+ prompt = ps1val();
+ break;
case 2:
- return ps2val();
- default:
- return "<internal prompt error>";
- }
-}
-
-static void
-setprompt(int which)
-{
- whichprompt = which;
- putprompt(getprompt(NULL));
+ prompt = ps2val();
+ break;
+ default: /* 0 */
+ prompt = "";
+ }
+ putprompt(prompt);
}
#endif
-
/*
- * Process a list of redirection commands. If the REDIR_PUSH flag is set,
- * old file descriptors are stashed away so that the redirection can be
- * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
- * standard output, and the standard error if it becomes a duplicate of
- * stdout.
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
*/
+static inline int
+noclobberopen(const char *fname)
+{
+ int r, fd;
+ struct stat finfo, finfo2;
-static void
-redirect(redir, flags)
- union node *redir;
- int flags;
- {
- union node *n;
- struct redirtab *sv = NULL;
- int i;
- int fd;
- int newfd;
- int try;
- char memory[10]; /* file descriptors to write to memory */
-
- for (i = 10 ; --i >= 0 ; )
- memory[i] = 0;
- memory[1] = flags & REDIR_BACKQ;
- if (flags & REDIR_PUSH) {
- sv = ckmalloc(sizeof (struct redirtab));
- for (i = 0 ; i < 10 ; i++)
- sv->renamed[i] = EMPTY;
- sv->next = redirlist;
- redirlist = sv;
+ /*
+ * If the file exists and is a regular file, return an error
+ * immediately.
+ */
+ r = stat(fname, &finfo);
+ if (r == 0 && S_ISREG(finfo.st_mode)) {
+ errno = EEXIST;
+ return -1;
}
- for (n = redir ; n ; n = n->nfile.next) {
- fd = n->nfile.fd;
- try = 0;
- if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
- n->ndup.dupfd == fd)
- continue; /* redirect from/to same file descriptor */
- INTOFF;
- newfd = openredirect(n);
- if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
- if (newfd == fd) {
- try++;
- } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
- switch (errno) {
- case EBADF:
- if (!try) {
- dupredirect(n, newfd, memory);
- try++;
- break;
- }
- /* FALLTHROUGH*/
- default:
- if (newfd >= 0) {
- close(newfd);
- }
- INTON;
- error("%d: %m", fd);
- /* NOTREACHED */
- }
- }
- if (!try) {
- close(fd);
- if (flags & REDIR_PUSH) {
- sv->renamed[fd] = i;
- }
- }
- } else if (fd != newfd) {
- close(fd);
+ /*
+ * If the file was not present (r != 0), make sure we open it
+ * exclusively so that if it is created before we open it, our open
+ * will fail. Make sure that we do not truncate an existing file.
+ * Note that we don't turn on O_EXCL unless the stat failed -- if the
+ * file was not a regular file, we leave O_EXCL off.
+ */
+ if (r != 0)
+ return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+ fd = open(fname, O_WRONLY|O_CREAT, 0666);
+
+ /* If the open failed, return the file descriptor right away. */
+ if (fd < 0)
+ return fd;
+
+ /*
+ * OK, the open succeeded, but the file may have been changed from a
+ * non-regular file to a regular file between the stat and the open.
+ * We are assuming that the O_EXCL open handles the case where FILENAME
+ * did not exist and is symlinked to an existing file between the stat
+ * and open.
+ */
+
+ /*
+ * If we can open it and fstat the file descriptor, and neither check
+ * revealed that it was a regular file, and the file has not been
+ * replaced, return the file descriptor.
+ */
+ if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
+ finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
+ return fd;
+
+ /* The file has been replaced. badness. */
+ close(fd);
+ errno = EEXIST;
+ return -1;
+}
+
+/*
+ * Handle here documents. Normally we fork off a process to write the
+ * data to a pipe. If the document is short, we can stuff the data in
+ * the pipe without forking.
+ */
+
+static inline int
+openhere(const union node *redir)
+{
+ int pip[2];
+ int len = 0;
+
+ if (pipe(pip) < 0)
+ 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);
+ goto out;
}
- if (fd == 0)
- fd0_redirected++;
- if (!try)
- dupredirect(n, newfd, memory);
- INTON;
}
+ if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+ close(pip[0]);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+ signal(SIGPIPE, SIG_DFL);
+ if (redir->type == NHERE)
+ xwrite(pip[1], redir->nhere.doc->narg.text, len);
+ else
+ expandhere(redir->nhere.doc, pip[1]);
+ _exit(0);
+ }
+out:
+ close(pip[1]);
+ return pip[0];
}
-static int
-openredirect(redir)
- union node *redir;
- {
+static inline int
+openredirect(const union node *redir)
+{
char *fname;
int f;
}
+/*
+ * Process a list of redirection commands. If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
+ * standard output, and the standard error if it becomes a duplicate of
+ * stdout.
+ */
+
static void
-dupredirect(union node *redir, int f, char memory[10])
+redirect(union node *redir, int flags)
{
- int fd = redir->nfile.fd;
+ union node *n;
+ struct redirtab *sv = NULL;
+ int i;
+ int fd;
+ int newfd;
+ int try;
+ int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */
- memory[fd] = 0;
- if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
- if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
- if (memory[redir->ndup.dupfd])
- memory[fd] = 1;
- else
- dup_as_newfd(redir->ndup.dupfd, fd);
- }
- return;
+ if (flags & REDIR_PUSH) {
+ sv = ckmalloc(sizeof (struct redirtab));
+ for (i = 0 ; i < 10 ; i++)
+ sv->renamed[i] = EMPTY;
+ sv->next = redirlist;
+ redirlist = sv;
}
+ for (n = redir ; n ; n = n->nfile.next) {
+ fd = n->nfile.fd;
+ try = 0;
+ if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
+ n->ndup.dupfd == fd)
+ continue; /* redirect from/to same file descriptor */
- if (f != fd) {
- dup_as_newfd(f, fd);
- close(f);
+ INTOFF;
+ newfd = openredirect(n);
+ if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
+ if (newfd == fd) {
+ try++;
+ } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
+ switch (errno) {
+ case EBADF:
+ if (!try) {
+ dupredirect(n, newfd, fd1dup);
+ try++;
+ break;
+ }
+ /* FALLTHROUGH*/
+ default:
+ if (newfd >= 0) {
+ close(newfd);
+ }
+ INTON;
+ error("%d: %m", fd);
+ /* NOTREACHED */
+ }
+ }
+ if (!try) {
+ close(fd);
+ if (flags & REDIR_PUSH) {
+ sv->renamed[fd] = i;
+ }
+ }
+ } else if (fd != newfd) {
+ close(fd);
+ }
+ if (fd == 0)
+ fd0_redirected++;
+ if (!try)
+ dupredirect(n, newfd, fd1dup);
+ INTON;
}
- return;
}
-
-/*
- * Handle here documents. Normally we fork off a process to write the
- * data to a pipe. If the document is short, we can stuff the data in
- * the pipe without forking.
- */
-
-static int
-openhere(redir)
- union node *redir;
- {
- int pip[2];
- int len = 0;
-
- if (pipe(pip) < 0)
- 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);
- goto out;
- }
- }
- if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
- close(pip[0]);
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-#ifdef SIGTSTP
- signal(SIGTSTP, SIG_IGN);
-#endif
- signal(SIGPIPE, SIG_DFL);
- if (redir->type == NHERE)
- xwrite(pip[1], redir->nhere.doc->narg.text, len);
- else
- expandhere(redir->nhere.doc, pip[1]);
- _exit(0);
+
+static void
+dupredirect(const union node *redir, int f, int fd1dup)
+{
+ int fd = redir->nfile.fd;
+
+ if(fd==1)
+ fd1dup = 0;
+ if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+ if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
+ if (redir->ndup.dupfd!=1 || fd1dup!=1)
+ dup_as_newfd(redir->ndup.dupfd, fd);
+ }
+ return;
}
-out:
- close(pip[1]);
- return pip[0];
+
+ if (f != fd) {
+ dup_as_newfd(f, fd);
+ close(f);
+ }
+ return;
}
+
/*
* Undo the effects of the last redirection.
*/
return newfd;
}
-/*
- * Open a file in noclobber mode.
- * The code was copied from bash.
- */
-static int
-noclobberopen(const char *fname)
-{
- int r, fd;
- struct stat finfo, finfo2;
-
- /*
- * If the file exists and is a regular file, return an error
- * immediately.
- */
- r = stat(fname, &finfo);
- if (r == 0 && S_ISREG(finfo.st_mode)) {
- errno = EEXIST;
- return -1;
- }
-
- /*
- * If the file was not present (r != 0), make sure we open it
- * exclusively so that if it is created before we open it, our open
- * will fail. Make sure that we do not truncate an existing file.
- * Note that we don't turn on O_EXCL unless the stat failed -- if the
- * file was not a regular file, we leave O_EXCL off.
- */
- if (r != 0)
- return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
- fd = open(fname, O_WRONLY|O_CREAT, 0666);
-
- /* If the open failed, return the file descriptor right away. */
- if (fd < 0)
- return fd;
-
- /*
- * OK, the open succeeded, but the file may have been changed from a
- * non-regular file to a regular file between the stat and the open.
- * We are assuming that the O_EXCL open handles the case where FILENAME
- * did not exist and is symlinked to an existing file between the stat
- * and open.
- */
-
- /*
- * If we can open it and fstat the file descriptor, and neither check
- * revealed that it was a regular file, and the file has not been
- * replaced, return the file descriptor.
- */
- if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
- finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
- return fd;
-
- /* The file has been replaced. badness. */
- close(fd);
- errno = EEXIST;
- return -1;
-}
-/*#ifdef __weak_alias
-__weak_alias(getmode,_getmode)
-__weak_alias(setmode,_setmode)
-#endif*/
-
-#ifdef __GLIBC__
-#define S_ISTXT __S_ISVTX
-#endif
-
-#define SET_LEN 6 /* initial # of bitcmd struct to malloc */
-#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */
-
-typedef struct bitcmd {
- char cmd;
- char cmd2;
- mode_t bits;
-} BITCMD;
-
-#define CMD2_CLR 0x01
-#define CMD2_SET 0x02
-#define CMD2_GBITS 0x04
-#define CMD2_OBITS 0x08
-#define CMD2_UBITS 0x10
-
-static BITCMD *addcmd (BITCMD *, int, int, int, u_int);
-static void compress_mode (BITCMD *);
-#ifdef SETMODE_DEBUG
-static void dumpmode (BITCMD *);
-#endif
-
-/*
- * Given the old mode and an array of bitcmd structures, apply the operations
- * described in the bitcmd structures to the old mode, and return the new mode.
- * Note that there is no '=' command; a strict assignment is just a '-' (clear
- * bits) followed by a '+' (set bits).
- */
-static mode_t
-getmode(bbox, omode)
- const void *bbox;
- mode_t omode;
-{
- const BITCMD *set;
- mode_t clrval, newmode, value;
-
- _DIAGASSERT(bbox != NULL);
-
- set = (const BITCMD *)bbox;
- newmode = omode;
- for (value = 0;; set++)
- switch(set->cmd) {
- /*
- * When copying the user, group or other bits around, we "know"
- * where the bits are in the mode so that we can do shifts to
- * copy them around. If we don't use shifts, it gets real
- * grundgy with lots of single bit checks and bit sets.
- */
- case 'u':
- value = (newmode & S_IRWXU) >> 6;
- goto common;
-
- case 'g':
- value = (newmode & S_IRWXG) >> 3;
- goto common;
-
- case 'o':
- value = newmode & S_IRWXO;
-common: if (set->cmd2 & CMD2_CLR) {
- clrval =
- (set->cmd2 & CMD2_SET) ? S_IRWXO : value;
- if (set->cmd2 & CMD2_UBITS)
- newmode &= ~((clrval<<6) & set->bits);
- if (set->cmd2 & CMD2_GBITS)
- newmode &= ~((clrval<<3) & set->bits);
- if (set->cmd2 & CMD2_OBITS)
- newmode &= ~(clrval & set->bits);
- }
- if (set->cmd2 & CMD2_SET) {
- if (set->cmd2 & CMD2_UBITS)
- newmode |= (value<<6) & set->bits;
- if (set->cmd2 & CMD2_GBITS)
- newmode |= (value<<3) & set->bits;
- if (set->cmd2 & CMD2_OBITS)
- newmode |= value & set->bits;
- }
- break;
-
- case '+':
- newmode |= set->bits;
- break;
-
- case '-':
- newmode &= ~set->bits;
- break;
-
- case 'X':
- if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
- newmode |= set->bits;
- break;
-
- case '\0':
- default:
-#ifdef SETMODE_DEBUG
- (void)printf("getmode:%04o -> %04o\n", omode, newmode);
-#endif
- return (newmode);
- }
-}
-
-#define ADDCMD(a, b, c, d) do { \
- if (set >= endset) { \
- BITCMD *newset; \
- setlen += SET_LEN_INCR; \
- newset = realloc(saveset, sizeof(BITCMD) * setlen); \
- if (newset == NULL) { \
- free(saveset); \
- return (NULL); \
- } \
- set = newset + (set - saveset); \
- saveset = newset; \
- endset = newset + (setlen - 2); \
- } \
- set = addcmd(set, (a), (b), (c), (d)); \
-} while (/*CONSTCOND*/0)
-
-#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
-
-static void *
-setmode(p)
- const char *p;
-{
- int perm, who;
- char op, *ep;
- BITCMD *set, *saveset, *endset;
- sigset_t mysigset, sigoset;
- mode_t mask;
- int equalopdone = 0; /* pacify gcc */
- int permXbits, setlen;
-
- if (!*p)
- return (NULL);
-
- /*
- * Get a copy of the mask for the permissions that are mask relative.
- * Flip the bits, we want what's not set. Since it's possible that
- * the caller is opening files inside a signal handler, protect them
- * as best we can.
- */
- sigfillset(&mysigset);
- (void)sigprocmask(SIG_BLOCK, &mysigset, &sigoset);
- (void)umask(mask = umask(0));
- mask = ~mask;
- (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
-
- setlen = SET_LEN + 2;
-
- if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
- return (NULL);
- saveset = set;
- endset = set + (setlen - 2);
-
- /*
- * If an absolute number, get it and return; disallow non-octal digits
- * or illegal bits.
- */
- if (isdigit((unsigned char)*p)) {
- perm = (mode_t)strtol(p, &ep, 8);
- if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) {
- free(saveset);
- return (NULL);
- }
- ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
- set->cmd = 0;
- return (saveset);
- }
-
- /*
- * Build list of structures to set/clear/copy bits as described by
- * each clause of the symbolic mode.
- */
- for (;;) {
- /* First, find out which bits might be modified. */
- for (who = 0;; ++p) {
- switch (*p) {
- case 'a':
- who |= STANDARD_BITS;
- break;
- case 'u':
- who |= S_ISUID|S_IRWXU;
- break;
- case 'g':
- who |= S_ISGID|S_IRWXG;
- break;
- case 'o':
- who |= S_IRWXO;
- break;
- default:
- goto getop;
- }
- }
-
-getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
- free(saveset);
- return (NULL);
- }
- if (op == '=')
- equalopdone = 0;
-
- who &= ~S_ISTXT;
- for (perm = 0, permXbits = 0;; ++p) {
- switch (*p) {
- case 'r':
- perm |= S_IRUSR|S_IRGRP|S_IROTH;
- break;
- case 's':
- /*
- * If specific bits where requested and
- * only "other" bits ignore set-id.
- */
- if (who == 0 || (who & ~S_IRWXO))
- perm |= S_ISUID|S_ISGID;
- break;
- case 't':
- /*
- * If specific bits where requested and
- * only "other" bits ignore set-id.
- */
- if (who == 0 || (who & ~S_IRWXO)) {
- who |= S_ISTXT;
- perm |= S_ISTXT;
- }
- break;
- case 'w':
- perm |= S_IWUSR|S_IWGRP|S_IWOTH;
- break;
- case 'X':
- permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
- break;
- case 'x':
- perm |= S_IXUSR|S_IXGRP|S_IXOTH;
- break;
- case 'u':
- case 'g':
- case 'o':
- /*
- * When ever we hit 'u', 'g', or 'o', we have
- * to flush out any partial mode that we have,
- * and then do the copying of the mode bits.
- */
- if (perm) {
- ADDCMD(op, who, perm, mask);
- perm = 0;
- }
- if (op == '=')
- equalopdone = 1;
- if (op == '+' && permXbits) {
- ADDCMD('X', who, permXbits, mask);
- permXbits = 0;
- }
- ADDCMD(*p, who, op, mask);
- break;
-
- default:
- /*
- * Add any permissions that we haven't already
- * done.
- */
- if (perm || (op == '=' && !equalopdone)) {
- if (op == '=')
- equalopdone = 1;
- ADDCMD(op, who, perm, mask);
- perm = 0;
- }
- if (permXbits) {
- ADDCMD('X', who, permXbits, mask);
- permXbits = 0;
- }
- goto apply;
- }
- }
-
-apply: if (!*p)
- break;
- if (*p != ',')
- goto getop;
- ++p;
- }
- set->cmd = 0;
-#ifdef SETMODE_DEBUG
- (void)printf("Before compress_mode()\n");
- dumpmode(saveset);
-#endif
- compress_mode(saveset);
-#ifdef SETMODE_DEBUG
- (void)printf("After compress_mode()\n");
- dumpmode(saveset);
-#endif
- return (saveset);
-}
-
-static BITCMD *
-addcmd(set, op, who, oparg, mask)
- BITCMD *set;
- int oparg, who;
- int op;
- u_int mask;
-{
-
- _DIAGASSERT(set != NULL);
-
- switch (op) {
- case '=':
- set->cmd = '-';
- set->bits = who ? who : STANDARD_BITS;
- set++;
-
- op = '+';
- /* FALLTHROUGH */
- case '+':
- case '-':
- case 'X':
- set->cmd = op;
- set->bits = (who ? who : mask) & oparg;
- break;
-
- case 'u':
- case 'g':
- case 'o':
- set->cmd = op;
- if (who) {
- set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
- ((who & S_IRGRP) ? CMD2_GBITS : 0) |
- ((who & S_IROTH) ? CMD2_OBITS : 0);
- set->bits = (mode_t)~0;
- } else {
- set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
- set->bits = mask;
- }
-
- if (oparg == '+')
- set->cmd2 |= CMD2_SET;
- else if (oparg == '-')
- set->cmd2 |= CMD2_CLR;
- else if (oparg == '=')
- set->cmd2 |= CMD2_SET|CMD2_CLR;
- break;
- }
- return (set + 1);
-}
-
-#ifdef SETMODE_DEBUG
-static void
-dumpmode(set)
- BITCMD *set;
-{
-
- _DIAGASSERT(set != NULL);
-
- for (; set->cmd; ++set)
- (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
- set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
- set->cmd2 & CMD2_CLR ? " CLR" : "",
- set->cmd2 & CMD2_SET ? " SET" : "",
- set->cmd2 & CMD2_UBITS ? " UBITS" : "",
- set->cmd2 & CMD2_GBITS ? " GBITS" : "",
- set->cmd2 & CMD2_OBITS ? " OBITS" : "");
-}
-#endif
-
-/*
- * Given an array of bitcmd structures, compress by compacting consecutive
- * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
- * 'g' and 'o' commands continue to be separate. They could probably be
- * compacted, but it's not worth the effort.
- */
-static void
-compress_mode(set)
- BITCMD *set;
-{
- BITCMD *nset;
- int setbits, clrbits, Xbits, op;
-
- _DIAGASSERT(set != NULL);
-
- for (nset = set;;) {
- /* Copy over any 'u', 'g' and 'o' commands. */
- while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
- *set++ = *nset++;
- if (!op)
- return;
- }
-
- for (setbits = clrbits = Xbits = 0;; nset++) {
- if ((op = nset->cmd) == '-') {
- clrbits |= nset->bits;
- setbits &= ~nset->bits;
- Xbits &= ~nset->bits;
- } else if (op == '+') {
- setbits |= nset->bits;
- clrbits &= ~nset->bits;
- Xbits &= ~nset->bits;
- } else if (op == 'X')
- Xbits |= nset->bits & ~setbits;
- else
- break;
- }
- if (clrbits) {
- set->cmd = '-';
- set->cmd2 = 0;
- set->bits = clrbits;
- set++;
- }
- if (setbits) {
- set->cmd = '+';
- set->cmd2 = 0;
- set->bits = setbits;
- set++;
- }
- if (Xbits) {
- set->cmd = 'X';
- set->cmd2 = 0;
- set->bits = Xbits;
- set++;
- }
- }
-}
#ifdef DEBUG
static void shtree (union node *, int, char *, FILE*);
static void shcmd (union node *, FILE *);
for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
if (! first)
putchar(' ');
+#if 1
+ s = "*error*";
+ dftfd = 0;
+ if ((np->nfile.type <= NFROMFD) && (np->nfile.type >= NTO)) {
+ s = redir_strings[np->nfile.type - NTO];
+ if (*s == '>') {
+ dftfd = 1;
+ }
+ }
+#else
switch (np->nfile.type) {
case NTO: s = ">"; dftfd = 1; break;
case NAPPEND: s = ">>"; dftfd = 1; break;
case NFROMTO: s = "<>"; dftfd = 0; break;
default: s = "*error*"; dftfd = 0; break;
}
+#endif
if (np->nfile.fd != dftfd)
fprintf(fp, "%d", np->nfile.fd);
fputs(s, fp);
trace(const char *fmt, ...)
{
va_list va;
-#ifdef __STDC__
va_start(va, fmt);
-#else
- char *fmt;
- va_start(va);
- fmt = va_arg(va, char *);
-#endif
if (tracefile != NULL) {
(void) vfprintf(tracefile, fmt, va);
if (strchr(fmt, '\n'))
for (signo = 0 ; signo < NSIG ; signo++) {
if (trap[signo] != NULL) {
char *p;
+ const char *sn;
p = single_quote(trap[signo]);
- out1fmt("trap -- %s %s\n", p,
- signal_names[signo] + (signo ? 3 : 0)
- );
+ sn = sys_siglist[signo];
+ if(sn==NULL)
+ sn = u_signal_names(0, &signo, 0);
+ if(sn==NULL)
+ sn = "???";
+ printf("trap -- %s %s\n", p, sn);
stunalloc(p);
}
}
}
if (*t == S_HARD_IGN || *t == action)
return;
- switch (action) {
- case S_CATCH:
- act.sa_handler = onsig;
- break;
- case S_IGN:
- act.sa_handler = SIG_IGN;
- break;
- default:
- act.sa_handler = SIG_DFL;
- }
+ act.sa_handler = ((action == S_CATCH) ? onsig
+ : ((action == S_IGN) ? SIG_IGN : SIG_DFL));
*t = action;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
static int decode_signal(const char *string, int minsig)
{
int signo;
+ const char *name = u_signal_names(string, &signo, minsig);
- if (is_number(string, &signo)) {
- if (signo >= NSIG) {
- return -1;
- }
- return signo;
- }
-
- signo = minsig;
- if (!signo) {
- goto zero;
- }
- for (; signo < NSIG; signo++) {
- if (!strcasecmp(string, &(signal_names[signo])[3])) {
- return signo;
- }
-zero:
- if (!strcasecmp(string, signal_names[signo])) {
- return signo;
- }
- }
-
- return -1;
+ return name ? signo : -1;
}
+
static struct var **hashvar (const char *);
static void showvars (const char *, int, int);
static struct var **findvar (struct var **, const char *);
* Find the value of a variable. Returns NULL if not set.
*/
-static char *
+static const char *
lookupvar(name)
const char *name;
{
* Search the environment of a builtin command.
*/
-static char *
-bltinlookup(name)
- const char *name;
+static const char *
+bltinlookup(const char *name)
{
- struct strlist *sp;
+ const struct strlist *sp;
for (sp = cmdenviron ; sp ; sp = sp->next) {
if (varequal(sp->text, name))
*/
static int
-unsetvar(s)
- const char *s;
- {
+unsetvar(const char *s)
+{
struct var **vpp;
struct var *vp;
*/
static struct var **
-hashvar(p)
- const char *p;
- {
+hashvar(const char *p)
+{
unsigned int hashval;
hashval = ((unsigned char) *p) << 4;
*/
static int
-varequal(p, q)
- const char *p, *q;
- {
+varequal(const char *p, const char *q)
+{
while (*p == *q++) {
if (*p++ == '=')
return 1;
len = p - vp->text;
p = single_quote(p);
- out1fmt(
- "%s%s%.*s%s\n", myprefix, sep, len,
- vp->text, p
- );
+ printf("%s%s%.*s%s\n", myprefix, sep, len,
+ vp->text, p);
stunalloc(p);
}
}
/*
* Copyright (c) 1999 Herbert Xu <herbert@debian.org>
* This file contains code for the times builtin.
- * $Id: ash.c,v 1.6 2001/07/06 04:26:23 andersen Exp $
+ * $Id: ash.c,v 1.22 2001/08/12 17:32:56 mjn3 Exp $
*/
static int timescmd (int argc, char **argv)
{
return 0;
}
+#ifdef ASH_MATH_SUPPORT
+/* The let builtin. */
+int letcmd(int argc, char **argv)
+{
+ int errcode;
+ long result=0;
+ if (argc == 2) {
+ char *tmp, *expression, p[13];
+ expression = strchr(argv[1], '=');
+ if (!expression) {
+ /* Cannot use 'error()' here, or the return code
+ * will be incorrect */
+ out2fmt("sh: let: syntax error: \"%s\"\n", argv[1]);
+ return 0;
+ }
+ *expression = '\0';
+ tmp = ++expression;
+ result = arith(tmp, &errcode);
+ if (errcode < 0) {
+ /* Cannot use 'error()' here, or the return code
+ * will be incorrect */
+ out2fmt("sh: let: ");
+ if(errcode == -2)
+ out2fmt("divide by zero");
+ else
+ out2fmt("syntax error: \"%s=%s\"\n", argv[1], expression);
+ return 0;
+ }
+ snprintf(p, 12, "%ld", result);
+ setvar(argv[1], savestr(p), 0);
+ } else if (argc >= 3)
+ synerror("invalid operand");
+ return !result;
+}
+#endif
+
+
/*-
* Copyright (c) 1989, 1991, 1993, 1994