Przeglądaj źródła

add sash, simpleinit and a uclibc config for nonmmu case

Waldemar Brodkorb 9 lat temu
rodzic
commit
8e2707b81b

+ 8 - 0
mk/image.mk

@@ -6,6 +6,8 @@ ifeq (${ADK_BINSH_ASH},y)
 BINSH:=ash
 else ifeq (${ADK_BINSH_BASH},y)
 BINSH:=bash
+else ifeq (${ADK_BINSH_SASH},y)
+BINSH:=sash
 else ifeq (${ADK_BINSH_HUSH},y)
 BINSH:=hush
 else ifeq (${ADK_BINSH_MKSH},y)
@@ -21,6 +23,8 @@ ifeq (${ADK_ROOTSH_ASH},y)
 ROOTSH:=/bin/ash
 else ifeq (${ADK_ROOTSH_BASH},y)
 ROOTSH:=/bin/bash
+else ifeq (${ADK_ROOTSH_SASH},y)
+ROOTSH:=/bin/sash
 else ifeq (${ADK_ROOTSH_HUSH},y)
 ROOTSH:=/bin/hush
 else ifeq (${ADK_ROOTSH_MKSH},y)
@@ -54,9 +58,13 @@ image-prepare-post:
 			mkfontdir ${TARGET_DIR}/usr/share/fonts/X11/$${i}; \
 		done; \
 	fi
+ifeq (${ADK_ROOTSH_SASH},)
 	$(SED) '/^root:/s!:/bin/sh$$!:${ROOTSH}!' ${TARGET_DIR}/etc/passwd
+endif
+ifeq (${ADK_BINSH_SASH},)
 	-rm -f ${TARGET_DIR}/bin/sh
 	ln -sf ${BINSH} ${TARGET_DIR}/bin/sh
+endif
 	test -z $(GIT) || \
 	     $(GIT) log -1|head -1|sed -e 's#commit ##' \
 		> $(TARGET_DIR)/etc/.adkversion

+ 3 - 0
package/busybox/config/init/Config.in

@@ -44,6 +44,7 @@ config BUSYBOX_FEATURE_BOOTCHARTD_CONFIG_FILE
 	help
 	  Enable reading and parsing of $PWD/bootchartd.conf
 	  and /etc/bootchartd.conf files.
+
 config BUSYBOX_HALT
 	bool "poweroff, halt, and reboot"
 	default y
@@ -69,9 +70,11 @@ config BUSYBOX_TELINIT_PATH
 	  When busybox halt and friends have to call external telinit
 	  to facilitate proper shutdown, this path is to be used when
 	  locating telinit executable.
+
 config BUSYBOX_INIT
 	bool "init"
 	default y
+	depends on !ADK_TARGET_UCLINUX
 	select BUSYBOX_FEATURE_SYSLOG
 	help
 	  init is the first program run when the system boots.

+ 1 - 1
package/busybox/config/shell/Config.in

@@ -299,7 +299,7 @@ config BUSYBOX_MSH
 
 choice
 	prompt "Choose which shell is aliased to 'sh' name"
-	default FEATURE_SH_IS_ASH
+	default FEATURE_SH_IS_NONE
 	help
 	  Choose which shell you want to be executed by 'sh' alias.
 	  The ash shell is the most bash compatible and full featured one.

+ 25 - 0
package/sash/Makefile

@@ -0,0 +1,25 @@
+# This file is part of the OpenADK project. OpenADK is copyrighted
+# material, please see the LICENCE file in the top-level directory.
+
+include ${ADK_TOPDIR}/rules.mk
+
+PKG_NAME:=		sash
+PKG_VERSION:=		1.0
+PKG_RELEASE:=		1
+PKG_DESCR:=		standalone shell
+PKG_SECTION:=		base/shells
+
+NO_DISTFILES:=		1
+
+include ${ADK_TOPDIR}/mk/package.mk
+
+$(eval $(call PKG_template,SASH,sash,${PKG_VERSION}-${PKG_RELEASE},${PKG_DEPENDS},${PKG_DESCR},${PKG_SECTION}))
+
+CONFIG_STYLE:=		manual
+INSTALL_STYLE:=		manual
+
+do-install:
+	${INSTALL_DIR} ${IDIR_SASH}/bin
+	${INSTALL_BIN} ${WRKBUILD}/sh ${IDIR_SASH}/bin/sh
+
+include ${ADK_TOPDIR}/mk/pkg-bottom.mk

+ 47 - 0
package/sash/files/rc

@@ -0,0 +1,47 @@
+#!/bin/sh
+set -x
+export PATH=/bin:/sbin:/usr/bin:/usr/sbin
+ln -s /proc/self/fd/2 /dev/stderr
+: ${rcquiet=0}
+if [ $rcquiet -ne 1 ];then
+	echo "System initialization ..."
+fi
+
+# remount /dev with smaller size
+mount -o remount,nosuid,size=128k,mode=0755 -t tmpfs mdev /dev
+
+# start mdev dynamic device node management
+echo >/dev/mdev.seq
+if [ -f /proc/sys/kernel/hotplug ];then
+	echo "/sbin/mdev" >/proc/sys/kernel/hotplug
+fi
+# creates f.e. /dev/root
+mdev -s
+
+# seed some random
+cat /etc/.rnd >/dev/urandom 2>&1
+
+# setup cfgfs
+[ -x /sbin/cfgfs ] && {
+	cfgfs setup
+	mount -o remount,ro /
+}
+
+# remount /tmp with smaller size
+size=$(cat /etc/tmpfs 2>/dev/null)
+[ -z $size ] && size=2048
+mount -o remount,nosuid,nodev,mode=1777,size=${size}k -t tmpfs tmpfs /tmp
+
+# create some useful directories in tmpfs
+mkdir -p /var/log
+mkdir -p /var/run
+mkdir -p /var/tmp
+touch /var/log/lastlog
+touch /var/log/wtmp
+
+HOSTNAME=
+[[ -s /etc/hostname ]] && HOSTNAME=$(cat /etc/hostname)
+HOSTNAME=${HOSTNAME%%.*}
+echo ${HOSTNAME:=openadk} >/proc/sys/kernel/hostname
+
+chown 0:0 /tmp; chmod 1777 /tmp

+ 38 - 0
package/sash/src/Makefile

@@ -0,0 +1,38 @@
+
+SH = sh
+SHOBJS = sash.o cmds.o cmd_uclinux.o ls.o hexdump.o df.o free.o \
+	hostname.o date.o
+
+SHUTDOWN = shutdown
+SHUTDOWNOBJS = shutdown.o
+
+REBOOT = reboot
+REBOOTOBJS = reboot.o
+
+SHOBJS += ps.o
+CFLAGS += -DCONFIG_USER_SASH_PS
+
+LIBSASH = libsash/libsash.a
+
+CFLAGS += -DCOMMAND_HISTORY
+
+all: $(SH) $(SHUTDOWN) $(REBOOT)
+
+$(SH): $(SHOBJS) $(LIBSASH)
+	$(CC) $(LDFLAGS) -o $@ $(SHOBJS) $(LIBSASH) $(LDLIBS$(LDLIBS_$@))
+
+$(SHUTDOWN): $(SHUTDOWNOBJS) $(LIBSASH)
+	$(CC) $(LDFLAGS) -o $@ $(SHUTDOWNOBJS) $(LIBSASH) $(LDLIBS)
+
+$(REBOOT): $(REBOOTOBJS) $(LIBSASH)
+	$(CC) $(LDFLAGS) -o $@ $(REBOOTOBJS) $(LIBSASH) $(LDLIBS$(LDLIBS_$@))
+
+dummy_target:
+
+$(LIBSASH): dummy_target
+	$(MAKE) -C libsash
+
+clean:
+	-rm -f $(SH) $(SHUTDOWN) $(REBOOT) *.elf *.gdb *.o
+	$(MAKE) -C libsash clean
+

+ 6 - 0
package/sash/src/README

@@ -0,0 +1,6 @@
+
+
+  This shell is an adaption of David Bell's "sash", the stand-along shell,
+with some adaptions (and truncations) for our environment. It also includes
+a few utilities (like reboot and ps) that weren't part of the original sash.
+

+ 127 - 0
package/sash/src/cmd_uclinux.c

@@ -0,0 +1,127 @@
+
+#include "sash.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <unistd.h>
+
+#if 0
+char psbuf[256];
+char name[40];
+int pid, state;
+char statec;
+
+void
+do_ps(argc, argv)
+	char	**argv;
+{
+	int i;
+	int h;
+	int max;
+	FILE * f;
+	DIR * d;
+	struct dirent * de;
+	int l;
+	
+	printf("  PID TTY STAT  TIME COMMAND\n");
+	
+	
+	d = opendir("/proc");
+	if (!d)
+		return;
+	
+	while (de = readdir(d)) {
+		for(i=0;i<strlen(de->d_name);i++)
+			if (!isdigit(de->d_name[i]))
+				goto next;
+		
+		sprintf(psbuf, "/proc/%s/stat", de->d_name);
+		h = open(psbuf, O_RDONLY);
+		
+		if (h==-1)
+			continue;
+			
+		l = read(h, psbuf, 255);
+		if (l<=0) {
+			perror("Unable to read status");
+			close(h);
+			continue;
+		}
+		
+		psbuf[l] = '\0';
+		psbuf[255] = '\0';
+		
+		
+		if (sscanf(psbuf, 
+			"%d %s %c",
+			&pid, name, &statec)<3)
+			{
+			perror("Unable to parse status");
+			close(h);
+			continue;
+		}
+		
+		state = statec;
+		
+		close(h);
+		
+		sprintf(psbuf, "/proc/%s/cmdline", de->d_name);
+		h = open(psbuf, O_RDONLY);
+		
+		if (h == -1) {
+			perror("Unable to open cmdline");
+			continue;
+		}
+		
+		l = read(h, psbuf, 255);
+		if (l < 0) {
+			perror("Unable to read cmdline");
+			close(h);
+			continue;
+		}
+		
+		close(h);
+		
+		psbuf[255] = psbuf[l] = '\0';
+		
+		printf("%5d %3s %c     --:-- %s\n", pid, "", state, psbuf);
+	next:
+	}
+	
+	closedir(d);
+}
+#endif
+
+void
+do_cat(argc, argv)
+	char	**argv;
+{
+	int	fd;
+	char	*name;
+	size_t	l;
+	char	buf[256];
+
+	while (argc-- > 1) {
+		if (intflag) {
+			return;
+		}
+		name = *(++argv);
+
+		fd = open(name, O_RDONLY);
+		if (fd < 0) {
+			perror(name);
+			return;
+		}
+
+		while ((l = read(fd, buf, sizeof(buf))) > 0) {
+			fwrite(buf, 1, l, stdout);
+		}
+		close(fd);
+	}
+}

+ 899 - 0
package/sash/src/cmds.c

