From 8e2707b81b0c90295c9fdf92a576925442d22147 Mon Sep 17 00:00:00 2001
From: Waldemar Brodkorb <wbx@openadk.org>
Date: Wed, 20 Aug 2014 21:11:13 +0200
Subject: add sash, simpleinit and a uclibc config for nonmmu case

---
 package/sash/Makefile             |   25 +
 package/sash/files/rc             |   47 ++
 package/sash/src/Makefile         |   38 ++
 package/sash/src/README           |    6 +
 package/sash/src/cmd_uclinux.c    |  127 +++++
 package/sash/src/cmds.c           |  899 +++++++++++++++++++++++++++++
 package/sash/src/cp.c             |   27 +
 package/sash/src/date.c           |   54 ++
 package/sash/src/df.c             |   55 ++
 package/sash/src/free.c           |   44 ++
 package/sash/src/hexdump.c        |  127 +++++
 package/sash/src/hostname.c       |   24 +
 package/sash/src/libsash/Makefile |   20 +
 package/sash/src/libsash/utils.c  |  706 +++++++++++++++++++++++
 package/sash/src/ls.c             |  311 +++++++++++
 package/sash/src/ps.c             |  317 +++++++++++
 package/sash/src/reboot.c         |   93 +++
 package/sash/src/sash.c           | 1118 +++++++++++++++++++++++++++++++++++++
 package/sash/src/sash.h           |   70 +++
 package/sash/src/shutdown.c       |   75 +++
 20 files changed, 4183 insertions(+)
 create mode 100644 package/sash/Makefile
 create mode 100755 package/sash/files/rc
 create mode 100644 package/sash/src/Makefile
 create mode 100644 package/sash/src/README
 create mode 100644 package/sash/src/cmd_uclinux.c
 create mode 100644 package/sash/src/cmds.c
 create mode 100644 package/sash/src/cp.c
 create mode 100644 package/sash/src/date.c
 create mode 100644 package/sash/src/df.c
 create mode 100644 package/sash/src/free.c
 create mode 100644 package/sash/src/hexdump.c
 create mode 100644 package/sash/src/hostname.c
 create mode 100644 package/sash/src/libsash/Makefile
 create mode 100644 package/sash/src/libsash/utils.c
 create mode 100644 package/sash/src/ls.c
 create mode 100644 package/sash/src/ps.c
 create mode 100644 package/sash/src/reboot.c
 create mode 100644 package/sash/src/sash.c
 create mode 100644 package/sash/src/sash.h
 create mode 100644 package/sash/src/shutdown.c

(limited to 'package/sash')

diff --git a/package/sash/Makefile b/package/sash/Makefile
new file mode 100644
index 000000000..16a767a70
--- /dev/null
+++ b/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
diff --git a/package/sash/files/rc b/package/sash/files/rc
new file mode 100755
index 000000000..33d60a4b6
--- /dev/null
+++ b/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
diff --git a/package/sash/src/Makefile b/package/sash/src/Makefile
new file mode 100644
index 000000000..90d6adf99
--- /dev/null
+++ b/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
+
diff --git a/package/sash/src/README b/package/sash/src/README
new file mode 100644
index 000000000..191c7de63
--- /dev/null
+++ b/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.
+
diff --git a/package/sash/src/cmd_uclinux.c b/package/sash/src/cmd_uclinux.c
new file mode 100644
index 000000000..bf66c4529
--- /dev/null
+++ b/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);
+	}
+}
diff --git a/package/sash/src/cmds.c b/package/sash/src/cmds.c
new file mode 100644
index 000000000..d822d9f6d
--- /dev/null
+++ b/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 */
diff --git a/package/sash/src/cp.c b/package/sash/src/cp.c
new file mode 100644
index 000000000..11d03f74c
--- /dev/null
+++ b/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);
+	}
+}
diff --git a/package/sash/src/date.c b/package/sash/src/date.c
new file mode 100644
index 000000000..c7317998b
--- /dev/null
+++ b/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);
+}
+
+
+
diff --git a/package/sash/src/df.c b/package/sash/src/df.c
new file mode 100644
index 000000000..99ac2217e
--- /dev/null
+++ b/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);
+}
+
diff --git a/package/sash/src/free.c b/package/sash/src/free.c
new file mode 100644
index 000000000..281f10995
--- /dev/null
+++ b/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);
+}
+
diff --git a/package/sash/src/hexdump.c b/package/sash/src/hexdump.c
new file mode 100644
index 000000000..f12f62494
--- /dev/null
+++ b/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);
+}
+
diff --git a/package/sash/src/hostname.c b/package/sash/src/hostname.c
new file mode 100644
index 000000000..8841e7fa3
--- /dev/null
+++ b/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);
+	}
+}
diff --git a/package/sash/src/libsash/Makefile b/package/sash/src/libsash/Makefile
new file mode 100644
index 000000000..03a2fafae
--- /dev/null
+++ b/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
diff --git a/package/sash/src/libsash/utils.c b/package/sash/src/libsash/utils.c
new file mode 100644
index 000000000..a05b33aee
--- /dev/null
+++ b/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 */
diff --git a/package/sash/src/ls.c b/package/sash/src/ls.c
new file mode 100644
index 000000000..9b337aeaa
--- /dev/null
+++ b/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 */
diff --git a/package/sash/src/ps.c b/package/sash/src/ps.c
new file mode 100644
index 000000000..ed4226cc9
--- /dev/null
+++ b/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);
+}
+
diff --git a/package/sash/src/reboot.c b/package/sash/src/reboot.c
new file mode 100644
index 000000000..8342ec992
--- /dev/null
+++ b/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 */
+}
+
diff --git a/package/sash/src/sash.c b/package/sash/src/sash.c
new file mode 100644
index 000000000..24fbddbf7
--- /dev/null
+++ b/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