aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changelog5
-rw-r--r--applets/busybox.c222
-rwxr-xr-xapplets/busybox.mkll16
-rwxr-xr-xapplets/busybox.sh5
-rw-r--r--busybox.c222
-rw-r--r--busybox.def.h74
-rwxr-xr-xbusybox.mkll16
-rwxr-xr-xbusybox.sh5
-rw-r--r--internal.h10
-rw-r--r--lash.c1255
-rw-r--r--sh.c1255
-rw-r--r--shell/lash.c1255
-rw-r--r--utility.c4
13 files changed, 2282 insertions, 2062 deletions
diff --git a/Changelog b/Changelog
index c6493f8a3..d81207c9d 100644
--- a/Changelog
+++ b/Changelog
@@ -1,4 +1,8 @@
0.43
+ * Busybox now includes a shell! It currently costs 7.5 k (plus an
+ additional 2.5 k if you compile in command line editing). Handles
+ job control, has the usual set of builtins, and does everything
+ except for handling programming statements (if, while, etc...)
* Busybox can now work perfectly when /proc is disabled, thereby
saving a bunch of memory (kernel /proc support is not thin). This
is done by making use of some nice kernel patches I wrote up to
@@ -11,6 +15,7 @@
with a ram disk. Contributed by Emanuele Caratti <wiz@iol.it>
and then adjusted a bit by me.
* Added tr and dirname from John Lombardo <john@deltanet.com>
+ * Added echo and test (from me).
* tar wouldn't create directory entries that don't end in '/',
now it does (thanks to Avery Pennarun <apenwarr@worldvisions.ca>)
* Several fixes from Pavel Roskin <pavel_roskin@geocities.com>:
diff --git a/applets/busybox.c b/applets/busybox.c
index 9aa46eaae..7582647d5 100644
--- a/applets/busybox.c
+++ b/applets/busybox.c
@@ -34,282 +34,304 @@ int atexit(void (*__func) (void))
void *__libc_stack_end;
#endif
-
static const struct Applet applets[] = {
#ifdef BB_BASENAME //usr/bin/basename
- {"basename", basename_main},
+ {"basename", basename_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_BUSYBOX //bin
- {"busybox", busybox_main},
+ {"busybox", busybox_main, _BB_DIR_BIN},
#endif
#ifdef BB_BLOCK_DEVICE //sbin
- {"block_device", block_device_main},
+ {"block_device", block_device_main, _BB_DIR_SBIN},
#endif
#ifdef BB_CAT //bin
- {"cat", cat_main},
+ {"cat", cat_main, _BB_DIR_BIN},
+#endif
+#ifdef BB_CHMOD_CHOWN_CHGRP //bin
+ {"chmod", chmod_chown_chgrp_main, _BB_DIR_BIN},
+#endif
+#ifdef BB_CHMOD_CHOWN_CHGRP //bin
+ {"chown", chmod_chown_chgrp_main, _BB_DIR_BIN},
#endif
#ifdef BB_CHMOD_CHOWN_CHGRP //bin
- {"chmod", chmod_chown_chgrp_main},
- {"chown", chmod_chown_chgrp_main},
- {"chgrp", chmod_chown_chgrp_main},
+ {"chgrp", chmod_chown_chgrp_main, _BB_DIR_BIN},
#endif
#ifdef BB_CHROOT //sbin
- {"chroot", chroot_main},
+ {"chroot", chroot_main, _BB_DIR_SBIN},
#endif
#ifdef BB_CLEAR //usr/bin
- {"clear", clear_main},
+ {"clear", clear_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_CHVT //usr/bin
- {"chvt", chvt_main},
+ {"chvt", chvt_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_CP_MV //bin
- {"cp", cp_mv_main},
- {"mv", cp_mv_main},
+ {"cp", cp_mv_main, _BB_DIR_BIN},
+#endif
+#ifdef BB_CP_MV //bin
+ {"mv", cp_mv_main, _BB_DIR_BIN},
#endif
#ifdef BB_DATE //bin
- {"date", date_main},
+ {"date", date_main, _BB_DIR_BIN},
#endif
#ifdef BB_DD //bin
- {"dd", dd_main},
+ {"dd", dd_main, _BB_DIR_BIN},
#endif
#ifdef BB_DF //bin
- {"df", df_main},
+ {"df", df_main, _BB_DIR_BIN},
#endif
#ifdef BB_DIRNAME //usr/bin
- {"dirname", dirname_main},
+ {"dirname", dirname_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_DMESG //bin
- {"dmesg", dmesg_main},
+ {"dmesg", dmesg_main, _BB_DIR_BIN},
#endif
#ifdef BB_DU //bin
- {"du", du_main},
+ {"du", du_main, _BB_DIR_BIN},
#endif
#ifdef BB_DUTMP //usr/sbin
- {"dutmp", dutmp_main},
+ {"dutmp", dutmp_main, _BB_DIR_USR_SBIN},
+#endif
+#ifdef BB_ECHO //bin
+ {"echo", echo_main, _BB_DIR_BIN},
#endif
#ifdef BB_FBSET //usr/sbin
- {"fbset", fbset_main},
+ {"fbset", fbset_main, _BB_DIR_USR_SBIN},
#endif
#ifdef BB_FDFLUSH //bin
- {"fdflush", fdflush_main},
+ {"fdflush", fdflush_main, _BB_DIR_BIN},
#endif
#ifdef BB_FIND //usr/bin
- {"find", find_main},
+ {"find", find_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_FREE //usr/bin
- {"free", free_main},
+ {"free", free_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_FREERAMDISK //sbin
- {"freeramdisk", freeramdisk_main},
+ {"freeramdisk", freeramdisk_main, _BB_DIR_SBIN},
#endif
#ifdef BB_DEALLOCVT //usr/bin
- {"deallocvt", deallocvt_main},
+ {"deallocvt", deallocvt_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_FSCK_MINIX //sbin
- {"fsck.minix", fsck_minix_main},
+ {"fsck.minix", fsck_minix_main, _BB_DIR_SBIN},
#endif
#ifdef BB_MKFS_MINIX //sbin
- {"mkfs.minix", mkfs_minix_main},
+ {"mkfs.minix", mkfs_minix_main, _BB_DIR_SBIN},
#endif
#ifdef BB_GREP //bin
- {"grep", grep_main},
+ {"grep", grep_main, _BB_DIR_BIN},
#endif
#ifdef BB_HALT //sbin
- {"halt", halt_main},
+ {"halt", halt_main, _BB_DIR_SBIN},
#endif
#ifdef BB_HEAD //bin
- {"head", head_main},
+ {"head", head_main, _BB_DIR_BIN},
#endif
#ifdef BB_HOSTID //usr/bin
- {"hostid", hostid_main},
+ {"hostid", hostid_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_HOSTNAME //bin
- {"hostname", hostname_main},
+ {"hostname", hostname_main, _BB_DIR_BIN},
#endif
#ifdef BB_INIT //sbin
- {"init", init_main},
+ {"init", init_main, _BB_DIR_SBIN},
#endif
#ifdef BB_INSMOD //sbin
- {"insmod", insmod_main},
+ {"insmod", insmod_main, _BB_DIR_SBIN},
#endif
#ifdef BB_FEATURE_LINUXRC //
- {"linuxrc", init_main},
+ {"linuxrc", init_main, _BB_DIR_ROOT},
#endif
#ifdef BB_KILL //bin
- {"kill", kill_main},
+ {"kill", kill_main, _BB_DIR_BIN},
#endif
#ifdef BB_KILLALL //usr/bin
- {"killall", kill_main},
+ {"killall", kill_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LENGTH //usr/bin
- {"length", length_main},
+ {"length", length_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LN //bin
- {"ln", ln_main},
+ {"ln", ln_main, _BB_DIR_BIN},
#endif
#ifdef BB_LOADACM //usr/bin
- {"loadacm", loadacm_main},
+ {"loadacm", loadacm_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LOADFONT //usr/bin
- {"loadfont", loadfont_main},
+ {"loadfont", loadfont_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LOADKMAP //sbin
- {"loadkmap", loadkmap_main},
+ {"loadkmap", loadkmap_main, _BB_DIR_SBIN},
#endif
#ifdef BB_LS //bin
- {"ls", ls_main},
+ {"ls", ls_main, _BB_DIR_BIN},
#endif
#ifdef BB_LSMOD //sbin
- {"lsmod", lsmod_main},
+ {"lsmod", lsmod_main, _BB_DIR_SBIN},
#endif
#ifdef BB_MAKEDEVS //sbin
- {"makedevs", makedevs_main},
+ {"makedevs", makedevs_main, _BB_DIR_SBIN},
#endif
#ifdef BB_MATH //usr/bin
- {"math", math_main},
+ {"math", math_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_MKDIR //bin
- {"mkdir", mkdir_main},
+ {"mkdir", mkdir_main, _BB_DIR_BIN},
#endif
#ifdef BB_MKFIFO //usr/bin
- {"mkfifo", mkfifo_main},
+ {"mkfifo", mkfifo_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_MKNOD //bin
- {"mknod", mknod_main},
+ {"mknod", mknod_main, _BB_DIR_BIN},
#endif
#ifdef BB_MKSWAP //sbin
- {"mkswap", mkswap_main},
+ {"mkswap", mkswap_main, _BB_DIR_SBIN},
#endif
#ifdef BB_MNC //usr/bin
- {"mnc", mnc_main},
+ {"mnc", mnc_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_MORE //bin
- {"more", more_main},
+ {"more", more_main, _BB_DIR_BIN},
#endif
#ifdef BB_MOUNT //bin
- {"mount", mount_main},
+ {"mount", mount_main, _BB_DIR_BIN},
#endif
#ifdef BB_MT //bin
- {"mt", mt_main},
+ {"mt", mt_main, _BB_DIR_BIN},
#endif
#ifdef BB_NSLOOKUP //usr/bin
- {"nslookup", nslookup_main},
+ {"nslookup", nslookup_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_PING //bin
- {"ping", ping_main},
+ {"ping", ping_main, _BB_DIR_BIN},
#endif
#ifdef BB_POWEROFF //sbin
- {"poweroff", poweroff_main},
+ {"poweroff", poweroff_main, _BB_DIR_SBIN},
#endif
#ifdef BB_PRINTF //usr/bin
- {"printf", printf_main},
+ {"printf", printf_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_PS //bin
- {"ps", ps_main},
+ {"ps", ps_main, _BB_DIR_BIN},
#endif
#ifdef BB_PWD //bin
- {"pwd", pwd_main},
+ {"pwd", pwd_main, _BB_DIR_BIN},
#endif
#ifdef BB_REBOOT //sbin
- {"reboot", reboot_main},
+ {"reboot", reboot_main, _BB_DIR_SBIN},
#endif
#ifdef BB_RM //bin
- {"rm", rm_main},
+ {"rm", rm_main, _BB_DIR_BIN},
#endif
#ifdef BB_RMDIR //bin
- {"rmdir", rmdir_main},
+ {"rmdir", rmdir_main, _BB_DIR_BIN},
#endif
#ifdef BB_RMMOD //sbin
- {"rmmod", rmmod_main},
+ {"rmmod", rmmod_main, _BB_DIR_SBIN},
#endif
#ifdef BB_SED //bin
- {"sed", sed_main},
+ {"sed", sed_main, _BB_DIR_BIN},
#endif
#ifdef BB_SH //bin
- {"sh", shell_main},
+ {"sh", shell_main, _BB_DIR_BIN},
#endif
#ifdef BB_SFDISK //sbin
- {"fdisk", sfdisk_main},
- {"sfdisk", sfdisk_main},
+ {"fdisk", sfdisk_main, _BB_DIR_SBIN},
+#ifdef BB_SFDISK //sbin
+#endif
+ {"sfdisk", sfdisk_main, _BB_DIR_SBIN},
#endif
#ifdef BB_SLEEP //bin
- {"sleep", sleep_main},
+ {"sleep", sleep_main, _BB_DIR_BIN},
#endif
#ifdef BB_SORT //bin
- {"sort", sort_main},
+ {"sort", sort_main, _BB_DIR_BIN},
#endif
#ifdef BB_SYNC //bin
- {"sync", sync_main},
+ {"sync", sync_main, _BB_DIR_BIN},
#endif
#ifdef BB_SYSLOGD //sbin
- {"syslogd", syslogd_main},
+ {"syslogd", syslogd_main, _BB_DIR_SBIN},
#endif
#ifdef BB_LOGGER //usr/bin
- {"logger", logger_main},
+ {"logger", logger_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LOGNAME //usr/bin
- {"logname", logname_main},
+ {"logname", logname_main, _BB_DIR_USR_BIN},
+#endif
+#ifdef BB_SWAPONOFF //sbin
+ {"swapon", swap_on_off_main, _BB_DIR_SBIN},
#endif
#ifdef BB_SWAPONOFF //sbin
- {"swapon", swap_on_off_main},
- {"swapoff", swap_on_off_main},
+ {"swapoff", swap_on_off_main, _BB_DIR_SBIN},
#endif
#ifdef BB_TAIL //usr/bin
- {"tail", tail_main},
+ {"tail", tail_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_TAR //bin
- {"tar", tar_main},
+ {"tar", tar_main, _BB_DIR_BIN},
#endif
#ifdef BB_TELNET //usr/bin
- {"telnet", telnet_main},
+ {"telnet", telnet_main, _BB_DIR_USR_BIN},
+#endif
+#ifdef BB_TEST //usr/bin
+ {"[", test_main, _BB_DIR_USR_BIN},
+#endif
+#ifdef BB_TEST //usr/bin
+ {"test", test_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_TEE //bin
- {"tee", tee_main},
+ {"tee", tee_main, _BB_DIR_BIN},
#endif
#ifdef BB_TOUCH //usr/bin
- {"touch", touch_main},
+ {"touch", touch_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_TR //usr/bin
- {"tr", tr_main},
+ {"tr", tr_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_TRUE_FALSE //bin
- {"true", true_main},
- {"false", false_main},
+ {"true", true_main, _BB_DIR_BIN},
+#endif
+#ifdef BB_TRUE_FALSE //bin
+ {"false", false_main, _BB_DIR_BIN},
#endif
#ifdef BB_TTY //usr/bin
- {"tty", tty_main},
+ {"tty", tty_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_UMOUNT //bin
- {"umount", umount_main},
+ {"umount", umount_main, _BB_DIR_BIN},
#endif
#ifdef BB_UNAME //bin
- {"uname", uname_main},
+ {"uname", uname_main, _BB_DIR_BIN},
#endif
#ifdef BB_UPTIME //usr/bin
- {"uptime", uptime_main},
+ {"uptime", uptime_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_UNIQ //bin
- {"uniq", uniq_main},
+ {"uniq", uniq_main, _BB_DIR_BIN},
#endif
#ifdef BB_UPDATE //sbin
- {"update", update_main},
+ {"update", update_main, _BB_DIR_SBIN},
#endif
#ifdef BB_WC //usr/bin
- {"wc", wc_main},
+ {"wc", wc_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_WHOAMI //usr/bin
- {"whoami", whoami_main},
+ {"whoami", whoami_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_YES //usr/bin
- {"yes", yes_main},
+ {"yes", yes_main, _BB_DIR_USR_BIN},
+#endif
+#ifdef BB_GUNZIP //bin
+ {"zcat", gunzip_main, _BB_DIR_BIN},
#endif
#ifdef BB_GUNZIP //bin
- {"zcat", gunzip_main},
- {"gunzip", gunzip_main},
+ {"gunzip", gunzip_main, _BB_DIR_BIN},
#endif
#ifdef BB_GZIP //bin
- {"gzip", gzip_main},
+ {"gzip", gzip_main, _BB_DIR_BIN},
#endif
{0}
};
@@ -318,11 +340,11 @@ static const struct Applet applets[] = {
int main(int argc, char **argv)
{
- char *s = argv[0];
- char *name = argv[0];
+ char *s;
+ char *name;
const struct Applet *a = applets;
- while (*s != '\0') {
+ for (s = name = argv[0]; *s != '\0';) {
if (*s++ == '/')
name = s;
}
diff --git a/applets/busybox.mkll b/applets/busybox.mkll
index c4420f50d..fa1bff209 100755
--- a/applets/busybox.mkll
+++ b/applets/busybox.mkll
@@ -4,12 +4,16 @@
DF="busybox.def.h"
MF="busybox.c"
-LIST="$(sed -n '/^#define/{s/^#define //p;}' $DF)"
+LIST="$(sed -n '/^#define/{s/^#define BB_FEATURE_.*//g;s/^#define //p;}' $DF)"
for def in ${LIST}; do
- i=`sed -n 's/^#ifdef \<'$def'\>.*\/\/\(.*$\)/\/\1\//gp' $MF`
- j=`sed -n '/^#ifdef \<'$def'\>.*/,/^#endif/{ s/.*\"\(.*\)\".*/\1/gp; }' $MF`
- for k in $j; do
- echo $i$k
- done
+ i=`sed -n '/^#ifdef \<'$def'\>.*/,/^#endif/{ s/.*\"\(.*\)\".*\(_BB_DIR_[A-Z_]*\).*$/\2\/\1/gp; }' $MF`
+ for j in $i; do
+ if [ -z $j ] ; then
+ continue;
+ fi;
+ echo $j | sed -e 's/_BB_DIR_ROOT//g;s/_BB_DIR_BIN/\/bin/g;' \
+ -e 's/_BB_DIR_SBIN/\/sbin/g;s/_BB_DIR_USR_BIN/\/usr\/bin/g;' \
+ -e 's/_BB_DIR_USR_SBIN/\/usr\/sbin/g;'
+ done;
done
diff --git a/applets/busybox.sh b/applets/busybox.sh
index cab248f11..304ac87e7 100755
--- a/applets/busybox.sh
+++ b/applets/busybox.sh
@@ -1,4 +1,3 @@
#!/bin/sh
-ls -1 `sed -n '/^#define/{s/.*BB_// ; s/$/.c/p; }' busybox.def.h | \
-tr [:upper:] [:lower:]` 2> /dev/null | sed -e 's/\.c$/\.o/g'
-
+sed -n -e 's/^#define.*BB_FEATURE.*$//g;y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/;' \
+ -e '/^#define/{s/.*bb_//;s/$/.o/p;}' busybox.def.h
diff --git a/busybox.c b/busybox.c
index 9aa46eaae..7582647d5 100644
--- a/busybox.c
+++ b/busybox.c
@@ -34,282 +34,304 @@ int atexit(void (*__func) (void))
void *__libc_stack_end;
#endif
-
static const struct Applet applets[] = {
#ifdef BB_BASENAME //usr/bin/basename
- {"basename", basename_main},
+ {"basename", basename_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_BUSYBOX //bin
- {"busybox", busybox_main},
+ {"busybox", busybox_main, _BB_DIR_BIN},
#endif
#ifdef BB_BLOCK_DEVICE //sbin
- {"block_device", block_device_main},
+ {"block_device", block_device_main, _BB_DIR_SBIN},
#endif
#ifdef BB_CAT //bin
- {"cat", cat_main},
+ {"cat", cat_main, _BB_DIR_BIN},
+#endif
+#ifdef BB_CHMOD_CHOWN_CHGRP //bin
+ {"chmod", chmod_chown_chgrp_main, _BB_DIR_BIN},
+#endif
+#ifdef BB_CHMOD_CHOWN_CHGRP //bin
+ {"chown", chmod_chown_chgrp_main, _BB_DIR_BIN},
#endif
#ifdef BB_CHMOD_CHOWN_CHGRP //bin
- {"chmod", chmod_chown_chgrp_main},
- {"chown", chmod_chown_chgrp_main},
- {"chgrp", chmod_chown_chgrp_main},
+ {"chgrp", chmod_chown_chgrp_main, _BB_DIR_BIN},
#endif
#ifdef BB_CHROOT //sbin
- {"chroot", chroot_main},
+ {"chroot", chroot_main, _BB_DIR_SBIN},
#endif
#ifdef BB_CLEAR //usr/bin
- {"clear", clear_main},
+ {"clear", clear_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_CHVT //usr/bin
- {"chvt", chvt_main},
+ {"chvt", chvt_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_CP_MV //bin
- {"cp", cp_mv_main},
- {"mv", cp_mv_main},
+ {"cp", cp_mv_main, _BB_DIR_BIN},
+#endif
+#ifdef BB_CP_MV //bin
+ {"mv", cp_mv_main, _BB_DIR_BIN},
#endif
#ifdef BB_DATE //bin
- {"date", date_main},
+ {"date", date_main, _BB_DIR_BIN},
#endif
#ifdef BB_DD //bin
- {"dd", dd_main},
+ {"dd", dd_main, _BB_DIR_BIN},
#endif
#ifdef BB_DF //bin
- {"df", df_main},
+ {"df", df_main, _BB_DIR_BIN},
#endif
#ifdef BB_DIRNAME //usr/bin
- {"dirname", dirname_main},
+ {"dirname", dirname_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_DMESG //bin
- {"dmesg", dmesg_main},
+ {"dmesg", dmesg_main, _BB_DIR_BIN},
#endif
#ifdef BB_DU //bin
- {"du", du_main},
+ {"du", du_main, _BB_DIR_BIN},
#endif
#ifdef BB_DUTMP //usr/sbin
- {"dutmp", dutmp_main},
+ {"dutmp", dutmp_main, _BB_DIR_USR_SBIN},
+#endif
+#ifdef BB_ECHO //bin
+ {"echo", echo_main, _BB_DIR_BIN},
#endif
#ifdef BB_FBSET //usr/sbin
- {"fbset", fbset_main},
+ {"fbset", fbset_main, _BB_DIR_USR_SBIN},
#endif
#ifdef BB_FDFLUSH //bin
- {"fdflush", fdflush_main},
+ {"fdflush", fdflush_main, _BB_DIR_BIN},
#endif
#ifdef BB_FIND //usr/bin
- {"find", find_main},
+ {"find", find_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_FREE //usr/bin
- {"free", free_main},
+ {"free", free_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_FREERAMDISK //sbin
- {"freeramdisk", freeramdisk_main},
+ {"freeramdisk", freeramdisk_main, _BB_DIR_SBIN},
#endif
#ifdef BB_DEALLOCVT //usr/bin
- {"deallocvt", deallocvt_main},
+ {"deallocvt", deallocvt_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_FSCK_MINIX //sbin
- {"fsck.minix", fsck_minix_main},
+ {"fsck.minix", fsck_minix_main, _BB_DIR_SBIN},
#endif
#ifdef BB_MKFS_MINIX //sbin
- {"mkfs.minix", mkfs_minix_main},
+ {"mkfs.minix", mkfs_minix_main, _BB_DIR_SBIN},
#endif
#ifdef BB_GREP //bin
- {"grep", grep_main},
+ {"grep", grep_main, _BB_DIR_BIN},
#endif
#ifdef BB_HALT //sbin
- {"halt", halt_main},
+ {"halt", halt_main, _BB_DIR_SBIN},
#endif
#ifdef BB_HEAD //bin
- {"head", head_main},
+ {"head", head_main, _BB_DIR_BIN},
#endif
#ifdef BB_HOSTID //usr/bin
- {"hostid", hostid_main},
+ {"hostid", hostid_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_HOSTNAME //bin
- {"hostname", hostname_main},
+ {"hostname", hostname_main, _BB_DIR_BIN},
#endif
#ifdef BB_INIT //sbin
- {"init", init_main},
+ {"init", init_main, _BB_DIR_SBIN},
#endif
#ifdef BB_INSMOD //sbin
- {"insmod", insmod_main},
+ {"insmod", insmod_main, _BB_DIR_SBIN},
#endif
#ifdef BB_FEATURE_LINUXRC //
- {"linuxrc", init_main},
+ {"linuxrc", init_main, _BB_DIR_ROOT},
#endif
#ifdef BB_KILL //bin
- {"kill", kill_main},
+ {"kill", kill_main, _BB_DIR_BIN},
#endif
#ifdef BB_KILLALL //usr/bin
- {"killall", kill_main},
+ {"killall", kill_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LENGTH //usr/bin
- {"length", length_main},
+ {"length", length_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LN //bin
- {"ln", ln_main},
+ {"ln", ln_main, _BB_DIR_BIN},
#endif
#ifdef BB_LOADACM //usr/bin
- {"loadacm", loadacm_main},
+ {"loadacm", loadacm_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LOADFONT //usr/bin
- {"loadfont", loadfont_main},
+ {"loadfont", loadfont_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LOADKMAP //sbin
- {"loadkmap", loadkmap_main},
+ {"loadkmap", loadkmap_main, _BB_DIR_SBIN},
#endif
#ifdef BB_LS //bin
- {"ls", ls_main},
+ {"ls", ls_main, _BB_DIR_BIN},
#endif
#ifdef BB_LSMOD //sbin
- {"lsmod", lsmod_main},
+ {"lsmod", lsmod_main, _BB_DIR_SBIN},
#endif
#ifdef BB_MAKEDEVS //sbin
- {"makedevs", makedevs_main},
+ {"makedevs", makedevs_main, _BB_DIR_SBIN},
#endif
#ifdef BB_MATH //usr/bin
- {"math", math_main},
+ {"math", math_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_MKDIR //bin
- {"mkdir", mkdir_main},
+ {"mkdir", mkdir_main, _BB_DIR_BIN},
#endif
#ifdef BB_MKFIFO //usr/bin
- {"mkfifo", mkfifo_main},
+ {"mkfifo", mkfifo_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_MKNOD //bin
- {"mknod", mknod_main},
+ {"mknod", mknod_main, _BB_DIR_BIN},
#endif
#ifdef BB_MKSWAP //sbin
- {"mkswap", mkswap_main},
+ {"mkswap", mkswap_main, _BB_DIR_SBIN},
#endif
#ifdef BB_MNC //usr/bin
- {"mnc", mnc_main},
+ {"mnc", mnc_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_MORE //bin
- {"more", more_main},
+ {"more", more_main, _BB_DIR_BIN},
#endif
#ifdef BB_MOUNT //bin
- {"mount", mount_main},
+ {"mount", mount_main, _BB_DIR_BIN},
#endif
#ifdef BB_MT //bin
- {"mt", mt_main},
+ {"mt", mt_main, _BB_DIR_BIN},
#endif
#ifdef BB_NSLOOKUP //usr/bin
- {"nslookup", nslookup_main},
+ {"nslookup", nslookup_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_PING //bin
- {"ping", ping_main},
+ {"ping", ping_main, _BB_DIR_BIN},
#endif
#ifdef BB_POWEROFF //sbin
- {"poweroff", poweroff_main},
+ {"poweroff", poweroff_main, _BB_DIR_SBIN},
#endif
#ifdef BB_PRINTF //usr/bin
- {"printf", printf_main},
+ {"printf", printf_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_PS //bin
- {"ps", ps_main},
+ {"ps", ps_main, _BB_DIR_BIN},
#endif
#ifdef BB_PWD //bin
- {"pwd", pwd_main},
+ {"pwd", pwd_main, _BB_DIR_BIN},
#endif
#ifdef BB_REBOOT //sbin
- {"reboot", reboot_main},
+ {"reboot", reboot_main, _BB_DIR_SBIN},
#endif
#ifdef BB_RM //bin
- {"rm", rm_main},
+ {"rm", rm_main, _BB_DIR_BIN},
#endif
#ifdef BB_RMDIR //bin
- {"rmdir", rmdir_main},
+ {"rmdir", rmdir_main, _BB_DIR_BIN},
#endif
#ifdef BB_RMMOD //sbin
- {"rmmod", rmmod_main},
+ {"rmmod", rmmod_main, _BB_DIR_SBIN},
#endif
#ifdef BB_SED //bin
- {"sed", sed_main},
+ {"sed", sed_main, _BB_DIR_BIN},
#endif
#ifdef BB_SH //bin
- {"sh", shell_main},
+ {"sh", shell_main, _BB_DIR_BIN},
#endif
#ifdef BB_SFDISK //sbin
- {"fdisk", sfdisk_main},
- {"sfdisk", sfdisk_main},
+ {"fdisk", sfdisk_main, _BB_DIR_SBIN},
+#ifdef BB_SFDISK //sbin
+#endif
+ {"sfdisk", sfdisk_main, _BB_DIR_SBIN},
#endif
#ifdef BB_SLEEP //bin
- {"sleep", sleep_main},
+ {"sleep", sleep_main, _BB_DIR_BIN},
#endif
#ifdef BB_SORT //bin
- {"sort", sort_main},
+ {"sort", sort_main, _BB_DIR_BIN},
#endif
#ifdef BB_SYNC //bin
- {"sync", sync_main},
+ {"sync", sync_main, _BB_DIR_BIN},
#endif
#ifdef BB_SYSLOGD //sbin
- {"syslogd", syslogd_main},
+ {"syslogd", syslogd_main, _BB_DIR_SBIN},
#endif
#ifdef BB_LOGGER //usr/bin
- {"logger", logger_main},
+ {"logger", logger_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_LOGNAME //usr/bin
- {"logname", logname_main},
+ {"logname", logname_main, _BB_DIR_USR_BIN},
+#endif
+#ifdef BB_SWAPONOFF //sbin
+ {"swapon", swap_on_off_main, _BB_DIR_SBIN},
#endif
#ifdef BB_SWAPONOFF //sbin
- {"swapon", swap_on_off_main},
- {"swapoff", swap_on_off_main},
+ {"swapoff", swap_on_off_main, _BB_DIR_SBIN},
#endif
#ifdef BB_TAIL //usr/bin
- {"tail", tail_main},
+ {"tail", tail_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_TAR //bin
- {"tar", tar_main},
+ {"tar", tar_main, _BB_DIR_BIN},
#endif
#ifdef BB_TELNET //usr/bin
- {"telnet", telnet_main},
+ {"telnet", telnet_main, _BB_DIR_USR_BIN},
+#endif
+#ifdef BB_TEST //usr/bin
+ {"[", test_main, _BB_DIR_USR_BIN},
+#endif
+#ifdef BB_TEST //usr/bin
+ {"test", test_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_TEE //bin
- {"tee", tee_main},
+ {"tee", tee_main, _BB_DIR_BIN},
#endif
#ifdef BB_TOUCH //usr/bin
- {"touch", touch_main},
+ {"touch", touch_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_TR //usr/bin
- {"tr", tr_main},
+ {"tr", tr_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_TRUE_FALSE //bin
- {"true", true_main},
- {"false", false_main},
+ {"true", true_main, _BB_DIR_BIN},
+#endif
+#ifdef BB_TRUE_FALSE //bin
+ {"false", false_main, _BB_DIR_BIN},
#endif
#ifdef BB_TTY //usr/bin
- {"tty", tty_main},
+ {"tty", tty_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_UMOUNT //bin
- {"umount", umount_main},
+ {"umount", umount_main, _BB_DIR_BIN},
#endif
#ifdef BB_UNAME //bin
- {"uname", uname_main},
+ {"uname", uname_main, _BB_DIR_BIN},
#endif
#ifdef BB_UPTIME //usr/bin
- {"uptime", uptime_main},
+ {"uptime", uptime_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_UNIQ //bin
- {"uniq", uniq_main},
+ {"uniq", uniq_main, _BB_DIR_BIN},
#endif
#ifdef BB_UPDATE //sbin
- {"update", update_main},
+ {"update", update_main, _BB_DIR_SBIN},
#endif
#ifdef BB_WC //usr/bin
- {"wc", wc_main},
+ {"wc", wc_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_WHOAMI //usr/bin
- {"whoami", whoami_main},
+ {"whoami", whoami_main, _BB_DIR_USR_BIN},
#endif
#ifdef BB_YES //usr/bin
- {"yes", yes_main},
+ {"yes", yes_main, _BB_DIR_USR_BIN},
+#endif
+#ifdef BB_GUNZIP //bin
+ {"zcat", gunzip_main, _BB_DIR_BIN},
#endif
#ifdef BB_GUNZIP //bin
- {"zcat", gunzip_main},
- {"gunzip", gunzip_main},
+ {"gunzip", gunzip_main, _BB_DIR_BIN},
#endif
#ifdef BB_GZIP //bin
- {"gzip", gzip_main},
+ {"gzip", gzip_main, _BB_DIR_BIN},
#endif
{0}
};
@@ -318,11 +340,11 @@ static const struct Applet applets[] = {
int main(int argc, char **argv)
{
- char *s = argv[0];
- char *name = argv[0];
+ char *s;
+ char *name;
const struct Applet *a = applets;
- while (*s != '\0') {
+ for (s = name = argv[0]; *s != '\0';) {
if (*s++ == '/')
name = s;
}
diff --git a/busybox.def.h b/busybox.def.h
index 0caa57380..d78a0efe8 100644
--- a/busybox.def.h
+++ b/busybox.def.h
@@ -22,13 +22,14 @@
#define BB_DMESG
//#define BB_DUTMP
#define BB_DU
-#define BB_FBSET
+#define BB_ECHO
+//#define BB_FBSET
//#define BB_FDFLUSH
#define BB_FIND
#define BB_FREE
-#define BB_FREERAMDISK
-#define BB_FSCK_MINIX
-#define BB_GREP
+//#define BB_FREERAMDISK
+//#define BB_FSCK_MINIX
+//#define BB_GREP
#define BB_GUNZIP
#define BB_GZIP
//#define BB_HALT
@@ -36,12 +37,9 @@
//#define BB_HOSTID
#define BB_HOSTNAME
#define BB_INIT
-// Don't turn BB_INSMOD on. It doesn't work.
+// Don't bother turning BB_INSMOD on. It doesn't work.
//#define BB_INSMOD
#define BB_KILL
-#ifdef BB_KILL
-#define BB_KILLALL
-#endif
#define BB_KLOGD
//#define BB_LENGTH
#define BB_LN
@@ -53,7 +51,7 @@
#define BB_LS
//#define BB_LSMOD
//#define BB_MAKEDEVS
-#define BB_MKFS_MINIX
+//#define BB_MKFS_MINIX
//#define BB_MATH
#define BB_MKDIR
//#define BB_MKFIFO
@@ -62,7 +60,7 @@
//#define BB_MNC
#define BB_MORE
#define BB_MOUNT
-#define BB_NFSMOUNT
+//#define BB_NFSMOUNT
//#define BB_MT
#define BB_NSLOOKUP
#define BB_PING
@@ -71,7 +69,6 @@
#define BB_PS
#define BB_PWD
#define BB_REBOOT
-#define BB_REGEXP
#define BB_RM
#define BB_RMDIR
//#define BB_RMMOD
@@ -86,6 +83,7 @@
#define BB_TAIL
#define BB_TAR
#define BB_TEE
+#define BB_TEST
// Don't turn BB_TELNET on. It doesn't work.
#define BB_TELNET
#define BB_TOUCH
@@ -104,7 +102,7 @@
//
//
//
-//
+// ---------------------------------------------------------
// This is where feature definitions go. Generally speaking,
// turning this stuff off makes things a bit smaller (and less
// pretty/useful).
@@ -117,10 +115,10 @@
// You can't use this and USE_PROCFS at the same time...
//#define BB_FEATURE_USE_DEVPS_PATCH
//
-//
// enable features that use the /proc filesystem (apps that
// break without this will tell you on compile)...
-// You can't use this and DEVPS_N_DEVMTAB at the same time...
+// You can't use this and BB_FEATURE_USE_DEVPS_PATCH
+// at the same time...
#define BB_FEATURE_USE_PROCFS
//
// Use termios to manipulate the screen ('more' is prettier with this on)
@@ -130,24 +128,23 @@
#define BB_FEATURE_AUTOWIDTH
//
// show username/groupnames (bypasses libc6 NSS) for ls
-#define BB_FEATURE_LS_USERNAME
+#define BB_FEATURE_LS_USERNAME
//
// show file timestamps in ls
#define BB_FEATURE_LS_TIMESTAMPS
//
// enable ls -p and -F
-#define BB_FEATURE_LS_FILETYPES
+#define BB_FEATURE_LS_FILETYPES
//
// Change ping implementation -- simplified, featureless, but really small.
//#define BB_SIMPLE_PING
-////
+//
// Make init use a simplified /etc/inittab file (recommended).
#define BB_FEATURE_USE_INITTAB
//
//Enable init being called as /linuxrc
//#define BB_FEATURE_LINUXRC
//
-//
//Simple tail implementation (2k vs 6k for the full one). Still
//provides 'tail -f' support -- but for only one file at a time.
#define BB_FEATURE_SIMPLE_TAIL
@@ -156,9 +153,7 @@
#define BB_FEATURE_MOUNT_LOOP
//
// Enable support for a real /etc/mtab file instead of /proc/mounts
-#ifdef BB_MOUNT
-//#define BB_MTAB
-#endif
+//#define BB_FEATURE_MOUNT_MTAB_SUPPORT
//
//
// Enable support for remounting filesystems
@@ -173,10 +168,41 @@
// Allow init to permenently chroot, and umount the old root fs
// just like an initrd does. Requires a kernel patch by Werner Almesberger.
// ftp://icaftp.epfl.ch/pub/people/almesber/misc/umount-root-*.tar.gz
-#ifdef BB_MOUNT
//#define BB_FEATURE_INIT_CHROOT
-#endif
//
//Make sure nothing is printed to the console on boot
#define BB_FEATURE_EXTRA_QUIET
-
+//
+// Enable full regular expressions. This adds about
+// 4k. When this is off, things that would normally
+// use regualr expressions (like grep) will just use
+// normal strings.
+#define BB_FEATURE_FULL_REGULAR_EXPRESSIONS
+//
+//
+// Enable command line editing in the shell
+#define BB_FEATURE_SH_COMMAND_EDITING
+//
+//
+// End of Features List
+//
+//
+//
+//
+//
+//
+//---------------------------------------------------
+// Nothing beyond this point should ever be touched by
+// mere mortals so leave this stuff alone.
+#ifdef BB_FEATURE_MOUNT_MTAB_SUPPORT
+#define BB_MTAB
+#endif
+//
+#ifdef BB_FEATURE_FULL_REGULAR_EXPRESSIONS
+#define BB_REGEXP
+#endif
+//
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+#define BB_CMDEDIT
+#endif
+//
diff --git a/busybox.mkll b/busybox.mkll
index c4420f50d..fa1bff209 100755
--- a/busybox.mkll
+++ b/busybox.mkll
@@ -4,12 +4,16 @@
DF="busybox.def.h"
MF="busybox.c"
-LIST="$(sed -n '/^#define/{s/^#define //p;}' $DF)"
+LIST="$(sed -n '/^#define/{s/^#define BB_FEATURE_.*//g;s/^#define //p;}' $DF)"
for def in ${LIST}; do
- i=`sed -n 's/^#ifdef \<'$def'\>.*\/\/\(.*$\)/\/\1\//gp' $MF`
- j=`sed -n '/^#ifdef \<'$def'\>.*/,/^#endif/{ s/.*\"\(.*\)\".*/\1/gp; }' $MF`
- for k in $j; do
- echo $i$k
- done
+ i=`sed -n '/^#ifdef \<'$def'\>.*/,/^#endif/{ s/.*\"\(.*\)\".*\(_BB_DIR_[A-Z_]*\).*$/\2\/\1/gp; }' $MF`
+ for j in $i; do
+ if [ -z $j ] ; then
+ continue;
+ fi;
+ echo $j | sed -e 's/_BB_DIR_ROOT//g;s/_BB_DIR_BIN/\/bin/g;' \
+ -e 's/_BB_DIR_SBIN/\/sbin/g;s/_BB_DIR_USR_BIN/\/usr\/bin/g;' \
+ -e 's/_BB_DIR_USR_SBIN/\/usr\/sbin/g;'
+ done;
done
diff --git a/busybox.sh b/busybox.sh
index cab248f11..304ac87e7 100755
--- a/busybox.sh
+++ b/busybox.sh
@@ -1,4 +1,3 @@
#!/bin/sh
-ls -1 `sed -n '/^#define/{s/.*BB_// ; s/$/.c/p; }' busybox.def.h | \
-tr [:upper:] [:lower:]` 2> /dev/null | sed -e 's/\.c$/\.o/g'
-
+sed -n -e 's/^#define.*BB_FEATURE.*$//g;y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/;' \
+ -e '/^#define/{s/.*bb_//;s/$/.o/p;}' busybox.def.h
diff --git a/internal.h b/internal.h
index e96eebba9..c91d3b19d 100644
--- a/internal.h
+++ b/internal.h
@@ -51,10 +51,18 @@
#define isOctal(ch) (((ch) >= '0') && ((ch) <= '7'))
#define isWildCard(ch) (((ch) == '*') || ((ch) == '?') || ((ch) == '['))
+enum Location {
+ _BB_DIR_ROOT = 0,
+ _BB_DIR_BIN,
+ _BB_DIR_SBIN,
+ _BB_DIR_USR_BIN,
+ _BB_DIR_USR_SBIN
+};
struct Applet {
const char* name;
int (*main)(int argc, char** argv);
+ enum Location location;
};
extern int basename_main(int argc, char **argv);
@@ -74,6 +82,7 @@ extern int df_main(int argc, char** argv);
extern int dmesg_main(int argc, char** argv);
extern int du_main(int argc, char** argv);
extern int dutmp_main(int argc, char** argv);
+extern int echo_main(int argc, char** argv);
extern int false_main(int argc, char** argv);
extern int fbset_main(int argc, char** argv);
extern int fdisk_main(int argc, char** argv);
@@ -134,6 +143,7 @@ extern int syslogd_main(int argc, char **argv);
extern int tail_main(int argc, char** argv);
extern int tar_main(int argc, char** argv);
extern int tee_main(int argc, char** argv);
+extern int test_main(int argc, char** argv);
extern int telnet_main(int argc, char** argv);
extern int touch_main(int argc, char** argv);
extern int tr_main(int argc, char** argv);
diff --git a/lash.c b/lash.c
index 9e467dc54..498f43779 100644
--- a/lash.c
+++ b/lash.c
@@ -39,293 +39,306 @@
#include <unistd.h>
-#define MAX_COMMAND_LEN 250 /* max length of a single command
- string */
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+#include "cmdedit.h"
+#endif
+
#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
-enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND };
+
+enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
+ REDIRECT_APPEND };
struct jobSet {
- struct job * head; /* head of list of running jobs */
- struct job * fg; /* current foreground job */
+ struct job *head; /* head of list of running jobs */
+ struct job *fg; /* current foreground job */
};
struct redirectionSpecifier {
- enum redirectionType type; /* type of redirection */
- int fd; /* file descriptor being redirected */
- char * filename; /* file to redirect fd to */
+ enum redirectionType type; /* type of redirection */
+ int fd; /* file descriptor being redirected */
+ char *filename; /* file to redirect fd to */
};
struct childProgram {
- pid_t pid; /* 0 if exited */
- char ** argv; /* program name and arguments */
- int numRedirections; /* elements in redirection array */
- struct redirectionSpecifier * redirections; /* I/O redirections */
- glob_t globResult; /* result of parameter globbing */
- int freeGlob; /* should we globfree(&globResult)? */
- int isStopped; /* is the program currently running? */
+ pid_t pid; /* 0 if exited */
+ char **argv; /* program name and arguments */
+ int numRedirections; /* elements in redirection array */
+ struct redirectionSpecifier *redirections; /* I/O redirections */
+ glob_t globResult; /* result of parameter globbing */
+ int freeGlob; /* should we globfree(&globResult)? */
+ int isStopped; /* is the program currently running? */
};
struct job {
- int jobId; /* job number */
- int numProgs; /* total number of programs in job */
- int runningProgs; /* number of programs running */
- char * text; /* name of job */
- char * cmdBuf; /* buffer various argv's point into */
- pid_t pgrp; /* process group ID for the job */
- struct childProgram * progs; /* array of programs in job */
- struct job * next; /* to track background commands */
- int stoppedProgs; /* number of programs alive, but stopped */
+ int jobId; /* job number */
+ int numProgs; /* total number of programs in job */
+ int runningProgs; /* number of programs running */
+ char *text; /* name of job */
+ char *cmdBuf; /* buffer various argv's point into */
+ pid_t pgrp; /* process group ID for the job */
+ struct childProgram *progs; /* array of programs in job */
+ struct job *next; /* to track background commands */
+ int stoppedProgs; /* number of programs alive, but stopped */
};
struct builtInCommand {
- char *cmd; /* name */
- char *descr; /* description */
- char *usage; /* usage */
- int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
+ char *cmd; /* name */
+ char *descr; /* description */
+ char *usage; /* usage */
+ int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
};
/* Some function prototypes */
-static int shell_cd(struct job* cmd, struct jobSet* junk);
-static int shell_env(struct job* dummy, struct jobSet* junk);
-static int shell_exit(struct job* cmd, struct jobSet* junk);
-static int shell_fg_bg(struct job* cmd, struct jobSet* jobList);
-static int shell_help(struct job* cmd, struct jobSet* junk);
-static int shell_jobs(struct job* dummy, struct jobSet* jobList);
-static int shell_pwd(struct job* dummy, struct jobSet* junk);
-static int shell_set(struct job* cmd, struct jobSet* junk);
-static int shell_source(struct job* cmd, struct jobSet* jobList);
-static int shell_unset(struct job* cmd, struct jobSet* junk);
-
-static void checkJobs(struct jobSet * jobList);
-static int getCommand(FILE * source, char * command);
-static int parseCommand(char ** commandPtr, struct job * job, int * isBg);
-static int setupRedirections(struct childProgram * prog);
-static int runCommand(struct job newJob, struct jobSet * jobList, int inBg);
+static int shell_cd(struct job *cmd, struct jobSet *junk);
+static int shell_env(struct job *dummy, struct jobSet *junk);
+static int shell_exit(struct job *cmd, struct jobSet *junk);
+static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
+static int shell_help(struct job *cmd, struct jobSet *junk);
+static int shell_jobs(struct job *dummy, struct jobSet *jobList);
+static int shell_pwd(struct job *dummy, struct jobSet *junk);
+static int shell_set(struct job *cmd, struct jobSet *junk);
+static int shell_source(struct job *cmd, struct jobSet *jobList);
+static int shell_unset(struct job *cmd, struct jobSet *junk);
+
+static void checkJobs(struct jobSet *jobList);
+static int getCommand(FILE * source, char *command);
+static int parseCommand(char **commandPtr, struct job *job, int *isBg);
+static int setupRedirections(struct childProgram *prog);
+static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
static int busy_loop(FILE * input);
-
+
/* Table of built-in functions */
static struct builtInCommand bltins[] = {
- {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
- {"cd", "Change working directory", "cd [dir]", shell_cd},
- {"env", "Print all environment variables", "env", shell_env},
- {"exit", "Exit from shell()", "exit", shell_exit},
- {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
- {"jobs", "Lists the active jobs", "jobs", shell_jobs},
- {"pwd", "Print current directory", "pwd", shell_pwd},
- {"set", "Set environment variable", "set [VAR=value]", shell_set},
- {"unset", "Unset environment variable", "unset VAR", shell_unset},
- //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
- {".", "Source-in and run commands in a file", ". filename", shell_source},
- {"help", "List shell built-in commands", "help", shell_help},
- {NULL, NULL, NULL, NULL}
+ {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
+ {"cd", "Change working directory", "cd [dir]", shell_cd},
+ //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
+ {"env", "Print all environment variables", "env", shell_env},
+ {"exit", "Exit from shell()", "exit", shell_exit},
+ {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
+ {"jobs", "Lists the active jobs", "jobs", shell_jobs},
+ {"pwd", "Print current directory", "pwd", shell_pwd},
+ {"set", "Set environment variable", "set [VAR=value]", shell_set},
+ {"unset", "Unset environment variable", "unset VAR", shell_unset},
+ {".", "Source-in and run commands in a file", ". filename",
+ shell_source},
+ {"help", "List shell built-in commands", "help", shell_help},
+ {NULL, NULL, NULL, NULL}
};
static const char shell_usage[] =
- "sh [FILE]...\n\n"
- "The BusyBox command interpreter (shell).\n\n";
+ "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
static char cwd[1024];
static char *prompt = "# ";
+
/* built-in 'cd <path>' handler */
-static int shell_cd(struct job* cmd, struct jobSet* junk)
+static int shell_cd(struct job *cmd, struct jobSet *junk)
{
- char *newdir;
- if (!cmd->progs[0].argv[1] == 1)
- newdir = getenv("HOME");
- else
- newdir = cmd->progs[0].argv[1];
- if (chdir(newdir)) {
- printf("cd: %s: %s\n", newdir, strerror(errno));
- return FALSE;
- }
- getcwd(cwd, sizeof(cwd));
+ char *newdir;
+ if (!cmd->progs[0].argv[1] == 1)
+ newdir = getenv("HOME");
+ else
+ newdir = cmd->progs[0].argv[1];
+ if (chdir(newdir)) {
+ printf("cd: %s: %s\n", newdir, strerror(errno));
+ return FALSE;
+ }
+ getcwd(cwd, sizeof(cwd));
- return TRUE;
+ return TRUE;
}
/* built-in 'env' handler */
-static int shell_env(struct job* dummy, struct jobSet* junk)
+static int shell_env(struct job *dummy, struct jobSet *junk)
{
- char **e;
+ char **e;
- for (e = environ ; *e ; e++) {
- fprintf(stdout, "%s\n", *e);
- }
- return (0);
+ for (e = environ; *e; e++) {
+ fprintf(stdout, "%s\n", *e);
+ }
+ return (0);
}
/* built-in 'exit' handler */
-static int shell_exit(struct job* cmd, struct jobSet* junk)
+static int shell_exit(struct job *cmd, struct jobSet *junk)
{
- if (!cmd->progs[0].argv[1] == 1)
- exit TRUE;
- else
- exit(atoi(cmd->progs[0].argv[1]));
+ if (!cmd->progs[0].argv[1] == 1)
+ exit TRUE;
+ else
+ exit(atoi(cmd->progs[0].argv[1]));
}
/* built-in 'fg' and 'bg' handler */
-static int shell_fg_bg(struct job* cmd, struct jobSet* jobList)
+static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
{
- int i, jobNum;
- struct job* job;
-
- if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
- fprintf(stderr, "%s: exactly one argument is expected\n",
- cmd->progs[0].argv[0]);
- return FALSE;
- }
-
- if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
- fprintf(stderr, "%s: bad argument '%s'\n",
- cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
- return FALSE;
- }
-
- for (job = jobList->head; job; job = job->next)
- if (job->jobId == jobNum) break;
-
- if (!job) {
- fprintf(stderr, "%s: unknown job %d\n",
- cmd->progs[0].argv[0], jobNum);
- return FALSE;
- }
-
- if (*cmd->progs[0].argv[0] == 'f') {
- /* Make this job the foreground job */
-
- if (tcsetpgrp(0, job->pgrp))
- perror("tcsetpgrp");
- jobList->fg = job;
- }
-
- /* Restart the processes in the job */
- for (i = 0; i < job->numProgs; i++)
- job->progs[i].isStopped = 0;
-
- kill(-job->pgrp, SIGCONT);
-
- job->stoppedProgs = 0;
-
- return TRUE;
+ int i, jobNum;
+ struct job *job;
+
+ if (!jobList->head) {
+ if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
+ fprintf(stderr, "%s: exactly one argument is expected\n",
+ cmd->progs[0].argv[0]);
+ return FALSE;
+ }
+ if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
+ fprintf(stderr, "%s: bad argument '%s'\n",
+ cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
+ return FALSE;
+ }
+ } else {
+ job = jobList->head;
+ }
+
+ for (job = jobList->head; job; job = job->next)
+ if (job->jobId == jobNum)
+ break;
+
+ if (!job) {
+ fprintf(stderr, "%s: unknown job %d\n",
+ cmd->progs[0].argv[0], jobNum);
+ return FALSE;
+ }
+
+ if (*cmd->progs[0].argv[0] == 'f') {
+ /* Make this job the foreground job */
+
+ if (tcsetpgrp(0, job->pgrp))
+ perror("tcsetpgrp");
+ jobList->fg = job;
+ }
+
+ /* Restart the processes in the job */
+ for (i = 0; i < job->numProgs; i++)
+ job->progs[i].isStopped = 0;
+
+ kill(-job->pgrp, SIGCONT);
+
+ job->stoppedProgs = 0;
+
+ return TRUE;
}
/* built-in 'help' handler */
-static int shell_help(struct job* cmd, struct jobSet* junk)
+static int shell_help(struct job *cmd, struct jobSet *junk)
{
- struct builtInCommand *x;
+ struct builtInCommand *x;
- fprintf(stdout, "\nBuilt-in commands:\n");
- fprintf(stdout, "-------------------\n");
- for ( x=bltins; x->cmd; x++) {
- fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
- }
- fprintf(stdout, "\n\n");
- return TRUE;
+ fprintf(stdout, "\nBuilt-in commands:\n");
+ fprintf(stdout, "-------------------\n");
+ for (x = bltins; x->cmd; x++) {
+ fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
+ }
+ fprintf(stdout, "\n\n");
+ return TRUE;
}
/* built-in 'jobs' handler */
-static int shell_jobs(struct job* dummy, struct jobSet* jobList)
+static int shell_jobs(struct job *dummy, struct jobSet *jobList)
{
- struct job * job;
- char * statusString;
- for (job = jobList->head; job; job = job->next) {
- if (job->runningProgs == job->stoppedProgs)
- statusString = "Stopped";
- else
- statusString = "Running";
-
- printf(JOB_STATUS_FORMAT, job->jobId, statusString,
- job->text);
- }
- return TRUE;
+ struct job *job;
+ char *statusString;
+ for (job = jobList->head; job; job = job->next) {
+ if (job->runningProgs == job->stoppedProgs)
+ statusString = "Stopped";
+ else
+ statusString = "Running";
+
+ printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
+ }
+ return TRUE;
}
/* built-in 'pwd' handler */
-static int shell_pwd(struct job* dummy, struct jobSet* junk)
+static int shell_pwd(struct job *dummy, struct jobSet *junk)
{
- getcwd(cwd, sizeof(cwd));
- fprintf(stdout, "%s\n", cwd);
- return TRUE;
+ getcwd(cwd, sizeof(cwd));
+ fprintf(stdout, "%s\n", cwd);
+ return TRUE;
}
/* built-in 'set VAR=value' handler */
-static int shell_set(struct job* cmd, struct jobSet* junk)
+static int shell_set(struct job *cmd, struct jobSet *junk)
{
- int res;
+ int res;
- if (!cmd->progs[0].argv[1] == 1) {
- return (shell_env(cmd, junk));
- }
- res = putenv(cmd->progs[0].argv[1]);
- if (res)
- fprintf(stdout, "set: %s\n", strerror(errno));
- return (res);
+ if (!cmd->progs[0].argv[1] == 1) {
+ return (shell_env(cmd, junk));
+ }
+ res = putenv(cmd->progs[0].argv[1]);
+ if (res)
+ fprintf(stdout, "set: %s\n", strerror(errno));
+ return (res);
}
/* Built-in '.' handler (read-in and execute commands from file) */
-static int shell_source(struct job* cmd, struct jobSet* junk)
+static int shell_source(struct job *cmd, struct jobSet *junk)
{
- FILE *input;
- int status;
-
- if (!cmd->progs[0].argv[1] == 1)
- return FALSE;
-
- input = fopen(cmd->progs[0].argv[1], "r");
- if (!input) {
- fprintf(stdout, "Couldn't open file '%s'\n", cmd->progs[0].argv[1]);
- return FALSE;
- }
+ FILE *input;
+ int status;
- /* Now run the file */
- status = busy_loop(input);
- return (status);
+ if (!cmd->progs[0].argv[1] == 1)
+ return FALSE;
+
+ input = fopen(cmd->progs[0].argv[1], "r");
+ if (!input) {
+ fprintf(stdout, "Couldn't open file '%s'\n",
+ cmd->progs[0].argv[1]);
+ return FALSE;
+ }
+
+ /* Now run the file */
+ status = busy_loop(input);
+ return (status);
}
/* built-in 'unset VAR' handler */
-static int shell_unset(struct job* cmd, struct jobSet* junk)
+static int shell_unset(struct job *cmd, struct jobSet *junk)
{
- if (!cmd->progs[0].argv[1] == 1) {
- fprintf(stdout, "unset: parameter required.\n");
- return FALSE;
- }
- unsetenv(cmd->progs[0].argv[1]);
- return TRUE;
+ if (!cmd->progs[0].argv[1] == 1) {
+ fprintf(stdout, "unset: parameter required.\n");
+ return FALSE;
+ }
+ unsetenv(cmd->progs[0].argv[1]);
+ return TRUE;
}
/* free up all memory from a job */
-static void freeJob(struct job * cmd)
+static void freeJob(struct job *cmd)
{
int i;
for (i = 0; i < cmd->numProgs; i++) {
- free(cmd->progs[i].argv);
- if (cmd->progs[i].redirections) free(cmd->progs[i].redirections);
- if (cmd->progs[i].freeGlob) globfree(&cmd->progs[i].globResult);
+ free(cmd->progs[i].argv);
+ if (cmd->progs[i].redirections)
+ free(cmd->progs[i].redirections);
+ if (cmd->progs[i].freeGlob)
+ globfree(&cmd->progs[i].globResult);
}
free(cmd->progs);
- if (cmd->text) free(cmd->text);
+ if (cmd->text)
+ free(cmd->text);
free(cmd->cmdBuf);
}
/* remove a job from the jobList */
-static void removeJob(struct jobSet * jobList, struct job * job)
+static void removeJob(struct jobSet *jobList, struct job *job)
{
- struct job * prevJob;
+ struct job *prevJob;
- freeJob(job);
+ freeJob(job);
if (job == jobList->head) {
- jobList->head = job->next;
+ jobList->head = job->next;
} else {
- prevJob = jobList->head;
- while (prevJob->next != job) prevJob = prevJob->next;
- prevJob->next = job->next;
+ prevJob = jobList->head;
+ while (prevJob->next != job)
+ prevJob = prevJob->next;
+ prevJob->next = job->next;
}
free(job);
@@ -333,56 +346,62 @@ static void removeJob(struct jobSet * jobList, struct job * job)
/* Checks to see if any background processes have exited -- if they
have, figure out why and see if a job has completed */
-static void checkJobs(struct jobSet * jobList)
+static void checkJobs(struct jobSet *jobList)
{
- struct job * job;
+ struct job *job;
pid_t childpid;
int status;
- int progNum=0;
-
+ int progNum = 0;
+
while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
- for (job = jobList->head; job; job = job->next) {
- progNum = 0;
- while (progNum < job->numProgs &&
- job->progs[progNum].pid != childpid)
- progNum++;
- if (progNum < job->numProgs) break;
- }
-
- if (WIFEXITED(status) || WIFSIGNALED(status)) {
- /* child exited */
- job->runningProgs--;
- job->progs[progNum].pid = 0;
-
- if (!job->runningProgs) {
- printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
- removeJob(jobList, job);
- }
- } else {
- /* child stopped */
- job->stoppedProgs++;
- job->progs[progNum].isStopped = 1;
-
- if (job->stoppedProgs == job->numProgs) {
- printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text);
- }
- }
+ for (job = jobList->head; job; job = job->next) {
+ progNum = 0;
+ while (progNum < job->numProgs &&
+ job->progs[progNum].pid != childpid) progNum++;
+ if (progNum < job->numProgs)
+ break;
+ }
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* child exited */
+ job->runningProgs--;
+ job->progs[progNum].pid = 0;
+
+ if (!job->runningProgs) {
+ printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
+ removeJob(jobList, job);
+ }
+ } else {
+ /* child stopped */
+ job->stoppedProgs++;
+ job->progs[progNum].isStopped = 1;
+
+ if (job->stoppedProgs == job->numProgs) {
+ printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
+ job->text);
+ }
+ }
}
if (childpid == -1 && errno != ECHILD)
- perror("waitpid");
+ perror("waitpid");
}
-static int getCommand(FILE * source, char * command)
+static int getCommand(FILE * source, char *command)
{
if (source == stdin) {
- fprintf(stdout, "%s %s", cwd, prompt);
- fflush(stdout);
+ fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
+ fflush(stdout);
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+ cmdedit_read_input(fileno(stdin), fileno(stdout), command);
+ return 0;
+#endif
}
- if (!fgets(command, MAX_COMMAND_LEN, source)) {
- if (source == stdin) printf("\n");
- return 1;
+ if (!fgets(command, BUFSIZ - 2, source)) {
+ if (source == stdin)
+ printf("\n");
+ return 1;
}
/* remove trailing newline */
@@ -391,46 +410,48 @@ static int getCommand(FILE * source, char * command)
return 0;
}
-static void globLastArgument(struct childProgram * prog, int * argcPtr,
- int * argcAllocedPtr)
+static void globLastArgument(struct childProgram *prog, int *argcPtr,
+ int *argcAllocedPtr)
{
int argc = *argcPtr;
int argcAlloced = *argcAllocedPtr;
int rc;
int flags;
int i;
- char * src, * dst;
+ char *src, *dst;
- if (argc > 1) { /* cmd->globResult is already initialized */
- flags = GLOB_APPEND;
- i = prog->globResult.gl_pathc;
+ if (argc > 1) { /* cmd->globResult is already initialized */
+ flags = GLOB_APPEND;
+ i = prog->globResult.gl_pathc;
} else {
- prog->freeGlob = 1;
- flags = 0;
- i = 0;
+ prog->freeGlob = 1;
+ flags = 0;
+ i = 0;
}
rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
if (rc == GLOB_NOSPACE) {
- fprintf(stderr, "out of space during glob operation\n");
- return;
- } else if (rc == GLOB_NOMATCH ||
- (!rc && (prog->globResult.gl_pathc - i) == 1 &&
- !strcmp(prog->argv[argc - 1],
- prog->globResult.gl_pathv[i]))) {
- /* we need to remove whatever \ quoting is still present */
- src = dst = prog->argv[argc - 1];
- while (*src) {
- if (*src != '\\') *dst++ = *src;
- src++;
- }
- *dst = '\0';
+ fprintf(stderr, "out of space during glob operation\n");
+ return;
+ } else if (rc == GLOB_NOMATCH ||
+ (!rc && (prog->globResult.gl_pathc - i) == 1 &&
+ !strcmp(prog->argv[argc - 1],
+ prog->globResult.gl_pathv[i]))) {
+ /* we need to remove whatever \ quoting is still present */
+ src = dst = prog->argv[argc - 1];
+ while (*src) {
+ if (*src != '\\')
+ *dst++ = *src;
+ src++;
+ }
+ *dst = '\0';
} else if (!rc) {
- argcAlloced += (prog->globResult.gl_pathc - i);
- prog->argv = realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
- memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
- sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
- argc += (prog->globResult.gl_pathc - i - 1);
+ argcAlloced += (prog->globResult.gl_pathc - i);
+ prog->argv =
+ realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
+ memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
+ sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
+ argc += (prog->globResult.gl_pathc - i - 1);
}
*argcAllocedPtr = argcAlloced;
@@ -442,27 +463,28 @@ static void globLastArgument(struct childProgram * prog, int * argcPtr,
the beginning of the next command (if the original command had more
then one job associated with it) or NULL if no more commands are
present. */
-static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
+static int parseCommand(char **commandPtr, struct job *job, int *isBg)
{
- char * command;
- char * returnCommand = NULL;
- char * src, * buf, * chptr;
+ char *command;
+ char *returnCommand = NULL;
+ char *src, *buf, *chptr;
int argc = 0;
int done = 0;
int argvAlloced;
int i;
- char quote = '\0';
+ char quote = '\0';
int count;
- struct childProgram * prog;
+ struct childProgram *prog;
/* skip leading white space */
- while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++;
-
- /* this handles empty lines or leading '#' characters */
- if (!**commandPtr || (**commandPtr=='#')) {
- job->numProgs = 0;
- *commandPtr = NULL;
- return 0;
+ while (**commandPtr && isspace(**commandPtr))
+ (*commandPtr)++;
+
+ /* this handles empty lines or leading '#' characters */
+ if (!**commandPtr || (**commandPtr == '#')) {
+ job->numProgs = 0;
+ *commandPtr = NULL;
+ return 0;
}
*isBg = 0;
@@ -491,177 +513,185 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
buf = command;
src = *commandPtr;
while (*src && !done) {
- if (quote == *src) {
- quote = '\0';
- } else if (quote) {
- if (*src == '\\') {
- src++;
- if (!*src) {
- fprintf(stderr, "character expected after \\\n");
- freeJob(job);
- return 1;
- }
-
- /* in shell, "\'" should yield \' */
- if (*src != quote) *buf++ = '\\';
- } else if (*src == '*' || *src == '?' || *src == '[' ||
- *src == ']')
- *buf++ = '\\';
- *buf++ = *src;
- } else if (isspace(*src)) {
- if (*prog->argv[argc]) {
- buf++, argc++;
- /* +1 here leaves room for the NULL which ends argv */
- if ((argc + 1) == argvAlloced) {
- argvAlloced += 5;
- prog->argv = realloc(prog->argv,
- sizeof(*prog->argv) * argvAlloced);
- }
- prog->argv[argc] = buf;
-
- globLastArgument(prog, &argc, &argvAlloced);
- }
- } else switch (*src) {
- case '"':
- case '\'':
- quote = *src;
- break;
-
- case '#': /* comment */
- done = 1;
- break;
-
- case '>': /* redirections */
- case '<':
- i = prog->numRedirections++;
- prog->redirections = realloc(prog->redirections,
- sizeof(*prog->redirections) * (i + 1));
-
- prog->redirections[i].fd = -1;
- if (buf != prog->argv[argc]) {
- /* the stuff before this character may be the file number
- being redirected */
- prog->redirections[i].fd = strtol(prog->argv[argc], &chptr, 10);
-
- if (*chptr && *prog->argv[argc]) {
- buf++, argc++;
- globLastArgument(prog, &argc, &argvAlloced);
- }
- }
-
- if (prog->redirections[i].fd == -1) {
- if (*src == '>')
- prog->redirections[i].fd = 1;
- else
- prog->redirections[i].fd = 0;
- }
-
- if (*src++ == '>') {
- if (*src == '>')
- prog->redirections[i].type = REDIRECT_APPEND, src++;
- else
- prog->redirections[i].type = REDIRECT_OVERWRITE;
- } else {
- prog->redirections[i].type = REDIRECT_INPUT;
- }
-
- /* This isn't POSIX sh compliant. Oh well. */
- chptr = src;
- while (isspace(*chptr)) chptr++;
-
- if (!*chptr) {
- fprintf(stderr, "file name expected after %c\n", *src);
- freeJob(job);
- return 1;
- }
-
- prog->redirections[i].filename = buf;
- while (*chptr && !isspace(*chptr))
- *buf++ = *chptr++;
-
- src = chptr - 1; /* we src++ later */
- prog->argv[argc] = ++buf;
- break;
-
- case '|': /* pipe */
- /* finish this command */
- if (*prog->argv[argc]) argc++;
- if (!argc) {
- fprintf(stderr, "empty command in pipe\n");
- freeJob(job);
- return 1;
- }
- prog->argv[argc] = NULL;
-
- /* and start the next */
- job->numProgs++;
- job->progs = realloc(job->progs,
- sizeof(*job->progs) * job->numProgs);
- prog = job->progs + (job->numProgs - 1);
- prog->numRedirections = 0;
- prog->redirections = NULL;
- prog->freeGlob = 0;
- argc = 0;
-
- argvAlloced = 5;
- prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
- prog->argv[0] = ++buf;
-
- src++;
- while (*src && isspace(*src)) src++;
-
- if (!*src) {
- fprintf(stderr, "empty command in pipe\n");
- return 1;
- }
- src--; /* we'll ++ it at the end of the loop */
-
- break;
-
- case '&': /* background */
- *isBg = 1;
- case ';': /* multiple commands */
- done = 1;
- returnCommand = *commandPtr + (src - *commandPtr) + 1;
- break;
-
- case '\\':
- src++;
- if (!*src) {
- freeJob(job);
- fprintf(stderr, "character expected after \\\n");
- return 1;
- }
- if (*src == '*' || *src == '[' || *src == ']' || *src == '?')
- *buf++ = '\\';
- /* fallthrough */
- default:
- *buf++ = *src;
- }
-
- src++;
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote) {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ fprintf(stderr, "character expected after \\\n");
+ freeJob(job);
+ return 1;
+ }
+
+ /* in shell, "\'" should yield \' */
+ if (*src != quote)
+ *buf++ = '\\';
+ } else if (*src == '*' || *src == '?' || *src == '[' ||
+ *src == ']') *buf++ = '\\';
+ *buf++ = *src;
+ } else if (isspace(*src)) {
+ if (*prog->argv[argc]) {
+ buf++, argc++;
+ /* +1 here leaves room for the NULL which ends argv */
+ if ((argc + 1) == argvAlloced) {
+ argvAlloced += 5;
+ prog->argv = realloc(prog->argv,
+ sizeof(*prog->argv) *
+ argvAlloced);
+ }
+ prog->argv[argc] = buf;
+
+ globLastArgument(prog, &argc, &argvAlloced);
+ }
+ } else
+ switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ break;
+
+ case '#': /* comment */
+ done = 1;
+ break;
+
+ case '>': /* redirections */
+ case '<':
+ i = prog->numRedirections++;
+ prog->redirections = realloc(prog->redirections,
+ sizeof(*prog->redirections) *
+ (i + 1));
+
+ prog->redirections[i].fd = -1;
+ if (buf != prog->argv[argc]) {
+ /* the stuff before this character may be the file number
+ being redirected */
+ prog->redirections[i].fd =
+ strtol(prog->argv[argc], &chptr, 10);
+
+ if (*chptr && *prog->argv[argc]) {
+ buf++, argc++;
+ globLastArgument(prog, &argc, &argvAlloced);
+ }
+ }
+
+ if (prog->redirections[i].fd == -1) {
+ if (*src == '>')
+ prog->redirections[i].fd = 1;
+ else
+ prog->redirections[i].fd = 0;
+ }
+
+ if (*src++ == '>') {
+ if (*src == '>')
+ prog->redirections[i].type =
+ REDIRECT_APPEND, src++;
+ else
+ prog->redirections[i].type = REDIRECT_OVERWRITE;
+ } else {
+ prog->redirections[i].type = REDIRECT_INPUT;
+ }
+
+ /* This isn't POSIX sh compliant. Oh well. */
+ chptr = src;
+ while (isspace(*chptr))
+ chptr++;
+
+ if (!*chptr) {
+ fprintf(stderr, "file name expected after %c\n", *src);
+ freeJob(job);
+ return 1;
+ }
+
+ prog->redirections[i].filename = buf;
+ while (*chptr && !isspace(*chptr))
+ *buf++ = *chptr++;
+
+ src = chptr - 1; /* we src++ later */
+ prog->argv[argc] = ++buf;
+ break;
+
+ case '|': /* pipe */
+ /* finish this command */
+ if (*prog->argv[argc])
+ argc++;
+ if (!argc) {
+ fprintf(stderr, "empty command in pipe\n");
+ freeJob(job);
+ return 1;
+ }
+ prog->argv[argc] = NULL;
+
+ /* and start the next */
+ job->numProgs++;
+ job->progs = realloc(job->progs,
+ sizeof(*job->progs) * job->numProgs);
+ prog = job->progs + (job->numProgs - 1);
+ prog->numRedirections = 0;
+ prog->redirections = NULL;
+ prog->freeGlob = 0;
+ argc = 0;
+
+ argvAlloced = 5;
+ prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
+ prog->argv[0] = ++buf;
+
+ src++;
+ while (*src && isspace(*src))
+ src++;
+
+ if (!*src) {
+ fprintf(stderr, "empty command in pipe\n");
+ return 1;
+ }
+ src--; /* we'll ++ it at the end of the loop */
+
+ break;
+
+ case '&': /* background */
+ *isBg = 1;
+ case ';': /* multiple commands */
+ done = 1;
+ returnCommand = *commandPtr + (src - *commandPtr) + 1;
+ break;
+
+ case '\\':
+ src++;
+ if (!*src) {
+ freeJob(job);
+ fprintf(stderr, "character expected after \\\n");
+ return 1;
+ }
+ if (*src == '*' || *src == '[' || *src == ']'
+ || *src == '?') *buf++ = '\\';
+ /* fallthrough */
+ default:
+ *buf++ = *src;
+ }
+
+ src++;
}
if (*prog->argv[argc]) {
- argc++;
- globLastArgument(prog, &argc, &argvAlloced);
+ argc++;
+ globLastArgument(prog, &argc, &argvAlloced);
}
if (!argc) {
- freeJob(job);
- return 0;
+ freeJob(job);
+ return 0;
}
prog->argv[argc] = NULL;
if (!returnCommand) {
- job->text = malloc(strlen(*commandPtr) + 1);
- strcpy(job->text, *commandPtr);
+ job->text = malloc(strlen(*commandPtr) + 1);
+ strcpy(job->text, *commandPtr);
} else {
- /* This leaves any trailing spaces, which is a bit sloppy */
+ /* This leaves any trailing spaces, which is a bit sloppy */
- count = returnCommand - *commandPtr;
- job->text = malloc(count + 1);
- strncpy(job->text, *commandPtr, count);
- job->text[count] = '\0';
+ count = returnCommand - *commandPtr;
+ job->text = malloc(count + 1);
+ strncpy(job->text, *commandPtr, count);
+ job->text[count] = '\0';
}
*commandPtr = returnCommand;
@@ -669,63 +699,64 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
return 0;
}
-static int runCommand(struct job newJob, struct jobSet * jobList,
- int inBg)
+static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
{
- struct job * job;
+ struct job *job;
int i;
int nextin, nextout;
- int pipefds[2]; /* pipefd[0] is for reading */
+ int pipefds[2]; /* pipefd[0] is for reading */
struct builtInCommand *x;
/* handle built-ins here -- we don't fork() so we can't background
these very easily */
- for( x=bltins ; x->cmd ; x++) {
- if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
- return(x->function(&newJob, jobList));
- }
+ for (x = bltins; x->cmd; x++) {
+ if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
+ return (x->function(&newJob, jobList));
+ }
}
nextin = 0, nextout = 1;
for (i = 0; i < newJob.numProgs; i++) {
- if ((i + 1) < newJob.numProgs) {
- pipe(pipefds);
- nextout = pipefds[1];
- } else {
- nextout = 1;
- }
-
- if (!(newJob.progs[i].pid = fork())) {
- signal(SIGTTOU, SIG_DFL);
-
- if (nextin != 0) {
- dup2(nextin, 0);
- close(nextin);
- }
-
- if (nextout != 1) {
- dup2(nextout, 1);
- close(nextout);
- }
-
- /* explicit redirections override pipes */
- setupRedirections(newJob.progs + i);
-
- execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
- fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0],
- strerror(errno));
- }
-
- /* put our child in the process group whose leader is the
- first process in this pipe */
- setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
-
- if (nextin != 0) close(nextin);
- if (nextout != 1) close(nextout);
-
- /* If there isn't another process, nextin is garbage
- but it doesn't matter */
- nextin = pipefds[0];
+ if ((i + 1) < newJob.numProgs) {
+ pipe(pipefds);
+ nextout = pipefds[1];
+ } else {
+ nextout = 1;
+ }
+
+ if (!(newJob.progs[i].pid = fork())) {
+ signal(SIGTTOU, SIG_DFL);
+
+ if (nextin != 0) {
+ dup2(nextin, 0);
+ close(nextin);
+ }
+
+ if (nextout != 1) {
+ dup2(nextout, 1);
+ close(nextout);
+ }
+
+ /* explicit redirections override pipes */
+ setupRedirections(newJob.progs + i);
+
+ execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
+ fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
+ strerror(errno));
+ }
+
+ /* put our child in the process group whose leader is the
+ first process in this pipe */
+ setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
+
+ if (nextin != 0)
+ close(nextin);
+ if (nextout != 1)
+ close(nextout);
+
+ /* If there isn't another process, nextin is garbage
+ but it doesn't matter */
+ nextin = pipefds[0];
}
newJob.pgrp = newJob.progs[0].pid;
@@ -733,16 +764,16 @@ static int runCommand(struct job newJob, struct jobSet * jobList,
/* find the ID for the job to use */
newJob.jobId = 1;
for (job = jobList->head; job; job = job->next)
- if (job->jobId >= newJob.jobId)
- newJob.jobId = job->jobId + 1;
+ if (job->jobId >= newJob.jobId)
+ newJob.jobId = job->jobId + 1;
/* add the job to the list of running jobs */
if (!jobList->head) {
- job = jobList->head = malloc(sizeof(*job));
+ job = jobList->head = malloc(sizeof(*job));
} else {
- for (job = jobList->head; job->next; job = job->next);
- job->next = malloc(sizeof(*job));
- job = job->next;
+ for (job = jobList->head; job->next; job = job->next);
+ job->next = malloc(sizeof(*job));
+ job = job->next;
}
*job = newJob;
@@ -751,165 +782,177 @@ static int runCommand(struct job newJob, struct jobSet * jobList,
job->stoppedProgs = 0;
if (inBg) {
- /* we don't wait for background jobs to return -- append it
- to the list of backgrounded jobs and leave it alone */
+ /* we don't wait for background jobs to return -- append it
+ to the list of backgrounded jobs and leave it alone */
- printf("[%d] %d\n", job->jobId,
- newJob.progs[newJob.numProgs - 1].pid);
+ printf("[%d] %d\n", job->jobId,
+ newJob.progs[newJob.numProgs - 1].pid);
} else {
- jobList->fg = job;
+ jobList->fg = job;
+
+ /* move the new process group into the foreground */
- /* move the new process group into the foreground */
-
- if (tcsetpgrp(0, newJob.pgrp))
- perror("tcsetpgrp");
+ if (tcsetpgrp(0, newJob.pgrp))
+ perror("tcsetpgrp");
}
return 0;
}
-static int setupRedirections(struct childProgram * prog)
+static int setupRedirections(struct childProgram *prog)
{
int i;
int openfd;
- int mode=O_RDONLY;
- struct redirectionSpecifier * redir = prog->redirections;
+ int mode = O_RDONLY;
+ struct redirectionSpecifier *redir = prog->redirections;
for (i = 0; i < prog->numRedirections; i++, redir++) {
- switch (redir->type) {
- case REDIRECT_INPUT:
- mode = O_RDONLY;
- break;
- case REDIRECT_OVERWRITE:
- mode = O_RDWR | O_CREAT | O_TRUNC;
- break;
- case REDIRECT_APPEND:
- mode = O_RDWR | O_CREAT | O_APPEND;
- break;
- }
-
- openfd = open(redir->filename, mode, 0666);
- if (openfd < 0) {
- /* this could get lost if stderr has been redirected, but
- bash and ash both lose it as well (though zsh doesn't!) */
- fprintf(stderr, "error opening %s: %s\n", redir->filename,
- strerror(errno));
- return 1;
- }
-
- if (openfd != redir->fd) {
- dup2(openfd, redir->fd);
- close(openfd);
- }
+ switch (redir->type) {
+ case REDIRECT_INPUT:
+ mode = O_RDONLY;
+ break;
+ case REDIRECT_OVERWRITE:
+ mode = O_RDWR | O_CREAT | O_TRUNC;
+ break;
+ case REDIRECT_APPEND:
+ mode = O_RDWR | O_CREAT | O_APPEND;
+ break;
+ }
+
+ openfd = open(redir->filename, mode, 0666);
+ if (openfd < 0) {
+ /* this could get lost if stderr has been redirected, but
+ bash and ash both lose it as well (though zsh doesn't!) */
+ fprintf(stderr, "error opening %s: %s\n", redir->filename,
+ strerror(errno));
+ return 1;
+ }
+
+ if (openfd != redir->fd) {
+ dup2(openfd, redir->fd);
+ close(openfd);
+ }
}
return 0;
}
-static int busy_loop(FILE * input)
+static int busy_loop(FILE * input)
{
- char command[MAX_COMMAND_LEN + 1];
- char * nextCommand = NULL;
+ char *command;
+ char *nextCommand = NULL;
struct jobSet jobList = { NULL, NULL };
struct job newJob;
int i;
int status;
int inBg;
+ command = (char*) calloc(BUFSIZ, sizeof(char));
+
/* don't pay any attention to this signal; it just confuses
things and isn't really meant for shells anyway */
signal(SIGTTOU, SIG_IGN);
-
+
while (1) {
- if (!jobList.fg) {
- /* no job is in the foreground */
-
- /* see if any background processes have exited */
- checkJobs(&jobList);
-
- if (!nextCommand) {
- if (getCommand(input, command)) break;
- nextCommand = command;
- }
-
- if (!parseCommand(&nextCommand, &newJob, &inBg) &&
- newJob.numProgs) {
- runCommand(newJob, &jobList, inBg);
- }
- } else {
- /* a job is running in the foreground; wait for it */
- i = 0;
- while (!jobList.fg->progs[i].pid ||
- jobList.fg->progs[i].isStopped) i++;
-
- waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
-
- if (WIFEXITED(status) || WIFSIGNALED(status)) {
- /* the child exited */
- jobList.fg->runningProgs--;
- jobList.fg->progs[i].pid = 0;
-
- if (!jobList.fg->runningProgs) {
- /* child exited */
-
- removeJob(&jobList, jobList.fg);
- jobList.fg = NULL;
-
- /* move the shell to the foreground */
- if (tcsetpgrp(0, getpid()))
- perror("tcsetpgrp");
- }
- } else {
- /* the child was stopped */
- jobList.fg->stoppedProgs++;
- jobList.fg->progs[i].isStopped = 1;
-
- if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
- printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
- "Stopped", jobList.fg->text);
- jobList.fg = NULL;
- }
- }
-
- if (!jobList.fg) {
- /* move the shell to the foreground */
- if (tcsetpgrp(0, getpid()))
- perror("tcsetpgrp");
- }
- }
- }
+ if (!jobList.fg) {
+ /* no job is in the foreground */
+
+ /* see if any background processes have exited */
+ checkJobs(&jobList);
+
+ if (!nextCommand) {
+ if (getCommand(input, command))
+ break;
+ nextCommand = command;
+ }
+
+ if (!parseCommand(&nextCommand, &newJob, &inBg) &&
+ newJob.numProgs) {
+ runCommand(newJob, &jobList, inBg);
+ }
+ } else {
+ /* a job is running in the foreground; wait for it */
+ i = 0;
+ while (!jobList.fg->progs[i].pid ||
+ jobList.fg->progs[i].isStopped) i++;
+
+ waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* the child exited */
+ jobList.fg->runningProgs--;
+ jobList.fg->progs[i].pid = 0;
+
+ if (!jobList.fg->runningProgs) {
+ /* child exited */
+
+ removeJob(&jobList, jobList.fg);
+ jobList.fg = NULL;
+
+ /* move the shell to the foreground */
+ if (tcsetpgrp(0, getpid()))
+ perror("tcsetpgrp");
+ }
+ } else {
+ /* the child was stopped */
+ jobList.fg->stoppedProgs++;
+ jobList.fg->progs[i].isStopped = 1;
+
+ if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
+ printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
+ "Stopped", jobList.fg->text);
+ jobList.fg = NULL;
+ }
+ }
+
+ if (!jobList.fg) {
+ /* move the shell to the foreground */
+ if (tcsetpgrp(0, getpid()))
+ perror("tcsetpgrp");
+ }
+ }
+ }
+ free( command);
return 0;
}
-int shell_main(int argc, char ** argv)
+int shell_main(int argc, char **argv)
{
- FILE * input = stdin;
+ FILE *input = stdin;
if (argc > 2) {
- usage( shell_usage);
- }
- /* initialize the cwd */
- getcwd(cwd, sizeof(cwd));
-
-
- //if (argv[0] && argv[0][0] == '-') {
- // shell_source("/etc/profile");
- //}
-
- if (argc < 2) {
- fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
- fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
- } else {
- input = fopen(argv[1], "r");
- if (!input)
- fatalError("A: Couldn't open file '%s': %s\n", argv[1], strerror(errno));
-// else
-// fatalError("Got it.\n");
- //exit(shell_source(argv[1]));
- }
-
- return (busy_loop( input));
+ usage(shell_usage);
+ }
+ /* initialize the cwd */
+ getcwd(cwd, sizeof(cwd));
+
+
+ //if (argv[0] && argv[0][0] == '-') {
+ // shell_source("/etc/profile");
+ //}
+
+ if (argc < 2) {
+ fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
+ BB_BT);
+ fprintf(stdout,
+ "Enter 'help' for a list of built-in commands.\n\n");
+ } else {
+ input = fopen(argv[1], "r");
+ if (!input)
+ fatalError("A: Couldn't open file '%s': %s\n", argv[1],
+ strerror(errno));
+// else
+// fatalError("Got it.\n");
+ //exit(shell_source(argv[1]));
+
+ /* Set terminal IO to canonical mode, and save old term settings. */
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+ cmdedit_init();
+#endif
+ }
+
+ return (busy_loop(input));
}
diff --git a/sh.c b/sh.c
index 9e467dc54..498f43779 100644
--- a/sh.c
+++ b/sh.c
@@ -39,293 +39,306 @@
#include <unistd.h>
-#define MAX_COMMAND_LEN 250 /* max length of a single command
- string */
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+#include "cmdedit.h"
+#endif
+
#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
-enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND };
+
+enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
+ REDIRECT_APPEND };
struct jobSet {
- struct job * head; /* head of list of running jobs */
- struct job * fg; /* current foreground job */
+ struct job *head; /* head of list of running jobs */
+ struct job *fg; /* current foreground job */
};
struct redirectionSpecifier {
- enum redirectionType type; /* type of redirection */
- int fd; /* file descriptor being redirected */
- char * filename; /* file to redirect fd to */
+ enum redirectionType type; /* type of redirection */
+ int fd; /* file descriptor being redirected */
+ char *filename; /* file to redirect fd to */
};
struct childProgram {
- pid_t pid; /* 0 if exited */
- char ** argv; /* program name and arguments */
- int numRedirections; /* elements in redirection array */
- struct redirectionSpecifier * redirections; /* I/O redirections */
- glob_t globResult; /* result of parameter globbing */
- int freeGlob; /* should we globfree(&globResult)? */
- int isStopped; /* is the program currently running? */
+ pid_t pid; /* 0 if exited */
+ char **argv; /* program name and arguments */
+ int numRedirections; /* elements in redirection array */
+ struct redirectionSpecifier *redirections; /* I/O redirections */
+ glob_t globResult; /* result of parameter globbing */
+ int freeGlob; /* should we globfree(&globResult)? */
+ int isStopped; /* is the program currently running? */
};
struct job {
- int jobId; /* job number */
- int numProgs; /* total number of programs in job */
- int runningProgs; /* number of programs running */
- char * text; /* name of job */
- char * cmdBuf; /* buffer various argv's point into */
- pid_t pgrp; /* process group ID for the job */
- struct childProgram * progs; /* array of programs in job */
- struct job * next; /* to track background commands */
- int stoppedProgs; /* number of programs alive, but stopped */
+ int jobId; /* job number */
+ int numProgs; /* total number of programs in job */
+ int runningProgs; /* number of programs running */
+ char *text; /* name of job */
+ char *cmdBuf; /* buffer various argv's point into */
+ pid_t pgrp; /* process group ID for the job */
+ struct childProgram *progs; /* array of programs in job */
+ struct job *next; /* to track background commands */
+ int stoppedProgs; /* number of programs alive, but stopped */
};
struct builtInCommand {
- char *cmd; /* name */
- char *descr; /* description */
- char *usage; /* usage */
- int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
+ char *cmd; /* name */
+ char *descr; /* description */
+ char *usage; /* usage */
+ int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
};
/* Some function prototypes */
-static int shell_cd(struct job* cmd, struct jobSet* junk);
-static int shell_env(struct job* dummy, struct jobSet* junk);
-static int shell_exit(struct job* cmd, struct jobSet* junk);
-static int shell_fg_bg(struct job* cmd, struct jobSet* jobList);
-static int shell_help(struct job* cmd, struct jobSet* junk);
-static int shell_jobs(struct job* dummy, struct jobSet* jobList);
-static int shell_pwd(struct job* dummy, struct jobSet* junk);
-static int shell_set(struct job* cmd, struct jobSet* junk);
-static int shell_source(struct job* cmd, struct jobSet* jobList);
-static int shell_unset(struct job* cmd, struct jobSet* junk);
-
-static void checkJobs(struct jobSet * jobList);
-static int getCommand(FILE * source, char * command);
-static int parseCommand(char ** commandPtr, struct job * job, int * isBg);
-static int setupRedirections(struct childProgram * prog);
-static int runCommand(struct job newJob, struct jobSet * jobList, int inBg);
+static int shell_cd(struct job *cmd, struct jobSet *junk);
+static int shell_env(struct job *dummy, struct jobSet *junk);
+static int shell_exit(struct job *cmd, struct jobSet *junk);
+static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
+static int shell_help(struct job *cmd, struct jobSet *junk);
+static int shell_jobs(struct job *dummy, struct jobSet *jobList);
+static int shell_pwd(struct job *dummy, struct jobSet *junk);
+static int shell_set(struct job *cmd, struct jobSet *junk);
+static int shell_source(struct job *cmd, struct jobSet *jobList);
+static int shell_unset(struct job *cmd, struct jobSet *junk);
+
+static void checkJobs(struct jobSet *jobList);
+static int getCommand(FILE * source, char *command);
+static int parseCommand(char **commandPtr, struct job *job, int *isBg);
+static int setupRedirections(struct childProgram *prog);
+static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
static int busy_loop(FILE * input);
-
+
/* Table of built-in functions */
static struct builtInCommand bltins[] = {
- {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
- {"cd", "Change working directory", "cd [dir]", shell_cd},
- {"env", "Print all environment variables", "env", shell_env},
- {"exit", "Exit from shell()", "exit", shell_exit},
- {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
- {"jobs", "Lists the active jobs", "jobs", shell_jobs},
- {"pwd", "Print current directory", "pwd", shell_pwd},
- {"set", "Set environment variable", "set [VAR=value]", shell_set},
- {"unset", "Unset environment variable", "unset VAR", shell_unset},
- //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
- {".", "Source-in and run commands in a file", ". filename", shell_source},
- {"help", "List shell built-in commands", "help", shell_help},
- {NULL, NULL, NULL, NULL}
+ {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
+ {"cd", "Change working directory", "cd [dir]", shell_cd},
+ //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
+ {"env", "Print all environment variables", "env", shell_env},
+ {"exit", "Exit from shell()", "exit", shell_exit},
+ {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
+ {"jobs", "Lists the active jobs", "jobs", shell_jobs},
+ {"pwd", "Print current directory", "pwd", shell_pwd},
+ {"set", "Set environment variable", "set [VAR=value]", shell_set},
+ {"unset", "Unset environment variable", "unset VAR", shell_unset},
+ {".", "Source-in and run commands in a file", ". filename",
+ shell_source},
+ {"help", "List shell built-in commands", "help", shell_help},
+ {NULL, NULL, NULL, NULL}
};
static const char shell_usage[] =
- "sh [FILE]...\n\n"
- "The BusyBox command interpreter (shell).\n\n";
+ "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
static char cwd[1024];
static char *prompt = "# ";
+
/* built-in 'cd <path>' handler */
-static int shell_cd(struct job* cmd, struct jobSet* junk)
+static int shell_cd(struct job *cmd, struct jobSet *junk)
{
- char *newdir;
- if (!cmd->progs[0].argv[1] == 1)
- newdir = getenv("HOME");
- else
- newdir = cmd->progs[0].argv[1];
- if (chdir(newdir)) {
- printf("cd: %s: %s\n", newdir, strerror(errno));
- return FALSE;
- }
- getcwd(cwd, sizeof(cwd));
+ char *newdir;
+ if (!cmd->progs[0].argv[1] == 1)
+ newdir = getenv("HOME");
+ else
+ newdir = cmd->progs[0].argv[1];
+ if (chdir(newdir)) {
+ printf("cd: %s: %s\n", newdir, strerror(errno));
+ return FALSE;
+ }
+ getcwd(cwd, sizeof(cwd));
- return TRUE;
+ return TRUE;
}
/* built-in 'env' handler */
-static int shell_env(struct job* dummy, struct jobSet* junk)
+static int shell_env(struct job *dummy, struct jobSet *junk)
{
- char **e;
+ char **e;
- for (e = environ ; *e ; e++) {
- fprintf(stdout, "%s\n", *e);
- }
- return (0);
+ for (e = environ; *e; e++) {
+ fprintf(stdout, "%s\n", *e);
+ }
+ return (0);
}
/* built-in 'exit' handler */
-static int shell_exit(struct job* cmd, struct jobSet* junk)
+static int shell_exit(struct job *cmd, struct jobSet *junk)
{
- if (!cmd->progs[0].argv[1] == 1)
- exit TRUE;
- else
- exit(atoi(cmd->progs[0].argv[1]));
+ if (!cmd->progs[0].argv[1] == 1)
+ exit TRUE;
+ else
+ exit(atoi(cmd->progs[0].argv[1]));
}
/* built-in 'fg' and 'bg' handler */
-static int shell_fg_bg(struct job* cmd, struct jobSet* jobList)
+static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
{
- int i, jobNum;
- struct job* job;
-
- if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
- fprintf(stderr, "%s: exactly one argument is expected\n",
- cmd->progs[0].argv[0]);
- return FALSE;
- }
-
- if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
- fprintf(stderr, "%s: bad argument '%s'\n",
- cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
- return FALSE;
- }
-
- for (job = jobList->head; job; job = job->next)
- if (job->jobId == jobNum) break;
-
- if (!job) {
- fprintf(stderr, "%s: unknown job %d\n",
- cmd->progs[0].argv[0], jobNum);
- return FALSE;
- }
-
- if (*cmd->progs[0].argv[0] == 'f') {
- /* Make this job the foreground job */
-
- if (tcsetpgrp(0, job->pgrp))
- perror("tcsetpgrp");
- jobList->fg = job;
- }
-
- /* Restart the processes in the job */
- for (i = 0; i < job->numProgs; i++)
- job->progs[i].isStopped = 0;
-
- kill(-job->pgrp, SIGCONT);
-
- job->stoppedProgs = 0;
-
- return TRUE;
+ int i, jobNum;
+ struct job *job;
+
+ if (!jobList->head) {
+ if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
+ fprintf(stderr, "%s: exactly one argument is expected\n",
+ cmd->progs[0].argv[0]);
+ return FALSE;
+ }
+ if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
+ fprintf(stderr, "%s: bad argument '%s'\n",
+ cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
+ return FALSE;
+ }
+ } else {
+ job = jobList->head;
+ }
+
+ for (job = jobList->head; job; job = job->next)
+ if (job->jobId == jobNum)
+ break;
+
+ if (!job) {
+ fprintf(stderr, "%s: unknown job %d\n",
+ cmd->progs[0].argv[0], jobNum);
+ return FALSE;
+ }
+
+ if (*cmd->progs[0].argv[0] == 'f') {
+ /* Make this job the foreground job */
+
+ if (tcsetpgrp(0, job->pgrp))
+ perror("tcsetpgrp");
+ jobList->fg = job;
+ }
+
+ /* Restart the processes in the job */
+ for (i = 0; i < job->numProgs; i++)
+ job->progs[i].isStopped = 0;
+
+ kill(-job->pgrp, SIGCONT);
+
+ job->stoppedProgs = 0;
+
+ return TRUE;
}
/* built-in 'help' handler */
-static int shell_help(struct job* cmd, struct jobSet* junk)
+static int shell_help(struct job *cmd, struct jobSet *junk)
{
- struct builtInCommand *x;
+ struct builtInCommand *x;
- fprintf(stdout, "\nBuilt-in commands:\n");
- fprintf(stdout, "-------------------\n");
- for ( x=bltins; x->cmd; x++) {
- fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
- }
- fprintf(stdout, "\n\n");
- return TRUE;
+ fprintf(stdout, "\nBuilt-in commands:\n");
+ fprintf(stdout, "-------------------\n");
+ for (x = bltins; x->cmd; x++) {
+ fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
+ }
+ fprintf(stdout, "\n\n");
+ return TRUE;
}
/* built-in 'jobs' handler */
-static int shell_jobs(struct job* dummy, struct jobSet* jobList)
+static int shell_jobs(struct job *dummy, struct jobSet *jobList)
{
- struct job * job;
- char * statusString;
- for (job = jobList->head; job; job = job->next) {
- if (job->runningProgs == job->stoppedProgs)
- statusString = "Stopped";
- else
- statusString = "Running";
-
- printf(JOB_STATUS_FORMAT, job->jobId, statusString,
- job->text);
- }
- return TRUE;
+ struct job *job;
+ char *statusString;
+ for (job = jobList->head; job; job = job->next) {
+ if (job->runningProgs == job->stoppedProgs)
+ statusString = "Stopped";
+ else
+ statusString = "Running";
+
+ printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
+ }
+ return TRUE;
}
/* built-in 'pwd' handler */
-static int shell_pwd(struct job* dummy, struct jobSet* junk)
+static int shell_pwd(struct job *dummy, struct jobSet *junk)
{
- getcwd(cwd, sizeof(cwd));
- fprintf(stdout, "%s\n", cwd);
- return TRUE;
+ getcwd(cwd, sizeof(cwd));
+ fprintf(stdout, "%s\n", cwd);
+ return TRUE;
}
/* built-in 'set VAR=value' handler */
-static int shell_set(struct job* cmd, struct jobSet* junk)
+static int shell_set(struct job *cmd, struct jobSet *junk)
{
- int res;
+ int res;
- if (!cmd->progs[0].argv[1] == 1) {
- return (shell_env(cmd, junk));
- }
- res = putenv(cmd->progs[0].argv[1]);
- if (res)
- fprintf(stdout, "set: %s\n", strerror(errno));
- return (res);
+ if (!cmd->progs[0].argv[1] == 1) {
+ return (shell_env(cmd, junk));
+ }
+ res = putenv(cmd->progs[0].argv[1]);
+ if (res)
+ fprintf(stdout, "set: %s\n", strerror(errno));
+ return (res);
}
/* Built-in '.' handler (read-in and execute commands from file) */
-static int shell_source(struct job* cmd, struct jobSet* junk)
+static int shell_source(struct job *cmd, struct jobSet *junk)
{
- FILE *input;
- int status;
-
- if (!cmd->progs[0].argv[1] == 1)
- return FALSE;
-
- input = fopen(cmd->progs[0].argv[1], "r");
- if (!input) {
- fprintf(stdout, "Couldn't open file '%s'\n", cmd->progs[0].argv[1]);
- return FALSE;
- }
+ FILE *input;
+ int status;
- /* Now run the file */
- status = busy_loop(input);
- return (status);
+ if (!cmd->progs[0].argv[1] == 1)
+ return FALSE;
+
+ input = fopen(cmd->progs[0].argv[1], "r");
+ if (!input) {
+ fprintf(stdout, "Couldn't open file '%s'\n",
+ cmd->progs[0].argv[1]);
+ return FALSE;
+ }
+
+ /* Now run the file */
+ status = busy_loop(input);
+ return (status);
}
/* built-in 'unset VAR' handler */
-static int shell_unset(struct job* cmd, struct jobSet* junk)
+static int shell_unset(struct job *cmd, struct jobSet *junk)
{
- if (!cmd->progs[0].argv[1] == 1) {
- fprintf(stdout, "unset: parameter required.\n");
- return FALSE;
- }
- unsetenv(cmd->progs[0].argv[1]);
- return TRUE;
+ if (!cmd->progs[0].argv[1] == 1) {
+ fprintf(stdout, "unset: parameter required.\n");
+ return FALSE;
+ }
+ unsetenv(cmd->progs[0].argv[1]);
+ return TRUE;
}
/* free up all memory from a job */
-static void freeJob(struct job * cmd)
+static void freeJob(struct job *cmd)
{
int i;
for (i = 0; i < cmd->numProgs; i++) {
- free(cmd->progs[i].argv);
- if (cmd->progs[i].redirections) free(cmd->progs[i].redirections);
- if (cmd->progs[i].freeGlob) globfree(&cmd->progs[i].globResult);
+ free(cmd->progs[i].argv);
+ if (cmd->progs[i].redirections)
+ free(cmd->progs[i].redirections);
+ if (cmd->progs[i].freeGlob)
+ globfree(&cmd->progs[i].globResult);
}
free(cmd->progs);
- if (cmd->text) free(cmd->text);
+ if (cmd->text)
+ free(cmd->text);
free(cmd->cmdBuf);
}
/* remove a job from the jobList */
-static void removeJob(struct jobSet * jobList, struct job * job)
+static void removeJob(struct jobSet *jobList, struct job *job)
{
- struct job * prevJob;
+ struct job *prevJob;
- freeJob(job);
+ freeJob(job);
if (job == jobList->head) {
- jobList->head = job->next;
+ jobList->head = job->next;
} else {
- prevJob = jobList->head;
- while (prevJob->next != job) prevJob = prevJob->next;
- prevJob->next = job->next;
+ prevJob = jobList->head;
+ while (prevJob->next != job)
+ prevJob = prevJob->next;
+ prevJob->next = job->next;
}
free(job);
@@ -333,56 +346,62 @@ static void removeJob(struct jobSet * jobList, struct job * job)
/* Checks to see if any background processes have exited -- if they
have, figure out why and see if a job has completed */
-static void checkJobs(struct jobSet * jobList)
+static void checkJobs(struct jobSet *jobList)
{
- struct job * job;
+ struct job *job;
pid_t childpid;
int status;
- int progNum=0;
-
+ int progNum = 0;
+
while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
- for (job = jobList->head; job; job = job->next) {
- progNum = 0;
- while (progNum < job->numProgs &&
- job->progs[progNum].pid != childpid)
- progNum++;
- if (progNum < job->numProgs) break;
- }
-
- if (WIFEXITED(status) || WIFSIGNALED(status)) {
- /* child exited */
- job->runningProgs--;
- job->progs[progNum].pid = 0;
-
- if (!job->runningProgs) {
- printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
- removeJob(jobList, job);
- }
- } else {
- /* child stopped */
- job->stoppedProgs++;
- job->progs[progNum].isStopped = 1;
-
- if (job->stoppedProgs == job->numProgs) {
- printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text);
- }
- }
+ for (job = jobList->head; job; job = job->next) {
+ progNum = 0;
+ while (progNum < job->numProgs &&
+ job->progs[progNum].pid != childpid) progNum++;
+ if (progNum < job->numProgs)
+ break;
+ }
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* child exited */
+ job->runningProgs--;
+ job->progs[progNum].pid = 0;
+
+ if (!job->runningProgs) {
+ printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
+ removeJob(jobList, job);
+ }
+ } else {
+ /* child stopped */
+ job->stoppedProgs++;
+ job->progs[progNum].isStopped = 1;
+
+ if (job->stoppedProgs == job->numProgs) {
+ printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
+ job->text);
+ }
+ }
}
if (childpid == -1 && errno != ECHILD)
- perror("waitpid");
+ perror("waitpid");
}
-static int getCommand(FILE * source, char * command)
+static int getCommand(FILE * source, char *command)
{
if (source == stdin) {
- fprintf(stdout, "%s %s", cwd, prompt);
- fflush(stdout);
+ fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
+ fflush(stdout);
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+ cmdedit_read_input(fileno(stdin), fileno(stdout), command);
+ return 0;
+#endif
}
- if (!fgets(command, MAX_COMMAND_LEN, source)) {
- if (source == stdin) printf("\n");
- return 1;
+ if (!fgets(command, BUFSIZ - 2, source)) {
+ if (source == stdin)
+ printf("\n");
+ return 1;
}
/* remove trailing newline */
@@ -391,46 +410,48 @@ static int getCommand(FILE * source, char * command)
return 0;
}
-static void globLastArgument(struct childProgram * prog, int * argcPtr,
- int * argcAllocedPtr)
+static void globLastArgument(struct childProgram *prog, int *argcPtr,
+ int *argcAllocedPtr)
{
int argc = *argcPtr;
int argcAlloced = *argcAllocedPtr;
int rc;
int flags;
int i;
- char * src, * dst;
+ char *src, *dst;
- if (argc > 1) { /* cmd->globResult is already initialized */
- flags = GLOB_APPEND;
- i = prog->globResult.gl_pathc;
+ if (argc > 1) { /* cmd->globResult is already initialized */
+ flags = GLOB_APPEND;
+ i = prog->globResult.gl_pathc;
} else {
- prog->freeGlob = 1;
- flags = 0;
- i = 0;
+ prog->freeGlob = 1;
+ flags = 0;
+ i = 0;
}
rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
if (rc == GLOB_NOSPACE) {
- fprintf(stderr, "out of space during glob operation\n");
- return;
- } else if (rc == GLOB_NOMATCH ||
- (!rc && (prog->globResult.gl_pathc - i) == 1 &&
- !strcmp(prog->argv[argc - 1],
- prog->globResult.gl_pathv[i]))) {
- /* we need to remove whatever \ quoting is still present */
- src = dst = prog->argv[argc - 1];
- while (*src) {
- if (*src != '\\') *dst++ = *src;
- src++;
- }
- *dst = '\0';
+ fprintf(stderr, "out of space during glob operation\n");
+ return;
+ } else if (rc == GLOB_NOMATCH ||
+ (!rc && (prog->globResult.gl_pathc - i) == 1 &&
+ !strcmp(prog->argv[argc - 1],
+ prog->globResult.gl_pathv[i]))) {
+ /* we need to remove whatever \ quoting is still present */
+ src = dst = prog->argv[argc - 1];
+ while (*src) {
+ if (*src != '\\')
+ *dst++ = *src;
+ src++;
+ }
+ *dst = '\0';
} else if (!rc) {
- argcAlloced += (prog->globResult.gl_pathc - i);
- prog->argv = realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
- memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
- sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
- argc += (prog->globResult.gl_pathc - i - 1);
+ argcAlloced += (prog->globResult.gl_pathc - i);
+ prog->argv =
+ realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
+ memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
+ sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
+ argc += (prog->globResult.gl_pathc - i - 1);
}
*argcAllocedPtr = argcAlloced;
@@ -442,27 +463,28 @@ static void globLastArgument(struct childProgram * prog, int * argcPtr,
the beginning of the next command (if the original command had more
then one job associated with it) or NULL if no more commands are
present. */
-static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
+static int parseCommand(char **commandPtr, struct job *job, int *isBg)
{
- char * command;
- char * returnCommand = NULL;
- char * src, * buf, * chptr;
+ char *command;
+ char *returnCommand = NULL;
+ char *src, *buf, *chptr;
int argc = 0;
int done = 0;
int argvAlloced;
int i;
- char quote = '\0';
+ char quote = '\0';
int count;
- struct childProgram * prog;
+ struct childProgram *prog;
/* skip leading white space */
- while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++;
-
- /* this handles empty lines or leading '#' characters */
- if (!**commandPtr || (**commandPtr=='#')) {
- job->numProgs = 0;
- *commandPtr = NULL;
- return 0;
+ while (**commandPtr && isspace(**commandPtr))
+ (*commandPtr)++;
+
+ /* this handles empty lines or leading '#' characters */
+ if (!**commandPtr || (**commandPtr == '#')) {
+ job->numProgs = 0;
+ *commandPtr = NULL;
+ return 0;
}
*isBg = 0;
@@ -491,177 +513,185 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
buf = command;
src = *commandPtr;
while (*src && !done) {
- if (quote == *src) {
- quote = '\0';
- } else if (quote) {
- if (*src == '\\') {
- src++;
- if (!*src) {
- fprintf(stderr, "character expected after \\\n");
- freeJob(job);
- return 1;
- }
-
- /* in shell, "\'" should yield \' */
- if (*src != quote) *buf++ = '\\';
- } else if (*src == '*' || *src == '?' || *src == '[' ||
- *src == ']')
- *buf++ = '\\';
- *buf++ = *src;
- } else if (isspace(*src)) {
- if (*prog->argv[argc]) {
- buf++, argc++;
- /* +1 here leaves room for the NULL which ends argv */
- if ((argc + 1) == argvAlloced) {
- argvAlloced += 5;
- prog->argv = realloc(prog->argv,
- sizeof(*prog->argv) * argvAlloced);
- }
- prog->argv[argc] = buf;
-
- globLastArgument(prog, &argc, &argvAlloced);
- }
- } else switch (*src) {
- case '"':
- case '\'':
- quote = *src;
- break;
-
- case '#': /* comment */
- done = 1;
- break;
-
- case '>': /* redirections */
- case '<':
- i = prog->numRedirections++;
- prog->redirections = realloc(prog->redirections,
- sizeof(*prog->redirections) * (i + 1));
-
- prog->redirections[i].fd = -1;
- if (buf != prog->argv[argc]) {
- /* the stuff before this character may be the file number
- being redirected */
- prog->redirections[i].fd = strtol(prog->argv[argc], &chptr, 10);
-
- if (*chptr && *prog->argv[argc]) {
- buf++, argc++;
- globLastArgument(prog, &argc, &argvAlloced);
- }
- }
-
- if (prog->redirections[i].fd == -1) {
- if (*src == '>')
- prog->redirections[i].fd = 1;
- else
- prog->redirections[i].fd = 0;
- }
-
- if (*src++ == '>') {
- if (*src == '>')
- prog->redirections[i].type = REDIRECT_APPEND, src++;
- else
- prog->redirections[i].type = REDIRECT_OVERWRITE;
- } else {
- prog->redirections[i].type = REDIRECT_INPUT;
- }
-
- /* This isn't POSIX sh compliant. Oh well. */
- chptr = src;
- while (isspace(*chptr)) chptr++;
-
- if (!*chptr) {
- fprintf(stderr, "file name expected after %c\n", *src);
- freeJob(job);
- return 1;
- }
-
- prog->redirections[i].filename = buf;
- while (*chptr && !isspace(*chptr))
- *buf++ = *chptr++;
-
- src = chptr - 1; /* we src++ later */
- prog->argv[argc] = ++buf;
- break;
-
- case '|': /* pipe */
- /* finish this command */
- if (*prog->argv[argc]) argc++;
- if (!argc) {
- fprintf(stderr, "empty command in pipe\n");
- freeJob(job);
- return 1;
- }
- prog->argv[argc] = NULL;
-
- /* and start the next */
- job->numProgs++;
- job->progs = realloc(job->progs,
- sizeof(*job->progs) * job->numProgs);
- prog = job->progs + (job->numProgs - 1);
- prog->numRedirections = 0;
- prog->redirections = NULL;
- prog->freeGlob = 0;
- argc = 0;
-
- argvAlloced = 5;
- prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
- prog->argv[0] = ++buf;
-
- src++;
- while (*src && isspace(*src)) src++;
-
- if (!*src) {
- fprintf(stderr, "empty command in pipe\n");
- return 1;
- }
- src--; /* we'll ++ it at the end of the loop */
-
- break;
-
- case '&': /* background */
- *isBg = 1;
- case ';': /* multiple commands */
- done = 1;
- returnCommand = *commandPtr + (src - *commandPtr) + 1;
- break;
-
- case '\\':
- src++;
- if (!*src) {
- freeJob(job);
- fprintf(stderr, "character expected after \\\n");
- return 1;
- }
- if (*src == '*' || *src == '[' || *src == ']' || *src == '?')
- *buf++ = '\\';
- /* fallthrough */
- default:
- *buf++ = *src;
- }
-
- src++;
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote) {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ fprintf(stderr, "character expected after \\\n");
+ freeJob(job);
+ return 1;
+ }
+
+ /* in shell, "\'" should yield \' */
+ if (*src != quote)
+ *buf++ = '\\';
+ } else if (*src == '*' || *src == '?' || *src == '[' ||
+ *src == ']') *buf++ = '\\';
+ *buf++ = *src;
+ } else if (isspace(*src)) {
+ if (*prog->argv[argc]) {
+ buf++, argc++;
+ /* +1 here leaves room for the NULL which ends argv */
+ if ((argc + 1) == argvAlloced) {
+ argvAlloced += 5;
+ prog->argv = realloc(prog->argv,
+ sizeof(*prog->argv) *
+ argvAlloced);
+ }
+ prog->argv[argc] = buf;
+
+ globLastArgument(prog, &argc, &argvAlloced);
+ }
+ } else
+ switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ break;
+
+ case '#': /* comment */
+ done = 1;
+ break;
+
+ case '>': /* redirections */
+ case '<':
+ i = prog->numRedirections++;
+ prog->redirections = realloc(prog->redirections,
+ sizeof(*prog->redirections) *
+ (i + 1));
+
+ prog->redirections[i].fd = -1;
+ if (buf != prog->argv[argc]) {
+ /* the stuff before this character may be the file number
+ being redirected */
+ prog->redirections[i].fd =
+ strtol(prog->argv[argc], &chptr, 10);
+
+ if (*chptr && *prog->argv[argc]) {
+ buf++, argc++;
+ globLastArgument(prog, &argc, &argvAlloced);
+ }
+ }
+
+ if (prog->redirections[i].fd == -1) {
+ if (*src == '>')
+ prog->redirections[i].fd = 1;
+ else
+ prog->redirections[i].fd = 0;
+ }
+
+ if (*src++ == '>') {
+ if (*src == '>')
+ prog->redirections[i].type =
+ REDIRECT_APPEND, src++;
+ else
+ prog->redirections[i].type = REDIRECT_OVERWRITE;
+ } else {
+ prog->redirections[i].type = REDIRECT_INPUT;
+ }
+
+ /* This isn't POSIX sh compliant. Oh well. */
+ chptr = src;
+ while (isspace(*chptr))
+ chptr++;
+
+ if (!*chptr) {
+ fprintf(stderr, "file name expected after %c\n", *src);
+ freeJob(job);
+ return 1;
+ }
+
+ prog->redirections[i].filename = buf;
+ while (*chptr && !isspace(*chptr))
+ *buf++ = *chptr++;
+
+ src = chptr - 1; /* we src++ later */
+ prog->argv[argc] = ++buf;
+ break;
+
+ case '|': /* pipe */
+ /* finish this command */
+ if (*prog->argv[argc])
+ argc++;
+ if (!argc) {
+ fprintf(stderr, "empty command in pipe\n");
+ freeJob(job);
+ return 1;
+ }
+ prog->argv[argc] = NULL;
+
+ /* and start the next */
+ job->numProgs++;
+ job->progs = realloc(job->progs,
+ sizeof(*job->progs) * job->numProgs);
+ prog = job->progs + (job->numProgs - 1);
+ prog->numRedirections = 0;
+ prog->redirections = NULL;
+ prog->freeGlob = 0;
+ argc = 0;
+
+ argvAlloced = 5;
+ prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
+ prog->argv[0] = ++buf;
+
+ src++;
+ while (*src && isspace(*src))
+ src++;
+
+ if (!*src) {
+ fprintf(stderr, "empty command in pipe\n");
+ return 1;
+ }
+ src--; /* we'll ++ it at the end of the loop */
+
+ break;
+
+ case '&': /* background */
+ *isBg = 1;
+ case ';': /* multiple commands */
+ done = 1;
+ returnCommand = *commandPtr + (src - *commandPtr) + 1;
+ break;
+
+ case '\\':
+ src++;
+ if (!*src) {
+ freeJob(job);
+ fprintf(stderr, "character expected after \\\n");
+ return 1;
+ }
+ if (*src == '*' || *src == '[' || *src == ']'
+ || *src == '?') *buf++ = '\\';
+ /* fallthrough */
+ default:
+ *buf++ = *src;
+ }
+
+ src++;
}
if (*prog->argv[argc]) {
- argc++;
- globLastArgument(prog, &argc, &argvAlloced);
+ argc++;
+ globLastArgument(prog, &argc, &argvAlloced);
}
if (!argc) {
- freeJob(job);
- return 0;
+ freeJob(job);
+ return 0;
}
prog->argv[argc] = NULL;
if (!returnCommand) {
- job->text = malloc(strlen(*commandPtr) + 1);
- strcpy(job->text, *commandPtr);
+ job->text = malloc(strlen(*commandPtr) + 1);
+ strcpy(job->text, *commandPtr);
} else {
- /* This leaves any trailing spaces, which is a bit sloppy */
+ /* This leaves any trailing spaces, which is a bit sloppy */
- count = returnCommand - *commandPtr;
- job->text = malloc(count + 1);
- strncpy(job->text, *commandPtr, count);
- job->text[count] = '\0';
+ count = returnCommand - *commandPtr;
+ job->text = malloc(count + 1);
+ strncpy(job->text, *commandPtr, count);
+ job->text[count] = '\0';
}
*commandPtr = returnCommand;
@@ -669,63 +699,64 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
return 0;
}
-static int runCommand(struct job newJob, struct jobSet * jobList,
- int inBg)
+static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
{
- struct job * job;
+ struct job *job;
int i;
int nextin, nextout;
- int pipefds[2]; /* pipefd[0] is for reading */
+ int pipefds[2]; /* pipefd[0] is for reading */
struct builtInCommand *x;
/* handle built-ins here -- we don't fork() so we can't background
these very easily */
- for( x=bltins ; x->cmd ; x++) {
- if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
- return(x->function(&newJob, jobList));
- }
+ for (x = bltins; x->cmd; x++) {
+ if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
+ return (x->function(&newJob, jobList));
+ }
}
nextin = 0, nextout = 1;
for (i = 0; i < newJob.numProgs; i++) {
- if ((i + 1) < newJob.numProgs) {
- pipe(pipefds);
- nextout = pipefds[1];
- } else {
- nextout = 1;
- }
-
- if (!(newJob.progs[i].pid = fork())) {
- signal(SIGTTOU, SIG_DFL);
-
- if (nextin != 0) {
- dup2(nextin, 0);
- close(nextin);
- }
-
- if (nextout != 1) {
- dup2(nextout, 1);
- close(nextout);
- }
-
- /* explicit redirections override pipes */
- setupRedirections(newJob.progs + i);
-
- execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
- fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0],
- strerror(errno));
- }
-
- /* put our child in the process group whose leader is the
- first process in this pipe */
- setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
-
- if (nextin != 0) close(nextin);
- if (nextout != 1) close(nextout);
-
- /* If there isn't another process, nextin is garbage
- but it doesn't matter */
- nextin = pipefds[0];
+ if ((i + 1) < newJob.numProgs) {
+ pipe(pipefds);
+ nextout = pipefds[1];
+ } else {
+ nextout = 1;
+ }
+
+ if (!(newJob.progs[i].pid = fork())) {
+ signal(SIGTTOU, SIG_DFL);
+
+ if (nextin != 0) {
+ dup2(nextin, 0);
+ close(nextin);
+ }
+
+ if (nextout != 1) {
+ dup2(nextout, 1);
+ close(nextout);
+ }
+
+ /* explicit redirections override pipes */
+ setupRedirections(newJob.progs + i);
+
+ execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
+ fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
+ strerror(errno));
+ }
+
+ /* put our child in the process group whose leader is the
+ first process in this pipe */
+ setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
+
+ if (nextin != 0)
+ close(nextin);
+ if (nextout != 1)
+ close(nextout);
+
+ /* If there isn't another process, nextin is garbage
+ but it doesn't matter */
+ nextin = pipefds[0];
}
newJob.pgrp = newJob.progs[0].pid;
@@ -733,16 +764,16 @@ static int runCommand(struct job newJob, struct jobSet * jobList,
/* find the ID for the job to use */
newJob.jobId = 1;
for (job = jobList->head; job; job = job->next)
- if (job->jobId >= newJob.jobId)
- newJob.jobId = job->jobId + 1;
+ if (job->jobId >= newJob.jobId)
+ newJob.jobId = job->jobId + 1;
/* add the job to the list of running jobs */
if (!jobList->head) {
- job = jobList->head = malloc(sizeof(*job));
+ job = jobList->head = malloc(sizeof(*job));
} else {
- for (job = jobList->head; job->next; job = job->next);
- job->next = malloc(sizeof(*job));
- job = job->next;
+ for (job = jobList->head; job->next; job = job->next);
+ job->next = malloc(sizeof(*job));
+ job = job->next;
}
*job = newJob;
@@ -751,165 +782,177 @@ static int runCommand(struct job newJob, struct jobSet * jobList,
job->stoppedProgs = 0;
if (inBg) {
- /* we don't wait for background jobs to return -- append it
- to the list of backgrounded jobs and leave it alone */
+ /* we don't wait for background jobs to return -- append it
+ to the list of backgrounded jobs and leave it alone */
- printf("[%d] %d\n", job->jobId,
- newJob.progs[newJob.numProgs - 1].pid);
+ printf("[%d] %d\n", job->jobId,
+ newJob.progs[newJob.numProgs - 1].pid);
} else {
- jobList->fg = job;
+ jobList->fg = job;
+
+ /* move the new process group into the foreground */
- /* move the new process group into the foreground */
-
- if (tcsetpgrp(0, newJob.pgrp))
- perror("tcsetpgrp");
+ if (tcsetpgrp(0, newJob.pgrp))
+ perror("tcsetpgrp");
}
return 0;
}
-static int setupRedirections(struct childProgram * prog)
+static int setupRedirections(struct childProgram *prog)
{
int i;
int openfd;
- int mode=O_RDONLY;
- struct redirectionSpecifier * redir = prog->redirections;
+ int mode = O_RDONLY;
+ struct redirectionSpecifier *redir = prog->redirections;
for (i = 0; i < prog->numRedirections; i++, redir++) {
- switch (redir->type) {
- case REDIRECT_INPUT:
- mode = O_RDONLY;
- break;
- case REDIRECT_OVERWRITE:
- mode = O_RDWR | O_CREAT | O_TRUNC;
- break;
- case REDIRECT_APPEND:
- mode = O_RDWR | O_CREAT | O_APPEND;
- break;
- }
-
- openfd = open(redir->filename, mode, 0666);
- if (openfd < 0) {
- /* this could get lost if stderr has been redirected, but
- bash and ash both lose it as well (though zsh doesn't!) */
- fprintf(stderr, "error opening %s: %s\n", redir->filename,
- strerror(errno));
- return 1;
- }
-
- if (openfd != redir->fd) {
- dup2(openfd, redir->fd);
- close(openfd);
- }
+ switch (redir->type) {
+ case REDIRECT_INPUT:
+ mode = O_RDONLY;
+ break;
+ case REDIRECT_OVERWRITE:
+ mode = O_RDWR | O_CREAT | O_TRUNC;
+ break;
+ case REDIRECT_APPEND:
+ mode = O_RDWR | O_CREAT | O_APPEND;
+ break;
+ }
+
+ openfd = open(redir->filename, mode, 0666);
+ if (openfd < 0) {
+ /* this could get lost if stderr has been redirected, but
+ bash and ash both lose it as well (though zsh doesn't!) */
+ fprintf(stderr, "error opening %s: %s\n", redir->filename,
+ strerror(errno));
+ return 1;
+ }
+
+ if (openfd != redir->fd) {
+ dup2(openfd, redir->fd);
+ close(openfd);
+ }
}
return 0;
}
-static int busy_loop(FILE * input)
+static int busy_loop(FILE * input)
{
- char command[MAX_COMMAND_LEN + 1];
- char * nextCommand = NULL;
+ char *command;
+ char *nextCommand = NULL;
struct jobSet jobList = { NULL, NULL };
struct job newJob;
int i;
int status;
int inBg;
+ command = (char*) calloc(BUFSIZ, sizeof(char));
+
/* don't pay any attention to this signal; it just confuses
things and isn't really meant for shells anyway */
signal(SIGTTOU, SIG_IGN);
-
+
while (1) {
- if (!jobList.fg) {
- /* no job is in the foreground */
-
- /* see if any background processes have exited */
- checkJobs(&jobList);
-
- if (!nextCommand) {
- if (getCommand(input, command)) break;
- nextCommand = command;
- }
-
- if (!parseCommand(&nextCommand, &newJob, &inBg) &&
- newJob.numProgs) {
- runCommand(newJob, &jobList, inBg);
- }
- } else {
- /* a job is running in the foreground; wait for it */
- i = 0;
- while (!jobList.fg->progs[i].pid ||
- jobList.fg->progs[i].isStopped) i++;
-
- waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
-
- if (WIFEXITED(status) || WIFSIGNALED(status)) {
- /* the child exited */
- jobList.fg->runningProgs--;
- jobList.fg->progs[i].pid = 0;
-
- if (!jobList.fg->runningProgs) {
- /* child exited */
-
- removeJob(&jobList, jobList.fg);
- jobList.fg = NULL;
-
- /* move the shell to the foreground */
- if (tcsetpgrp(0, getpid()))
- perror("tcsetpgrp");
- }
- } else {
- /* the child was stopped */
- jobList.fg->stoppedProgs++;
- jobList.fg->progs[i].isStopped = 1;
-
- if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
- printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
- "Stopped", jobList.fg->text);
- jobList.fg = NULL;
- }
- }
-
- if (!jobList.fg) {
- /* move the shell to the foreground */
- if (tcsetpgrp(0, getpid()))
- perror("tcsetpgrp");
- }
- }
- }
+ if (!jobList.fg) {
+ /* no job is in the foreground */
+
+ /* see if any background processes have exited */
+ checkJobs(&jobList);
+
+ if (!nextCommand) {
+ if (getCommand(input, command))
+ break;
+ nextCommand = command;
+ }
+
+ if (!parseCommand(&nextCommand, &newJob, &inBg) &&
+ newJob.numProgs) {
+ runCommand(newJob, &jobList, inBg);
+ }
+ } else {
+ /* a job is running in the foreground; wait for it */
+ i = 0;
+ while (!jobList.fg->progs[i].pid ||
+ jobList.fg->progs[i].isStopped) i++;
+
+ waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* the child exited */
+ jobList.fg->runningProgs--;
+ jobList.fg->progs[i].pid = 0;
+
+ if (!jobList.fg->runningProgs) {
+ /* child exited */
+
+ removeJob(&jobList, jobList.fg);
+ jobList.fg = NULL;
+
+ /* move the shell to the foreground */
+ if (tcsetpgrp(0, getpid()))
+ perror("tcsetpgrp");
+ }
+ } else {
+ /* the child was stopped */
+ jobList.fg->stoppedProgs++;
+ jobList.fg->progs[i].isStopped = 1;
+
+ if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
+ printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
+ "Stopped", jobList.fg->text);
+ jobList.fg = NULL;
+ }
+ }
+
+ if (!jobList.fg) {
+ /* move the shell to the foreground */
+ if (tcsetpgrp(0, getpid()))
+ perror("tcsetpgrp");
+ }
+ }
+ }
+ free( command);
return 0;
}
-int shell_main(int argc, char ** argv)
+int shell_main(int argc, char **argv)
{
- FILE * input = stdin;
+ FILE *input = stdin;
if (argc > 2) {
- usage( shell_usage);
- }
- /* initialize the cwd */
- getcwd(cwd, sizeof(cwd));
-
-
- //if (argv[0] && argv[0][0] == '-') {
- // shell_source("/etc/profile");
- //}
-
- if (argc < 2) {
- fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
- fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
- } else {
- input = fopen(argv[1], "r");
- if (!input)
- fatalError("A: Couldn't open file '%s': %s\n", argv[1], strerror(errno));
-// else
-// fatalError("Got it.\n");
- //exit(shell_source(argv[1]));
- }
-
- return (busy_loop( input));
+ usage(shell_usage);
+ }
+ /* initialize the cwd */
+ getcwd(cwd, sizeof(cwd));
+
+
+ //if (argv[0] && argv[0][0] == '-') {
+ // shell_source("/etc/profile");
+ //}
+
+ if (argc < 2) {
+ fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
+ BB_BT);
+ fprintf(stdout,
+ "Enter 'help' for a list of built-in commands.\n\n");
+ } else {
+ input = fopen(argv[1], "r");
+ if (!input)
+ fatalError("A: Couldn't open file '%s': %s\n", argv[1],
+ strerror(errno));
+// else
+// fatalError("Got it.\n");
+ //exit(shell_source(argv[1]));
+
+ /* Set terminal IO to canonical mode, and save old term settings. */
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+ cmdedit_init();
+#endif
+ }
+
+ return (busy_loop(input));
}
diff --git a/shell/lash.c b/shell/lash.c
index 9e467dc54..498f43779 100644
--- a/shell/lash.c
+++ b/shell/lash.c
@@ -39,293 +39,306 @@
#include <unistd.h>
-#define MAX_COMMAND_LEN 250 /* max length of a single command
- string */
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+#include "cmdedit.h"
+#endif
+
#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
-enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND };
+
+enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
+ REDIRECT_APPEND };
struct jobSet {
- struct job * head; /* head of list of running jobs */
- struct job * fg; /* current foreground job */
+ struct job *head; /* head of list of running jobs */
+ struct job *fg; /* current foreground job */
};
struct redirectionSpecifier {
- enum redirectionType type; /* type of redirection */
- int fd; /* file descriptor being redirected */
- char * filename; /* file to redirect fd to */
+ enum redirectionType type; /* type of redirection */
+ int fd; /* file descriptor being redirected */
+ char *filename; /* file to redirect fd to */
};
struct childProgram {
- pid_t pid; /* 0 if exited */
- char ** argv; /* program name and arguments */
- int numRedirections; /* elements in redirection array */
- struct redirectionSpecifier * redirections; /* I/O redirections */
- glob_t globResult; /* result of parameter globbing */
- int freeGlob; /* should we globfree(&globResult)? */
- int isStopped; /* is the program currently running? */
+ pid_t pid; /* 0 if exited */
+ char **argv; /* program name and arguments */
+ int numRedirections; /* elements in redirection array */
+ struct redirectionSpecifier *redirections; /* I/O redirections */
+ glob_t globResult; /* result of parameter globbing */
+ int freeGlob; /* should we globfree(&globResult)? */
+ int isStopped; /* is the program currently running? */
};
struct job {
- int jobId; /* job number */
- int numProgs; /* total number of programs in job */
- int runningProgs; /* number of programs running */
- char * text; /* name of job */
- char * cmdBuf; /* buffer various argv's point into */
- pid_t pgrp; /* process group ID for the job */
- struct childProgram * progs; /* array of programs in job */
- struct job * next; /* to track background commands */
- int stoppedProgs; /* number of programs alive, but stopped */
+ int jobId; /* job number */
+ int numProgs; /* total number of programs in job */
+ int runningProgs; /* number of programs running */
+ char *text; /* name of job */
+ char *cmdBuf; /* buffer various argv's point into */
+ pid_t pgrp; /* process group ID for the job */
+ struct childProgram *progs; /* array of programs in job */
+ struct job *next; /* to track background commands */
+ int stoppedProgs; /* number of programs alive, but stopped */
};
struct builtInCommand {
- char *cmd; /* name */
- char *descr; /* description */
- char *usage; /* usage */
- int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
+ char *cmd; /* name */
+ char *descr; /* description */
+ char *usage; /* usage */
+ int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
};
/* Some function prototypes */
-static int shell_cd(struct job* cmd, struct jobSet* junk);
-static int shell_env(struct job* dummy, struct jobSet* junk);
-static int shell_exit(struct job* cmd, struct jobSet* junk);
-static int shell_fg_bg(struct job* cmd, struct jobSet* jobList);
-static int shell_help(struct job* cmd, struct jobSet* junk);
-static int shell_jobs(struct job* dummy, struct jobSet* jobList);
-static int shell_pwd(struct job* dummy, struct jobSet* junk);
-static int shell_set(struct job* cmd, struct jobSet* junk);
-static int shell_source(struct job* cmd, struct jobSet* jobList);
-static int shell_unset(struct job* cmd, struct jobSet* junk);
-
-static void checkJobs(struct jobSet * jobList);
-static int getCommand(FILE * source, char * command);
-static int parseCommand(char ** commandPtr, struct job * job, int * isBg);
-static int setupRedirections(struct childProgram * prog);
-static int runCommand(struct job newJob, struct jobSet * jobList, int inBg);
+static int shell_cd(struct job *cmd, struct jobSet *junk);
+static int shell_env(struct job *dummy, struct jobSet *junk);
+static int shell_exit(struct job *cmd, struct jobSet *junk);
+static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
+static int shell_help(struct job *cmd, struct jobSet *junk);
+static int shell_jobs(struct job *dummy, struct jobSet *jobList);
+static int shell_pwd(struct job *dummy, struct jobSet *junk);
+static int shell_set(struct job *cmd, struct jobSet *junk);
+static int shell_source(struct job *cmd, struct jobSet *jobList);
+static int shell_unset(struct job *cmd, struct jobSet *junk);
+
+static void checkJobs(struct jobSet *jobList);
+static int getCommand(FILE * source, char *command);
+static int parseCommand(char **commandPtr, struct job *job, int *isBg);
+static int setupRedirections(struct childProgram *prog);
+static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
static int busy_loop(FILE * input);
-
+
/* Table of built-in functions */
static struct builtInCommand bltins[] = {
- {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
- {"cd", "Change working directory", "cd [dir]", shell_cd},
- {"env", "Print all environment variables", "env", shell_env},
- {"exit", "Exit from shell()", "exit", shell_exit},
- {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
- {"jobs", "Lists the active jobs", "jobs", shell_jobs},
- {"pwd", "Print current directory", "pwd", shell_pwd},
- {"set", "Set environment variable", "set [VAR=value]", shell_set},
- {"unset", "Unset environment variable", "unset VAR", shell_unset},
- //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
- {".", "Source-in and run commands in a file", ". filename", shell_source},
- {"help", "List shell built-in commands", "help", shell_help},
- {NULL, NULL, NULL, NULL}
+ {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
+ {"cd", "Change working directory", "cd [dir]", shell_cd},
+ //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
+ {"env", "Print all environment variables", "env", shell_env},
+ {"exit", "Exit from shell()", "exit", shell_exit},
+ {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
+ {"jobs", "Lists the active jobs", "jobs", shell_jobs},
+ {"pwd", "Print current directory", "pwd", shell_pwd},
+ {"set", "Set environment variable", "set [VAR=value]", shell_set},
+ {"unset", "Unset environment variable", "unset VAR", shell_unset},
+ {".", "Source-in and run commands in a file", ". filename",
+ shell_source},
+ {"help", "List shell built-in commands", "help", shell_help},
+ {NULL, NULL, NULL, NULL}
};
static const char shell_usage[] =
- "sh [FILE]...\n\n"
- "The BusyBox command interpreter (shell).\n\n";
+ "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
static char cwd[1024];
static char *prompt = "# ";
+
/* built-in 'cd <path>' handler */
-static int shell_cd(struct job* cmd, struct jobSet* junk)
+static int shell_cd(struct job *cmd, struct jobSet *junk)
{
- char *newdir;
- if (!cmd->progs[0].argv[1] == 1)
- newdir = getenv("HOME");
- else
- newdir = cmd->progs[0].argv[1];
- if (chdir(newdir)) {
- printf("cd: %s: %s\n", newdir, strerror(errno));
- return FALSE;
- }
- getcwd(cwd, sizeof(cwd));
+ char *newdir;
+ if (!cmd->progs[0].argv[1] == 1)
+ newdir = getenv("HOME");
+ else
+ newdir = cmd->progs[0].argv[1];
+ if (chdir(newdir)) {
+ printf("cd: %s: %s\n", newdir, strerror(errno));
+ return FALSE;
+ }
+ getcwd(cwd, sizeof(cwd));
- return TRUE;
+ return TRUE;
}
/* built-in 'env' handler */
-static int shell_env(struct job* dummy, struct jobSet* junk)
+static int shell_env(struct job *dummy, struct jobSet *junk)
{
- char **e;
+ char **e;
- for (e = environ ; *e ; e++) {
- fprintf(stdout, "%s\n", *e);
- }
- return (0);
+ for (e = environ; *e; e++) {
+ fprintf(stdout, "%s\n", *e);
+ }
+ return (0);
}
/* built-in 'exit' handler */
-static int shell_exit(struct job* cmd, struct jobSet* junk)
+static int shell_exit(struct job *cmd, struct jobSet *junk)
{
- if (!cmd->progs[0].argv[1] == 1)
- exit TRUE;
- else
- exit(atoi(cmd->progs[0].argv[1]));
+ if (!cmd->progs[0].argv[1] == 1)
+ exit TRUE;
+ else
+ exit(atoi(cmd->progs[0].argv[1]));
}
/* built-in 'fg' and 'bg' handler */
-static int shell_fg_bg(struct job* cmd, struct jobSet* jobList)
+static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
{
- int i, jobNum;
- struct job* job;
-
- if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
- fprintf(stderr, "%s: exactly one argument is expected\n",
- cmd->progs[0].argv[0]);
- return FALSE;
- }
-
- if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
- fprintf(stderr, "%s: bad argument '%s'\n",
- cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
- return FALSE;
- }
-
- for (job = jobList->head; job; job = job->next)
- if (job->jobId == jobNum) break;
-
- if (!job) {
- fprintf(stderr, "%s: unknown job %d\n",
- cmd->progs[0].argv[0], jobNum);
- return FALSE;
- }
-
- if (*cmd->progs[0].argv[0] == 'f') {
- /* Make this job the foreground job */
-
- if (tcsetpgrp(0, job->pgrp))
- perror("tcsetpgrp");
- jobList->fg = job;
- }
-
- /* Restart the processes in the job */
- for (i = 0; i < job->numProgs; i++)
- job->progs[i].isStopped = 0;
-
- kill(-job->pgrp, SIGCONT);
-
- job->stoppedProgs = 0;
-
- return TRUE;
+ int i, jobNum;
+ struct job *job;
+
+ if (!jobList->head) {
+ if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
+ fprintf(stderr, "%s: exactly one argument is expected\n",
+ cmd->progs[0].argv[0]);
+ return FALSE;
+ }
+ if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
+ fprintf(stderr, "%s: bad argument '%s'\n",
+ cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
+ return FALSE;
+ }
+ } else {
+ job = jobList->head;
+ }
+
+ for (job = jobList->head; job; job = job->next)
+ if (job->jobId == jobNum)
+ break;
+
+ if (!job) {
+ fprintf(stderr, "%s: unknown job %d\n",
+ cmd->progs[0].argv[0], jobNum);
+ return FALSE;
+ }
+
+ if (*cmd->progs[0].argv[0] == 'f') {
+ /* Make this job the foreground job */
+
+ if (tcsetpgrp(0, job->pgrp))
+ perror("tcsetpgrp");
+ jobList->fg = job;
+ }
+
+ /* Restart the processes in the job */
+ for (i = 0; i < job->numProgs; i++)
+ job->progs[i].isStopped = 0;
+
+ kill(-job->pgrp, SIGCONT);
+
+ job->stoppedProgs = 0;
+
+ return TRUE;
}
/* built-in 'help' handler */
-static int shell_help(struct job* cmd, struct jobSet* junk)
+static int shell_help(struct job *cmd, struct jobSet *junk)
{
- struct builtInCommand *x;
+ struct builtInCommand *x;
- fprintf(stdout, "\nBuilt-in commands:\n");
- fprintf(stdout, "-------------------\n");
- for ( x=bltins; x->cmd; x++) {
- fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
- }
- fprintf(stdout, "\n\n");
- return TRUE;
+ fprintf(stdout, "\nBuilt-in commands:\n");
+ fprintf(stdout, "-------------------\n");
+ for (x = bltins; x->cmd; x++) {
+ fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
+ }
+ fprintf(stdout, "\n\n");
+ return TRUE;
}
/* built-in 'jobs' handler */
-static int shell_jobs(struct job* dummy, struct jobSet* jobList)
+static int shell_jobs(struct job *dummy, struct jobSet *jobList)
{
- struct job * job;
- char * statusString;
- for (job = jobList->head; job; job = job->next) {
- if (job->runningProgs == job->stoppedProgs)
- statusString = "Stopped";
- else
- statusString = "Running";
-
- printf(JOB_STATUS_FORMAT, job->jobId, statusString,
- job->text);
- }
- return TRUE;
+ struct job *job;
+ char *statusString;
+ for (job = jobList->head; job; job = job->next) {
+ if (job->runningProgs == job->stoppedProgs)
+ statusString = "Stopped";
+ else
+ statusString = "Running";
+
+ printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
+ }
+ return TRUE;
}
/* built-in 'pwd' handler */
-static int shell_pwd(struct job* dummy, struct jobSet* junk)
+static int shell_pwd(struct job *dummy, struct jobSet *junk)
{
- getcwd(cwd, sizeof(cwd));
- fprintf(stdout, "%s\n", cwd);
- return TRUE;
+ getcwd(cwd, sizeof(cwd));
+ fprintf(stdout, "%s\n", cwd);
+ return TRUE;
}
/* built-in 'set VAR=value' handler */
-static int shell_set(struct job* cmd, struct jobSet* junk)
+static int shell_set(struct job *cmd, struct jobSet *junk)
{
- int res;
+ int res;
- if (!cmd->progs[0].argv[1] == 1) {
- return (shell_env(cmd, junk));
- }
- res = putenv(cmd->progs[0].argv[1]);
- if (res)
- fprintf(stdout, "set: %s\n", strerror(errno));
- return (res);
+ if (!cmd->progs[0].argv[1] == 1) {
+ return (shell_env(cmd, junk));
+ }
+ res = putenv(cmd->progs[0].argv[1]);
+ if (res)
+ fprintf(stdout, "set: %s\n", strerror(errno));
+ return (res);
}
/* Built-in '.' handler (read-in and execute commands from file) */
-static int shell_source(struct job* cmd, struct jobSet* junk)
+static int shell_source(struct job *cmd, struct jobSet *junk)
{
- FILE *input;
- int status;
-
- if (!cmd->progs[0].argv[1] == 1)
- return FALSE;
-
- input = fopen(cmd->progs[0].argv[1], "r");
- if (!input) {
- fprintf(stdout, "Couldn't open file '%s'\n", cmd->progs[0].argv[1]);
- return FALSE;
- }
+ FILE *input;
+ int status;
- /* Now run the file */
- status = busy_loop(input);
- return (status);
+ if (!cmd->progs[0].argv[1] == 1)
+ return FALSE;
+
+ input = fopen(cmd->progs[0].argv[1], "r");
+ if (!input) {
+ fprintf(stdout, "Couldn't open file '%s'\n",
+ cmd->progs[0].argv[1]);
+ return FALSE;
+ }
+
+ /* Now run the file */
+ status = busy_loop(input);
+ return (status);
}
/* built-in 'unset VAR' handler */
-static int shell_unset(struct job* cmd, struct jobSet* junk)
+static int shell_unset(struct job *cmd, struct jobSet *junk)
{
- if (!cmd->progs[0].argv[1] == 1) {
- fprintf(stdout, "unset: parameter required.\n");
- return FALSE;
- }
- unsetenv(cmd->progs[0].argv[1]);
- return TRUE;
+ if (!cmd->progs[0].argv[1] == 1) {
+ fprintf(stdout, "unset: parameter required.\n");
+ return FALSE;
+ }
+ unsetenv(cmd->progs[0].argv[1]);
+ return TRUE;
}
/* free up all memory from a job */
-static void freeJob(struct job * cmd)
+static void freeJob(struct job *cmd)
{
int i;
for (i = 0; i < cmd->numProgs; i++) {
- free(cmd->progs[i].argv);
- if (cmd->progs[i].redirections) free(cmd->progs[i].redirections);
- if (cmd->progs[i].freeGlob) globfree(&cmd->progs[i].globResult);
+ free(cmd->progs[i].argv);
+ if (cmd->progs[i].redirections)
+ free(cmd->progs[i].redirections);
+ if (cmd->progs[i].freeGlob)
+ globfree(&cmd->progs[i].globResult);
}
free(cmd->progs);
- if (cmd->text) free(cmd->text);
+ if (cmd->text)
+ free(cmd->text);
free(cmd->cmdBuf);
}
/* remove a job from the jobList */
-static void removeJob(struct jobSet * jobList, struct job * job)
+static void removeJob(struct jobSet *jobList, struct job *job)
{
- struct job * prevJob;
+ struct job *prevJob;
- freeJob(job);
+ freeJob(job);
if (job == jobList->head) {
- jobList->head = job->next;
+ jobList->head = job->next;
} else {
- prevJob = jobList->head;
- while (prevJob->next != job) prevJob = prevJob->next;
- prevJob->next = job->next;
+ prevJob = jobList->head;
+ while (prevJob->next != job)
+ prevJob = prevJob->next;
+ prevJob->next = job->next;
}
free(job);
@@ -333,56 +346,62 @@ static void removeJob(struct jobSet * jobList, struct job * job)
/* Checks to see if any background processes have exited -- if they
have, figure out why and see if a job has completed */
-static void checkJobs(struct jobSet * jobList)
+static void checkJobs(struct jobSet *jobList)
{
- struct job * job;
+ struct job *job;
pid_t childpid;
int status;
- int progNum=0;
-
+ int progNum = 0;
+
while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
- for (job = jobList->head; job; job = job->next) {
- progNum = 0;
- while (progNum < job->numProgs &&
- job->progs[progNum].pid != childpid)
- progNum++;
- if (progNum < job->numProgs) break;
- }
-
- if (WIFEXITED(status) || WIFSIGNALED(status)) {
- /* child exited */
- job->runningProgs--;
- job->progs[progNum].pid = 0;
-
- if (!job->runningProgs) {
- printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
- removeJob(jobList, job);
- }
- } else {
- /* child stopped */
- job->stoppedProgs++;
- job->progs[progNum].isStopped = 1;
-
- if (job->stoppedProgs == job->numProgs) {
- printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text);
- }
- }
+ for (job = jobList->head; job; job = job->next) {
+ progNum = 0;
+ while (progNum < job->numProgs &&
+ job->progs[progNum].pid != childpid) progNum++;
+ if (progNum < job->numProgs)
+ break;
+ }
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* child exited */
+ job->runningProgs--;
+ job->progs[progNum].pid = 0;
+
+ if (!job->runningProgs) {
+ printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
+ removeJob(jobList, job);
+ }
+ } else {
+ /* child stopped */
+ job->stoppedProgs++;
+ job->progs[progNum].isStopped = 1;
+
+ if (job->stoppedProgs == job->numProgs) {
+ printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
+ job->text);
+ }
+ }
}
if (childpid == -1 && errno != ECHILD)
- perror("waitpid");
+ perror("waitpid");
}
-static int getCommand(FILE * source, char * command)
+static int getCommand(FILE * source, char *command)
{
if (source == stdin) {
- fprintf(stdout, "%s %s", cwd, prompt);
- fflush(stdout);
+ fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
+ fflush(stdout);
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+ cmdedit_read_input(fileno(stdin), fileno(stdout), command);
+ return 0;
+#endif
}
- if (!fgets(command, MAX_COMMAND_LEN, source)) {
- if (source == stdin) printf("\n");
- return 1;
+ if (!fgets(command, BUFSIZ - 2, source)) {
+ if (source == stdin)
+ printf("\n");
+ return 1;
}
/* remove trailing newline */
@@ -391,46 +410,48 @@ static int getCommand(FILE * source, char * command)
return 0;
}
-static void globLastArgument(struct childProgram * prog, int * argcPtr,
- int * argcAllocedPtr)
+static void globLastArgument(struct childProgram *prog, int *argcPtr,
+ int *argcAllocedPtr)
{
int argc = *argcPtr;
int argcAlloced = *argcAllocedPtr;
int rc;
int flags;
int i;
- char * src, * dst;
+ char *src, *dst;
- if (argc > 1) { /* cmd->globResult is already initialized */
- flags = GLOB_APPEND;
- i = prog->globResult.gl_pathc;
+ if (argc > 1) { /* cmd->globResult is already initialized */
+ flags = GLOB_APPEND;
+ i = prog->globResult.gl_pathc;
} else {
- prog->freeGlob = 1;
- flags = 0;
- i = 0;
+ prog->freeGlob = 1;
+ flags = 0;
+ i = 0;
}
rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
if (rc == GLOB_NOSPACE) {
- fprintf(stderr, "out of space during glob operation\n");
- return;
- } else if (rc == GLOB_NOMATCH ||
- (!rc && (prog->globResult.gl_pathc - i) == 1 &&
- !strcmp(prog->argv[argc - 1],
- prog->globResult.gl_pathv[i]))) {
- /* we need to remove whatever \ quoting is still present */
- src = dst = prog->argv[argc - 1];
- while (*src) {
- if (*src != '\\') *dst++ = *src;
- src++;
- }
- *dst = '\0';
+ fprintf(stderr, "out of space during glob operation\n");
+ return;
+ } else if (rc == GLOB_NOMATCH ||
+ (!rc && (prog->globResult.gl_pathc - i) == 1 &&
+ !strcmp(prog->argv[argc - 1],
+ prog->globResult.gl_pathv[i]))) {
+ /* we need to remove whatever \ quoting is still present */
+ src = dst = prog->argv[argc - 1];
+ while (*src) {
+ if (*src != '\\')
+ *dst++ = *src;
+ src++;
+ }
+ *dst = '\0';
} else if (!rc) {
- argcAlloced += (prog->globResult.gl_pathc - i);
- prog->argv = realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
- memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
- sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
- argc += (prog->globResult.gl_pathc - i - 1);
+ argcAlloced += (prog->globResult.gl_pathc - i);
+ prog->argv =
+ realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
+ memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
+ sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
+ argc += (prog->globResult.gl_pathc - i - 1);
}
*argcAllocedPtr = argcAlloced;
@@ -442,27 +463,28 @@ static void globLastArgument(struct childProgram * prog, int * argcPtr,
the beginning of the next command (if the original command had more
then one job associated with it) or NULL if no more commands are
present. */
-static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
+static int parseCommand(char **commandPtr, struct job *job, int *isBg)
{
- char * command;
- char * returnCommand = NULL;
- char * src, * buf, * chptr;
+ char *command;
+ char *returnCommand = NULL;
+ char *src, *buf, *chptr;
int argc = 0;
int done = 0;
int argvAlloced;
int i;
- char quote = '\0';
+ char quote = '\0';
int count;
- struct childProgram * prog;
+ struct childProgram *prog;
/* skip leading white space */
- while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++;
-
- /* this handles empty lines or leading '#' characters */
- if (!**commandPtr || (**commandPtr=='#')) {
- job->numProgs = 0;
- *commandPtr = NULL;
- return 0;
+ while (**commandPtr && isspace(**commandPtr))
+ (*commandPtr)++;
+
+ /* this handles empty lines or leading '#' characters */
+ if (!**commandPtr || (**commandPtr == '#')) {
+ job->numProgs = 0;
+ *commandPtr = NULL;
+ return 0;
}
*isBg = 0;
@@ -491,177 +513,185 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
buf = command;
src = *commandPtr;
while (*src && !done) {
- if (quote == *src) {
- quote = '\0';
- } else if (quote) {
- if (*src == '\\') {
- src++;
- if (!*src) {
- fprintf(stderr, "character expected after \\\n");
- freeJob(job);
- return 1;
- }
-
- /* in shell, "\'" should yield \' */
- if (*src != quote) *buf++ = '\\';
- } else if (*src == '*' || *src == '?' || *src == '[' ||
- *src == ']')
- *buf++ = '\\';
- *buf++ = *src;
- } else if (isspace(*src)) {
- if (*prog->argv[argc]) {
- buf++, argc++;
- /* +1 here leaves room for the NULL which ends argv */
- if ((argc + 1) == argvAlloced) {
- argvAlloced += 5;
- prog->argv = realloc(prog->argv,
- sizeof(*prog->argv) * argvAlloced);
- }
- prog->argv[argc] = buf;
-
- globLastArgument(prog, &argc, &argvAlloced);
- }
- } else switch (*src) {
- case '"':
- case '\'':
- quote = *src;
- break;
-
- case '#': /* comment */
- done = 1;
- break;
-
- case '>': /* redirections */
- case '<':
- i = prog->numRedirections++;
- prog->redirections = realloc(prog->redirections,
- sizeof(*prog->redirections) * (i + 1));
-
- prog->redirections[i].fd = -1;
- if (buf != prog->argv[argc]) {
- /* the stuff before this character may be the file number
- being redirected */
- prog->redirections[i].fd = strtol(prog->argv[argc], &chptr, 10);
-
- if (*chptr && *prog->argv[argc]) {
- buf++, argc++;
- globLastArgument(prog, &argc, &argvAlloced);
- }
- }
-
- if (prog->redirections[i].fd == -1) {
- if (*src == '>')
- prog->redirections[i].fd = 1;
- else
- prog->redirections[i].fd = 0;
- }
-
- if (*src++ == '>') {
- if (*src == '>')
- prog->redirections[i].type = REDIRECT_APPEND, src++;
- else
- prog->redirections[i].type = REDIRECT_OVERWRITE;
- } else {
- prog->redirections[i].type = REDIRECT_INPUT;
- }
-
- /* This isn't POSIX sh compliant. Oh well. */
- chptr = src;
- while (isspace(*chptr)) chptr++;
-
- if (!*chptr) {
- fprintf(stderr, "file name expected after %c\n", *src);
- freeJob(job);
- return 1;
- }
-
- prog->redirections[i].filename = buf;
- while (*chptr && !isspace(*chptr))
- *buf++ = *chptr++;
-
- src = chptr - 1; /* we src++ later */
- prog->argv[argc] = ++buf;
- break;
-
- case '|': /* pipe */
- /* finish this command */
- if (*prog->argv[argc]) argc++;
- if (!argc) {
- fprintf(stderr, "empty command in pipe\n");
- freeJob(job);
- return 1;
- }
- prog->argv[argc] = NULL;
-
- /* and start the next */
- job->numProgs++;
- job->progs = realloc(job->progs,
- sizeof(*job->progs) * job->numProgs);
- prog = job->progs + (job->numProgs - 1);
- prog->numRedirections = 0;
- prog->redirections = NULL;
- prog->freeGlob = 0;
- argc = 0;
-
- argvAlloced = 5;
- prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
- prog->argv[0] = ++buf;
-
- src++;
- while (*src && isspace(*src)) src++;
-
- if (!*src) {
- fprintf(stderr, "empty command in pipe\n");
- return 1;
- }
- src--; /* we'll ++ it at the end of the loop */
-
- break;
-
- case '&': /* background */
- *isBg = 1;
- case ';': /* multiple commands */
- done = 1;
- returnCommand = *commandPtr + (src - *commandPtr) + 1;
- break;
-
- case '\\':
- src++;
- if (!*src) {
- freeJob(job);
- fprintf(stderr, "character expected after \\\n");
- return 1;
- }
- if (*src == '*' || *src == '[' || *src == ']' || *src == '?')
- *buf++ = '\\';
- /* fallthrough */
- default:
- *buf++ = *src;
- }
-
- src++;
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote) {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ fprintf(stderr, "character expected after \\\n");
+ freeJob(job);
+ return 1;
+ }
+
+ /* in shell, "\'" should yield \' */
+ if (*src != quote)
+ *buf++ = '\\';
+ } else if (*src == '*' || *src == '?' || *src == '[' ||
+ *src == ']') *buf++ = '\\';
+ *buf++ = *src;
+ } else if (isspace(*src)) {
+ if (*prog->argv[argc]) {
+ buf++, argc++;
+ /* +1 here leaves room for the NULL which ends argv */
+ if ((argc + 1) == argvAlloced) {
+ argvAlloced += 5;
+ prog->argv = realloc(prog->argv,
+ sizeof(*prog->argv) *
+ argvAlloced);
+ }
+ prog->argv[argc] = buf;
+
+ globLastArgument(prog, &argc, &argvAlloced);
+ }
+ } else
+ switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ break;
+
+ case '#': /* comment */
+ done = 1;
+ break;
+
+ case '>': /* redirections */
+ case '<':
+ i = prog->numRedirections++;
+ prog->redirections = realloc(prog->redirections,
+ sizeof(*prog->redirections) *
+ (i + 1));
+
+ prog->redirections[i].fd = -1;
+ if (buf != prog->argv[argc]) {
+ /* the stuff before this character may be the file number
+ being redirected */
+ prog->redirections[i].fd =
+ strtol(prog->argv[argc], &chptr, 10);
+
+ if (*chptr && *prog->argv[argc]) {
+ buf++, argc++;
+ globLastArgument(prog, &argc, &argvAlloced);
+ }
+ }
+
+ if (prog->redirections[i].fd == -1) {
+ if (*src == '>')
+ prog->redirections[i].fd = 1;
+ else
+ prog->redirections[i].fd = 0;
+ }
+
+ if (*src++ == '>') {
+ if (*src == '>')
+ prog->redirections[i].type =
+ REDIRECT_APPEND, src++;
+ else
+ prog->redirections[i].type = REDIRECT_OVERWRITE;
+ } else {
+ prog->redirections[i].type = REDIRECT_INPUT;
+ }
+
+ /* This isn't POSIX sh compliant. Oh well. */
+ chptr = src;
+ while (isspace(*chptr))
+ chptr++;
+
+ if (!*chptr) {
+ fprintf(stderr, "file name expected after %c\n", *src);
+ freeJob(job);
+ return 1;
+ }
+
+ prog->redirections[i].filename = buf;
+ while (*chptr && !isspace(*chptr))
+ *buf++ = *chptr++;
+
+ src = chptr - 1; /* we src++ later */
+ prog->argv[argc] = ++buf;
+ break;
+
+ case '|': /* pipe */
+ /* finish this command */
+ if (*prog->argv[argc])
+ argc++;
+ if (!argc) {
+ fprintf(stderr, "empty command in pipe\n");
+ freeJob(job);
+ return 1;
+ }
+ prog->argv[argc] = NULL;
+
+ /* and start the next */
+ job->numProgs++;
+ job->progs = realloc(job->progs,
+ sizeof(*job->progs) * job->numProgs);
+ prog = job->progs + (job->numProgs - 1);
+ prog->numRedirections = 0;
+ prog->redirections = NULL;
+ prog->freeGlob = 0;
+ argc = 0;
+
+ argvAlloced = 5;
+ prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
+ prog->argv[0] = ++buf;
+
+ src++;
+ while (*src && isspace(*src))
+ src++;
+
+ if (!*src) {
+ fprintf(stderr, "empty command in pipe\n");
+ return 1;
+ }
+ src--; /* we'll ++ it at the end of the loop */
+
+ break;
+
+ case '&': /* background */
+ *isBg = 1;
+ case ';': /* multiple commands */
+ done = 1;
+ returnCommand = *commandPtr + (src - *commandPtr) + 1;
+ break;
+
+ case '\\':
+ src++;
+ if (!*src) {
+ freeJob(job);
+ fprintf(stderr, "character expected after \\\n");
+ return 1;
+ }
+ if (*src == '*' || *src == '[' || *src == ']'
+ || *src == '?') *buf++ = '\\';
+ /* fallthrough */
+ default:
+ *buf++ = *src;
+ }
+
+ src++;
}
if (*prog->argv[argc]) {
- argc++;
- globLastArgument(prog, &argc, &argvAlloced);
+ argc++;
+ globLastArgument(prog, &argc, &argvAlloced);
}
if (!argc) {
- freeJob(job);
- return 0;
+ freeJob(job);
+ return 0;
}
prog->argv[argc] = NULL;
if (!returnCommand) {
- job->text = malloc(strlen(*commandPtr) + 1);
- strcpy(job->text, *commandPtr);
+ job->text = malloc(strlen(*commandPtr) + 1);
+ strcpy(job->text, *commandPtr);
} else {
- /* This leaves any trailing spaces, which is a bit sloppy */
+ /* This leaves any trailing spaces, which is a bit sloppy */
- count = returnCommand - *commandPtr;
- job->text = malloc(count + 1);
- strncpy(job->text, *commandPtr, count);
- job->text[count] = '\0';
+ count = returnCommand - *commandPtr;
+ job->text = malloc(count + 1);
+ strncpy(job->text, *commandPtr, count);
+ job->text[count] = '\0';
}
*commandPtr = returnCommand;
@@ -669,63 +699,64 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
return 0;
}
-static int runCommand(struct job newJob, struct jobSet * jobList,
- int inBg)
+static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
{
- struct job * job;
+ struct job *job;
int i;
int nextin, nextout;
- int pipefds[2]; /* pipefd[0] is for reading */
+ int pipefds[2]; /* pipefd[0] is for reading */
struct builtInCommand *x;
/* handle built-ins here -- we don't fork() so we can't background
these very easily */
- for( x=bltins ; x->cmd ; x++) {
- if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
- return(x->function(&newJob, jobList));
- }
+ for (x = bltins; x->cmd; x++) {
+ if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
+ return (x->function(&newJob, jobList));
+ }
}
nextin = 0, nextout = 1;
for (i = 0; i < newJob.numProgs; i++) {
- if ((i + 1) < newJob.numProgs) {
- pipe(pipefds);
- nextout = pipefds[1];
- } else {
- nextout = 1;
- }
-
- if (!(newJob.progs[i].pid = fork())) {
- signal(SIGTTOU, SIG_DFL);
-
- if (nextin != 0) {
- dup2(nextin, 0);
- close(nextin);
- }
-
- if (nextout != 1) {
- dup2(nextout, 1);
- close(nextout);
- }
-
- /* explicit redirections override pipes */
- setupRedirections(newJob.progs + i);
-
- execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
- fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0],
- strerror(errno));
- }
-
- /* put our child in the process group whose leader is the
- first process in this pipe */
- setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
-
- if (nextin != 0) close(nextin);
- if (nextout != 1) close(nextout);
-
- /* If there isn't another process, nextin is garbage
- but it doesn't matter */
- nextin = pipefds[0];
+ if ((i + 1) < newJob.numProgs) {
+ pipe(pipefds);
+ nextout = pipefds[1];
+ } else {
+ nextout = 1;
+ }
+
+ if (!(newJob.progs[i].pid = fork())) {
+ signal(SIGTTOU, SIG_DFL);
+
+ if (nextin != 0) {
+ dup2(nextin, 0);
+ close(nextin);
+ }
+
+ if (nextout != 1) {
+ dup2(nextout, 1);
+ close(nextout);
+ }
+
+ /* explicit redirections override pipes */
+ setupRedirections(newJob.progs + i);
+
+ execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
+ fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
+ strerror(errno));
+ }
+
+ /* put our child in the process group whose leader is the
+ first process in this pipe */
+ setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
+
+ if (nextin != 0)
+ close(nextin);
+ if (nextout != 1)
+ close(nextout);
+
+ /* If there isn't another process, nextin is garbage
+ but it doesn't matter */
+ nextin = pipefds[0];
}
newJob.pgrp = newJob.progs[0].pid;
@@ -733,16 +764,16 @@ static int runCommand(struct job newJob, struct jobSet * jobList,
/* find the ID for the job to use */
newJob.jobId = 1;
for (job = jobList->head; job; job = job->next)
- if (job->jobId >= newJob.jobId)
- newJob.jobId = job->jobId + 1;
+ if (job->jobId >= newJob.jobId)
+ newJob.jobId = job->jobId + 1;
/* add the job to the list of running jobs */
if (!jobList->head) {
- job = jobList->head = malloc(sizeof(*job));
+ job = jobList->head = malloc(sizeof(*job));
} else {
- for (job = jobList->head; job->next; job = job->next);
- job->next = malloc(sizeof(*job));
- job = job->next;
+ for (job = jobList->head; job->next; job = job->next);
+ job->next = malloc(sizeof(*job));
+ job = job->next;
}
*job = newJob;
@@ -751,165 +782,177 @@ static int runCommand(struct job newJob, struct jobSet * jobList,
job->stoppedProgs = 0;
if (inBg) {
- /* we don't wait for background jobs to return -- append it
- to the list of backgrounded jobs and leave it alone */
+ /* we don't wait for background jobs to return -- append it
+ to the list of backgrounded jobs and leave it alone */
- printf("[%d] %d\n", job->jobId,
- newJob.progs[newJob.numProgs - 1].pid);
+ printf("[%d] %d\n", job->jobId,
+ newJob.progs[newJob.numProgs - 1].pid);
} else {
- jobList->fg = job;
+ jobList->fg = job;
+
+ /* move the new process group into the foreground */
- /* move the new process group into the foreground */
-
- if (tcsetpgrp(0, newJob.pgrp))
- perror("tcsetpgrp");
+ if (tcsetpgrp(0, newJob.pgrp))
+ perror("tcsetpgrp");
}
return 0;
}
-static int setupRedirections(struct childProgram * prog)
+static int setupRedirections(struct childProgram *prog)
{
int i;
int openfd;
- int mode=O_RDONLY;
- struct redirectionSpecifier * redir = prog->redirections;
+ int mode = O_RDONLY;
+ struct redirectionSpecifier *redir = prog->redirections;
for (i = 0; i < prog->numRedirections; i++, redir++) {
- switch (redir->type) {
- case REDIRECT_INPUT:
- mode = O_RDONLY;
- break;
- case REDIRECT_OVERWRITE:
- mode = O_RDWR | O_CREAT | O_TRUNC;
- break;
- case REDIRECT_APPEND:
- mode = O_RDWR | O_CREAT | O_APPEND;
- break;
- }
-
- openfd = open(redir->filename, mode, 0666);
- if (openfd < 0) {
- /* this could get lost if stderr has been redirected, but
- bash and ash both lose it as well (though zsh doesn't!) */
- fprintf(stderr, "error opening %s: %s\n", redir->filename,
- strerror(errno));
- return 1;
- }
-
- if (openfd != redir->fd) {
- dup2(openfd, redir->fd);
- close(openfd);
- }
+ switch (redir->type) {
+ case REDIRECT_INPUT:
+ mode = O_RDONLY;
+ break;
+ case REDIRECT_OVERWRITE:
+ mode = O_RDWR | O_CREAT | O_TRUNC;
+ break;
+ case REDIRECT_APPEND:
+ mode = O_RDWR | O_CREAT | O_APPEND;
+ break;
+ }
+
+ openfd = open(redir->filename, mode, 0666);
+ if (openfd < 0) {
+ /* this could get lost if stderr has been redirected, but
+ bash and ash both lose it as well (though zsh doesn't!) */
+ fprintf(stderr, "error opening %s: %s\n", redir->filename,
+ strerror(errno));
+ return 1;
+ }
+
+ if (openfd != redir->fd) {
+ dup2(openfd, redir->fd);
+ close(openfd);
+ }
}
return 0;
}
-static int busy_loop(FILE * input)
+static int busy_loop(FILE * input)
{
- char command[MAX_COMMAND_LEN + 1];
- char * nextCommand = NULL;
+ char *command;
+ char *nextCommand = NULL;
struct jobSet jobList = { NULL, NULL };
struct job newJob;
int i;
int status;
int inBg;
+ command = (char*) calloc(BUFSIZ, sizeof(char));
+
/* don't pay any attention to this signal; it just confuses
things and isn't really meant for shells anyway */
signal(SIGTTOU, SIG_IGN);
-
+
while (1) {
- if (!jobList.fg) {
- /* no job is in the foreground */
-
- /* see if any background processes have exited */
- checkJobs(&jobList);
-
- if (!nextCommand) {
- if (getCommand(input, command)) break;
- nextCommand = command;
- }
-
- if (!parseCommand(&nextCommand, &newJob, &inBg) &&
- newJob.numProgs) {
- runCommand(newJob, &jobList, inBg);
- }
- } else {
- /* a job is running in the foreground; wait for it */
- i = 0;
- while (!jobList.fg->progs[i].pid ||
- jobList.fg->progs[i].isStopped) i++;
-
- waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
-
- if (WIFEXITED(status) || WIFSIGNALED(status)) {
- /* the child exited */
- jobList.fg->runningProgs--;
- jobList.fg->progs[i].pid = 0;
-
- if (!jobList.fg->runningProgs) {
- /* child exited */
-
- removeJob(&jobList, jobList.fg);
- jobList.fg = NULL;
-
- /* move the shell to the foreground */
- if (tcsetpgrp(0, getpid()))
- perror("tcsetpgrp");
- }
- } else {
- /* the child was stopped */
- jobList.fg->stoppedProgs++;
- jobList.fg->progs[i].isStopped = 1;
-
- if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
- printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
- "Stopped", jobList.fg->text);
- jobList.fg = NULL;
- }
- }
-
- if (!jobList.fg) {
- /* move the shell to the foreground */
- if (tcsetpgrp(0, getpid()))
- perror("tcsetpgrp");
- }
- }
- }
+ if (!jobList.fg) {
+ /* no job is in the foreground */
+
+ /* see if any background processes have exited */
+ checkJobs(&jobList);
+
+ if (!nextCommand) {
+ if (getCommand(input, command))
+ break;
+ nextCommand = command;
+ }
+
+ if (!parseCommand(&nextCommand, &newJob, &inBg) &&
+ newJob.numProgs) {
+ runCommand(newJob, &jobList, inBg);
+ }
+ } else {
+ /* a job is running in the foreground; wait for it */
+ i = 0;
+ while (!jobList.fg->progs[i].pid ||
+ jobList.fg->progs[i].isStopped) i++;
+
+ waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* the child exited */
+ jobList.fg->runningProgs--;
+ jobList.fg->progs[i].pid = 0;
+
+ if (!jobList.fg->runningProgs) {
+ /* child exited */
+
+ removeJob(&jobList, jobList.fg);
+ jobList.fg = NULL;
+
+ /* move the shell to the foreground */
+ if (tcsetpgrp(0, getpid()))
+ perror("tcsetpgrp");
+ }
+ } else {
+ /* the child was stopped */
+ jobList.fg->stoppedProgs++;
+ jobList.fg->progs[i].isStopped = 1;
+
+ if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
+ printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
+ "Stopped", jobList.fg->text);
+ jobList.fg = NULL;
+ }
+ }
+
+ if (!jobList.fg) {
+ /* move the shell to the foreground */
+ if (tcsetpgrp(0, getpid()))
+ perror("tcsetpgrp");
+ }
+ }
+ }
+ free( command);
return 0;
}
-int shell_main(int argc, char ** argv)
+int shell_main(int argc, char **argv)
{
- FILE * input = stdin;
+ FILE *input = stdin;
if (argc > 2) {
- usage( shell_usage);
- }
- /* initialize the cwd */
- getcwd(cwd, sizeof(cwd));
-
-
- //if (argv[0] && argv[0][0] == '-') {
- // shell_source("/etc/profile");
- //}
-
- if (argc < 2) {
- fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
- fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
- } else {
- input = fopen(argv[1], "r");
- if (!input)
- fatalError("A: Couldn't open file '%s': %s\n", argv[1], strerror(errno));
-// else
-// fatalError("Got it.\n");
- //exit(shell_source(argv[1]));
- }
-
- return (busy_loop( input));
+ usage(shell_usage);
+ }
+ /* initialize the cwd */
+ getcwd(cwd, sizeof(cwd));
+
+
+ //if (argv[0] && argv[0][0] == '-') {
+ // shell_source("/etc/profile");
+ //}
+
+ if (argc < 2) {
+ fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
+ BB_BT);
+ fprintf(stdout,
+ "Enter 'help' for a list of built-in commands.\n\n");
+ } else {
+ input = fopen(argv[1], "r");
+ if (!input)
+ fatalError("A: Couldn't open file '%s': %s\n", argv[1],
+ strerror(errno));
+// else
+// fatalError("Got it.\n");
+ //exit(shell_source(argv[1]));
+
+ /* Set terminal IO to canonical mode, and save old term settings. */
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+ cmdedit_init();
+#endif
+ }
+
+ return (busy_loop(input));
}
diff --git a/utility.c b/utility.c
index d0ffda2a2..4b26a1b76 100644
--- a/utility.c
+++ b/utility.c
@@ -1247,7 +1247,7 @@ extern int device_open(char *device, int mode)
#endif /* BB_INIT BB_SYSLOGD */
-#if defined BB_KILLALL || defined BB_FEATURE_LINUXRC && ( defined BB_HALT || defined BB_REBOOT || defined BB_POWEROFF )
+#if defined BB_FEATURE_LINUXRC && ( defined BB_HALT || defined BB_REBOOT || defined BB_POWEROFF )
#ifdef BB_FEATURE_USE_DEVPS_PATCH
#include <linux/devps.h>
@@ -1363,7 +1363,7 @@ extern pid_t findPidByName( char* pidName)
return 0;
}
#endif /* BB_FEATURE_USE_DEVPS_PATCH */
-#endif /* BB_INIT || BB_HALT || BB_REBOOT || BB_KILLALL || BB_POWEROFF */
+#endif /* BB_INIT || BB_HALT || BB_REBOOT || BB_POWEROFF */
#if defined BB_GUNZIP \
|| defined BB_GZIP \