@@ -0,0 +1,899 @@
+/*
+ * Modifications for uClinux
+ * Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>
+ *
+ * Original code
+ * Copyright (c) 1993 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * Most simple built-in commands are here.
+ */
+
+#include "sash.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <utime.h>
+#include <errno.h>
+#ifdef EMBED
+#include <config/autoconf.h>
+#endif
+
+void
+do_echo(argc, argv)
+	char	**argv;
+{
+	BOOL	first;
+
+	first = TRUE;
+	while (argc-- > 1) {
+		if (!first)
+			fputc(' ', stdout);
+		first = FALSE;
+		fputs(*++argv, stdout);
+	}
+	fputc('\n', stdout);
+}
+
+
+void
+do_pwd(argc, argv)
+	char	**argv;
+{
+	char	buf[PATHLEN];
+
+	if (getcwd(buf, PATHLEN) == NULL) {
+		fprintf(stderr, "Cannot get current directory\n");
+		return;
+	}
+
+	printf("%s\n", buf);
+}
+
+void
+do_time(argc, argv)
+	char ** argv;
+{
+	struct timeval tv;
+	gettimeofday(&tv, 0);
+	printf("Time of day = %d.%6.6d seconds\n", tv.tv_sec, tv.tv_usec);
+}
+
+void
+do_cd(argc, argv)
+	char	**argv;
+{
+	char	*path;
+
+	if (argc > 1)
+		path = argv[1];
+	else {
+		path = getenv("HOME");
+		if (path == NULL) {
+			fprintf(stderr, "No HOME environment variable\n");
+			return;
+		}
+	}
+
+	if (chdir(path) < 0)
+		perror(path);
+}
+
+
+void
+do_mkdir(argc, argv)
+	char	**argv;
+{
+	int state = 0, mode = -1;
+
+	while (argc-- > 1) {
+		if (state == 0) {
+			if (strcmp(argv[1], "-m") == 0)
+				state = 1;
+			else if (mkdir(argv[1], 0777) < 0)
+				perror(argv[1]);
+			else if (mode != -1 && chmod(argv[1], mode) < 0)
+				perror(argv[1]);
+		} else if (state == 1) {
+			mode = strtol(argv[1], NULL, 8);
+			state = 0;
+		}
+		argv++;
+	}
+}
+
+void
+do_sleep(argc, argv) 
+	char	**argv;
+{
+	if (argc > 1)
+		sleep(atoi(argv[1]));
+}
+
+void
+do_mknod(argc, argv)
+	char	**argv;
+{
+	char	*cp;
+	int	mode;
+	int	major;
+	int	minor;
+
+	mode = 0666;
+
+	if (strcmp(argv[2], "b") == 0)
+		mode |= S_IFBLK;
+	else if (strcmp(argv[2], "c") == 0)
+		mode |= S_IFCHR;
+	else {
+		fprintf(stderr, "Bad device type\n");
+		return;
+	}
+
+	major = 0;
+	cp = argv[3];
+	while (isdecimal(*cp))
+		major = major * 10 + *cp++ - '0';
+
+	if (*cp || (major < 0) || (major > 255)) {
+		fprintf(stderr, "Bad major number\n");
+		return;
+	}
+
+	minor = 0;
+	cp = argv[4];
+	while (isdecimal(*cp))
+		minor = minor * 10 + *cp++ - '0';
+
+	if (*cp || (minor < 0) || (minor > 255)) {
+		fprintf(stderr, "Bad minor number\n");
+		return;
+	}
+
+	if (mknod(argv[1], mode, major * 256 + minor) < 0)
+		perror(argv[1]);
+}
+
+
+void
+do_rmdir(argc, argv)
+	char	**argv;
+{
+	while (argc-- > 1) {
+		if (rmdir(argv[1]) < 0)
+			perror(argv[1]);
+		argv++;
+	}
+}
+
+
+void
+do_sync(argc, argv)
+	char	**argv;
+{
+#ifdef CONFIG_USER_FLATFSD_FLATFSD
+	system("exec flatfsd -s");
+#endif
+	sync();
+}
+
+
+void
+do_rm(argc, argv)
+	char	**argv;
+{
+	while (argc-- > 1) {
+		if (unlink(argv[1]) < 0)
+			perror(argv[1]);
+		argv++;
+	}
+}
+
+
+void
+do_chmod(argc, argv)
+	char	**argv;
+{
+	char	*cp;
+	int	mode;
+
+	mode = 0;
+	cp = argv[1];
+	while (isoctal(*cp))
+		mode = mode * 8 + (*cp++ - '0');
+
+	if (*cp) {
+		fprintf(stderr, "Mode must be octal\n");
+		return;
+	}
+	argc--;
+	argv++;
+
+	while (argc-- > 1) {
+		if (chmod(argv[1], mode) < 0)
+			perror(argv[1]);
+		argv++;
+	}
+}
+
+
+void
+do_chown(argc, argv)
+	char	**argv;
+{
+	char		*cp;
+	int		uid;
+	struct passwd	*pwd;
+	struct stat	statbuf;
+
+	cp = argv[1];
+	if (isdecimal(*cp)) {
+		uid = 0;
+		while (isdecimal(*cp))
+			uid = uid * 10 + (*cp++ - '0');
+
+		if (*cp) {
+			fprintf(stderr, "Bad uid value\n");
+			return;
+		}
+	} else {
+		pwd = getpwnam(cp);
+		if (pwd == NULL) {
+			fprintf(stderr, "Unknown user name\n");
+			return;
+		}
+
+		uid = pwd->pw_uid;
+	}
+
+	argc--;
+	argv++;
+
+	while (argc-- > 1) {
+		argv++;
+		if ((stat(*argv, &statbuf) < 0) ||
+			(chown(*argv, uid, statbuf.st_gid) < 0))
+				perror(*argv);
+	}
+}
+
+
+void
+do_chgrp(argc, argv)
+	char	**argv;
+{
+	char		*cp;
+	int		gid;
+	struct group	*grp;
+	struct stat	statbuf;
+
+	cp = argv[1];
+	if (isdecimal(*cp)) {
+		gid = 0;
+		while (isdecimal(*cp))
+			gid = gid * 10 + (*cp++ - '0');
+
+		if (*cp) {
+			fprintf(stderr, "Bad gid value\n");
+			return;
+		}
+	} else {
+		grp = getgrnam(cp);
+		if (grp == NULL) {
+			fprintf(stderr, "Unknown group name\n");
+			return;
+		}
+
+		gid = grp->gr_gid;
+	}
+
+	argc--;
+	argv++;
+
+	while (argc-- > 1) {
+		argv++;
+		if ((stat(*argv, &statbuf) < 0) ||
+			(chown(*argv, statbuf.st_uid, gid) < 0))
+				perror(*argv);
+	}
+}
+
+
+void
+do_touch(argc, argv)
+        char    **argv;
+{
+        char            *name;
+        int             fd;
+        struct  utimbuf now;
+
+        time(&now.actime);
+        now.modtime = now.actime;
+
+        while (argc-- > 1) {
+                name = *(++argv);
+
+                if (utime(name, &now) <0)
+                {
+                fd = open(name, O_CREAT | O_WRONLY | O_EXCL, 0666);
+                if (fd >= 0)
+                        {
+                        close(fd);
+                        continue;
+                        }
+                perror(name);
+                }
+        }
+}
+
+
+void
+do_mv(argc, argv)
+	char	**argv;
+{
+	int	dirflag;
+	char	*srcname;
+	char	*destname;
+	char	*lastarg;
+
+	lastarg = argv[argc - 1];
+
+	dirflag = isadir(lastarg);
+
+	if ((argc > 3) && !dirflag) {
+		fprintf(stderr, "%s: not a directory\n", lastarg);
+		return;
+	}
+
+	while (argc-- > 2) {
+		srcname = *(++argv);
+		if (access(srcname, 0) < 0) {
+			perror(srcname);
+			continue;
+		}
+
+		destname = lastarg;
+		if (dirflag)
+			destname = buildname(destname, srcname);
+
+		if (rename(srcname, destname) >= 0)
+			continue;
+
+		if (errno != EXDEV) {
+			perror(destname);
+			continue;
+		}
+
+		if (!copyfile(srcname, destname, TRUE))
+			continue;
+
+		if (unlink(srcname) < 0)
+			perror(srcname);
+	}
+}
+
+
+void
+do_ln(argc, argv)
+	char	**argv;
+{
+	int	dirflag;
+	char	*srcname;
+	char	*destname;
+	char	*lastarg;
+
+	if (argv[1][0] == '-') {
+		if (strcmp(argv[1], "-s")) {
+			fprintf(stderr, "Unknown option\n");
+			return;
+		}
+
+		if (argc != 4) {
+			fprintf(stderr, "Wrong number of arguments for symbolic link\n");
+			return;
+		}
+
+#ifdef	S_ISLNK
+		if (symlink(argv[2], argv[3]) < 0)
+			perror(argv[3]);
+#else
+		fprintf(stderr, "Symbolic links are not allowed\n");
+#endif
+		return;
+	}
+
+	/*
+	 * Here for normal hard links.
+	 */
+	lastarg = argv[argc - 1];
+	dirflag = isadir(lastarg);
+
+	if ((argc > 3) && !dirflag) {
+		fprintf(stderr, "%s: not a directory\n", lastarg);
+		return;
+	}
+
+	while (argc-- > 2) {
+		srcname = *(++argv);
+		if (access(srcname, 0) < 0) {
+			perror(srcname);
+			continue;
+		}
+
+		destname = lastarg;
+		if (dirflag)
+			destname = buildname(destname, srcname);
+
+		if (link(srcname, destname) < 0) {
+			perror(destname);
+			continue;
+		}
+	}
+}
+
+
+void
+do_cp(argc, argv)
+	char	**argv;
+{
+	BOOL	dirflag;
+	char	*srcname;
+	char	*destname;
+	char	*lastarg;
+
+	lastarg = argv[argc - 1];
+
+	dirflag = isadir(lastarg);
+
+	if ((argc > 3) && !dirflag) {
+		fprintf(stderr, "%s: not a directory\n", lastarg);
+		return;
+	}
+
+	while (argc-- > 2) {
+		destname = lastarg;
+		srcname = *++argv;
+		if (dirflag)
+			destname = buildname(destname, srcname);
+
+		(void) copyfile(srcname, destname, FALSE);
+	}
+}
+
+
+void
+do_mount(argc, argv)
+	char	**argv;
+{
+	char	*str;
+	char	*type;
+
+	argc--;
+	argv++;
+	type = "minix";
+
+	while ((argc > 0) && (**argv == '-')) {
+		argc--;
+		str = *argv++ ;
+
+		while (*++str) switch (*str) {
+			case 't':
+				if ((argc <= 0) || (**argv == '-')) {
+					fprintf(stderr, "Missing file system type\n");
+					return;
+				}
+
+				type = *argv++;
+				argc--;
+				break;
+
+			default:
+				fprintf(stderr, "Unknown option\n");
+				return;
+		}
+	}
+
+	if (argc != 2) {
+		fprintf(stderr, "Wrong number of arguments for mount\n");
+		return;
+	}
+
+	if (mount(argv[0], argv[1], type, 0, 0) < 0)
+		perror("mount failed");
+}
+
+
+void
+do_umount(argc, argv)
+	char	**argv;
+{
+	if (umount(argv[1]) < 0)
+		perror(argv[1]);
+}
+
+
+void
+do_cmp(argc, argv)
+	char	**argv;
+{
+	int		fd1;
+	int		fd2;
+	int		cc1;
+	int		cc2;
+	long		pos;
+	char		*srcname;
+	char		*destname;
+	char		*lastarg;
+	char		*bp1;
+	char		*bp2;
+	char		*buf1;
+	char		*buf2;
+	struct	stat	statbuf1;
+	struct	stat	statbuf2;
+	
+	if (stat(argv[1], &statbuf1) < 0) {
+		perror(argv[1]);
+		return;
+	}
+
+	if (stat(argv[2], &statbuf2) < 0) {
+		perror(argv[2]);
+		return;
+	}
+
+	if ((statbuf1.st_dev == statbuf2.st_dev) &&
+		(statbuf1.st_ino == statbuf2.st_ino))
+	{
+		printf("Files are links to each other\n");
+		return;
+	}
+
+	if (statbuf1.st_size != statbuf2.st_size) {
+		printf("Files are different sizes\n");
+		return;
+	}
+	
+	fd1 = open(argv[1], 0);
+	if (fd1 < 0) {
+		perror(argv[1]);
+		return;
+	}
+
+	fd2 = open(argv[2], 0);
+	if (fd2 < 0) {
+		perror(argv[2]);
+		close(fd1);
+		return;
+	}
+	
+	buf1 = malloc(8192-16);
+	buf2 = malloc(8192-16);
+
+	pos = 0;
+	while (TRUE) {
+		if (intflag)
+			goto closefiles;
+
+		cc1 = read(fd1, buf1, 8192-16);
+		if (cc1 < 0) {
+			perror(argv[1]);
+			goto closefiles;
+		}
+
+		cc2 = read(fd2, buf2, 8192-16);
+		if (cc2 < 0) {
+			perror(argv[2]);
+			goto closefiles;
+		}
+
+		if ((cc1 == 0) && (cc2 == 0)) {
+			printf("Files are identical\n");
+			goto closefiles;
+		}
+
+		if (cc1 < cc2) {
+			printf("First file is shorter than second\n");
+			goto closefiles;
+		}
+
+		if (cc1 > cc2) {
+			printf("Second file is shorter than first\n");
+			goto closefiles;
+		}
+
+		if (memcmp(buf1, buf2, cc1) == 0) {
+			pos += cc1;
+			continue;
+		}
+
+		bp1 = buf1;
+		bp2 = buf2;
+		while (*bp1++ == *bp2++)
+			pos++;
+
+		printf("Files differ at byte position %ld\n", pos);
+		goto closefiles;
+	}
+
+closefiles:
+	close(fd1);
+	close(fd2);
+	free(buf1);
+	free(buf2);
+}
+
+
+void
+do_more(argc, argv)
+	char	**argv;
+{
+	FILE	*fp;
+	char	*name;
+	int	ch;
+	int	line;
+	int	col;
+	char	buf[80];
+
+	while (argc-- > 1) {
+		name = *(++argv);
+
+		fp = fopen(name, "r");
+		if (fp == NULL) {
+			perror(name);
+			return;
+		}
+
+		printf("<< %s >>\n", name);
+		line = 1;
+		col = 0;
+
+		while (fp && ((ch = fgetc(fp)) != EOF)) {
+			switch (ch) {
+				case '\r':
+					col = 0;
+					break;
+
+				case '\n':
+					line++;
+					col = 0;
+					break;
+
+				case '\t':
+					col = ((col + 1) | 0x07) + 1;
+					break;
+
+				case '\b':
+					if (col > 0)
+						col--;
+					break;
+
+				default:
+					col++;
+			}
+
+			putchar(ch);
+			if (col >= 80) {
+				col -= 80;
+				line++;
+			}
+
+			if (line < 24)
+				continue;
+
+			if (col > 0)
+				putchar('\n');
+
+			printf("--More--");
+			fflush(stdout);
+
+			if (intflag || (read(0, buf, sizeof(buf)) < 0)) {
+				if (fp)
+					fclose(fp);
+				return;
+			}
+
+			ch = buf[0];
+			if (ch == ':')
+				ch = buf[1];
+
+			switch (ch) {
+				case 'N':
+				case 'n':
+					fclose(fp);
+					fp = NULL;
+					break;
+
+				case 'Q':
+				case 'q':
+					fclose(fp);
+					return;
+			}
+
+			col = 0;
+			line = 1;
+		}
+		if (fp)
+			fclose(fp);
+	}
+}
+
+
+void
+do_exit(argc, argv)
+	char	**argv;
+{
+	exit(0);
+}
+
+
+void
+do_setenv(argc, argv)
+	char	**argv;
+{
+	setenv(argv[1], argv[2], 1);
+}
+
+
+void
+do_printenv(argc, argv)
+	char	**argv;
+{
+	char		**env;
+	extern char	**environ;
+	int		len;
+
+	env = environ;
+
+	if (argc == 1) {
+		while (*env)
+			printf("%s\n", *env++);
+		return;
+	}
+
+	len = strlen(argv[1]);
+	while (*env) {
+		if ((strlen(*env) > len) && (env[0][len] == '=') &&
+			(memcmp(argv[1], *env, len) == 0))
+		{
+			printf("%s\n", &env[0][len+1]);
+			return;
+		}
+		env++;
+	}
+}
+
+
+void
+do_umask(argc, argv)
+	char	**argv;
+{
+	char	*cp;
+	int	mask;
+
+	if (argc <= 1) {
+		mask = umask(0);
+		umask(mask);
+		printf("%03o\n", mask);
+		return;
+	}
+
+	mask = 0;
+	cp = argv[1];
+	while (isoctal(*cp))
+		mask = mask * 8 + *cp++ - '0';
+
+	if (*cp || (mask & ~0777)) {
+		fprintf(stderr, "Bad umask value\n");
+		return;
+	}
+
+	umask(mask);
+}
+
+
+void
+do_kill(argc, argv)
+	char	**argv;
+{
+	char	*cp;
+	int	sig;
+	int	pid;
+
+	sig = SIGTERM;
+
+	if (argv[1][0] == '-') {
+		cp = &argv[1][1];
+		if (strcmp(cp, "HUP") == 0)
+			sig = SIGHUP;
+		else if (strcmp(cp, "INT") == 0)
+			sig = SIGINT;
+		else if (strcmp(cp, "QUIT") == 0)
+			sig = SIGQUIT;
+		else if (strcmp(cp, "ILL") == 0)
+			sig = SIGILL;
+		else if (strcmp(cp, "TRAP") == 0)
+			sig = SIGTRAP;
+		else if (strcmp(cp, "ABRT") == 0)
+			sig = SIGABRT;
+		else if (strcmp(cp, "IOT") == 0)
+			sig = SIGIOT;
+		else if (strcmp(cp, "BUS") == 0)
+			sig = SIGBUS;
+		else if (strcmp(cp, "FPE") == 0)
+			sig = SIGFPE;
+		else if (strcmp(cp, "KILL") == 0)
+			sig = SIGKILL;
+		else if (strcmp(cp, "USR1") == 0)
+			sig = SIGUSR1;
+		else if (strcmp(cp, "SEGV") == 0)
+			sig = SIGSEGV;
+		else if (strcmp(cp, "USR2") == 0)
+			sig = SIGUSR2;
+		else if (strcmp(cp, "PIPE") == 0)
+			sig = SIGPIPE;
+ 		else if (strcmp(cp, "ALRM") == 0)
+			sig = SIGALRM;
+ 		else if (strcmp(cp, "TERM") == 0)
+			sig = SIGTERM;
+#ifdef SIGSTKFLT
+ 		else if (strcmp(cp, "STKFLT") == 0)
+			sig = SIGSTKFLT;
+#endif
+ 		else if (strcmp(cp, "CHLD") == 0)
+			sig = SIGCHLD;
+		else if (strcmp(cp, "CONT") == 0)
+			sig = SIGCONT;
+		else if (strcmp(cp, "STOP") == 0)
+			sig = SIGSTOP;
+		else if (strcmp(cp, "TSTP") == 0)
+			sig = SIGTSTP;
+ 		else if (strcmp(cp, "TTIN") == 0)
+			sig = SIGTTIN;
+ 		else if (strcmp(cp, "TTOU") == 0)
+			sig = SIGTTOU;
+ 		else if (strcmp(cp, "URG") == 0)
+			sig = SIGURG;
+ 		else if (strcmp(cp, "PWR") == 0)
+			sig = SIGPWR;
+		else {
+			sig = 0;
+			while (isdecimal(*cp))
+				sig = sig * 10 + *cp++ - '0';
+
+			if (*cp) {
+				fprintf(stderr, "Unknown signal\n");
+				exit_code = 1;
+				return;
+			}
+		}
+		argc--;
+		argv++;
+	}
+
+	while (argc-- > 1) {
+		cp = *++argv;
+		pid = 0;
+		while (isdecimal(*cp))
+			pid = pid * 10 + *cp++ - '0';
+
+		if (*cp) {
+			fprintf(stderr, "Non-numeric pid\n");
+			exit_code = 1;
+			return;
+		}
+
+		if (kill(pid, sig) < 0) {
+			perror(*argv);
+			exit_code = 1;
+		}
+	}
+}
+
+/* END CODE */

+ 27 - 0
package/sash/src/cp.c

@@ -0,0 +1,27 @@
+
+void
+do_cp(argc, argv)
+	char	**argv;
+{
+	BOOL	dirflag;
+	char	*srcname;
+	char	*destname;
+	char	*lastarg;
+
+	lastarg = argv[argc - 1];
+
+	dirflag = isadir(lastarg);
+
+	if ((argc > 3) && !dirflag) {
+		fprintf(stderr, "%s: not a directory\n", lastarg);
+		return;
+	}
+
+	while (argc-- > 2) {
+		destname = lastarg;
+		if (dirflag)
+			destname = buildname(destname, srcname);
+
+		(void) copyfile(*++argv, destname, FALSE);
+	}
+}

+ 54 - 0
package/sash/src/date.c

@@ -0,0 +1,54 @@
+/* date.c bradkemp@indusriver.com */
+
+#include <time.h>
+#include <stdio.h>
+
+static const char invalid_date[] = "Invalid date %s\n";
+int do_date(int argc, char * argv[])
+{
+
+    time_t tm;
+    struct tm tm_time;
+    time(&tm);
+    memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
+    
+    if (argc > 1) {
+	int nr;
+        
+	nr = sscanf(argv[1], "%2d%2d%2d%2d%d",
+                    &(tm_time.tm_mon),
+                    &(tm_time.tm_mday),
+                    &(tm_time.tm_hour),
+                    &(tm_time.tm_min), &(tm_time.tm_year));
+        
+	if (nr < 4 || nr > 5) {
+            fprintf(stderr, invalid_date, argv[1]);
+            return(0);
+	}
+        
+	/* correct for century  - minor Y2K problem here? */
+	if (tm_time.tm_year >= 1900)
+            tm_time.tm_year -= 1900;
+	/* adjust date */
+	tm_time.tm_mon -= 1;
+        
+        if((tm = mktime(&tm_time)) < 0) {
+            fprintf(stderr, invalid_date, argv[1]);
+            return(0);
+        }
+        
+        if(stime(&tm) < 0) {
+            fprintf(stderr, "Unable to set date\n");
+            return(0);
+        }
+            
+        return (0);
+        
+    }
+    printf("%s\n",asctime(&tm_time));
+
+    return(0);
+}
+
+
+

+ 55 - 0
package/sash/src/df.c

@@ -0,0 +1,55 @@
+/* df.c:
+ *
+ * Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "sash.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <linux/major.h>
+#ifdef __UC_LIBC__
+#include <linux/types.h>
+#endif
+#include <sys/time.h>
+#include <sys/param.h>
+#include <errno.h>
+
+void
+do_df(int argc, char * argv[])
+{
+	char * name;
+	struct statfs stbuf;
+
+#if 0
+	fclose(stdin);
+#endif
+
+	if (argc<2)
+		name = "/";
+	else
+		name = argv[1];
+	
+	if (statfs(name, &stbuf) == -1) {
+		printf("Unable to get disk space of %s: %s\n", name, strerror(errno));
+		return;
+	}
+	
+	printf("Total Kbytes: %ld\n", (stbuf.f_bsize / 256) * (stbuf.f_blocks / 4));
+	printf("Free  Kbytes: %ld\n", (stbuf.f_bsize / 256) * (stbuf.f_bfree / 4));
+	printf("Total  nodes: %ld\n", stbuf.f_files);
+	printf("Free   nodes: %ld\n", stbuf.f_ffree);
+}
+

+ 44 - 0
package/sash/src/free.c

@@ -0,0 +1,44 @@
+/* free.c:
+ *
+ * Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "sash.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+
+void
+do_free(argc, argv)
+	char	**argv;
+{
+	int i;
+	FILE * f;
+	char buf[256];
+
+	f = fopen("/proc/meminfo", "r");
+	
+	if (!f) {
+		perror("Unable to open /proc/meminfo: ");
+		return;
+	}
+	
+	for(i=0;i<3;i++) {
+		fgets(buf, 250, f);
+		fputs(buf, stdout);
+	}
+	
+	fclose(f);
+}
+

+ 127 - 0
package/sash/src/hexdump.c

@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1993 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * Most simple built-in commands are here.
+ */
+
+#include "sash.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <utime.h>
+#include <errno.h>
+
+void
+do_hexdump(argc, argv)
+	int	argc;
+	char	**argv;
+{
+	FILE	*fp;
+	int	count;
+	int	c;
+	char	text[17];
+	unsigned char	buf[130];
+
+	char	*name = 0;
+	unsigned long pos = 0;
+	char	*myname = argv[0];
+ 
+	if ( (argc > 2) && !strcmp(argv[1],"-s") ) {
+		pos = strtoul(argv[2], 0, 0);
+		argc -= 2;
+		argv += 2;
+	}
+	
+	if (argc <= 1) {
+		fprintf(stderr, "No filename provided\n");
+		return;
+	}
+
+	name = argv[1];
+	fp = fopen(name, "r");
+	if (!fp) {
+		fprintf(stderr, "Failed to open file '%s': %s\n",
+			name, strerror(errno));
+		return;
+	}
+
+	if (pos)
+		fseek(fp, pos, SEEK_SET);
+	
+	c = 0;
+	
+	text[16] = 0;
+	
+	while(!feof(fp)) {
+	
+	  strcmp(text, "                ");
+	
+	  while (c < (pos & 0xf)) {
+	    if (c == 0)
+	      printf("%4X:", pos & 0xfffffff0);
+	    printf( (c == 8) ? "-  " : "   ");
+	    text[c] = ' ';
+	    c++;
+	  }
+	
+	  {
+	    int p = 0;
+            count = fread(buf, 1, 128 - (pos % 16), fp);
+          
+            if (count <= 0)
+              break;
+
+            while (p < count) {
+              c = (pos & 0xf);
+            
+              if (c == 0)
+                printf("%4X:", pos & 0xfffffff0);
+              
+              if ((buf[p] < 32) || (buf[p]>126))
+                text[c] = '.';
+              else
+                text[c] = buf[p];
+            
+	      printf( (c==15) ? " %02.2X" : (c == 8) ? "-%02.2X" : " %02.2X", buf[p]);
+	      
+	      if (c == 15)
+	        printf(" %s\n", text);
+	    
+              pos++;
+              p++;
+            }
+	  }
+	  
+	  if (c = (pos & 0x0f)) {
+
+	    while (c < 16) {
+	      printf( (c == 8) ? "-  " : "   ");
+	      text[c] = ' ';
+	      c++;
+	    }
+	  
+	    printf(" %s\n", text);
+	  }
+	    
+	  if (feof(fp))
+	    break;
+	  
+	  printf("--more--");
+	  fflush(stdout);
+	  
+	  fgets(buf, 80, stdin);
+	  if (toupper(buf[0]) == 'Q')
+	    break;
+	}
+
+	if (fp != stdin)
+		fclose(fp);
+}
+

+ 24 - 0
package/sash/src/hostname.c

@@ -0,0 +1,24 @@
+/* hostname.c - poe@daimi.aau.dk */
+
+#include "sash.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void do_hostname(int argc, char **argv)
+{
+	char hn[PATHLEN + 1];
+	
+	if(argc >= 2) {
+		if(strlen(argv[1]) > PATHLEN) {
+			printf("That name is too long.\n");
+		} else {
+			sethostname(argv[1], strlen(argv[1]));
+		}
+	} else {
+		gethostname(hn, PATHLEN);
+		printf("%s\n", hn);
+	}
+}

+ 20 - 0
package/sash/src/libsash/Makefile

