From d75af99529879e6cd38164fd110732052a9cdda4 Mon Sep 17 00:00:00 2001 From: Erik Andersen Date: Thu, 16 Mar 2000 08:09:09 +0000 Subject: [PATCH] Major build system updates... -Erik --- Changelog | 5 + applets/busybox.c | 222 ++++---- applets/busybox.mkll | 16 +- applets/busybox.sh | 5 +- busybox.c | 222 ++++---- busybox.def.h | 74 ++- busybox.mkll | 16 +- busybox.sh | 5 +- internal.h | 10 + lash.c | 1255 ++++++++++++++++++++++-------------------- sh.c | 1255 ++++++++++++++++++++++-------------------- shell/lash.c | 1255 ++++++++++++++++++++++-------------------- utility.c | 4 +- 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 and then adjusted a bit by me. * Added tr and dirname from John Lombardo + * Added echo and test (from me). * tar wouldn't create directory entries that don't end in '/', now it does (thanks to Avery Pennarun ) * Several fixes from Pavel Roskin : 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 -#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 ' 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 -#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 ' 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 -#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 ' 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 @@ -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 \ -- 2.25.1