@@ -0,0 +1,20 @@
+
+LIB = libsash.a
+CHOPSRC = utils.c
+LIBOBJS = intflag.o modestring.o timestring.o isadir.o copyfile.o \
+	buildname.o expandwildcards.o namesort.o match.o makeargs.o \
+	makestring.o chunks.o expandenvvar.o
+
+CFLAGS += -I../
+
+
+all: $(EXEC) $(LIB)
+
+$(LIBOBJS): $(CHOPSRC)
+	$(CC) $(CFLAGS) -DL_$(basename $*) -o $(basename $*).o -c $^
+
+$(LIB): $(LIB)($(LIBOBJS))
+	$(RANLIB) $(LIB)
+
+clean:
+	rm -f $(LIB) $(EXEC) *.o

+ 706 - 0
package/sash/src/libsash/utils.c

@@ -0,0 +1,706 @@
+/*
+ * Copyright (c) 1993 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * Utility routines.
+ */
+
+#include "sash.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <time.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+
+#ifdef L_intflag
+
+int intflag;
+
+#endif
+
+#ifdef L_modestring
+
+/*
+ * Return the standard ls-like mode string from a file mode.
+ * This is static and so is overwritten on each call.
+ */
+char *
+modestring(mode)
+{
+	static	char	buf[12];
+
+	strcpy(buf, "----------");
+
+	/*
+	 * Fill in the file type.
+	 */
+	if (S_ISDIR(mode))
+		buf[0] = 'd';
+	if (S_ISCHR(mode))
+		buf[0] = 'c';
+	if (S_ISBLK(mode))
+		buf[0] = 'b';
+	if (S_ISFIFO(mode))
+		buf[0] = 'p';
+#ifdef	S_ISLNK
+	if (S_ISLNK(mode))
+		buf[0] = 'l';
+#endif
+#ifdef	S_ISSOCK
+	if (S_ISSOCK(mode))
+		buf[0] = 's';
+#endif
+
+	/*
+	 * Now fill in the normal file permissions.
+	 */
+	if (mode & S_IRUSR)
+		buf[1] = 'r';
+	if (mode & S_IWUSR)
+		buf[2] = 'w';
+	if (mode & S_IXUSR)
+		buf[3] = 'x';
+	if (mode & S_IRGRP)
+		buf[4] = 'r';
+	if (mode & S_IWGRP)
+		buf[5] = 'w';
+	if (mode & S_IXGRP)
+		buf[6] = 'x';
+	if (mode & S_IROTH)
+		buf[7] = 'r';
+	if (mode & S_IWOTH)
+		buf[8] = 'w';
+	if (mode & S_IXOTH)
+		buf[9] = 'x';
+
+	/*
+	 * Finally fill in magic stuff like suid and sticky text.
+	 */
+	if (mode & S_ISUID)
+		buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
+	if (mode & S_ISGID)
+		buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
+	if (mode & S_ISVTX)
+		buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
+
+	return buf;
+}
+
+#endif
+
+#ifdef L_timestring
+
+/*
+ * Get the time to be used for a file.
+ * This is down to the minute for new files, but only the date for old files.
+ * The string is returned from a static buffer, and so is overwritten for
+ * each call.
+ */
+char *
+timestring(t)
+	long	t;
+{
+	long		now;
+	char		*str;
+	static	char	buf[26];
+
+	time(&now);
+
+	str = ctime(&t);
+
+	strcpy(buf, &str[4]);
+	buf[12] = '\0';
+
+	if ((t > now) || (t < now - 365*24*60*60L)) {
+		strcpy(&buf[7], &str[20]);
+		buf[11] = '\0';
+	}
+
+	return buf;
+}
+
+#endif
+
+#ifdef L_isadir
+
+/*
+ * Return TRUE if a filename is a directory.
+ * Nonexistant files return FALSE.
+ */
+BOOL
+isadir(name)
+	char	*name;
+{
+	struct	stat	statbuf;
+
+	if (stat(name, &statbuf) < 0)
+		return FALSE;
+
+	return S_ISDIR(statbuf.st_mode);
+}
+
+#endif
+
+#ifdef L_copyfile
+
+/*
+ * Copy one file to another, while possibly preserving its modes, times,
+ * and modes.  Returns TRUE if successful, or FALSE on a failure with an
+ * error message output.  (Failure is not indicted if the attributes cannot
+ * be set.)
+ */
+BOOL
+copyfile(srcname, destname, setmodes)
+	char	*srcname;
+	char	*destname;
+	BOOL	setmodes;
+{
+	int		rfd;
+	int		wfd;
+	int		rcc;
+	int		wcc;
+	char		*bp;
+	struct	stat	statbuf1;
+	struct	stat	statbuf2;
+	struct	utimbuf	times;
+	int len = 8192-16;
+	char * buf = 0;
+	
+	if (stat(srcname, &statbuf1) < 0) {
+		perror(srcname);
+		return FALSE;
+	}
+
+	if (stat(destname, &statbuf2) < 0) {
+		statbuf2.st_ino = -1;
+		statbuf2.st_dev = -1;
+	}
+
+	if (S_ISREG(statbuf1.st_mode) &&
+			(statbuf1.st_dev == statbuf2.st_dev) &&
+		(statbuf1.st_ino == statbuf2.st_ino))
+	{
+		fprintf(stderr, "Copying file \"%s\" to itself\n", srcname);
+		return FALSE;
+	}
+
+	rfd = open(srcname, 0);
+	if (rfd < 0) {
+		perror(srcname);
+		return FALSE;
+	}
+
+	wfd = open(destname, O_WRONLY|O_CREAT|O_TRUNC, statbuf1.st_mode);
+	if (wfd < 0) {
+		perror(destname);
+		close(rfd);
+		return FALSE;
+	}
+
+	buf = malloc(len);
+	if (!buf) {
+		fprintf(stderr,"Unable to allocate buffer of %d bytes\n", len);
+		return FALSE;
+	}
+	
+	while ((rcc = read(rfd, buf, len)) > 0) {
+		if (intflag) {
+			close(rfd);
+			close(wfd);
+			free(buf);
+			return FALSE;
+		}
+
+		bp = buf;
+		while (rcc > 0) {
+			wcc = write(wfd, bp, rcc);
+			if (wcc < 0) {
+				perror(destname);
+				free(buf);
+				goto error_exit;
+			}
+			bp += wcc;
+			rcc -= wcc;
+		}
+	}
+	
+	free(buf);
+
+	if (rcc < 0) {
+		perror(srcname);
+		goto error_exit;
+	}
+
+	close(rfd);
+	if (close(wfd) < 0) {
+		perror(destname);
+		return FALSE;
+	}
+
+	if (setmodes) {
+		(void) chmod(destname, statbuf1.st_mode);
+
+		(void) chown(destname, statbuf1.st_uid, statbuf1.st_gid);
+
+		times.actime = statbuf1.st_atime;
+		times.modtime = statbuf1.st_mtime;
+
+		(void) utime(destname, &times);
+	}
+
+	return TRUE;
+
+
+error_exit:
+	close(rfd);
+	close(wfd);
+
+	return FALSE;
+}
+
+#endif
+
+#ifdef L_buildname
+
+/*
+ * Build a path name from the specified directory name and file name.
+ * If the directory name is NULL, then the original filename is returned.
+ * The built path is in a static area, and is overwritten for each call.
+ */
+char *
+buildname(dirname, filename)
+	char	*dirname;
+	char	*filename;
+{
+	char		*cp;
+	static	char	buf[PATHLEN];
+
+	if ((dirname == NULL) || (*dirname == '\0'))
+		return filename;
+
+	cp = strrchr(filename, '/');
+	if (cp)
+		filename = cp + 1;
+
+	strcpy(buf, dirname);
+	strcat(buf, "/");
+	strcat(buf, filename);
+
+	return buf;
+}
+
+#endif
+
+#ifdef L_expandwildcards
+
+/*
+ * Expand the wildcards in a filename, if any.
+ * Returns an argument list with matching filenames in sorted order.
+ * The expanded names are stored in memory chunks which can later all
+ * be freed at once.  Returns zero if the name is not a wildcard, or
+ * returns the count of matched files if the name is a wildcard and
+ * there was at least one match, or returns -1 if too many filenames
+ * matched (with an error output).
+ * If the name is a wildcard and no names match, returns 0 as
+ * if the name were not a wildcard.
+ */
+int
+expandwildcards(name, maxargc, retargv)
+	char	*name;
+	int	maxargc;
+	char	*retargv[];
+{
+	char	*last;
+	char	*cp1, *cp2, *cp3;
+	DIR	*dirp;
+	struct	dirent	*dp;
+	int	dirlen;
+	int	matches;
+	char	dirname[PATHLEN];
+
+	last = strrchr(name, '/');
+	if (last)
+		last++;
+	else
+		last = name;
+
+	cp1 = strchr(name, '*');
+	cp2 = strchr(name, '?');
+	cp3 = strchr(name, '[');
+
+	if ((cp1 == NULL) && (cp2 == NULL) && (cp3 == NULL))
+		return 0;
+
+	if ((cp1 && (cp1 < last)) || (cp2 && (cp2 < last)) ||
+		(cp3 && (cp3 < last)))
+	{
+		fprintf(stderr, "Wildcards only implemented for last filename component\n");
+		return -1;
+	}
+
+	dirname[0] = '.';
+	dirname[1] = '\0';
+
+	if (last != name) {
+		memcpy(dirname, name, last - name);
+		dirname[last - name - 1] = '\0';
+		if (dirname[0] == '\0') {
+			dirname[0] = '/';
+			dirname[1] = '\0';
+		}
+	}
+
+	dirp = opendir(dirname);
+	if (dirp == NULL) {
+		perror(dirname);
+		return -1;
+	}
+
+	dirlen = strlen(dirname);
+	if (last == name) {
+		dirlen = 0;
+		dirname[0] = '\0';
+	} else if (dirname[dirlen - 1] != '/') {
+		dirname[dirlen++] = '/';
+		dirname[dirlen] = '\0';
+	}
+
+	matches = 0;
+
+	while ((dp = readdir(dirp)) != NULL) {
+		if ((strcmp(dp->d_name, ".") == 0) ||
+			(strcmp(dp->d_name, "..") == 0))
+				continue;
+
+		if (!match(dp->d_name, last))
+			continue;
+
+		if (matches >= maxargc) {
+			fprintf(stderr, "Too many filename matches\n");
+			closedir(dirp);
+			return -1;
+		}
+
+		cp1 = getchunk(dirlen + strlen(dp->d_name) + 1);
+		if (cp1 == NULL) {
+			fprintf(stderr, "No memory for filename\n");
+			closedir(dirp);
+			return -1;
+		}
+
+		if (dirlen)
+			memcpy(cp1, dirname, dirlen);
+		strcpy(cp1 + dirlen, dp->d_name);
+
+		retargv[matches++] = cp1;
+	}
+
+	closedir(dirp);
+
+	if (matches == 0) {
+		return 0;
+	}
+
+	qsort((char *) retargv, matches, sizeof(char *), namesort);
+
+	return matches;
+}
+
+#endif
+
+#ifdef L_namesort
+
+/*
+ * Sort routine for list of filenames.
+ */
+int
+namesort(p1, p2)
+	char	**p1;
+	char	**p2;
+{
+	return strcmp(*p1, *p2);
+}
+
+#endif
+
+#ifdef L_match
+
+/*
+ * Routine to see if a text string is matched by a wildcard pattern.
+ * Returns TRUE if the text is matched, or FALSE if it is not matched
+ * or if the pattern is invalid.
+ *  *		matches zero or more characters
+ *  ?		matches a single character
+ *  [abc]	matches 'a', 'b' or 'c'
+ *  \c		quotes character c
+ *  Adapted from code written by Ingo Wilken.
+ */
+BOOL
+match(text, pattern)
+	char	*text;
+	char	*pattern;
+{
+	return fnmatch(pattern, text, 0) == 0;
+}
+#endif
+
+#ifdef L_makeargs
+
+/*
+ * Take a command string, and break it up into an argc, argv list.
+ * The returned argument list and strings are in static memory, and so
+ * are overwritten on each call.  The argument array is ended with an
+ * extra NULL pointer for convenience.  Returns TRUE if successful,
+ * or FALSE on an error with a message already output.
+ *
+ * Note that leading quotes are *not* removed at this point, but
+ * trailing quotes are.
+ */
+BOOL
+makeargs(cmd, argcptr, argvptr)
+	char	*cmd;
+	int	*argcptr;
+	char	***argvptr;
+{
+	char		*cp;
+	int		argc;
+	static char	strings[CMDLEN+1];
+	static char	*argtable[MAXARGS+1];
+	static char	quoted[MAXARGS+1];
+
+	/*
+	 * Copy the command string and then break it apart
+	 * into separate arguments.
+	 */
+	strcpy(strings, cmd);
+	argc = 0;
+	cp = strings;
+
+	while (*cp) {
+		if (argc >= MAXARGS) {
+			fprintf(stderr, "Too many arguments\n");
+			return FALSE;
+		}
+
+		quoted[argc] = 0;
+		argtable[argc++] = cp;
+
+		while (*cp && !isblank(*cp)) {
+			if (*cp == '"' || *cp == '\'') {
+				char *sp = cp++;
+
+				while (*cp && *cp != *sp)
+					cp++;
+
+				if (*cp == *sp) {
+					/* Chop off the trailing quote, but leave the leading quote
+					 * so that later processing will know the argument is quoted
+					 */
+					*cp++ = 0;
+				}
+			} else
+				cp++;
+		}
+
+		while (isblank(*cp))
+ 			*cp++ = '\0';
+	}
+
+	argtable[argc] = NULL;
+
+	*argcptr = argc;
+	*argvptr = argtable;
+
+ 	return TRUE;
+}
+
+#endif
+
+#ifdef L_makestring
+
+/*
+ * Make a NULL-terminated string out of an argc, argv pair.
+ * Returns TRUE if successful, or FALSE if the string is too long,
+ * with an error message given.  This does not handle spaces within
+ * arguments correctly.
+ */
+BOOL
+makestring(argc, argv, buf, buflen)
+	char	**argv;
+	char	*buf;
+{
+	int	len;
+
+	while (argc-- > 0) {
+		len = strlen(*argv);
+		if (len >= buflen) {
+			fprintf(stderr, "Argument string too long\n");
+			return FALSE;
+		}
+
+		strcpy(buf, *argv++);
+
+		buf += len;
+		buflen -= len;
+
+		if (argc)
+			*buf++ = ' ';
+		buflen--; 
+	}
+
+	*buf = '\0';
+
+	return TRUE;
+}
+
+#endif
+
+#ifdef L_chunks
+
+typedef	struct	chunk	CHUNK;
+#define	CHUNKINITSIZE	4
+struct	chunk	{
+	CHUNK	*next;
+	char	data[CHUNKINITSIZE];	/* actually of varying length */
+};
+
+
+static	CHUNK *	chunklist;
+
+
+/*
+ * Allocate a chunk of memory (like malloc).
+ * The difference, though, is that the memory allocated is put on a
+ * list of chunks which can be freed all at one time.  You CAN NOT free
+ * an individual chunk.
+ */
+char *
+getchunk(size)
+{
+	CHUNK	*chunk;
+
+	if (size < CHUNKINITSIZE)
+		size = CHUNKINITSIZE;
+
+	chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNKINITSIZE);
+	if (chunk == NULL)
+		return NULL;
+
+	chunk->next = chunklist;
+	chunklist = chunk;
+
+	return chunk->data;
+}
+
+
+/*
+ * Free all chunks of memory that had been allocated since the last
+ * call to this routine.
+ */
+void
+freechunks()
+{
+	CHUNK	*chunk;
+
+	while (chunklist) {
+		chunk = chunklist;
+		chunklist = chunk->next;
+		free((char *) chunk);
+	}
+}
+
+#endif
+
+
+#ifdef L_expandenvvar
+
+/* Expand environment variables
+ * Variable names must use a-z, A-Z, 0-9, or _
+ * Backslashes are also interpreted to preserve the literal value of the
+ * next character.
+ * Returns NULL if there is an error, otherwise returns a pointer
+ * to a static buffer containing the expand command line.
+ *
+ * Makes a lame attempt to not expand inside single quotes.
+ */
+char *
+expandenvvar(cmd)
+	char	*cmd;
+{
+	static char newcmd[CMDLEN+1];
+	char* newp = newcmd;
+	int freelength = CMDLEN;	/* Don't include final terminator */
+	char varname[CMDLEN+1];
+	char* varp;
+	char* value;
+	int valuelength;
+	int quoted = 0;
+
+	if (cmd == NULL) {
+		return NULL;
+	}
+
+	if (strlen(cmd) > freelength) {
+		fprintf(stderr, "Variable expansion too long\n");
+		return NULL;
+	}
+
+	while (*cmd) {
+		int copy = 1;
+
+		switch (*cmd) {
+		case '$':
+			if (!quoted) {
+				copy = 0;
+				cmd++;
+				varp = varname;
+				while (isalnum(*cmd) || (*cmd == '_') || (*cmd == '?')) {
+					*varp++ = *cmd++;
+				}
+				*varp = '\0';
+				if ((*varname) && (value = getenv(varname))) {
+					valuelength = strlen(value);
+					if (valuelength > freelength) {
+						fprintf(stderr, "Variable expansion too long\n");
+						return NULL;
+					}
+					strncpy(newp, value, valuelength);
+					newp += valuelength;
+					freelength -= valuelength;
+				}
+			}
+			break;
+
+		case '\'':
+			quoted = !quoted;
+			break;
+
+		case '\\':
+			cmd++;
+			break;
+		}
+
+		if (copy) {
+			if (freelength < 1) {
+				fprintf(stderr, "Variable expansion too long\n");
+				return NULL;
+			}
+			*newp++ = *cmd++;
+			freelength--;
+		}
+	}
+
+	*newp = '\0';
+
+	return newcmd;
+}
+
+#endif
+
+
+/* END CODE */

+ 311 - 0
package/sash/src/ls.c

@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 1993 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * The "ls" built-in command.
+ */
+
+#include "sash.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+
+#define	LISTSIZE	256
+
+#define	COLWIDTH	20
+
+#ifdef	S_ISLNK
+#define	LSTAT	lstat
+#else
+#define	LSTAT	stat
+#endif
+
+
+/*
+ * Flags for the LS command.
+ */
+#define	LSF_LONG	0x01
+#define	LSF_DIR		0x02
+#define	LSF_INODE	0x04
+#define	LSF_MULT	0x08
+#define LSF_ALL		0x10
+#define LSF_COMPACT	0x20
+
+
+static	char	**list;
+static	int	listsize;
+static	int	listused;
+static	int	linelen = 0;
+
+
+static	void	lsfile();
+
+
+void
+do_ls(argc, argv)
+	int argc;
+	char	**argv;
+{
+	char		*cp;
+	char		*name;
+	int		flags;
+	int		i;
+	DIR		*dirp;
+	BOOL		endslash;
+	char		**newlist;
+	struct	dirent	*dp;
+	char		fullname[PATHLEN];
+	struct	stat	statbuf;
+	static		char *def[2];
+
+	if (listsize == 0) {
+		list = (char **) malloc(LISTSIZE * sizeof(char *));
+		if (list == NULL) {
+			fprintf(stderr, "No memory for ls buffer\n");
+			return;
+		}
+		listsize = LISTSIZE;
+	}
+	listused = 0;
+	
+	flags = 0;
+	if ((argc > 1) && (argv[1][0] == '-'))
+	{
+		argc--;
+		cp = *(++argv) + 1;
+
+		while (*cp) switch (*cp++) {
+			case 'l':	flags |= LSF_LONG; break;
+			case 'd':	flags |= LSF_DIR; break;
+			case 'i':	flags |= LSF_INODE; break;
+			case 'a':	flags |= LSF_ALL; break;
+			case 'C':	flags |= LSF_COMPACT; break;
+			default:
+				fprintf(stderr, "Unknown option -%c\n", cp[-1]);
+				return;
+		}
+	}
+
+	if ((flags & LSF_COMPACT) && (flags & ~LSF_COMPACT)) {
+		fprintf(stderr, "Cannot do compact list with other options\n");
+		return;
+	}
+
+	if (argc <= 1) {
+		argc = 2;
+		argv = def;
+		argv[0] = "ls";
+		argv[1] = ".";
+	}
+
+	if (argc > 2)
+		flags |= LSF_MULT;
+
+	while (argc-- > 1) {
+		name = *(++argv);
+		endslash = (*name && (name[strlen(name) - 1] == '/'));
+
+		if (LSTAT(name, &statbuf) < 0) {
+			perror(name);
+			continue;
+		}
+
+		if ((flags & LSF_DIR) || (!S_ISDIR(statbuf.st_mode))) {
+			lsfile(name, &statbuf, flags);
+			continue;
+		}
+
+		/*
+		 * Do all the files in a directory.
+		 */
+		dirp = opendir(name);
+		if (dirp == NULL) {
+			perror(name);
+			continue;
+		}
+
+		if (flags & LSF_MULT)
+			printf("\n%s:\n", name);
+
+		while ((dp = readdir(dirp)) != NULL) {
+		
+			if ((dp->d_name[0] == '.') && !(flags & LSF_ALL))
+				continue;
+
+			fullname[0] = '\0';
+
+			if ((*name != '.') || (name[1] != '\0')) {
+				strcpy(fullname, name);
+				if (!endslash)
+					strcat(fullname, "/");
+			}
+
+			strcat(fullname, dp->d_name);
+
+			if (listused >= listsize) {
+				newlist = malloc((sizeof(char **)) * (listsize + LISTSIZE));
+				if (newlist == NULL) {
+					fprintf(stderr, "No memory for ls buffer\n");
+					break;
+				}
+				memcpy(newlist, list, sizeof(char**) * listsize);
+				free(list);
+				listsize += LISTSIZE;
+			}
+
+			list[listused] = strdup(fullname);
+			if (list[listused] == NULL) {
+				fprintf(stderr, "No memory for filenames\n");
+				break;
+			}
+			listused++;
+		}
+
+		closedir(dirp);
+
+		/*
+		 * Sort the files.
+		 */
+		qsort((char *) list, listused, sizeof(char *), namesort);
+
+		/*
+		 * Now finally list the filenames.
+		 */
+		for (i = 0; i < listused; i++) {
+			name = list[i];
+
+			if (LSTAT(name, &statbuf) < 0) {
+				perror(name);
+				free(name);
+				continue;
+			}
+
+			cp = strrchr(name, '/');
+			if (cp)
+				cp++;
+			else
+				cp = name;
+
+			lsfile(cp, &statbuf, flags);
+
+			free(name);
+		}
+
+		listused = 0;
+	}
+
+	if (linelen)
+		fputc('\n', stdout);
+}
+
+
+/*
+ * Do an LS of a particular file name according to the flags.
+ */
+static void
+lsfile(name, statbuf, flags)
+	char	*name;
+	struct	stat	*statbuf;
+{
+	char		*cp;
+	struct	passwd	*pwd;
+	struct	group	*grp;
+	int		len;
+	char		buf[PATHLEN];
+	static	char	username[12];
+	static	int	userid;
+	static	BOOL	useridknown;
+	static	char	groupname[12];
+	static	int	groupid;
+	static	BOOL	groupidknown;
+
+	cp = buf;
+	*cp = '\0';
+
+	if (flags & LSF_INODE) {
+		sprintf(cp, "%5d ", statbuf->st_ino);
+		cp += strlen(cp);
+	}
+
+	if (flags & LSF_LONG) {
+		strcpy(cp, modestring(statbuf->st_mode));
+		cp += strlen(cp);
+
+		sprintf(cp, "%3d ", statbuf->st_nlink);
+		cp += strlen(cp);
+
+		if (!useridknown || (statbuf->st_uid != userid)) {
+			/*pwd = getpwuid(statbuf->st_uid);
+			if (pwd)
+				strcpy(username, pwd->pw_name);
+			else*/
+				sprintf(username, "%d", statbuf->st_uid);
+			userid = statbuf->st_uid;
+			useridknown = TRUE;
+		}
+
+		sprintf(cp, "%-8s ", username);
+		cp += strlen(cp);
+
+		if (!groupidknown || (statbuf->st_gid != groupid)) {
+			/*grp = getgrgid(statbuf->st_gid);
+			if (grp)
+				strcpy(groupname, grp->gr_name);
+			else*/
+				sprintf(groupname, "%d", statbuf->st_gid);
+			groupid = statbuf->st_gid;
+			groupidknown = TRUE;
+		}
+
+		sprintf(cp, "%-8s ", groupname);
+		cp += strlen(cp);
+
+		if (S_ISBLK(statbuf->st_mode) || S_ISCHR(statbuf->st_mode))
+			sprintf(cp, "%3d, %3d ", major(statbuf->st_rdev),
+				minor(statbuf->st_rdev));
+		else
+			sprintf(cp, "%8d ", statbuf->st_size);
+		cp += strlen(cp);
+
+		sprintf(cp, " %-12s ", timestring(statbuf->st_mtime));
+	}
+
+	fputs(buf, stdout);
+	fputs(name, stdout);
+
+#ifdef	S_ISLNK
+	if ((flags & LSF_LONG) && S_ISLNK(statbuf->st_mode)) {
+		len = readlink(name, buf, PATHLEN - 1);
+		if (len >= 0) {
+			buf[len] = '\0';
+			printf(" -> %s", buf);
+		}
+	}
+#endif
+
+	if (flags & LSF_COMPACT) {
+		len = strlen(name);
+		if (len < COLWIDTH) {
+			for (; (len < COLWIDTH); len++)
+				fputc(' ', stdout);
+			linelen += COLWIDTH;
+		} else {
+			linelen = 80;
+		}
+
+		if (linelen >= 80) {
+			fputc('\n', stdout);
+			linelen = 0;
+		}
+	} else {
+		fputc('\n', stdout);
+	}
+}
+
+/* END CODE */

+ 317 - 0
package/sash/src/ps.c

@@ -0,0 +1,317 @@
+/* ps.c:
+ *
+ * Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "sash.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <linux/major.h>
+#ifdef __UC_LIBC_
+#include <linux/types.h>
+#endif
+#include <sys/time.h>
+#include <sys/param.h>
+#ifdef __UC_LIBC__
+#include <mathf.h>
+#endif
+
+char psbuf[256];
+char name[40];
+int pid, state;
+char statec;
+int ppid, pgrp, session;
+dev_t tty;
+char tty_name[10];
+
+char master[] = "pqrstuvwxyzabcde";
+
+#define MAJOR(x) ((x) >> 8)
+#define MINOR(x) ((x) & 0xff)
+
+int port_xlate[16] = {1, 3, 5, 7,9 ,11,13,15,
+                      2, 4, 6, 8,10,12,14,16};
+
+void dev_to_name(dev_t dev, char * ttyname)
+{
+	strcpy(ttyname, "");
+	if (MAJOR(dev) == 75)
+		sprintf(ttyname,"X%d", MINOR(dev));
+	else if (MAJOR(dev) == TTY_MAJOR)
+		sprintf(ttyname,"S%d", MINOR(dev)-64);
+	else if (MAJOR(dev) == PTY_SLAVE_MAJOR)
+		sprintf(ttyname,"%c%x", master[MINOR(dev) / 16], MINOR(dev) & 0xf);
+}
+
+void
+do_ps(argc, argv)
+	char	**argv;
+{
+	int i;
+	int h;
+	int max;
+	FILE * f;
+	DIR * d;
+	unsigned long bytes, sbytes;
+	struct dirent * de;
+	char *ext;
+	int l;
+	time_t time_now;
+	long uptime_secs;
+	float idle_secs;
+	float seconds, start, total_time;
+	int utime, stime, start_time;
+	int pcpu;
+	/*extern int _vfprintf_fp_ref, _vfscanf_fp_ref;*/
+
+#if 0
+	fclose(stdin);
+#endif 
+
+	printf("  PID PORT STAT  SIZE SHARED %%CPU COMMAND\n"/*, _vfprintf_fp_ref, _vfscanf_fp_ref*/);
+
+	h = open("/proc/uptime", O_RDONLY);
+		
+	if (h==-1) {
+		perror("Unable to open /proc/uptime\n");
+		return;
+	}
+	
+	l = read(h, psbuf, 255);
+
+	close(h);  
+
+
+	if (l<=0) {
+		perror("Unable to read uptime");
+		return;
+	}
+
+
+	psbuf[l] = '\0';
+	psbuf[255] = '\0';
+		
+	ext = psbuf;
+
+
+	uptime_secs = atol(ext);
+
+	
+	time_now = time(0);
+	
+	d = opendir("/proc");
+	if (!d)
+		return;
+	
+	while (de = readdir(d)) {
+	
+	
+		for(i=0;i<strlen(de->d_name);i++)
+			if (!isdigit(de->d_name[i]))
+				goto next;
+
+		sprintf(psbuf, "/proc/%s/stat", de->d_name);
+		
+		h = open(psbuf, O_RDONLY);
+		
+		if (h==-1)
+			continue;
+			
+		l = read(h, psbuf, 255);
+		if (l<=0) {
+			perror("Unable to read status");
+			close(h);
+			continue;
+		}
+		
+		psbuf[l] = '\0';
+		psbuf[255] = '\0';
+		
+		ext = strrchr(psbuf, ')');
+		ext[0] = '\0';
+
+		statec = ext[2];
+
+		ext += 4;
+		
+		ppid = atoi(ext);
+		ext = strchr(ext, ' ')+1;
+
+		pgrp = atoi(ext);
+		ext = strchr(ext, ' ')+1;
+		
+		session = atoi(ext);
+		ext = strchr(ext, ' ')+1;
+
+		tty = atoi(ext);
+		ext = strchr(ext, ' ')+1;
+
+		//printf("1|%s\n", ext);
+		//tpgid
+		ext = strchr(ext, ' ')+1;
+		
+		//printf("2|%s\n", ext);
+		//flags
+		ext = strchr(ext, ' ')+1;
+
+		//printf("3|%s\n", ext);
+		//min_flt
+		ext = strchr(ext, ' ')+1;
+
+		//printf("4|%s\n", ext);
+		//cmin_flt
+		ext = strchr(ext, ' ')+1;
+
+		//printf("5|%s\n", ext);
+		//maj_flt
+		ext = strchr(ext, ' ')+1;
+
+		//printf("6|%s\n", ext);
+		//cmaj_flt
+		ext = strchr(ext, ' ')+1;
+
+		//printf("7|%s\n", ext);
+		utime = atoi(ext);
+		ext = strchr(ext, ' ')+1;
+
+		//printf("8|%s\n", ext);
+		stime = atoi(ext);
+		ext = strchr(ext, ' ')+1;
+		
+		//printf("9|%s\n", ext);
+		//cutime
+		ext = strchr(ext, ' ')+1;
+
+		//printf("10|%s\n", ext);
+		//cstime
+		ext = strchr(ext, ' ')+1;
+		
+		//priority
+		ext = strchr(ext, ' ')+1;
+		
+		//nice
+		ext = strchr(ext, ' ')+1;
+		
+		//timeout
+		ext = strchr(ext, ' ')+1;
+
+		//it_real_value
+		ext = strchr(ext, ' ')+1;
+
+		start_time = atoi(ext);
+		
+		ext = strchr(psbuf, '(');
+		ext++;
+		strcpy(name, ext);
+		
+		pid = atoi(psbuf);
+		
+		
+		state = statec;
+		
+		close(h);
+		
+		dev_to_name(tty, tty_name);
+		
+		bytes = 0;
+		sbytes = 0;
+		sprintf(psbuf, "/proc/%s/status", de->d_name);
+
+		f = fopen(psbuf, "r");
+		
+		if (f) {
+			while (fgets(psbuf, 250, f)) {
+				if (strncmp(psbuf, "Mem:", 4) == 0) {
+					bytes = atol(psbuf+5);
+					bytes /= 1024;
+				} else if (strncmp(psbuf, "Shared:", 7) == 0) {
+					sbytes = atol(psbuf+8);
+					sbytes /= 1024;
+				} else if (strncmp(psbuf, "VmSize:", 7) == 0) {
+					bytes = atol(psbuf+8);
+				}
+			}
+			fclose(f);
+		}
+		
+
+		seconds = ((uptime_secs * (long)HZ) - start_time) / HZ;
+		
+		/*printf("seconds=%s\n", gcvt(seconds, 15, psbuf));*/
+		
+		start = time_now - seconds;
+		
+		/*
+		printf("1\n");
+
+		gcvt(start, 15, psbuf);
+
+		printf("2\n");
+		
+		printf("start=%s\n", psbuf);
+		
+		printf("utime=%d, stime=%d. start_time=%d\n", utime, stime, start_time);
+		*/
+		
+		total_time = (utime + stime);
+
+		/*printf("total_time=%s\n", gcvt(total_time, 15, psbuf));*/
+
+		pcpu = 	seconds ? 
+			(total_time * 10.0f * 100.0f / (float)HZ) / seconds :
+			0; 
+		if (pcpu > 999) pcpu = 999;
+
+
+		sprintf(psbuf, "/proc/%s/cmdline", de->d_name);
+		h = open(psbuf, O_RDONLY);
+		
+		if (h == -1) {
+			perror("Unable to open cmdline");
+			continue;
+		}
+		
+		l = read(h, psbuf, 255);
+		if (l < 0) {
+			perror("Unable to read cmdline");
+			close(h);
+			continue;
+		}
+		
+		close(h);
+		
+		/*
+		 * the args are NUL separated, substitute spaces instead
+		 */
+		psbuf[l] = '\0';
+		i=l;
+		while(psbuf[i] == '\0')
+			i--;		/* Don't bother with trailing NULs */
+		while(--i > 0)
+			if (psbuf[i] == '\0')
+				psbuf[i] = ' ';
+
+		printf("%5d %4s %c    %4ldK   %3ldK %2u.%u %s\n", pid, tty_name, state,
+			bytes, sbytes, 
+			 pcpu / 10, pcpu % 10, 
+			 /*(int)seconds / 60, (int)seconds % 60,*/
+			 l ? psbuf : name);
+	next:
+		;
+	}
+	
+	closedir(d);
+}
+

+ 93 - 0
package/sash/src/reboot.c

@@ -0,0 +1,93 @@
+/* shutdown.c:
+ *
+ * Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * JUN/99 -- copied from shutdown.c to make new reboot command.
+ *           (gerg@snapgear.com)
+ * AUG/99 -- added delay option to reboot
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <getopt.h>
+
+#if __GNU_LIBRARY__ > 5
+#include <sys/reboot.h>
+#endif
+
+int main(int argc, char *argv[])
+{
+	int delay = 0; /* delay in seconds before rebooting */
+	int rc;
+	int force = 0;
+  
+	while ((rc = getopt(argc, argv, "h?d:f")) > 0) {
+		switch (rc) {
+		case 'd':
+			delay = atoi(optarg);
+			break;
+		case 'f':
+			force = 1;
+			break;
+		case 'h':
+		case '?':
+		default:
+			printf("usage: reboot [-h] [-d <delay>] [-f]\n");
+			exit(0);
+			break;
+		}
+	}
+
+	if(delay > 0)
+		sleep(delay);
+#ifdef CONFIG_DISKtel
+	printf("unmounting /home\n");
+	if(umount("/home") != 0){
+		printf("unmounting failed!!!\n");
+	}
+#endif
+
+#ifdef CONFIG_USER_FLATFSD_FLATFSD
+	if (!force) {
+		/* Ask flatfsd to reboot us safely */
+		execlp("flatfsd", "flatfsd", "-b", NULL);
+		/* if this returns,  then force a reboot */
+	}
+#endif
+
+	kill(1, SIGTSTP);
+	sync();
+	signal(SIGTERM,SIG_IGN);
+	signal(SIGHUP,SIG_IGN);
+	setpgrp();
+	kill(-1, SIGTERM);
+	sleep(1);
+	kill(-1, SIGHUP);
+	sleep(1);
+	sync();
+	sleep(1);
+#if __GNU_LIBRARY__ > 5
+	reboot(0x01234567);
+#else
+	reboot(0xfee1dead, 672274793, 0x01234567);
+#endif
+	exit(0); /* Shrug */
+}
+

+ 1118 - 0
package/sash/src/sash.c

@@ -0,0 +1,1118 @@
+/*
+ * Copyright (c) 1993 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * Stand-alone shell for system maintainance for Linux.
+ * This program should NOT be built using shared libraries.
+ *
+ * 1.1.1, 	hacked to re-allow cmd line invocation of script file
+ *		Pat Adamo, padamo@unix.asb.com
+ */
+
+#include "sash.h"
+
+#ifndef CMD_HELP
+#define	CMD_HELP
+#endif
+#undef INTERNAL_PATH_EXPANSION
+#define FAVOUR_EXTERNAL_COMMANDS
+
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+static char version[] = "1.1.1";
+
+extern int intflag;
+
+extern void do_test();
+
+typedef struct {
+	char	name[10];
+	char	usage[30];
+	void	(*func)();
+	int	minargs;
+	int	maxargs;
+} CMDTAB;
+
+
+CMDTAB	cmdtab[] = {
+/*
+	"alias",	"[name [command]]", 	do_alias,
+	1,		MAXARGS,
+*/
+	"cd",		"[dirname]",		do_cd,
+	1,		2,
+			
+	"sleep",		"seconds",		do_sleep,
+	1,		2,
+
+	"chgrp",	"gid filename ...",	do_chgrp,
+	3,		MAXARGS,
+
+	"chmod",	"mode filename ...",	do_chmod,
+	3,		MAXARGS,
+
+	"chown",	"uid filename ...",	do_chown,
+	3,		MAXARGS,
+
+	"cmp",		"filename1 filename2",	do_cmp,
+	3,		3,
+
+	"cp",		"srcname ... destname",	do_cp,
+	3,		MAXARGS,
+
+/*
+	"dd",		"if=name of=name [bs=n] [count=n] [skip=n] [seek=n]", do_dd,
+	3,		MAXARGS,
+*/
+	"df",		"[file-system]",	do_df,
+	1,		2,
+
+	"echo",	"[args] ...",			do_echo,
+	1,		MAXARGS,
+
+/*
+	"ed",		"[filename]",		do_ed,
+	1,		2,
+*/
+
+	"exec",		"filename [args]",	do_exec,
+	2,		MAXARGS,
+
+	"exit",		"",			do_exit,
+	1,		1,
+
+	"free",		"",			do_free,
+	1,		1,
+
+/*
+	"-grep",	"[-in] word filename ...",	do_grep,
+	3,		MAXARGS,
+*/
+
+#ifdef CMD_HELP
+	"help",		"",			do_help,
+	1,		MAXARGS,
+#endif
+
+	"hexdump",	"[-s pos] filename",	do_hexdump,
+	1,		4,
+
+	"hostname",	"[hostname]",		do_hostname,
+	1,		2,
+
+	"kill",		"[-sig] pid ...",	do_kill,
+	2,		MAXARGS,
+
+	"ln",		"[-s] srcname ... destname",	do_ln,
+	3,		MAXARGS,
+
+	"ls",		"[-lidC] filename ...",	do_ls,
+	1,		MAXARGS,
+
+	"mkdir",	"dirname ...",		do_mkdir,
+	2,		MAXARGS,
+
+	"mknod",	"filename type major minor",	do_mknod,
+	5,		5,
+
+	"more",	"filename ...",		do_more,
+	2,		MAXARGS,
+
+	"mount",	"[-t type] devname dirname",	do_mount,
+	3,		MAXARGS,
+
+	"mv",		"srcname ... destname",	do_mv,
+	3,		MAXARGS,
+
+	"printenv",	"[name]",		do_printenv,
+	1,		2,
+
+	"pwd",		"",			do_pwd,
+	1,		1,
+
+	"pid",		"",			do_pid,
+	1,		1,
+
+	"quit",		"",			do_exit,
+	1,		1,
+
+	"rm",		"filename ...",		do_rm,
+	2,		MAXARGS,
+
+	"rmdir",	"dirname ...",		do_rmdir,
+	2,		MAXARGS,
+
+	"setenv",	"name value",		do_setenv,
+	3,		3,
+
+	"source",	"filename",		do_source,
+	2,		2,
+
+	"sync",	"",			do_sync,
+	1,		1,
+
+/*	"time",	"",			do_time,
+	1,		1,
+*/
+/*
+	"tar",		"[xtv]f devname filename ...",	do_tar,
+	2,		MAXARGS,
+*/
+	"touch",	"filename ...",		do_touch,
+	2,		MAXARGS,
+
+	"umask",	"[mask]",		do_umask,
+	1,		2,
+
+	"umount",	"filename",		do_umount,
+	2,		2,
+
+/*
+	"unalias",	"name",			do_unalias,
+	2,		2,
+*/
+#ifdef CONFIG_USER_SASH_PS
+	"ps",		"",			do_ps,
+	1,		MAXARGS,
+#endif
+
+/*	"reboot",	"",			do_reboot,
+	1,		MAXARGS,
+*/
+	"cat",		"filename ...",		do_cat,
+	2,		MAXARGS,
+
+	"date",		"date [MMDDhhmm[YYYY]]",	do_date,
+	1,		2,
+
+	0,		0,			0,
+	0,		0
+};
+
+
+typedef struct {
+	char	*name;
+	char	*value;
+} ALIAS;
+
+
+static	ALIAS	*aliastable;
+static	int	aliascount;
+
+static	FILE	*sourcefiles[MAXSOURCE];
+static	int	sourcecount;
+
+volatile static	BOOL	intcrlf = TRUE;
+
+
+static	void	catchint();
+static	void	catchquit();
+static	void	catchchild();
+static	void	readfile();
+static	void	command();
+#ifdef COMMAND_HISTORY
+#define do_command(c,h)	command(c,h)
+#else
+#define do_command(c,h)	command(c)
+#endif
+static	void	runcmd();
+static	void	showprompt();
+static	BOOL	trybuiltin();
+static	BOOL	command_in_path();
+static	ALIAS	*findalias();
+
+extern char ** environ;
+
+/* 
+char text1[] = "Text";
+char * text2 = text1;
+char ** text3 = &text2;
+*/
+
+char	buf[CMDLEN];
+int exit_code = 0;
+
+main(argc, argv, env)
+	char	**argv;
+	char	*env[];
+{
+	struct sigaction act;
+	char	*cp;
+/*	char	buf[PATHLEN];*/
+	int dofile = 0;
+	
+	if ((argc > 1) && !strcmp(argv[1], "-c")) {
+		/* We are that fancy a shell */
+		buf[0] = '\0';
+		for (dofile = 2; dofile < argc; dofile++) {
+			strncat(buf, argv[dofile], sizeof(buf));
+			if (dofile + 1 < argc)
+				strncat(buf, " ", sizeof(buf));
+		}
+		do_command(buf, FALSE);
+		exit(exit_code);
+	}
+
+	//;'pa990523 +
+	if ((argc > 1) && strcmp(argv[1], "-t"))
+		{
+		dofile++;
+		printf("Shell invoked to run file: %s\n",argv[1]);
+		}
+	else
+		printf("\nSash command shell (version %s)\n", version);
+	fflush(stdout);
+
+	signal(SIGINT, catchint);
+	signal(SIGQUIT, catchquit);
+
+	memset(&act, 0, sizeof(act));
+	act.sa_handler = catchchild;
+	act.sa_flags = SA_RESTART;
+	sigaction(SIGCHLD, &act, NULL);
+
+	if (getenv("PATH") == NULL)
+		putenv("PATH=/bin:/usr/bin:/etc:/sbin:/usr/sbin");
+
+/*	cp = getenv("HOME");
+	if (cp) {
+		strcpy(buf, cp);
+		strcat(buf, "/");
+		strcat(buf, ".aliasrc");
+
+		if ((access(buf, 0) == 0) || (errno != ENOENT))
+			readfile(buf);
+	}
+*/	
+	//;'pa990523 -1/+
+	//readfile(NULL);
+	if (dofile)
+		{
+		//open the file for reading!
+		readfile(argv[1]);
+		}
+	   else
+		{
+		readfile(NULL); //no arguments!
+		} //end if arguments supplied
+	exit(exit_code);
+}
+
+
+/*
+ * Read commands from the specified file.
+ * A null name pointer indicates to read from stdin.
+ */
+static void
+readfile(name)
+	char	*name;
+{
+	FILE	*fp;
+	int	cc;
+	BOOL	ttyflag;
+	char	*ptr;
+
+	if (sourcecount >= MAXSOURCE) {
+		fprintf(stderr, "Too many source files\n");
+		return;
+	}
+
+	fp = stdin;
+	if (name) {
+		fp = fopen(name, "r");
+		if (fp == NULL) {
+			perror(name);
+			return;
+		}
+	}
+	sourcefiles[sourcecount++] = fp;
+
+	ttyflag = isatty(fileno(fp));
+
+	while (TRUE) {
+		fflush(stdout);
+		//;'pa990523 -1/+1
+		//if (1)
+		if (fp == stdin) //using terminal, so show prompt
+			showprompt();
+
+		if (intflag && !ttyflag && (fp != stdin)) {
+			fclose(fp);
+			sourcecount--;
+			return;
+		}
+
+		if (fgets(buf, CMDLEN - 1, fp) == NULL) {
+			if (ferror(fp) && (errno == EINTR)) {
+				clearerr(fp);
+				continue;
+			}
+			break;
+		}
+
+		cc = strlen(buf);
+
+		while ((cc > 0) && isspace(buf[cc - 1]))
+			cc--;
+		buf[cc] = '\0';
+		/* remove leading spaces and look for a '#' */
+		ptr = &buf[0];
+		while (*ptr == ' ') {
+			ptr++;
+		}
+		if (*ptr != '#') {
+			//;'pa990523 +
+			if (fp != stdin) {
+				//taking commands from file - echo
+				printf("Command: %s\n",buf);
+			} //end if (fp != stdin)
+
+			do_command(buf, fp == stdin);
+		}
+	}
+
+
+
+	if (ferror(fp)) {
+		perror("Reading command line");
+		if (fp == stdin)
+			exit(1);
+	}
+
+	clearerr(fp);
+	if (fp != stdin)
+		{//;'pa990523 added braces and printf
+		fclose(fp);
+		printf("Execution Finished, Exiting\n");
+		} //end if (fp != stdin)
+
+	sourcecount--;
+}
+
+
+/*
+ * Parse and execute one null-terminated command line string.
+ * This breaks the command line up into words, checks to see if the
+ * command is an alias, and expands wildcards.
+ */
+static void
+#ifdef COMMAND_HISTORY
+command(cmd, do_history)
+	int do_history;
+#else
+command(cmd)
+#endif
+	char	*cmd;
+{
+	ALIAS	*alias;
+	char	**argv;
+	int	argc;
+	int 	bg;
+	char   *c;
+
+	char last_exit_code[10];
+
+	sprintf(last_exit_code, "%d", exit_code);
+
+	intflag = FALSE;
+	exit_code = 0;
+
+	freechunks();
+
+	while (isblank(*cmd))
+		cmd++;
+
+#ifdef COMMAND_HISTORY
+	if (do_history) {
+		int i;
+		static char *history[HISTORY_SIZE];
+
+		if (*cmd == '!') {
+			if (cmd[1] == '!')
+				i = 0;
+			else {
+				i = atoi(cmd+1) - 1;
+				if (i < 0 || i >= HISTORY_SIZE) {
+					printf("%s: Out of range\n", cmd);
+					return;
+				}
+			}
+			if (history[i] == NULL) {
+				printf("%s: Null entry\n", cmd);
+				return;
+			}
+			strcpy(cmd, history[i]);
+		} else if (*cmd == 'h' && cmd[1] == '\0') {
+			for (i=0; i<HISTORY_SIZE; i++) {
+				if (history[i] != NULL)
+					printf("%2d: %s\n", i+1, history[i]);
+			}
+			return;
+		} else if (*cmd != '\0') {
+			if (history[HISTORY_SIZE-1] != NULL)
+				free(history[HISTORY_SIZE-1]);
+			for (i=HISTORY_SIZE-1; i>0; i--)
+				history[i] = history[i-1];
+			history[0] = strdup(cmd);
+		}
+	}
+#endif
+	if (c = strchr(cmd, '&')) {
+		*c = '\0';
+		bg = 1;
+	} else
+		bg = 0;
+
+	/* Set the last exit code */
+	setenv("?", last_exit_code, 1);
+	
+	if ((cmd = expandenvvar(cmd)) == NULL)
+		return;
+
+	if ((*cmd == '\0') || !makeargs(cmd, &argc, &argv))
+		return;
+
+	/*
+	 * Search for the command in the alias table.
+	 * If it is found, then replace the command name with
+	 * the alias, and append any other arguments to it.
+	 */
+	alias = findalias(argv[0]);
+	if (alias) {
+		cmd = buf;
+		strcpy(cmd, alias->value);
+
+		while (--argc > 0) {
+			strcat(cmd, " ");
+			strcat(cmd, *++argv);
+		}
+
+		if (!makeargs(cmd, &argc, &argv))
+			return;
+	}
+
+	/*
+	 * BASH-style variable setting
+	 */
+	if (argc == 1) {
+		c = index(argv[0], '=');
+		if (c > argv[0]) {
+			*c++ = '\0';
+			setenv(argv[0], c, 1);
+			return;
+		}
+	}
+		
+	/*
+	 * Now look for the command in the builtin table, and execute
+	 * the command if found.
+	 */
+#ifdef FAVOUR_EXTERNAL_COMMANDS
+	if (!command_in_path(argv[0]))
+#endif
+	if (trybuiltin(argc, argv))
+		return;
+
+	/*
+	 * Not found, run the program along the PATH list.
+	 */
+	runcmd(cmd, bg, argc, argv);
+}
+
+
+#ifdef FAVOUR_EXTERNAL_COMMANDS
+/*
+ * return true if we find this command in our
+ * path.
+ */
+static BOOL
+command_in_path(char *cmd)
+{
+	struct stat	stat_buf;
+
+	if (strchr(cmd, '/') == 0) {
+		char	* path;
+		static char	path_copy[PATHLEN];
+		
+		/* Search path for binary */
+		for (path = getenv("PATH"); path && *path; ) {
+			char * p2;
+
+			strcpy(path_copy, path);
+			if (p2 = strchr(path_copy, ':')) {
+				*p2 = '\0';
+			}
+		
+			if (strlen(path_copy))
+				strcat(path_copy, "/");
+			strcat(path_copy, cmd);
+			
+			if (!stat(path_copy, &stat_buf) && (stat_buf.st_mode & 0111))
+				return(TRUE);
+			
+			p2 = strchr(path, ':');
+			if (p2)
+				path = p2 + 1;
+			else
+				path = 0;
+		}
+	} else if (!stat(cmd, &stat_buf) && (stat_buf.st_mode & 0111))
+		return(TRUE);
+	return(FALSE);
+}
+#endif /* FAVOUR_EXTERNAL_COMMANDS */
+
+
+/*
+ * Try to execute a built-in command.
+ * Returns TRUE if the command is a built in, whether or not the
+ * command succeeds.  Returns FALSE if this is not a built-in command.
+ */
+static BOOL
+trybuiltin(argc, argv)
+	char	**argv;
+{
+	CMDTAB	*cmdptr;
+	int	oac;
+	int	newargc;
+	int	matches;
+	int	i;
+	char	*newargv[MAXARGS];
+	char	*nametable[MAXARGS];
+
+	cmdptr = cmdtab - 1;
+	do {
+		cmdptr++;
+		if (cmdptr->name[0] == 0)
+			return FALSE;
+
+	} while (strcmp(argv[0], cmdptr->name));
+	
+	/*
+	 * Give a usage string if the number of arguments is too large
+	 * or too small.
+	 */
+	if ((argc < cmdptr->minargs) || (argc > cmdptr->maxargs)) {
+		fprintf(stderr, "usage: %s %s\n",
+			cmdptr->name, cmdptr->usage);
+		fflush(stderr);
+
+		return TRUE;
+	}
+
+	/*
+	 * Check here for several special commands which do not
+	 * have wildcarding done for them.
+	 */
+
+/*        if (cmdptr->func == do_prompt) {
+		(*cmdptr->func)(argc, argv);
+		return TRUE;
+	}
+*/
+
+	/*
+	 * Now for each command argument, see if it is a wildcard, and if
+	 * so, replace the argument with the list of matching filenames.
+	 */
+	newargv[0] = argv[0];
+	newargc = 1;
+	oac = 0;
+
+	while (++oac < argc) {
+		if (argv[oac][0] == '"' || argv[oac][0] == '\'') {
+			argv[oac]++;
+			matches = 0;
+		}
+		else {
+			matches = expandwildcards(argv[oac], MAXARGS, nametable);
+			if (matches < 0)
+				return TRUE;
+		}
+
+		if ((newargc + matches) >= MAXARGS) {
+			fprintf(stderr, "Too many arguments\n");
+			return TRUE;
+		}
+
+		if (matches == 0)
+			newargv[newargc++] = argv[oac];
+
+		for (i = 0; i < matches; i++)
+			newargv[newargc++] = nametable[i];
+	}
+
+	(*cmdptr->func)(newargc, newargv);
+
+	return TRUE;
+}
+
+
+/*
+ * Execute the specified command.
+ */
+static void
+runcmd(cmd, bg, argc, argv)
+	char	*cmd;
+	int	bg;
+	int	argc;
+	char	**argv;
+{
+	register char *	cp;
+	BOOL		magic;
+	int		pid;
+	int		status;
+	int oac;
+	int newargc;
+	int matches;
+	int i;
+	char	*newargv[MAXARGS];
+	char	*nametable[MAXARGS];
+	struct sigaction act;
+	
+	newargv[0] = argv[0];
+	
+#ifdef INTERNAL_PATH_EXPANSION
+	if (strchr(argv[0], '/') == 0) {
+		char	* path;
+		struct stat	stat_buf;
+		static char	path_copy[PATHLEN];
+		
+		/* Search path for binary */
+		for (path = getenv("PATH"); path && *path; ) {
+			char * p2;
+			strncpy(path_copy, path, sizeof(path_copy - 1));
+			if (p2 = strchr(path_copy, ':')) {
+				*p2 = '\0';
+			}
+		
+			if (strlen(path_copy))
+				strncat(path_copy, "/", sizeof(path_copy));
+			strncat(path_copy, argv[0], sizeof(path_copy));
+			
+			if (!stat(path_copy, &stat_buf) && (stat_buf.st_mode & 0111)) {
+				newargv[0] = path_copy;
+				break;
+			}
+			
+			p2 = strchr(path, ':');
+			if (p2)
+				path = p2 + 1;
+			else
+				path = 0;
+		}
+	}
+#endif
+
+	/*
+	 * Now for each command argument, see if it is a wildcard, and if
+	 * so, replace the argument with the list of matching filenames.
+	 */
+	newargc = 1;
+	oac = 0;
+
+	while (++oac < argc) {
+		if (argv[oac][0] == '"' || argv[oac][0] == '\'') {
+			argv[oac]++;
+			matches = 0;
+		}
+		else {
+			matches = expandwildcards(argv[oac], MAXARGS, nametable);
+			if (matches < 0)
+				return;
+		}
+
+		if ((newargc + matches) >= MAXARGS) {
+			fprintf(stderr, "Too many arguments\n");
+			return;
+		}
+
+		if (matches == 0)
+			newargv[newargc++] = argv[oac];
+
+		for (i = 0; i < matches; i++)
+			newargv[newargc++] = nametable[i];
+	}
+	
+	newargv[newargc] = 0;
+
+	magic = FALSE;
+	
+	/*
+	for (cp = cmd; *cp; cp++) {
+		if ((*cp >= 'a') && (*cp <= 'z'))
+			continue;
+		if ((*cp >= 'A') && (*cp <= 'Z'))
+			continue;	
+		if (isdecimal(*cp))
+			continue;
+		if (isblank(*cp))
+			continue;
+
+		if ((*cp == '.') || (*cp == '/') || (*cp == '-') ||
+			(*cp == '+') || (*cp == '=') || (*cp == '_') ||
+			(*cp == ':') || (*cp == ','))
+				continue;
+
+		magic = TRUE;
+	}
+	*/
+
+	if (magic) {
+		printf("%s: no such file or directory\n", cmd);
+		system(cmd);
+		return;
+	}
+	
+	if (!bg)
+		signal(SIGCHLD, SIG_DFL);
+
+	/*
+	 * No magic characters in the expanded command, so do the fork and
+	 * exec ourself.  If this fails with ENOEXEC, then run the
+	 * shell anyway since it might be a shell script.
+	 */
+	if (!(pid = vfork())) {
+		int	ci;
+
+		/*
+		 * We are the child, so run the program.
+		 * First close any extra file descriptors we have opened.
+		 * be sure not to modify any globals after the vfork !
+		 */	
+		
+		for (ci = 0; ci < sourcecount; ci++)
+			if (sourcefiles[ci] != stdin)
+				close(fileno(sourcefiles[ci]));
+		
+		signal(SIGINT, SIG_DFL);
+		signal(SIGQUIT, SIG_DFL);
+		signal(SIGCHLD, SIG_DFL);
+		
+		execvp(newargv[0], newargv);
+
+		printf("%s: %s\n", newargv[0], (errno == ENOENT) ? "Bad command or file name" : strerror(errno));
+		
+		_exit(0);
+	}
+	
+	if (pid < 0) {
+		memset(&act, 0, sizeof(act));
+		act.sa_handler = catchchild;
+		act.sa_flags = SA_RESTART;
+		sigaction(SIGCHLD, &act, NULL);
+
+		perror("vfork failed");
+		return;
+	}
+	
+	if (bg) {
+		printf("[%d]\n", pid);
+		return;
+	}
+
+	if (pid) {
+		int cpid;
+		status = 0;
+		intcrlf = FALSE;
+
+		for (;;) {
+			cpid = wait4(pid, &status, 0, 0);
+			if ((cpid < 0) && (errno == EINTR))
+				continue;
+			if (cpid < 0)
+				break;
+			if (cpid != pid) {
+				fprintf(stderr, "sh %d: child %d died\n", getpid(), cpid);
+				continue;
+			}
+		}
+
+		act.sa_handler = catchchild;
+		memset(&act.sa_mask, 0, sizeof(act.sa_mask));
+		act.sa_flags = SA_RESTART;
+		sigaction(SIGCHLD, &act, NULL);
+		
+		intcrlf = TRUE;
+
+		if (WIFEXITED(status)) {
+			if (WEXITSTATUS(status) == 0)
+				return;
+			exit_code = WEXITSTATUS(status);
+		} else
+			exit_code = 1;
+
+		return;
+	}
+	
+	perror(argv[0]);
+	exit(1);
+}
+
+#ifdef CMD_HELP
+void
+do_help(argc, argv)
+	char	**argv;
+{
+	CMDTAB	*cmdptr;
+
+	for (cmdptr = cmdtab; cmdptr->name && cmdptr->name[0]; cmdptr++)
+		printf("%-10s %s\n", cmdptr->name, cmdptr->usage);
+}
+#endif /* CMD_HELP */
+
+#ifdef CMD_ALIAS
+void
+do_alias(argc, argv)
+	char	**argv;
+{
+	char	*name;
+	char	*value;
+	ALIAS	*alias;
+	int	count;
+	char	buf[CMDLEN];
+
+	if (argc < 2) {
+		count = aliascount;
+		for (alias = aliastable; count-- > 0; alias++)
+			printf("%s\t%s\n", alias->name, alias->value);
+		return;
+	}
+
+	name = argv[1];
+	if (argc == 2) {
+		alias = findalias(name);
+		if (alias)
+			printf("%s\n", alias->value);
+		else
+			fprintf(stderr, "Alias \"%s\" is not defined\n", name);
+		return;	
+	}
+
+	if (strcmp(name, "alias") == 0) {
+		fprintf(stderr, "Cannot alias \"alias\"\n");
+		return;
+	}
+
+	if (!makestring(argc - 2, argv + 2, buf, CMDLEN))
+		return;
+
+	value = malloc(strlen(buf) + 1);
+
+	if (value == NULL) {
+		fprintf(stderr, "No memory for alias value\n");
+		return;
+	}
+
+	strcpy(value, buf);
+
+	alias = findalias(name);
+	if (alias) {
+		free(alias->value);
+		alias->value = value;
+		return;
+	}
+
+	if ((aliascount % ALIASALLOC) == 0) {
+		count = aliascount + ALIASALLOC;
+
+		if (aliastable)
+			alias = (ALIAS *) realloc(aliastable,
+				sizeof(ALIAS *) * count);
+		else
+			alias = (ALIAS *) malloc(sizeof(ALIAS *) * count);
+
+		if (alias == NULL) {
+			free(value);
+			fprintf(stderr, "No memory for alias table\n");
+			return;
+		}
+
+		aliastable = alias;
+	}
+
+	alias = &aliastable[aliascount];
+
+	alias->name = malloc(strlen(name) + 1);
+
+	if (alias->name == NULL) {
+		free(value);
+		fprintf(stderr, "No memory for alias name\n");
+		return;
+	}
+
+	strcpy(alias->name, name);
+	alias->value = value;
+	aliascount++;
+}
+#endif /* CMD_ALIAS */
+
+/*
+ * Look up an alias name, and return a pointer to it.
+ * Returns NULL if the name does not exist.
+ */
+static ALIAS *
+findalias(name)
+	char	*name;
+{
+	ALIAS	*alias;
+	int	count;
+
+	count = aliascount;
+	for (alias = aliastable; count-- > 0; alias++) {
+		if (strcmp(name, alias->name) == 0)
+			return alias;
+	}
+
+	return NULL;
+}
+
+
+void
+do_source(argc, argv)
+	char	**argv;
+{
+	readfile(argv[1]);
+}
+
+/*void
+do_cd(argc, argv)
+	char	**argv;
+{
+	char	*name;
+
+	name = argv[1];
+	
+	if (chdir(name))
+		perror("Unable to chdir to %s");
+	
+}*/
+
+void
+do_pid(argc, argv)
+{
+	printf("%d\n", getpid());
+}
+
+void
+do_exec(argc, argv)
+	char	**argv;
+{
+	while (--sourcecount >= 0) {
+		if (sourcefiles[sourcecount] != stdin)
+			fclose(sourcefiles[sourcecount]);
+	}
+
+	argv[argc] = NULL;
+	execvp(argv[1], &argv[1]);
+
+	perror(argv[1]);
+	exit(1);
+}
+
+/*void
+do_exit(argc, argv)
+	char	**argv;
+{
+	if (argc>1)
+		exit(atoi(argv[1]));
+	else
+		exit(0);
+}*/
+
+
+#ifdef CMD_ALIAS
+void
+do_unalias(argc, argv)
+	char	**argv;
+{
+	ALIAS	*alias;
+
+	while (--argc > 0) {
+		alias = findalias(*++argv);
+		if (alias == NULL)
+			continue;
+
+		free(alias->name);
+		free(alias->value);
+		aliascount--;
+		alias->name = aliastable[aliascount].name;
+		alias->value = aliastable[aliascount].value;	
+	}
+}
+#endif /* CMD_ALIAS */
+
+/*
+ * Display the prompt string.
+ */
+static void
+showprompt()
+{
+	char	*cp;
+	//;'pa990523 changed from 6...
+	char buf[60];
+	
+	if ((cp = getenv("PS1")) != NULL) {
+		printf("%s", cp);
+	}
+	else {
+		*buf = '\0';
+		getcwd(buf, sizeof(buf) - 1);
+		printf("%s> ", buf);
+	}
+	fflush(stdout);
+}	
+
+
+static void
+catchint()
+{
+	signal(SIGINT, catchint);
+
+	intflag = TRUE;
+
+	if (intcrlf)
+		write(STDOUT, "\n", 1);
+}
+
+
+static void
+catchquit()
+{
+	signal(SIGQUIT, catchquit);
+
+	intflag = TRUE;
+
+	if (intcrlf)
+		write(STDOUT, "\n", 1);
+}
+
+static void
+catchchild()
+{
+	char buf[40];
+	pid_t pid;
+	int status;
+	
+	/*signal(SIGCHLD, catchchild);*/ /* Unneeded */
+
+	pid = wait4(-1, &status, WUNTRACED, 0);
+	if (WIFSTOPPED(status))
+		sprintf(buf, "sh %d: Child %d stopped\n", getpid(), pid);
+	else
+		sprintf(buf, "sh %d: Child %d died\n", getpid(), pid);
+	
+	if (intcrlf)
+		write(STDOUT, "\n", 1);
+	
+	write(STDOUT, buf, strlen(buf));
+}
+
+/* END CODE */

+ 70 - 0
package/sash/src/sash.h

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1993 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * Definitions for stand-alone shell for system maintainance for Linux.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <ctype.h>
+
+#define	PATHLEN		256	
+#define	CMDLEN		1024
+#define	MAXARGS		50	
+#define	ALIASALLOC	20
+#define	STDIN		0
+#define	STDOUT		1
+#define	MAXSOURCE	10
+#ifdef COMMAND_HISTORY
+#define HISTORY_SIZE	20	/* Number of entries in command history */
+#endif
+
+#ifndef	isblank
+#define	isblank(ch)	(((ch) == ' ') || ((ch) == '\t'))
+#endif
+
+#define	isquote(ch)	(((ch) == '"') || ((ch) == '\''))
+#define	isdecimal(ch)	(((ch) >= '0') && ((ch) <= '9'))
+#define	isoctal(ch)	(((ch) >= '0') && ((ch) <= '7'))
+
+
+typedef	int	BOOL;
+
+#define	FALSE	((BOOL) 0)
+#define	TRUE	((BOOL) 1)
+
+
+extern	void	do_alias(), do_cd(), do_exec(), do_exit(), do_prompt();
+extern	void	do_source(), do_umask(), do_unalias(), do_help(), do_ln();
+extern	void	do_cp(), do_mv(), do_rm(), do_chmod(), do_mkdir(), do_rmdir();
+extern	void	do_mknod(), do_chown(), do_chgrp(), do_sync(), do_printenv();
+extern	void	do_more(), do_cmp(), do_touch(), do_ls(), do_dd(), do_tar();
+extern	void	do_mount(), do_umount(), do_setenv(), do_pwd(), do_echo();
+extern	void	do_kill(), do_grep(), do_ed(), do_hexdump(), do_pid();
+extern	void	do_df(), do_ps(), do_reboot(), do_cat(), do_time(), do_free();
+extern	void	do_hostname(), do_sleep();
+extern	void	do_date();
+
+
+extern	char	*buildname();
+extern	char	*modestring();
+extern	char	*timestring();
+extern	BOOL	isadir();
+extern	BOOL	copyfile();
+extern	BOOL	match();
+extern	BOOL	makestring();
+extern	BOOL	makeargs();
+extern	int	expandwildcards();
+extern	int	namesort();
+extern	char	*getchunk();
+extern	void	freechunks();
+extern	char	*expandenvvar();
+
+extern	BOOL	intflag;
+extern	int	exit_code;
+
+/* END CODE */

+ 75 - 0
package/sash/src/shutdown.c

@@ -0,0 +1,75 @@
+/* shutdown.c:
+ *
+ * Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "sash.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <signal.h>
+
+#if __GNU_LIBRARY__ > 5
+#include <sys/reboot.h>
+#endif
+
+int
+main(argc, argv)
+	int argc;
+	char	**argv;
+{
+	char *progname = argv[0];
+
+	if (argc > 2 && strcmp(argv[1], "-d") == 0) {
+		sleep(atoi(argv[2]));
+		argc -= 2;
+	}
+	if ((argc != 3) || (strcmp(argv[1], "-h") && strcmp(argv[1], "-r")) || strcmp(argv[2], "now")) {
+		printf("Usage: %s [-d delay] -h|-r now\n", progname);
+		exit(0);
+	}
+	
+	kill(1, SIGTSTP);
+	sync();
+	signal(SIGTERM,SIG_IGN);
+	setpgrp();
+	kill(-1, SIGTERM);
+	sleep(1);
+	kill(-1, SIGHUP); /* Force PPPD's down, too */
+	sleep(1);
+	kill(-1, SIGKILL);
+	sync();
+	sleep(1);
+	
+	if (strcmp(argv[1], "-h")==0) {
+#if __GNU_LIBRARY__ > 5
+		reboot(0xCDEF0123);
+#else
+		reboot(0xfee1dead, 672274793, 0xCDEF0123);
+#endif
+	} else {
+#if __GNU_LIBRARY__ > 5
+		reboot(0x01234567);
+#else
+		reboot(0xfee1dead, 672274793, 0x01234567);
+#endif
+	}
+	
+	exit(0); /* Shrug */
+}
+

+ 30 - 0
package/simpleinit/Makefile

@@ -0,0 +1,30 @@
+# This file is part of the OpenADK project. OpenADK is copyrighted
+# material, please see the LICENCE file in the top-level directory.
+
+include ${ADK_TOPDIR}/rules.mk
+
+PKG_NAME:=		simpleinit
+PKG_VERSION:=		1.0
+PKG_RELEASE:=		1
+PKG_DESCR:=		simple init for systems without mmu
+PKG_SECTION:=		base/apps
+
+NO_DISTFILES:=		1
+
+include ${ADK_TOPDIR}/mk/package.mk
+
+$(eval $(call PKG_template,SIMPLEINIT,simpleinit,${PKG_VERSION}-${PKG_RELEASE},${PKG_DEPENDS},${PKG_DESCR},${PKG_SECTION}))
+
+CONFIG_STYLE:=		manual
+BUILD_STYLE:=		manual
+INSTALL_STYLE:=		manual
+
+do-build:
+	${TARGET_CC} ${TARGET_CPPFLAGS} ${TARGET_CFLAGS} ${TARGET_LDFLAGS} \
+		-o ${WRKBUILD}/simpleinit ${WRKBUILD}/simpleinit.c
+
+do-install:
+	${INSTALL_DIR} ${IDIR_SIMPLEINIT}/sbin
+	${INSTALL_BIN} ${WRKBUILD}/simpleinit ${IDIR_SIMPLEINIT}/sbin/init
+
+include ${ADK_TOPDIR}/mk/pkg-bottom.mk

+ 47 - 0
package/simpleinit/files/rc

@@ -0,0 +1,47 @@
+#!/bin/sh
+set -x
+export PATH=/bin:/sbin:/usr/bin:/usr/sbin
+ln -s /proc/self/fd/2 /dev/stderr
+: ${rcquiet=0}
+if [ $rcquiet -ne 1 ];then
+	echo "System initialization ..."
+fi
+
+# remount /dev with smaller size
+mount -o remount,nosuid,size=128k,mode=0755 -t tmpfs mdev /dev
+
+# start mdev dynamic device node management
+echo >/dev/mdev.seq
+if [ -f /proc/sys/kernel/hotplug ];then
+	echo "/sbin/mdev" >/proc/sys/kernel/hotplug
+fi
+# creates f.e. /dev/root
+mdev -s
+
+# seed some random
+cat /etc/.rnd >/dev/urandom 2>&1
+
+# setup cfgfs
+[ -x /sbin/cfgfs ] && {
+	cfgfs setup
+	mount -o remount,ro /
+}
+
+# remount /tmp with smaller size
+size=$(cat /etc/tmpfs 2>/dev/null)
+[ -z $size ] && size=2048
+mount -o remount,nosuid,nodev,mode=1777,size=${size}k -t tmpfs tmpfs /tmp
+
+# create some useful directories in tmpfs
+mkdir -p /var/log
+mkdir -p /var/run
+mkdir -p /var/tmp
+touch /var/log/lastlog
+touch /var/log/wtmp
+
+HOSTNAME=
+[[ -s /etc/hostname ]] && HOSTNAME=$(cat /etc/hostname)
+HOSTNAME=${HOSTNAME%%.*}
+echo ${HOSTNAME:=openadk} >/proc/sys/kernel/hostname
+
+chown 0:0 /tmp; chmod 1777 /tmp

+ 37 - 0
package/simpleinit/src/pathnames.h

@@ -0,0 +1,37 @@
+/*
+ *	@(#)pathnames.h	5.3 (Berkeley) 5/9/89
+ *
+ * Heavily modified by poe@daimi.aau.dk for Linux
+ */
+
+#include <paths.h>
+
+#ifndef __STDC__
+# error "we need an ANSI compiler"
+#endif
+
+#ifndef SBINDIR
+# define SBINDIR "/sbin"
+#endif
+
+#define _PATH_BSHELL    "/bin/sh"
+#define _PATH_CSHELL    "/bin/csh"
+#define _PATH_TTY       "/dev/tty"
+#define TTYTYPES        "/etc/ttytype"
+#define SECURETTY       "/etc/securetty"
+
+#define	_PATH_HUSHLOGIN	".hushlogin"
+#define	_PATH_MOTDFILE	"/etc/motd"
+#define	_PATH_NOLOGIN	"/etc/nologin"
+
+#define _PATH_LOGIN	"/bin/login"
+#define _PATH_INITTAB	"/etc/inittab"
+#define _PATH_RC	"/etc/rc"
+#define _PATH_REBOOT	"/bin/reboot"
+#define _PATH_SINGLE	"/etc/singleboot"
+#define _PATH_SECURE	"/etc/securesingle"
+#define _PATH_USERTTY   "/etc/usertty"
+
+#define _PATH_CONFIGRC	"/etc/config/start"
+#define _PATH_CONFIGTAB	"/etc/config/inittab"
+#define _PATH_FIREWALL  "/bin/firewall"

+ 1046 - 0
package/simpleinit/src/simpleinit.c

@@ -0,0 +1,1046 @@
+/* simpleinit.c - poe@daimi.aau.dk */
+/* Version 1.21 */
+
+/* gerg@snapgear.com -- modified for direct console support DEC/1999 */
+/* davidm@snapgear.com -- modified for init.conf SEP/2004 */
+/* toby@snapgear.com -- allow the array of commands to grow as needed OCT/2004 */
+/* davidm@snapgear.com -- use dynamically allocated tables APR/2005 */
+
+#define _GNU_SOURCE	/* For crypt() and termios defines */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/termios.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <linux/version.h>
+#include <utmp.h>
+#include <errno.h>
+#include <time.h>
+#include <termios.h>
+#ifdef SHADOW_PWD
+#include <shadow.h>
+#endif
+
+#if __GNU_LIBRARY__ > 5
+#include <sys/reboot.h>
+#endif
+
+#include "pathnames.h"
+
+#define BUF_SIZ 100	/* max size of a line in inittab */
+#define NUMCMD 30	/* step size when realloc more space for the commands */
+#define NUMTOK 20	/* max number of tokens in inittab command */
+
+/* Threshold time for detecting "fast" spawning processes */
+static int testtime  = 90;
+/* Number of rapid respawns that counts as too fast */
+static int maxspawn  = 5;
+/* Maximum delay between runs */
+static int maxdelay  = 595;
+/* Time between successive runs of a process */
+static int delaytime = 5;
+
+#define MAXTRIES 3	/* number of tries allowed when giving the password */
+
+#define RUN_RC		/* Use a console if possible */
+
+#ifndef CTRL
+#define CTRL(X) ((X)&037)
+#endif
+
+#ifdef INCLUDE_TIMEZONE
+char tzone[BUF_SIZ];
+#endif
+/* #define DEBUGGING */
+
+/* Define this if you want init to ignore the termcap field in inittab for
+   console ttys. */
+/* #define SPECIAL_CONSOLE_TERM */
+
+struct initline {
+	pid_t	pid;
+	time_t	lastrun;
+	time_t	nextrun;
+	char	**toks;
+	short	delay;
+	char	tty[10];
+	char	termcap[30];
+	char	*line;
+	char	*fullline;
+	unsigned char xcnt;
+};
+
+struct initline *inittab;
+/* How many struct initline's will fit in the memory pointed to by inittab */
+int inittab_size = 0; 
+int numcmd;
+int stopped = 0;	/* are we stopped */
+int reload = 0;	/* are we stopped */
+int run_sigint_processing = 0;
+
+extern void spawn(int);
+extern void hup_handler();
+extern void reload_inittab();
+extern void read_inittab(void);
+static int  read_initfile(const char *);
+extern void tstp_handler();
+extern void int_handler();
+extern void sigint_processing();
+extern void cont_handler();
+extern void set_tz(void);
+extern void write_wtmp(void);
+extern void make_ascii_tty(void);
+extern void make_console(const char *);
+extern int boot_single(int singlearg, int argc, char *argv[]);
+#ifdef CONFIG_USER_INIT_CONF
+extern void load_init_conf(void);
+#endif
+
+/* Keep track of console device, if any... */
+#if LINUX_VERSION_CODE < 0x020100
+char	*console_device = NULL;
+int	console_baud = -1;
+#else
+int have_console = 0;
+#endif
+
+
+static void err(const char *s)
+{
+	struct iovec output[2];
+#if LINUX_VERSION_CODE < 0x020100
+	int fd;
+#endif
+	output[0].iov_base = "init: ";
+	output[0].iov_len = 6;
+	output[1].iov_base = (void *)s;
+	output[1].iov_len = strlen(s);
+#if LINUX_VERSION_CODE < 0x020100	
+	if (console_device == NULL) return;
+	if((fd = open(console_device, O_WRONLY)) < 0) return;
+	writev(fd, output, 2);
+	close(fd);
+#else
+	if (have_console)
+		writev(1, output, 2);
+#endif
+}
+
+void
+add_tok(struct initline *p, char *tok)
+{
+	int i;
+	for (i = 0; p->toks && p->toks[i]; i++)
+		;
+
+	/* allocate space for new entry and terminating NULL */
+	p->toks = (char **) realloc(p->toks, (i + 2) * sizeof(char *));
+	if (!p->toks) {
+		err("malloc failed\n");
+		_exit(1);
+	}
+	p->toks[i++] = tok;
+	p->toks[i] = NULL;
+}
+
+static void enter_single(void)
+{
+	pid_t pid;
+	char *av[2];
+
+    err("Booting to single user mode\n");
+    av[0] = _PATH_BSHELL;
+    av[1] = NULL;
+    if((pid = vfork()) == 0) {
+    extern char **environ;
+	/* the child */
+	execve(_PATH_BSHELL, av, environ);
+	err("exec of single user shell failed\n");
+	_exit(0);
+    } else if(pid > 0) {
+    int i;
+	while(wait(&i) != pid) /* nothing */;
+    } else if(pid < 0) {
+	err("fork of single user shell failed\n");
+    }
+    unlink(_PATH_SINGLE);
+}
+
+
+#if LINUX_VERSION_CODE < 0x020100
+static void
+set_console_baud(int baud)
+{
+	switch (baud) {
+	case 50:     console_baud = B50; break;
+	case 75:     console_baud = B75; break;
+	case 110:    console_baud = B110; break;
+	case 134:    console_baud = B134; break;
+	case 150:    console_baud = B150; break;
+	case 200:    console_baud = B200; break;
+	case 300:    console_baud = B300; break;
+	case 600:    console_baud = B600; break;
+	case 1200:   console_baud = B1200; break;
+	case 1800:   console_baud = B1800; break;
+	case 2400:   console_baud = B2400; break;
+	case 4800:   console_baud = B4800; break;
+	default:
+	case 9600:   console_baud = B9600; break;
+	case 19200:  console_baud = B19200; break;
+	case 38400:  console_baud = B38400; break;
+	case 57600:  console_baud = B57600; break;
+	case 115200: console_baud = B115200; break;
+	case 230400: console_baud = B230400; break;
+	case 460800: console_baud = B460800; break;
+	}
+}
+#endif
+
+static int do_command(const char *path, const char *filename, int dowait)
+{
+	pid_t pid, wpid;
+	int stat, st;
+	
+	if((pid = vfork()) == 0) {
+		/* the child */
+		char *argv[3];
+#ifdef INCLUDE_TIMEZONE
+		char tz[BUF_SIZ];
+#endif
+		char *env[3];
+
+		/* Use /dev/null for stdin */
+		close(0);
+		open("/dev/null", O_RDONLY);
+
+		argv[0] = (char *)path;
+		argv[1] = (char *)filename;
+		argv[2] = NULL;
+
+		env[0] = "PATH=/bin:/usr/bin:/etc:/sbin:/usr/sbin";
+#ifdef INCLUDE_TIMEZONE
+		strcpy(tz, "TZ=");
+		strcat(tz, tzone);
+		env[1] = tz;
+		env[2] = NULL;
+#else
+		env[1] = NULL;
+#endif
+
+		execve(path, argv, env);
+
+		err("exec rc failed\n");
+		_exit(2);
+	} else if(pid > 0) {
+		if (!dowait)
+			stat = 0;
+		else {
+			/* parent, wait till rc process dies before spawning */
+			while ((wpid = wait(&stat)) != pid)
+				if (wpid == -1 && errno == ECHILD) { /* see wait(2) manpage */
+					stat = 0;
+					break;
+				}
+		}
+	} else if(pid < 0) {
+		err("fork of rc shell failed\n");
+		stat = -1;
+	}
+	st = WEXITSTATUS(stat);
+	return st;
+}
+
+/*
+ * run /etc/rc. The environment is passed to the script, so the RC environment
+ * variable can be used to decide what to do. RC may be set from LILO.
+ */
+static int do_rc(void)
+{
+	int rc;
+
+	rc = do_command(_PATH_BSHELL, _PATH_RC, 1);
+	if (rc)
+		return(rc);
+#ifdef CONFIG_USER_INIT_RUN_FIREWALL
+	rc = do_command(_PATH_FIREWALL, "-i", 1);
+	if (rc)
+		err(_PATH_FIREWALL " failed!");
+#endif
+#ifdef CONFIG_USER_FLATFSD_FLATFSD
+	rc = do_command(_PATH_BSHELL, _PATH_CONFIGRC, 1);
+	if (rc)
+		err(_PATH_CONFIGRC " failed!");
+#endif
+#ifdef CONFIG_USER_INIT_RUN_FIREWALL
+	rc = do_command(_PATH_FIREWALL, NULL, 0);
+	if (rc)
+		err(_PATH_FIREWALL " failed!");
+#endif
+#ifdef INCLUDE_TIMEZONE
+	/* We read the timezone file here, because the flat file system
+	 * has probably been created by now.
+	 */
+	set_tz();
+#endif
+	return(0);
+}
+
+void respawn_children(int signo) {
+	int i, delta = -1;
+	time_t now;
+	alarm(0);
+	if ((now = time(NULL)) == 0) now = 1;
+	for(i = 0; i < numcmd; i++) {
+		if(inittab[i].pid < 0) {	/* Start jobs */
+			if(stopped)
+				inittab[i].pid = -1;
+			/*
+			** Do not spawn child from signal handler !
+			** SIGALRM would be blocked for the child
+			*/
+			else if (signo == 0)
+				spawn(i);
+		}
+		/* Check for naughty jobs */
+		if (inittab[i].nextrun > now) {
+		int d;
+			d = inittab[i].nextrun - now;
+			if (delta < 0 || d < delta)
+				delta = d;
+		}
+	}
+	if (delta > 0) {
+		alarm(delta);
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	int 	i;
+	struct sigaction sa;
+
+	/*
+	 * setup all the signal handlers here
+	 */
+
+	memset(&sa, 0, sizeof(sa));
+	/* sa.sa_flags = SA_RESETHAND we want to keep the handlers armed */
+
+	sa.sa_handler = tstp_handler;
+	sigaction(SIGTSTP, &sa, NULL);
+
+	sa.sa_handler = cont_handler;
+	sigaction(SIGCONT, &sa, NULL);
+
+	sa.sa_handler = int_handler;
+	sigaction(SIGINT, &sa, NULL);
+
+	sa.sa_handler = respawn_children;
+	sigaction(SIGALRM, &sa, NULL);
+
+	sa.sa_handler = hup_handler;
+	sigaction(SIGHUP, &sa, NULL);
+
+#if defined(CONSOLE_BAUD_RATE) && LINUX_VERSION_CODE < 0x020100
+	set_console_baud(CONSOLE_BAUD_RATE);
+#endif
+
+	/* 
+	 * start up in single user mode if /etc/singleboot exists or if
+	 * argv[1] is "single".
+	 */
+	if(boot_single(0, argc, argv)) enter_single();
+
+#ifdef RUN_RC
+	/* Register console if defined by boot */
+#if LINUX_VERSION_CODE < 0x020100
+	if ((console_device = getenv("CONSOLE"))) {
+	char	*sp;
+		unsetenv("CONSOLE");
+		if ((sp = strchr(console_device, ','))) {
+			*sp++ = 0;
+			set_console_baud(atoi(sp));
+		}
+	}
+
+	make_ascii_tty();
+#else
+{
+	struct stat st;
+
+	if (isatty(1)) {
+		have_console = 1;
+		make_ascii_tty();
+	} else if (fstat(1, &st) == -1 && errno == EBADF) {
+		/* No stdout, so send everything to /dev/null */
+		close(0); close(1); close(2);
+		open("/dev/null", O_RDWR);
+		dup(0);
+		dup(0);
+	}
+}
+#endif
+
+	/*If we get a SIGTSTP before multi-user mode, do nothing*/
+	while(stopped)	
+		pause();
+	if(do_rc() != 0 && boot_single(1, argc, argv) && !stopped)
+		enter_single();
+	while(stopped)	/*Also if /etc/rc fails & we get SIGTSTP*/
+		pause();
+#endif
+
+	/* initialize the array of commands */
+	inittab = (struct initline *)malloc(NUMCMD * sizeof(struct initline));
+	inittab_size = NUMCMD;
+
+	if (!inittab) {
+		/* failure case - what do you do if init fails? */
+		err("malloc failed");
+		_exit(1);
+	}
+
+	write_wtmp();	/* write boottime record */
+	read_inittab();
+
+#ifdef DEBUGGING
+	for(i = 0; i < numcmd; i++) {
+		char **p = inittab[i].toks;
+		printf("toks= %s %s %s %s\n",p[0], p[1], p[2], p[3]);
+		printf("tty= %s\n", inittab[i].tty);
+		printf("termcap= %s\n", inittab[i].termcap);
+	}
+	/*exit(0);*/
+#endif
+
+#if LINUX_VERSION_CODE < 0x020100
+	for(i = 0; i < getdtablesize(); i++) close(i);
+#else
+	/* Always leave 0, 1, and 2 connected (to /dev/null) for the child process */
+	for(i = 3; i < getdtablesize(); i++) close(i);
+#endif
+
+	for (;;) {
+		pid_t	pid;
+		int	vec;
+
+		if (run_sigint_processing) {
+			run_sigint_processing = 0;
+			sigint_processing();
+		}
+
+		respawn_children(0);
+
+		if (reload) {
+			reload = 0;
+			reload_inittab();
+			continue; /* process all reloads before waiting */
+		}
+
+		pid = wait(&vec);
+		alarm(0);
+
+		/* clear utmp entry, and append to wtmp if possible */
+#if 0		/* DAVIDM */
+		{
+		    struct utmp *ut;
+		    int ut_fd;
+
+		    utmpname(_PATH_UTMP);
+		    setutent();
+		    while((ut = getutent())) {
+			if(ut->ut_pid == pid) {
+			    time(&ut->ut_time);
+			    bzero(&ut->ut_user, UT_NAMESIZE);
+			    bzero(&ut->ut_host, sizeof(ut->ut_host));
+			    ut->ut_type = DEAD_PROCESS;
+			    ut->ut_pid = 0;
+			    ut->ut_addr = 0;
+			    endutent();
+			    pututline(ut);
+			    if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
+				flock(ut_fd, LOCK_EX|LOCK_NB);
+				write(ut_fd, (const void *)ut, sizeof(struct utmp));
+				flock(ut_fd, LOCK_UN|LOCK_NB);
+				close(ut_fd);
+			    }
+			    break;
+			}
+		    }
+		    endutent();
+		}
+#endif
+
+		for(i = 0; i < numcmd; i++) {
+			if(pid == inittab[i].pid) {
+				inittab[i].pid = -1;
+			}
+		}
+	}
+}	
+
+
+/*
+ * return true if we should boot up in singleuser mode. If argv[i] is 
+ * "single" or the file /etc/singleboot exists, then singleuser mode should
+ * be entered. If /etc/securesingle exists ask for root password first.
+ */
+int boot_single(int singlearg, int argc, char *argv[])
+{
+	char *pass, *rootpass = NULL;
+	struct passwd *pwd;
+	int i;
+
+	for(i = 1; i < argc; i++) {
+	    if(argv[i] && !strcmp(argv[i], "single")) singlearg = 1;
+	}
+	return 0;
+}
+
+void spawn(int i)
+{
+	pid_t pid;
+	int j;
+	time_t t;
+	struct initline *it;
+	char buf[150];
+	
+	it = inittab + i;
+
+	t = time(NULL);
+	/* Check for held process */
+	if ((unsigned long)(it->nextrun - t - 1) < maxdelay)
+		return;
+	if (it->lastrun + testtime > t) {	/* Respawning quickly */
+		if (it->xcnt < 0xff)
+			it->xcnt++;
+	} else {				/* Normal respawning */
+		it->xcnt = 0;
+		it->lastrun = t;
+		it->delay = delaytime;
+	}
+	if (it->xcnt >= maxspawn) {		/* Too many too quickly */
+		strcpy(buf, it->toks[0]);
+		strcat(buf, " respawning too fast\n");
+		err(buf);
+		it->pid = -1;
+		if (it->delay >= maxdelay)
+			it->delay = maxdelay;
+		else if (it->delay < delaytime)
+			it->delay = delaytime;
+		else if((it->delay *= 2) > maxdelay)
+			it->delay = maxdelay;
+		it->nextrun = t + it->delay;
+		/* Fiddle with the tracking vars to ensure that only
+		 * one attempt is made to run this next time around.
+		 */
+		it->lastrun = it->nextrun;
+		it->xcnt -= 2;
+		return;
+	}
+	it->nextrun = t + delaytime;
+	
+	if((pid = vfork()) < 0) {
+		it->pid = -1;
+		err("fork failed\n");
+		return;
+	}
+	if(pid) {
+		/* this is the parent */
+		it->pid = pid;
+		return;
+	} else {
+		/* this is the child */
+		char term[40];
+		char *prog;
+#ifdef INCLUDE_TIMEZONE
+		char tz[BUF_SIZ];
+#endif
+		char *env[4];
+		
+		setsid();
+
+		/* Close everything other than 0, 1 and 2 */
+		for(j = 3; j < getdtablesize(); j++) {
+			close(j);
+		}
+		/* Now set up 0, 1 and 2 */
+		make_console(it->tty);
+
+		strcpy(term, "TERM=");
+		strcat(term, it->termcap);
+		env[0] = term;
+		env[1] = "PATH=/bin:/usr/bin:/etc:/sbin:/usr/sbin";
+#ifdef INCLUDE_TIMEZONE
+		strcpy(tz, "TZ=");
+		strcat(tz, tzone);
+		env[2] = tz;
+		env[3] = NULL;
+#else
+		env[2] = NULL;
+#endif
+
+		prog = it->toks[0];
+		if (*prog == '-' && *(prog+1))
+			prog++;
+		execve(prog, it->toks, env);
+		strcpy(buf, it->toks[0]);
+		strcat(buf, " exec failed\n");
+		err(buf);
+		_exit(1);
+	}
+}
+
+static void init_itab(struct initline *p) {
+	bzero(p, sizeof(struct initline));
+	p->pid = -1;
+	p->nextrun = time(NULL);
+}
+
+static void clear_itab(struct initline *p) {
+	if (p->line)
+		free(p->line);
+	if (p->fullline)
+		free(p->fullline);
+	if (p->toks)
+		free(p->toks);
+	init_itab(p);
+}
+
+void read_inittab(void)
+{
+	int i;
+
+	/*
+	 * free any old data and start again
+	 */
+	for (i = 0; i < numcmd; i++)
+		clear_itab(&inittab[i]);
+	numcmd = 0;
+
+	/* Fake an inittab entry if boot console defined */
+#ifdef CONFIG_USER_INIT_CONSOLE_SH
+#if LINUX_VERSION_CODE < 0x020100
+	if (console_device && strcmp(console_device, "/dev/null"))
+#else
+	if (have_console)
+#endif
+	{
+	struct initline *p;
+		p = inittab + numcmd++;
+		init_itab(p);
+		p->fullline = strdup("console");
+		strcpy(p->tty, "console");
+		strcpy(p->termcap, "linux");
+		add_tok(p, "-/bin/sh");
+	}
+#endif
+
+	i = 0;
+	if (read_initfile(_PATH_INITTAB) == 0)
+		i++;
+
+#ifdef CONFIG_USER_FLATFSD_FLATFSD
+	if (read_initfile(_PATH_CONFIGTAB) == 0)
+		i++;
+#endif
+
+	if (i == 0) {
+		err("Failed to open " _PATH_INITTAB
+#ifdef CONFIG_USER_FLATFSD_FLATFSD
+				" or " _PATH_CONFIGTAB
+#endif
+				"."
+				);
+	}
+
+#ifdef CONFIG_USER_INIT_CONF
+	load_init_conf();
+#endif
+
+	/* if needed, shrink the array using realloc -
+	 * must be done here so that we include the results of all init files
+	 * when calculating number of commands */
+	if ((numcmd + 2) < (inittab_size - NUMCMD)) {
+		/* round up from numcmd to the nearest multiple of NUMCMD */
+		inittab_size = ((numcmd + 2) / NUMCMD + 1) * NUMCMD;
+		inittab = realloc(inittab, inittab_size * sizeof(struct initline));
+		if (!inittab) {
+			/* failure case - what do you do if init fails? */
+			err("malloc failed");
+			_exit(1);
+		}
+	}
+
+	if (numcmd == 0)
+		_exit(1);
+}
+
+static int
+read_initfile(const char *initfile)
+{
+	struct initline *p;
+	FILE *f;
+	char *buf = NULL;
+	size_t buf_len = 0;
+	int i,j,k;
+	char *ptr, *getty;
+#ifdef SPECIAL_CONSOLE_TERM
+	char tty[50];
+	struct stat stb;
+	char *termenv;
+
+	termenv = getenv("TERM");	/* set by kernel */
+#endif
+
+	i = numcmd;
+
+	if (!(f = fopen(initfile, "r")))
+		return 1;
+
+	while(!feof(f)) {
+		if (i+2 == inittab_size) {
+			/* need to realloc inittab */
+			inittab_size += NUMCMD;
+			inittab = realloc(inittab, inittab_size * sizeof(struct initline));
+			if (!inittab) {
+				/* failure case - what do you do if init fails? */
+				err("malloc failed");
+				_exit(1);
+			}
+		}
+		if (getline(&buf, &buf_len, f) == -1) break;
+
+		for(k = 0; k < buf_len && buf[k]; k++) {
+			if(buf[k] == '#') { 
+				buf[k] = '\0'; break; 
+			}
+		}
+
+		if(buf[0] == '\0' || buf[0] == '\n') continue;
+
+		p = inittab + i;
+		init_itab(p);
+		p->line = strdup(buf);
+		p->fullline = strdup(buf);
+		if (!p->line || !p->fullline) {
+			err("Not memory to allocate inittab entry");
+			clear_itab(p);
+			continue;
+		}
+		ptr = strtok(p->line, ":");
+		if (!ptr) {
+			err("Missing TTY/ID field in inittab");
+			clear_itab(p);
+			continue;
+		}
+		strncpy(p->tty, ptr, 9);
+		//p->tty[9] = '\0';
+		ptr = strtok(NULL, ":");
+		if (!ptr) {
+			err("Missing TERMTYPE field in inittab");
+			clear_itab(p);
+			continue;
+		}
+		strncpy(p->termcap, ptr, 29);
+		//p->termcap[29] = '\0';
+
+		getty = strtok(NULL, " \t\n");
+		if (!getty) {
+			err("Missing PROCESS field in inittab");
+			clear_itab(p);
+			continue;
+		}
+		add_tok(p, getty);
+		j = 1;
+		while((ptr = strtok(NULL, " \t\n")))
+			add_tok(p, ptr);
+
+#ifdef SPECIAL_CONSOLE_TERM
+		/* special-case termcap for the console ttys */
+		strcpy(tty, "/dev/");
+		strcat(tty, p->tty);
+		if(!termenv || stat(tty, &stb) < 0) {
+			err("no TERM or cannot stat tty\n");
+		} else {
+			/* is it a console tty? */
+			if(major(stb.st_rdev) == 4 && minor(stb.st_rdev) < 64) {
+				strncpy(p->termcap, termenv, 30);
+				p->termcap[29] = 0;
+			}
+		}
+#endif
+
+		i++;
+	}
+
+	if (buf)
+		free(buf);
+	
+	fclose(f);
+
+	numcmd = i;
+	return 0;
+}
+
+void hup_handler()
+{
+	reload = 1;
+}
+
+void reload_inittab()
+{
+	int i;
+	int oldnum;
+	char ** saveline = (char **) malloc(inittab_size * sizeof(char *));
+	pid_t * savepid = (pid_t*) malloc(inittab_size * sizeof(pid_t));
+
+	if (!saveline || !savepid) {
+		/* another failure case - what DO you do if init fails */
+		err("malloc failed");
+		_exit(1);
+	}
+
+	for (i=0; i<numcmd; i++) {
+		savepid[i] = inittab[i].pid;
+		saveline[i] = strdup(inittab[i].fullline);
+		if (!saveline[i]) {
+			err("malloc failed");
+			_exit(1);
+		}
+	}
+
+	oldnum = numcmd;		
+	read_inittab();
+
+	/* See which ones still exist */
+	for(i = 0; i < numcmd; i++) {
+		int j;
+		for(j = 0; j < oldnum; j++) {
+			if(strcmp(saveline[j], inittab[i].fullline) == 0) {
+				inittab[i].pid = savepid[j];
+				savepid[j] = -1;
+				break;
+			}
+		}
+	}
+
+	/* Kill off processes no longer needed and free memory */
+	for(i = 0; i < oldnum; i++) {
+		if (savepid[i] > 1)
+			kill(savepid[i], SIGTERM);
+		free(saveline[i]);
+	}
+
+	free(saveline);
+	free(savepid);
+}
+
+void tstp_handler()
+{
+	stopped++;
+}
+
+void cont_handler()
+{
+	stopped = 0;
+}
+
+void int_handler()
+{
+	run_sigint_processing = 1;
+}
+
+void sigint_processing()
+{
+	/*
+	 * After Linux 0.96b PL1, we get a SIGINT when
+	 * the user presses Ctrl-Alt-Del...
+	 */
+
+	int pid;
+	
+	sync();
+	sync();
+	if((pid = vfork()) == 0) {
+	char *av[2];
+	extern char **environ;
+		/* reboot properly... */
+		av[0] = _PATH_REBOOT;
+		av[1] = NULL;
+		
+		execve(_PATH_REBOOT, av, environ);
+#if __GNU_LIBRARY__ > 5
+		reboot(0x1234567);
+#else
+		reboot(0xfee1dead, 672274793, 0x1234567);
+#endif
+		_exit(2);
+	} else if(pid < 0) {
+		/* fork failed, try the hard way... */
+#if __GNU_LIBRARY__ > 5
+		reboot(0x1234567);
+#else
+		reboot(0xfee1dead, 672274793, 0x1234567);
+#endif
+	}
+}
+
+#ifdef INCLUDE_TIMEZONE
+void set_tz(void)
+{
+	FILE *f;
+	int len;
+
+	if((f = fopen("/etc/config/TZ", "r")) == NULL &&
+	   (f = fopen("/etc/TZ", "r")) == NULL)
+		return;
+	fgets(tzone, BUF_SIZ-2, f);
+	fclose(f);
+	if((len=strlen(tzone)) < 2)
+		return;
+	tzone[len-1] = 0; /* get rid of the '\n' */
+	setenv("TZ", tzone, 0);
+}
+#endif
+
+#ifdef CONFIG_USER_INIT_CONF
+void load_init_conf(void)
+{
+	char line[BUF_SIZ];
+	FILE *f;
+
+	if ((f = fopen("/etc/config/init.conf", "r")) == NULL &&
+			(f = fopen("/etc/init.conf", "r")) == NULL)
+		return;
+	while (fgets(line, sizeof(line) - 2, f)) {
+		if (strncasecmp(line, "delaytime=", 10) == 0)
+			delaytime = atoi(line + 10);
+		if (strncasecmp(line, "maxdelay=", 9) == 0)
+			maxdelay = atoi(line + 9);
+		if (strncasecmp(line, "maxspawn=", 9) == 0)
+			maxspawn = atoi(line + 9);
+		if (strncasecmp(line, "testtime=", 9) == 0)
+			testtime = atoi(line + 9);
+	}
+	fclose(f);
+}
+#endif
+
+void write_wtmp(void)
+{
+#if 0
+    int fd;
+    struct utmp ut;
+    
+    bzero((char *)&ut, sizeof(ut));
+    strcpy(ut.ut_line, "~");
+    bzero(ut.ut_name, sizeof(ut.ut_name));
+    time(&ut.ut_time);
+    ut.ut_type = BOOT_TIME;
+    
+    if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) >= 0) {
+	flock(fd, LOCK_EX|LOCK_NB); /* make sure init won't hang */
+	write(fd, (char *)&ut, sizeof(ut));
+	flock(fd, LOCK_UN|LOCK_NB);
+	close(fd);
+    }
+#endif
+}     
+
+void make_ascii_tty(void)
+{
+	struct termios tty;
+	const char *pt;
+
+	if (tcgetattr(0, &tty) < 0)
+		return;
+
+	tty.c_iflag &= ~(INLCR|IGNCR|IUCLC);
+	tty.c_iflag |= ICRNL;
+	tty.c_oflag &= ~(OCRNL|OLCUC|ONOCR|ONLRET|OFILL);
+	tty.c_oflag |= OPOST|ONLCR;
+	tty.c_cflag |= CLOCAL;
+	tty.c_lflag  = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE;
+#ifdef IEXTEN
+	tty.c_lflag  |= IEXTEN;
+#endif
+
+#if LINUX_VERSION_CODE < 0x020100
+	if (console_baud != -1)
+		cfsetospeed(&tty, console_baud);
+#endif
+
+	tty.c_cc[VINTR]  = CTRL('C');
+	tty.c_cc[VQUIT]  = CTRL('\\');
+	tty.c_cc[VERASE] = CTRL('H'); /*127*/
+	tty.c_cc[VKILL]  = CTRL('U'); /*Changed from non-standard ^X*/
+	tty.c_cc[VEOF]   = CTRL('D');
+	tty.c_cc[VTIME]  = 0;
+	tty.c_cc[VMIN]   = 1;
+	tty.c_cc[VSTART] = CTRL('Q');
+	tty.c_cc[VSTOP]  = CTRL('S');
+	tty.c_cc[VSUSP]  = CTRL('Z');
+#ifdef VWERASE
+	tty.c_cc[VWERASE]  = CTRL('W');
+#endif
+	/* Pick up simple environment setting of VERASE.
+	 * Useful for setting on kernel command line.
+	 * e.g. TTYERASE=^?
+	 */
+	pt = getenv("TTYERASE");
+	if (pt && pt[0] == '^' && pt[1]) {
+		tty.c_cc[VERASE] = (pt[1] == '?') ? 127 : CTRL(pt[1]);
+	}
+
+	tcsetattr(0, TCSANOW, &tty);
+}
+
+void make_console(const char *tty)
+{
+	char devname[32];
+
+	close(0); close(1); close(2);
+
+	if (tty && *tty) {
+#if LINUX_VERSION_CODE < 0x020100
+	    /*
+	     *	until we get proper console support under 2.0
+	     */
+	    if (strcmp(tty, "console") == 0) {
+		    strcpy(devname, console_device);
+	    }
+	    else
+#endif
+	    {
+		    strcpy(devname, "/dev/");
+		    strcat(devname, tty);
+	    }
+
+	    /* Try to open the specified console */
+	    if (open(devname, O_RDWR|O_NONBLOCK) >= 0) {
+		fcntl(0, F_SETFL, 0);
+		dup(0); 
+		dup(0);
+		make_ascii_tty();
+		ioctl(0, TIOCSCTTY, (char*)0);
+		return;
+	    }
+	}
+
+	/* No go, so send to /dev/null */
+	open("/dev/null", O_RDWR|O_NONBLOCK);
+	dup(0);
+	dup(0);
+}

+ 2 - 0
target/config/Config.in

@@ -16,6 +16,8 @@ config ADK_TARGET_TOOLCHAIN
 
 config ADK_TARGET_UCLINUX
 	select ADK_TARGET_USE_STATIC_LIBS
+	select ADK_PACKAGE_SASH
+	select ADK_PACKAGE_SIMPLEINIT
 	boolean
 
 config ADK_TARGET_QEMU

+ 14 - 2
target/config/Config.in.runtime

@@ -150,7 +150,7 @@ config ADK_RUNTIME_KBD_LAYOUT
 
 choice
 prompt "initial login shell for the root user"
-default ADK_ROOTSH_HUSH if ADK_TARGET_UCLINUX
+default ADK_ROOTSH_SASH if ADK_TARGET_UCLINUX
 default ADK_ROOTSH_MKSH
 
 config ADK_ROOTSH_MKSH
@@ -160,6 +160,12 @@ config ADK_ROOTSH_MKSH
 	  Use mksh (a Korn Shell variant) as standard login shell
 	  for the superuser.
 
+config ADK_ROOTSH_SASH
+	select ADK_PACKAGE_SASH if !ADK_TOOLCHAIN_ONLY
+	bool "sash (Shell compatible with non-MMU systems)"
+	help
+	  standalone shell.
+
 config ADK_ROOTSH_HUSH
 	select ADK_PACKAGE_HUSH if !ADK_TOOLCHAIN_ONLY
 	bool "hush (Shell compatible with non-MMU systems)"
@@ -197,7 +203,7 @@ endchoice
 
 choice
 prompt "system /bin/sh (POSIX script shell)"
-default ADK_BINSH_HUSH if ADK_TARGET_UCLINUX
+default ADK_BINSH_SASH if ADK_TARGET_UCLINUX
 default ADK_BINSH_MKSH
 
 config ADK_BINSH_MKSH
@@ -207,6 +213,12 @@ config ADK_BINSH_MKSH
 	  Use mksh (a Korn Shell variant) as system shell, which is
 	  both small and powerful, so quite suited for this task.
 
+config ADK_BINSH_SASH
+	select ADK_PACKAGE_SASH if !ADK_TOOLCHAIN_ONLY
+	bool "sash (Standalone Shell)"
+	help
+	  hush shell.
+
 config ADK_BINSH_HUSH
 	select ADK_PACKAGE_HUSH if !ADK_TOOLCHAIN_ONLY
 	bool "hush (busybox)"

+ 2 - 3
target/m68k/uclibc-ng.config

@@ -54,9 +54,8 @@ UCLIBC_HAS_THREADS=y
 # PTHREADS_DEBUG_SUPPORT is not set
 UCLIBC_HAS_SYSLOG=y
 UCLIBC_HAS_LFS=y
-MALLOC=y
-# MALLOC_SIMPLE is not set
-MALLOC_GLIBC_COMPAT=y
+MALLOC_SIMPLE=y
+# MALLOC_GLIBC_COMPAT is not set
 UCLIBC_HAS_OBSTACK=y
 UCLIBC_DYNAMIC_ATEXIT=y
 UCLIBC_SUSV2_LEGACY=y

+ 43 - 38
target/m68k/uclibc.config

@@ -1,19 +1,32 @@
 #
-# Automatically generated file; DO NOT EDIT.
-# uClibc-ng 1.0.0 C Library Configuration
+# Automatically generated make config: don't edit
+# Version: 0.9.33.2
+# Mon Aug 18 18:48:15 2014
 #
 # TARGET_alpha is not set
-# TARGET_arc is not set
 # TARGET_arm is not set
+# TARGET_avr32 is not set
 # TARGET_bfin is not set
+# TARGET_c6x is not set
+# TARGET_cris is not set
+# TARGET_e1 is not set
+# TARGET_frv is not set
+# TARGET_h8300 is not set
+# TARGET_hppa is not set
 # TARGET_i386 is not set
+# TARGET_i960 is not set
+# TARGET_ia64 is not set
 TARGET_m68k=y
-# TARGET_metag is not set
 # TARGET_microblaze is not set
 # TARGET_mips is not set
+# TARGET_nios is not set
+# TARGET_nios2 is not set
 # TARGET_powerpc is not set
 # TARGET_sh is not set
+# TARGET_sh64 is not set
 # TARGET_sparc is not set
+# TARGET_v850 is not set
+# TARGET_vax is not set
 # TARGET_x86_64 is not set
 # TARGET_xtensa is not set
 
@@ -23,58 +36,48 @@ TARGET_m68k=y
 TARGET_ARCH="m68k"
 FORCE_OPTIONS_FOR_ARCH=y
 TARGET_SUBARCH=""
-
-#
-# Using ELF file format
-#
-ARCH_HAS_DEPRECATED_SYSCALLS=y
+# UCLIBC_FORMAT_ELF is not set
+# UCLIBC_FORMAT_FDPIC_ELF is not set
+# UCLIBC_FORMAT_DSBT_ELF is not set
+# UCLIBC_FORMAT_FLAT is not set
+UCLIBC_FORMAT_FLAT_SEP_DATA=y
+# UCLIBC_FORMAT_SHARED_FLAT is not set
 ARCH_BIG_ENDIAN=y
 
 #
 # Using Big Endian
 #
-ARCH_HAS_MMU=y
-ARCH_USE_MMU=y
+# ARCH_HAS_MMU is not set
 UCLIBC_HAS_FLOATS=y
-UCLIBC_HAS_FPU=y
+# UCLIBC_HAS_FPU is not set
+UCLIBC_HAS_SOFT_FLOAT=y
 DO_C99_MATH=y
 # DO_XSI_MATH is not set
 UCLIBC_HAS_FENV=y
 UCLIBC_HAS_LONG_DOUBLE_MATH=y
 KERNEL_HEADERS=""
+UCLIBC_UCLINUX_BROKEN_MUNMAP=y
 HAVE_DOT_CONFIG=y
 
 #
 # General Library Settings
 #
 DOPIC=y
-HAVE_SHARED=y
-# FORCE_SHAREABLE_TEXT_SEGMENTS is not set
-LDSO_LDD_SUPPORT=y
-LDSO_CACHE_SUPPORT=y
-LDSO_PRELOAD_ENV_SUPPORT=y
-# LDSO_PRELOAD_FILE_SUPPORT is not set
-LDSO_BASE_FILENAME="ld.so"
-# LDSO_STANDALONE_SUPPORT is not set
-# LDSO_PRELINK_SUPPORT is not set
-UCLIBC_STATIC_LDCONFIG=y
-LDSO_RUNPATH=y
-LDSO_SAFE_RUNPATH=y
-LDSO_SEARCH_INTERP_PATH=y
-LDSO_LD_LIBRARY_PATH=y
-# LDSO_NO_CLEANUP is not set
-# LDSO_GNU_HASH_SUPPORT is not set
+ARCH_HAS_NO_SHARED=y
+ARCH_HAS_NO_LDSO=y
+UCLIBC_CTOR_DTOR=y
 HAS_NO_THREADS=y
+# LINUXTHREADS_OLD is not set
+# LINUXTHREADS_NEW is not set
 # UCLIBC_HAS_THREADS_NATIVE is not set
 UCLIBC_HAS_SYSLOG=y
 UCLIBC_HAS_LFS=y
-# MALLOC is not set
+MALLOC=y
 # MALLOC_SIMPLE is not set
-MALLOC_STANDARD=y
+# MALLOC_STANDARD is not set
 MALLOC_GLIBC_COMPAT=y
-UCLIBC_HAS_OBSTACK=y
 UCLIBC_DYNAMIC_ATEXIT=y
-UCLIBC_SUSV2_LEGACY=y
+# COMPAT_ATEXIT is not set
 UCLIBC_SUSV3_LEGACY=y
 # UCLIBC_SUSV3_LEGACY_MACROS is not set
 UCLIBC_SUSV4_LEGACY=y
@@ -104,6 +107,8 @@ UCLIBC_GRP_BUFFER_SIZE=256
 #
 # Support various families of functions
 #
+UCLIBC_LINUX_MODULE_26=y
+# UCLIBC_LINUX_MODULE_24 is not set
 UCLIBC_LINUX_SPECIFIC=y
 UCLIBC_HAS_GNU_ERROR=y
 UCLIBC_BSD_SPECIFIC=y
@@ -125,6 +130,7 @@ UCLIBC_HAS_NETWORK_SUPPORT=y
 UCLIBC_HAS_SOCKET=y
 UCLIBC_HAS_IPV4=y
 UCLIBC_HAS_IPV6=y
+# UCLIBC_HAS_RPC is not set
 UCLIBC_USE_NETLINK=y
 UCLIBC_SUPPORT_AI_ADDRCONFIG=y
 UCLIBC_HAS_BSD_RES_CLOSE=y
@@ -149,6 +155,8 @@ UCLIBC_HAS_WCHAR=y
 UCLIBC_HAS_HEXADECIMAL_FLOATS=y
 UCLIBC_HAS_GLIBC_CUSTOM_PRINTF=y
 UCLIBC_PRINTF_SCANF_POSITIONAL_ARGS=9
+# UCLIBC_HAS_SCANF_GLIBC_A_FLAG is not set
+# UCLIBC_HAS_STDIO_BUFSIZ_NONE is not set
 # UCLIBC_HAS_STDIO_BUFSIZ_256 is not set
 # UCLIBC_HAS_STDIO_BUFSIZ_512 is not set
 # UCLIBC_HAS_STDIO_BUFSIZ_1024 is not set
@@ -178,7 +186,9 @@ UCLIBC_HAS_GNU_GETSUBOPT=y
 # Big and Tall
 #
 UCLIBC_HAS_REGEX=y
+# UCLIBC_HAS_REGEX_OLD is not set
 UCLIBC_HAS_FNMATCH=y
+# UCLIBC_HAS_FNMATCH_OLD is not set
 UCLIBC_HAS_WORDEXP=y
 UCLIBC_HAS_NFTW=y
 UCLIBC_HAS_FTW=y
@@ -199,10 +209,7 @@ HARDWIRED_ABSPATH=y
 # Security options
 #
 UCLIBC_HAS_ARC4RANDOM=y
-# ARC4RANDOM_USES_NODEV is not set
 # UCLIBC_HAS_SSP is not set
-UCLIBC_BUILD_RELRO=y
-# UCLIBC_BUILD_NOW is not set
 UCLIBC_BUILD_NOEXECSTACK=y
 
 #
@@ -213,10 +220,8 @@ UCLIBC_EXTRA_CFLAGS=""
 # DODEBUG is not set
 # DOSTRIP is not set
 # DOASSERTS is not set
-# SUPPORT_LD_DEBUG is not set
-# SUPPORT_LD_DEBUG_EARLY is not set
 # UCLIBC_MALLOC_DEBUGGING is not set
-# UCLIBC_HAS_BACKTRACE is not set
 WARNINGS="-Wall"
 EXTRA_WARNINGS=y
 # DOMULTI is not set
+# UCLIBC_MJN3_ONLY is not set